[TD]refactor QGIFace into 2 files
- add PATPathMaker for the PAT hatch spec processing methods - remove PAT methods from QGIFace - remove obsolete conversion of svg hatch to pixmap
This commit is contained in:
@@ -235,6 +235,8 @@ SET(TechDrawGui_SRCS
|
||||
Widgets/CompassWidget.h
|
||||
Widgets/VectorEditWidget.cpp
|
||||
Widgets/VectorEditWidget.h
|
||||
PATPathMaker.cpp
|
||||
PATPathMaker.h
|
||||
)
|
||||
|
||||
SET(TechDrawGuiView_SRCS
|
||||
|
||||
279
src/Mod/TechDraw/Gui/PATPathMaker.cpp
Normal file
279
src/Mod/TechDraw/Gui/PATPathMaker.cpp
Normal file
@@ -0,0 +1,279 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2023 WandererFan <wandererfan@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
#ifndef _PreComp_
|
||||
# include <cmath>
|
||||
# include <QPainter>
|
||||
# include <QPainterPath>
|
||||
# include <QPointF>
|
||||
# include <QRectF>
|
||||
# include <QTransform>
|
||||
#endif
|
||||
#include <QByteArrayMatcher>
|
||||
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Parameter.h>
|
||||
#include <Mod/TechDraw/App/DrawUtil.h>
|
||||
#include <Mod/TechDraw/App/Preferences.h>
|
||||
|
||||
#include "Rez.h"
|
||||
#include "PATPathMaker.h"
|
||||
|
||||
using namespace TechDrawGui;
|
||||
using namespace TechDraw;
|
||||
|
||||
PATPathMaker::PATPathMaker(QGraphicsItem* parent, double lineWidth, double fillScale) :
|
||||
m_parent(parent),
|
||||
m_fillScale(fillScale),
|
||||
m_lineWidth(lineWidth)
|
||||
{
|
||||
m_maxSeg = Preferences::getPreferenceGroup("PAT")->GetInt("MaxSeg", 10000l);
|
||||
}
|
||||
|
||||
|
||||
/// convert the PAT line set to QGraphicsPathItems
|
||||
void PATPathMaker::lineSetToFillItems(LineSet& ls)
|
||||
{
|
||||
m_segCount = 0;
|
||||
QPen pen = getPen();
|
||||
for (auto& geom : ls.getGeoms()) {
|
||||
//geom is a tdGeometry representation of 1 line in the pattern
|
||||
if (ls.isDashed()) {
|
||||
double offset = 0.0;
|
||||
Base::Vector3d pStart = ls.getPatternStartPoint(geom, offset, m_fillScale);
|
||||
offset = Rez::guiX(offset);
|
||||
Base::Vector3d gStart(geom->getStartPoint().x,
|
||||
geom->getStartPoint().y,
|
||||
0.0);
|
||||
Base::Vector3d gEnd(geom->getEndPoint().x,
|
||||
geom->getEndPoint().y,
|
||||
0.0);
|
||||
if (DrawUtil::fpCompare(offset, 0.0, 0.00001)) { //no offset
|
||||
QGraphicsPathItem* item1 = lineFromPoints(pStart, gEnd, ls.getDashSpec());
|
||||
item1->setPen(pen);
|
||||
m_fillItems.push_back(item1);
|
||||
if (!pStart.IsEqual(gStart, 0.00001)) {
|
||||
QGraphicsPathItem* item2 = lineFromPoints(pStart, gStart, ls.getDashSpec().reversed());
|
||||
item2->setPen(pen);
|
||||
m_fillItems.push_back(item2);
|
||||
}
|
||||
} else { //offset - pattern start not in g
|
||||
double remain = dashRemain(decodeDashSpec(ls.getDashSpec()), offset);
|
||||
QGraphicsPathItem* shortItem = geomToStubbyLine(geom, remain, ls);
|
||||
shortItem->setPen(pen);
|
||||
m_fillItems.push_back(shortItem);
|
||||
}
|
||||
} else { //not dashed
|
||||
QGraphicsPathItem* fillItem = geomToLine(geom, ls);
|
||||
fillItem->setPen(pen);
|
||||
m_fillItems.push_back(fillItem);
|
||||
}
|
||||
|
||||
if (m_segCount > m_maxSeg) {
|
||||
Base::Console().Warning("PAT segment count exceeded: %ld\n", m_segCount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// create a PAT fill line from 2 points and a dash configuration
|
||||
QGraphicsPathItem* PATPathMaker::lineFromPoints(Base::Vector3d start, Base::Vector3d end, DashSpec ds)
|
||||
{
|
||||
QGraphicsPathItem* fillItem = new QGraphicsPathItem(m_parent);
|
||||
fillItem->setPath(dashedPPath(decodeDashSpec(ds),
|
||||
Rez::guiX(start),
|
||||
Rez::guiX(end)));
|
||||
return fillItem;
|
||||
}
|
||||
|
||||
|
||||
/// create a PAT fill line from geometry
|
||||
QGraphicsPathItem* PATPathMaker::geomToLine(TechDraw::BaseGeomPtr base, LineSet& ls)
|
||||
{
|
||||
QGraphicsPathItem* fillItem = new QGraphicsPathItem(m_parent);
|
||||
Base::Vector3d start(base->getStartPoint().x,
|
||||
base->getStartPoint().y,
|
||||
0.0);
|
||||
Base::Vector3d end(base->getEndPoint().x,
|
||||
base->getEndPoint().y,
|
||||
0.0);
|
||||
fillItem->setPath(dashedPPath(decodeDashSpec(ls.getDashSpec()),
|
||||
Rez::guiX(start),
|
||||
Rez::guiX(end)));
|
||||
return fillItem;
|
||||
}
|
||||
|
||||
|
||||
//! make a fragment (length = remain) of a dashed line, with pattern starting at +offset
|
||||
QGraphicsPathItem* PATPathMaker::geomToStubbyLine(TechDraw::BaseGeomPtr base, double remain, LineSet& ls)
|
||||
{
|
||||
QGraphicsPathItem* fillItem = new QGraphicsPathItem(m_parent);
|
||||
Base::Vector3d start(base->getStartPoint().x,
|
||||
base->getStartPoint().y,
|
||||
0.0);
|
||||
Base::Vector3d end(base->getEndPoint().x,
|
||||
base->getEndPoint().y,
|
||||
0.0);
|
||||
double origLen = (end - start).Length();
|
||||
|
||||
double appRemain = Rez::appX(remain);
|
||||
Base::Vector3d newEnd = start + (ls.getUnitDir() * appRemain);
|
||||
|
||||
double newLen = (newEnd - start).Length();
|
||||
|
||||
if (newLen > origLen) {
|
||||
newEnd = end;
|
||||
}
|
||||
|
||||
double offset = Rez::guiX(m_fillScale * ls.getDashSpec().length()) - remain;
|
||||
|
||||
fillItem->setPath(dashedPPath(offsetDash(decodeDashSpec(ls.getDashSpec()), offset),
|
||||
Rez::guiX(start),
|
||||
Rez::guiX(newEnd)));
|
||||
m_fillItems.push_back(fillItem);
|
||||
return fillItem;
|
||||
}
|
||||
|
||||
|
||||
//! convert from mm to scene units
|
||||
std::vector<double> PATPathMaker::decodeDashSpec(DashSpec patDash)
|
||||
{
|
||||
double penWidth = Rez::guiX(m_lineWidth);
|
||||
double scale = m_fillScale;
|
||||
double minPen = 0.01; //avoid trouble with cosmetic pen (zero width)?
|
||||
if (penWidth <= minPen) {
|
||||
penWidth = minPen;
|
||||
}
|
||||
std::vector<double> result;
|
||||
for (auto& d: patDash.get()) {
|
||||
double strokeLength;
|
||||
if (DrawUtil::fpCompare(d, 0.0)) { //pat dot
|
||||
strokeLength = penWidth;
|
||||
} else { //pat mark/space
|
||||
strokeLength = Rez::guiX(d);
|
||||
}
|
||||
result.push_back(scale * strokeLength);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//! make a dashed QPainterPath from start to end in scene coords
|
||||
QPainterPath PATPathMaker::dashedPPath(const std::vector<double> dashPattern, const Base::Vector3d start, const Base::Vector3d end)
|
||||
{
|
||||
QPainterPath result;
|
||||
Base::Vector3d dir = (end - start);
|
||||
dir.Normalize();
|
||||
result.moveTo(start.x, -start.y);
|
||||
Base::Vector3d currentPos = start;
|
||||
if (dashPattern.empty()) {
|
||||
result.lineTo(end.x, -end.y);
|
||||
m_segCount++;
|
||||
} else {
|
||||
double lineLength = (end - start).Length();
|
||||
double travel = 0.0;
|
||||
Base::Vector3d lineProgress;
|
||||
while (travel < lineLength) {
|
||||
bool stop = false;
|
||||
if (m_segCount > 10000) {
|
||||
Base::Console().Warning("PAT segment count exceeded: %ld\n", m_segCount);
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto& d: dashPattern) {
|
||||
travel += fabs(d);
|
||||
Base::Vector3d segmentEnd = (currentPos + dir * fabs(d));
|
||||
if ((start - segmentEnd).Length() > lineLength) { //don't draw past end of line
|
||||
segmentEnd = end;
|
||||
stop = true;
|
||||
}
|
||||
if (d < 0.0) {
|
||||
result.moveTo(segmentEnd.x, -segmentEnd.y); //space
|
||||
} else {
|
||||
result.lineTo(segmentEnd.x, -segmentEnd.y); //mark
|
||||
}
|
||||
if (stop) {
|
||||
break;
|
||||
}
|
||||
m_segCount++;
|
||||
currentPos = segmentEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//! convert a dash pattern to an offset dash pattern (ie offset -> end)
|
||||
// dashPattern & offset are already scaled.
|
||||
std::vector<double> PATPathMaker::offsetDash(const std::vector<double> dashPattern, const double offset)
|
||||
{
|
||||
std::vector<double> result;
|
||||
double length = 0.0;
|
||||
for (auto& d: dashPattern) {
|
||||
length += fabs(d);
|
||||
}
|
||||
if (offset > length) {
|
||||
result = dashPattern;
|
||||
return result;
|
||||
}
|
||||
//find the dash cell that includes offset
|
||||
double accum = 0;
|
||||
int i = 0;
|
||||
for (auto& d:dashPattern) {
|
||||
accum += fabs(d);
|
||||
if (accum > offset) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
double firstCell = accum - offset;
|
||||
if (dashPattern.at(i) < 0.0) { //offset found in a space cell
|
||||
result.push_back(-1.0* firstCell);
|
||||
} else {
|
||||
result.push_back(firstCell);
|
||||
}
|
||||
unsigned int iCell = i + 1;
|
||||
for ( ; iCell < dashPattern.size() ; iCell++) {
|
||||
result.push_back(dashPattern.at(iCell));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//! find remaining length of a dash pattern after offset
|
||||
double PATPathMaker::dashRemain(const std::vector<double> dashPattern, const double offset)
|
||||
{
|
||||
double length = 0.0;
|
||||
for (auto& d: dashPattern) {
|
||||
length += fabs(d);
|
||||
}
|
||||
if (offset > length) {
|
||||
return 0.0;
|
||||
}
|
||||
return length - offset;
|
||||
}
|
||||
|
||||
|
||||
79
src/Mod/TechDraw/Gui/PATPathMaker.h
Normal file
79
src/Mod/TechDraw/Gui/PATPathMaker.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2023 WandererFan <wandererfan@gmail.com> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TECHDRAWGUI_PATPATHMAKER_H
|
||||
#define TECHDRAWGUI_PATPATHMAKER_H
|
||||
|
||||
#include <Mod/TechDraw/TechDrawGlobal.h>
|
||||
|
||||
#include <QGraphicsPathItem>
|
||||
#include <QPen>
|
||||
|
||||
#include <Mod/TechDraw/App/HatchLine.h>
|
||||
#include <Mod/TechDraw/App/Geometry.h>
|
||||
|
||||
namespace TechDrawGui
|
||||
{
|
||||
|
||||
class PATPathMaker
|
||||
{
|
||||
public:
|
||||
explicit PATPathMaker(QGraphicsItem* parent = nullptr, double lineWidth = 0.50, double fillScale = 1.0);
|
||||
~PATPathMaker() = default;
|
||||
|
||||
void setLineWidth(double width) { m_lineWidth = width; }
|
||||
void setScale(double scale) { m_fillScale = scale; }
|
||||
void setPen(QPen pen) { m_pen = pen; }
|
||||
QPen getPen() { return m_pen; }
|
||||
void setParent(QGraphicsItem* parent) { m_parent = parent; }
|
||||
|
||||
void lineSetToFillItems(TechDraw::LineSet& ls);
|
||||
|
||||
protected:
|
||||
QGraphicsPathItem* geomToLine(TechDraw::BaseGeomPtr base, TechDraw::LineSet& ls);
|
||||
QGraphicsPathItem* geomToStubbyLine(TechDraw::BaseGeomPtr base, double offset, TechDraw::LineSet& ls);
|
||||
QGraphicsPathItem* lineFromPoints(Base::Vector3d start, Base::Vector3d end, TechDraw::DashSpec ds);
|
||||
std::vector<double> offsetDash(const std::vector<double> dv, const double offset);
|
||||
QPainterPath dashedPPath(const std::vector<double> dv, const Base::Vector3d start, const Base::Vector3d end);
|
||||
double dashRemain(const std::vector<double> dv, const double offset);
|
||||
double calcOffset(TechDraw::BaseGeomPtr g, TechDraw::LineSet ls);
|
||||
std::vector<double> decodeDashSpec(TechDraw::DashSpec d);
|
||||
|
||||
private:
|
||||
QGraphicsItem* m_parent;
|
||||
QPainterPath m_geomhatch; //crosshatch fill lines
|
||||
QPen m_pen;
|
||||
|
||||
std::vector<TechDraw::LineSet> m_lineSets;
|
||||
std::vector<TechDraw::DashSpec> m_dashSpecs;
|
||||
std::vector<QGraphicsPathItem*> m_fillItems;
|
||||
|
||||
|
||||
double m_fillScale;
|
||||
double m_lineWidth;
|
||||
long int m_segCount;
|
||||
long int m_maxSeg;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // TECHDRAWGUI_PATPATHMAKER_H
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
#include "PreferencesGui.h"
|
||||
#include "QGIFace.h"
|
||||
#include <QByteArrayMatcher>
|
||||
#include "QGCustomImage.h"
|
||||
#include "QGCustomRect.h"
|
||||
#include "QGCustomSvg.h"
|
||||
#include "QGICMark.h"
|
||||
@@ -72,9 +71,6 @@ QGIFace::QGIFace(int index) :
|
||||
setPrettyNormal();
|
||||
m_texture = QPixmap(); //empty texture
|
||||
|
||||
m_imageHatchArea = new QGCustomImage();
|
||||
m_imageHatchArea->setParentItem(this);
|
||||
|
||||
m_svgHatchArea = new QGCustomRect();
|
||||
m_svgHatchArea->setParentItem(this);
|
||||
|
||||
@@ -101,11 +97,13 @@ QGIFace::QGIFace(int index) :
|
||||
}
|
||||
|
||||
m_sharedRender = new QSvgRenderer();
|
||||
m_patMaker = new PATPathMaker(this, 1.0, 1.0);
|
||||
}
|
||||
|
||||
QGIFace::~QGIFace()
|
||||
{
|
||||
delete m_sharedRender;
|
||||
delete m_patMaker;
|
||||
}
|
||||
|
||||
/// redraw this face
|
||||
@@ -126,37 +124,27 @@ void QGIFace::draw()
|
||||
lineSetToFillItems(ls);
|
||||
}
|
||||
}
|
||||
m_imageHatchArea->hide();
|
||||
m_svgHatchArea->hide();
|
||||
} else if (m_mode == SvgFill) {
|
||||
m_brush.setTexture(QPixmap());
|
||||
m_fillNormal = m_fillDef;
|
||||
m_fillStyleCurrent = m_fillNormal;
|
||||
loadSvgHatch(m_fileSpec);
|
||||
if (m_hideSvgTiles) {
|
||||
//bitmap hatch doesn't need clipping
|
||||
setFlag(QGraphicsItem::ItemClipsChildrenToShape, false);
|
||||
buildPixHatch();
|
||||
m_svgHatchArea->hide();
|
||||
m_imageHatchArea->show();
|
||||
} else {
|
||||
//SVG tiles need to be clipped
|
||||
setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
|
||||
buildSvgHatch();
|
||||
m_imageHatchArea->hide();
|
||||
m_svgHatchArea->show();
|
||||
}
|
||||
//SVG tiles need to be clipped
|
||||
setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
|
||||
buildSvgHatch();
|
||||
m_svgHatchArea->show();
|
||||
} else if (m_mode == BitmapFill) {
|
||||
m_fillStyleCurrent = Qt::TexturePattern;
|
||||
m_texture = textureFromBitmap(m_fileSpec);
|
||||
m_brush.setTexture(m_texture);
|
||||
m_svgHatchArea->hide();
|
||||
} else if (m_mode == PlainFill) {
|
||||
setFill(m_colNormalFill, m_fillNormal);
|
||||
m_imageHatchArea->hide();
|
||||
m_svgHatchArea->hide();
|
||||
}
|
||||
} else {
|
||||
m_imageHatchArea->hide();
|
||||
// face is not hatched
|
||||
m_svgHatchArea->hide();
|
||||
}
|
||||
show();
|
||||
@@ -262,103 +250,10 @@ void QGIFace::addLineSet(LineSet& ls)
|
||||
/// convert the PAT line set to QGraphicsPathItems
|
||||
void QGIFace::lineSetToFillItems(LineSet& ls)
|
||||
{
|
||||
m_segCount = 0;
|
||||
QPen pen = setGeomPen();
|
||||
for (auto& geom : ls.getGeoms()) {
|
||||
//geom is a tdGeometry representation of 1 line in the pattern
|
||||
if (ls.isDashed()) {
|
||||
double offset = 0.0;
|
||||
Base::Vector3d pStart = ls.getPatternStartPoint(geom, offset, m_fillScale);
|
||||
offset = Rez::guiX(offset);
|
||||
Base::Vector3d gStart(geom->getStartPoint().x,
|
||||
geom->getStartPoint().y,
|
||||
0.0);
|
||||
Base::Vector3d gEnd(geom->getEndPoint().x,
|
||||
geom->getEndPoint().y,
|
||||
0.0);
|
||||
if (DrawUtil::fpCompare(offset, 0.0, 0.00001)) { //no offset
|
||||
QGraphicsPathItem* item1 = lineFromPoints(pStart, gEnd, ls.getDashSpec());
|
||||
item1->setPen(pen);
|
||||
m_fillItems.push_back(item1);
|
||||
if (!pStart.IsEqual(gStart, 0.00001)) {
|
||||
QGraphicsPathItem* item2 = lineFromPoints(pStart, gStart, ls.getDashSpec().reversed());
|
||||
item2->setPen(pen);
|
||||
m_fillItems.push_back(item2);
|
||||
}
|
||||
} else { //offset - pattern start not in g
|
||||
double remain = dashRemain(decodeDashSpec(ls.getDashSpec()), offset);
|
||||
QGraphicsPathItem* shortItem = geomToStubbyLine(geom, remain, ls);
|
||||
shortItem->setPen(pen);
|
||||
m_fillItems.push_back(shortItem);
|
||||
}
|
||||
} else { //not dashed
|
||||
QGraphicsPathItem* fillItem = geomToLine(geom, ls);
|
||||
fillItem->setPen(pen);
|
||||
m_fillItems.push_back(fillItem);
|
||||
}
|
||||
|
||||
if (m_segCount > m_maxSeg) {
|
||||
Base::Console().Warning("PAT segment count exceeded: %ld\n", m_segCount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// create a PAT fill line from 2 points and a dash configuration
|
||||
QGraphicsPathItem* QGIFace::lineFromPoints(Base::Vector3d start, Base::Vector3d end, DashSpec ds)
|
||||
{
|
||||
QGraphicsPathItem* fillItem = new QGraphicsPathItem(this);
|
||||
fillItem->setPath(dashedPPath(decodeDashSpec(ds),
|
||||
Rez::guiX(start),
|
||||
Rez::guiX(end)));
|
||||
return fillItem;
|
||||
}
|
||||
|
||||
/// create a PAT fill line from geometry
|
||||
QGraphicsPathItem* QGIFace::geomToLine(TechDraw::BaseGeomPtr base, LineSet& ls)
|
||||
{
|
||||
QGraphicsPathItem* fillItem = new QGraphicsPathItem(this);
|
||||
Base::Vector3d start(base->getStartPoint().x,
|
||||
base->getStartPoint().y,
|
||||
0.0);
|
||||
Base::Vector3d end(base->getEndPoint().x,
|
||||
base->getEndPoint().y,
|
||||
0.0);
|
||||
fillItem->setPath(dashedPPath(decodeDashSpec(ls.getDashSpec()),
|
||||
Rez::guiX(start),
|
||||
Rez::guiX(end)));
|
||||
return fillItem;
|
||||
}
|
||||
|
||||
|
||||
//! make a fragment (length = remain) of a dashed line, with pattern starting at +offset
|
||||
QGraphicsPathItem* QGIFace::geomToStubbyLine(TechDraw::BaseGeomPtr base, double remain, LineSet& ls)
|
||||
{
|
||||
QGraphicsPathItem* fillItem = new QGraphicsPathItem(this);
|
||||
Base::Vector3d start(base->getStartPoint().x,
|
||||
base->getStartPoint().y,
|
||||
0.0);
|
||||
Base::Vector3d end(base->getEndPoint().x,
|
||||
base->getEndPoint().y,
|
||||
0.0);
|
||||
double origLen = (end - start).Length();
|
||||
|
||||
double appRemain = Rez::appX(remain);
|
||||
Base::Vector3d newEnd = start + (ls.getUnitDir() * appRemain);
|
||||
|
||||
double newLen = (newEnd - start).Length();
|
||||
|
||||
if (newLen > origLen) {
|
||||
newEnd = end;
|
||||
}
|
||||
|
||||
double offset = Rez::guiX(m_fillScale * ls.getDashSpec().length()) - remain;
|
||||
|
||||
fillItem->setPath(dashedPPath(offsetDash(decodeDashSpec(ls.getDashSpec()), offset),
|
||||
Rez::guiX(start),
|
||||
Rez::guiX(newEnd)));
|
||||
m_fillItems.push_back(fillItem);
|
||||
return fillItem;
|
||||
m_patMaker->setLineWidth(Rez::guiX(m_geomWeight));
|
||||
m_patMaker->setScale(m_fillScale);
|
||||
m_patMaker->setPen(setGeomPen());
|
||||
m_patMaker->lineSetToFillItems(ls);
|
||||
}
|
||||
|
||||
QPen QGIFace::setGeomPen()
|
||||
@@ -370,123 +265,6 @@ QPen QGIFace::setGeomPen()
|
||||
return result;
|
||||
}
|
||||
|
||||
//! convert from mm to scene units
|
||||
std::vector<double> QGIFace::decodeDashSpec(DashSpec patDash)
|
||||
{
|
||||
double penWidth = Rez::guiX(m_geomWeight);
|
||||
double scale = m_fillScale;
|
||||
double minPen = 0.01; //avoid trouble with cosmetic pen (zero width)?
|
||||
if (penWidth <= minPen) {
|
||||
penWidth = minPen;
|
||||
}
|
||||
std::vector<double> result;
|
||||
for (auto& d: patDash.get()) {
|
||||
double strokeLength;
|
||||
if (DrawUtil::fpCompare(d, 0.0)) { //pat dot
|
||||
strokeLength = penWidth;
|
||||
} else { //pat mark/space
|
||||
strokeLength = Rez::guiX(d);
|
||||
}
|
||||
result.push_back(scale * strokeLength);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//! make a dashed QPainterPath from start to end in scene coords
|
||||
QPainterPath QGIFace::dashedPPath(const std::vector<double> dv, const Base::Vector3d start, const Base::Vector3d end)
|
||||
{
|
||||
QPainterPath result;
|
||||
Base::Vector3d dir = (end - start);
|
||||
dir.Normalize();
|
||||
result.moveTo(start.x, -start.y);
|
||||
Base::Vector3d currentPos = start;
|
||||
if (dv.empty()) {
|
||||
result.lineTo(end.x, -end.y);
|
||||
m_segCount++;
|
||||
} else {
|
||||
double lineLength = (end - start).Length();
|
||||
double travel = 0.0;
|
||||
Base::Vector3d lineProgress;
|
||||
while (travel < lineLength) {
|
||||
bool stop = false;
|
||||
if (m_segCount > 10000) {
|
||||
Base::Console().Warning("PAT segment count exceeded: %ld\n", m_segCount);
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto& d: dv) {
|
||||
travel += fabs(d);
|
||||
Base::Vector3d segmentEnd = (currentPos + dir * fabs(d));
|
||||
if ((start - segmentEnd).Length() > lineLength) { //don't draw past end of line
|
||||
segmentEnd = end;
|
||||
stop = true;
|
||||
}
|
||||
if (d < 0.0) {
|
||||
result.moveTo(segmentEnd.x, -segmentEnd.y); //space
|
||||
} else {
|
||||
result.lineTo(segmentEnd.x, -segmentEnd.y); //mark
|
||||
}
|
||||
if (stop) {
|
||||
break;
|
||||
}
|
||||
m_segCount++;
|
||||
currentPos = segmentEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//! convert a dash pattern to an offset dash pattern (ie offset -> end)
|
||||
// dv & offset are already scaled.
|
||||
std::vector<double> QGIFace::offsetDash(const std::vector<double> dv, const double offset)
|
||||
{
|
||||
std::vector<double> result;
|
||||
double length = 0.0;
|
||||
for (auto& d: dv) {
|
||||
length += fabs(d);
|
||||
}
|
||||
if (offset > length) {
|
||||
result = dv;
|
||||
return result;
|
||||
}
|
||||
//find the dash cell that includes offset
|
||||
double accum = 0;
|
||||
int i = 0;
|
||||
for (auto& d:dv) {
|
||||
accum += fabs(d);
|
||||
if (accum > offset) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
double firstCell = accum - offset;
|
||||
if (dv.at(i) < 0.0) { //offset found in a space cell
|
||||
result.push_back(-1.0* firstCell);
|
||||
} else {
|
||||
result.push_back(firstCell);
|
||||
}
|
||||
unsigned int iCell = i + 1;
|
||||
for ( ; iCell < dv.size() ; iCell++) {
|
||||
result.push_back(dv.at(iCell));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//! find remaining length of a dash pattern after offset
|
||||
double QGIFace::dashRemain(const std::vector<double> dv, const double offset)
|
||||
{
|
||||
double length = 0.0;
|
||||
for (auto& d: dv) {
|
||||
length += fabs(d);
|
||||
}
|
||||
if (offset > length) {
|
||||
return 0.0;
|
||||
}
|
||||
return length - offset;
|
||||
}
|
||||
|
||||
//! get zoom level (scale) from QGraphicsView
|
||||
// not used currently
|
||||
@@ -580,94 +358,6 @@ void QGIFace::clearSvg()
|
||||
hideSvg(true);
|
||||
}
|
||||
|
||||
/// make an array of bitmap tiles to cover this face
|
||||
void QGIFace::buildPixHatch()
|
||||
{
|
||||
// Base::Console().Message("QGIF::buildPixHatch() - offset: %s\n", DrawUtil::formatVector(getHatchOffset()).c_str());
|
||||
double wTile = SVGSIZEW * m_fillScale;
|
||||
double hTile = SVGSIZEH * m_fillScale;
|
||||
double faceWidth = m_outline.boundingRect().width();
|
||||
double faceHeight = m_outline.boundingRect().height();
|
||||
QRectF faceRect = m_outline.boundingRect();
|
||||
QPointF faceCenter = faceRect.center();
|
||||
double hatchOverlaySize = Preferences::svgHatchFactor() * std::max(faceWidth, faceHeight);
|
||||
double numberWide = ceil(hatchOverlaySize / wTile);
|
||||
double numberHigh = ceil(hatchOverlaySize / hTile);
|
||||
double overlayWidth = numberWide * wTile;
|
||||
double overlayHeight= numberHigh * hTile;
|
||||
|
||||
m_svgHatchArea->setRect(0., 0., overlayWidth, -overlayHeight);
|
||||
m_svgHatchArea->centerAt(faceCenter);
|
||||
|
||||
QByteArray before, after;
|
||||
before = QString::fromStdString(SVGCOLPREFIX + SVGCOLDEFAULT).toUtf8();
|
||||
after = QString::fromStdString(SVGCOLPREFIX + m_svgCol).toUtf8();
|
||||
QByteArray colorXML = m_svgXML.replace(before, after);
|
||||
QSvgRenderer renderer;
|
||||
bool success = renderer.load(colorXML);
|
||||
if (!success) {
|
||||
Base::Console().Error("QGIF::buildPixHatch - renderer failed to load\n");
|
||||
}
|
||||
|
||||
//get the svg tile graphics as a QImage
|
||||
QImage imageIn(64, 64, QImage::Format_ARGB32);
|
||||
imageIn.fill(Qt::transparent);
|
||||
QPainter painter(&imageIn);
|
||||
renderer.render(&painter);
|
||||
if (imageIn.isNull()) {
|
||||
Base::Console().Error("QGIF::buildPixHatch - imageIn is null\n");
|
||||
return;
|
||||
}
|
||||
//make a QPixmap tile of the QImage
|
||||
QPixmap pm(64, 64);
|
||||
pm = QPixmap::fromImage(imageIn);
|
||||
pm = pm.scaled(wTile, hTile);
|
||||
if (pm.isNull()) {
|
||||
Base::Console().Error("QGIF::buildPixHatch - pixmap is null\n");
|
||||
return;
|
||||
}
|
||||
|
||||
//layout a field of QPixmap tiles as a QImage
|
||||
QImage tileField(overlayWidth, overlayHeight, QImage::Format_ARGB32);
|
||||
QPointF fieldCenter(overlayWidth / 2.0, overlayHeight / 2.0);
|
||||
|
||||
tileField.fill(Qt::transparent);
|
||||
QPainter painter2(&tileField);
|
||||
QPainter::RenderHints hints = painter2.renderHints();
|
||||
hints = hints & QPainter::Antialiasing;
|
||||
painter2.setRenderHints(hints);
|
||||
QPainterPath clipper = path();
|
||||
QPointF offset = (fieldCenter - faceCenter);
|
||||
clipper.translate(offset);
|
||||
painter2.setClipPath(clipper);
|
||||
|
||||
long int tileCount = 0;
|
||||
for (int iw = 0; iw < int(numberWide); iw++) {
|
||||
for (int ih = 0; ih < int(numberHigh); ih++) {
|
||||
painter2.drawPixmap(QRectF(iw*wTile + getHatchOffset().x, ih*hTile + getHatchOffset().y,
|
||||
wTile, hTile), //target rect
|
||||
pm, //map
|
||||
QRectF(0, 0, wTile, hTile)); //source rect
|
||||
tileCount++;
|
||||
if (tileCount > m_maxTile) {
|
||||
Base::Console().Warning("Pixmap tile count exceeded: %ld. Change hatch scale or raise limit.\n", tileCount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tileCount > m_maxTile) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QPixmap bigMap(fabs(faceRect.width()), fabs(faceRect.height()));
|
||||
bigMap = QPixmap::fromImage(tileField);
|
||||
|
||||
QPixmap nothing;
|
||||
m_imageHatchArea->setPixmap(nothing);
|
||||
m_imageHatchArea->load(bigMap);
|
||||
m_imageHatchArea->centerAt(faceCenter);
|
||||
}
|
||||
|
||||
//this isn't used currently
|
||||
QPixmap QGIFace::textureFromSvg(std::string fileSpec)
|
||||
{
|
||||
@@ -698,6 +388,7 @@ void QGIFace::setHatchScale(double s)
|
||||
/// turn svg tiles on or off. QtSvg does not handle clipping,
|
||||
/// so we must be able to turn the hatching on/off when exporting a face with an
|
||||
/// svg hatch. Otherwise the full tile pattern is shown in the export.
|
||||
/// NOTE: there appears to have been a change in Qt that it now clips svg items
|
||||
void QGIFace::hideSvg(bool b)
|
||||
{
|
||||
m_hideSvgTiles = b;
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#include <Mod/TechDraw/App/HatchLine.h>
|
||||
|
||||
#include "PATPathMaker.h"
|
||||
#include "QGIPrimPath.h"
|
||||
|
||||
|
||||
@@ -93,9 +94,6 @@ public:
|
||||
void hideSvg(bool b);
|
||||
void clearSvg();
|
||||
|
||||
//tiled pixmap fill from svg
|
||||
void buildPixHatch();
|
||||
|
||||
//PAT fill parms & methods
|
||||
void setGeomHatchWeight(double w) { m_geomWeight = w; }
|
||||
void setLineWeight(double w);
|
||||
@@ -137,8 +135,6 @@ protected:
|
||||
std::string m_svgCol;
|
||||
std::string m_fileSpec; //for svg & bitmaps
|
||||
|
||||
QGCustomImage* m_imageHatchArea;
|
||||
|
||||
double m_fillScale;
|
||||
bool m_isHatched;
|
||||
QGIFace::fillMode m_mode;
|
||||
@@ -171,6 +167,8 @@ private:
|
||||
|
||||
QSvgRenderer *m_sharedRender;
|
||||
|
||||
PATPathMaker* m_patMaker;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user