DrawGeomHatch improvements
-draw & align dash patterns correctly on all QPainters using QGPathItem -Allow patterns to start with space -ensure horiz & vert lines pass through pattern origin -Scalable complex patterns
This commit is contained in:
@@ -289,7 +289,7 @@ std::vector<TopoDS_Edge> DrawGeomHatch::makeEdgeOverlay(PATLineSpec hl, Bnd_Box
|
||||
Base::Vector3d start;
|
||||
Base::Vector3d end;
|
||||
Base::Vector3d origin = hl.getOrigin();
|
||||
double interval = hl.getInterval() * scale;
|
||||
double interval = hl.getIntervalX() * scale;
|
||||
double angle = hl.getAngle();
|
||||
|
||||
//only dealing with angles -180:180 for now
|
||||
@@ -301,29 +301,33 @@ std::vector<TopoDS_Edge> DrawGeomHatch::makeEdgeOverlay(PATLineSpec hl, Bnd_Box
|
||||
double slope = hl.getSlope();
|
||||
|
||||
if (angle == 0.0) { //odd case 1: horizontal lines
|
||||
double y = origin.y;
|
||||
int repeatUp = (int) ceil(((maxY - y)/interval) + 1);
|
||||
int repeatDown = (int) ceil(((y - minY)/interval) + 1);
|
||||
int repeatTotal = repeatUp + repeatDown;
|
||||
interval = hl.getInterval() * scale;
|
||||
double atomY = origin.y;
|
||||
int repeatUp = (int) fabs((maxY - atomY)/interval);
|
||||
int repeatDown = (int) fabs(((atomY - minY)/interval));
|
||||
int repeatTotal = repeatUp + repeatDown + 1;
|
||||
double yStart = atomY - repeatDown * interval;
|
||||
|
||||
// make repeats
|
||||
for (int i = 0; i < repeatTotal; i++) {
|
||||
Base::Vector3d newStart(minX,minY + float(i)*interval,0);
|
||||
Base::Vector3d newEnd(maxX,minY + float(i)*interval,0);
|
||||
Base::Vector3d newStart(minX,yStart + float(i)*interval,0);
|
||||
Base::Vector3d newEnd(maxX,yStart + float(i)*interval,0);
|
||||
TopoDS_Edge newLine = makeLine(newStart,newEnd);
|
||||
result.push_back(newLine);
|
||||
}
|
||||
} else if ((angle == 90.0) ||
|
||||
(angle == -90.0)) { //odd case 2: vertical lines
|
||||
double x = origin.x;
|
||||
int repeatRight = (int) ceil(((maxX - x)/interval) + 1);
|
||||
int repeatLeft = (int) ceil(((x - minX)/interval) + 1);
|
||||
int repeatTotal = repeatRight + repeatLeft;
|
||||
interval = hl.getInterval() * scale;
|
||||
double atomX = origin.x;
|
||||
int repeatRight = (int) fabs((maxX - atomX)/interval);
|
||||
int repeatLeft = (int) fabs((atomX - minX)/interval);
|
||||
int repeatTotal = repeatRight + repeatLeft + 1;
|
||||
double xStart = atomX - repeatLeft * interval;
|
||||
|
||||
// make repeats
|
||||
for (int i = 0; i < repeatTotal; i++) {
|
||||
Base::Vector3d newStart(minX + float(i)*interval,minY,0);
|
||||
Base::Vector3d newEnd(minX + float(i)*interval,maxY,0);
|
||||
Base::Vector3d newStart(xStart + float(i)*interval,minY,0);
|
||||
Base::Vector3d newEnd(xStart + float(i)*interval,maxY,0);
|
||||
TopoDS_Edge newLine = makeLine(newStart,newEnd);
|
||||
result.push_back(newLine);
|
||||
}
|
||||
@@ -334,12 +338,12 @@ std::vector<TopoDS_Edge> DrawGeomHatch::makeEdgeOverlay(PATLineSpec hl, Bnd_Box
|
||||
double xLeftAtom = origin.x + (minY - origin.y)/slope; //the "atom" is the fill line that passes through the
|
||||
//pattern-origin (not necc. R2 origin)
|
||||
double xRightAtom = origin.x + (maxY - origin.y)/slope;
|
||||
int repeatRight = (int) ceil(((maxX - xLeftAtom)/interval) + 1);
|
||||
int repeatLeft = (int) ceil(((xRightAtom - minX)/interval) + 1);
|
||||
int repeatRight = (int) fabs((maxX - xLeftAtom)/interval);
|
||||
int repeatLeft = (int) fabs((xRightAtom - minX)/interval);
|
||||
|
||||
double leftStartX = xLeftAtom - (repeatLeft * interval);
|
||||
double leftEndX = xRightAtom - (repeatLeft * interval);
|
||||
int repeatTotal = repeatRight + repeatLeft;
|
||||
int repeatTotal = repeatRight + repeatLeft + 1;
|
||||
|
||||
//make repeats
|
||||
for (int i = 0; i < repeatTotal; i++) {
|
||||
@@ -353,11 +357,11 @@ std::vector<TopoDS_Edge> DrawGeomHatch::makeEdgeOverlay(PATLineSpec hl, Bnd_Box
|
||||
// Base::Console().Message("TRACE - DGH-makeEdgeOverlay - making angle < 0\n");
|
||||
double xRightAtom = origin.x + ((minY - origin.y)/slope); //x-coord of left end of Atom line
|
||||
double xLeftAtom = origin.x + ((maxY - origin.y)/slope); //x-coord of right end of Atom line
|
||||
int repeatRight = (int) ceil(((maxX - xLeftAtom)/interval) + 1); //number of lines to Right of Atom
|
||||
int repeatLeft = (int) ceil(((xRightAtom - minX)/interval) + 1);//number of lines to Left of Atom
|
||||
int repeatRight = (int) fabs((maxX - xLeftAtom)/interval); //number of lines to Right of Atom
|
||||
int repeatLeft = (int) fabs((xRightAtom - minX)/interval); //number of lines to Left of Atom
|
||||
double leftEndX = xLeftAtom - (repeatLeft * interval);
|
||||
double leftStartX = xRightAtom - (repeatLeft * interval);
|
||||
int repeatTotal = repeatRight + repeatLeft;
|
||||
int repeatTotal = repeatRight + repeatLeft + 1;
|
||||
|
||||
// make repeats
|
||||
for (int i = 0; i < repeatTotal; i++) {
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <stdexcept>
|
||||
#include <cmath>
|
||||
#endif
|
||||
|
||||
#include <TopoDS_Vertex.hxx>
|
||||
@@ -78,8 +79,7 @@ bool LineSet::isDashed(void)
|
||||
return result;
|
||||
}
|
||||
|
||||
//TODO: needs to incorporate deltaX parameter
|
||||
//! calculates the apparent start point for dashed lines
|
||||
//! calculates the apparent start point (ie start of overlay line) for dashed lines
|
||||
Base::Vector3d LineSet::calcApparentStart(TechDrawGeometry::BaseGeom* g)
|
||||
{
|
||||
Base::Vector3d result;
|
||||
@@ -99,6 +99,128 @@ Base::Vector3d LineSet::calcApparentStart(TechDrawGeometry::BaseGeom* g)
|
||||
return result;
|
||||
}
|
||||
|
||||
Base::Vector3d LineSet::getUnitDir(void)
|
||||
{
|
||||
Base::Vector3d result;
|
||||
Base::Vector3d start(m_geoms.at(0)->getStartPoint().x,
|
||||
m_geoms.at(0)->getStartPoint().y,
|
||||
0.0);
|
||||
Base::Vector3d end(m_geoms.at(0)->getEndPoint().x,
|
||||
m_geoms.at(0)->getEndPoint().y,
|
||||
0.0);
|
||||
result = end - start;
|
||||
result.Normalize();
|
||||
return result;
|
||||
}
|
||||
|
||||
Base::Vector3d LineSet::getUnitOrtho()
|
||||
{
|
||||
Base::Vector3d result;
|
||||
Base::Vector3d unit = getUnitDir();
|
||||
Base::Vector3d X(1.0,0.0,0.0);
|
||||
Base::Vector3d Y(0.0,1.0,0.0);
|
||||
if (unit.IsEqual(X,0.000001)) {
|
||||
result = Y;
|
||||
} else if (unit.IsEqual(Y,0.000001)) {
|
||||
result = X;
|
||||
} else {
|
||||
double unitX = unit.x;
|
||||
double unitY = unit.y;
|
||||
result = Base::Vector3d(unitY,-unitX, 0.0); //perpendicular
|
||||
}
|
||||
result.Normalize(); //probably redundant
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Base::Vector3d LineSet::findAtomStart(void)
|
||||
{
|
||||
Base::Vector3d result;
|
||||
Base::Vector3d origin = getOrigin();
|
||||
double angle = getAngle();
|
||||
if (angle == 0.0) {
|
||||
result = Base::Vector3d(getMinX(),origin.y,0.0);
|
||||
} else if ( (angle == 90.0) ||
|
||||
(angle == -90.0) ) {
|
||||
result = Base::Vector3d(origin.x,getMinY(),0.0);
|
||||
} else {
|
||||
double minY = getMinY();
|
||||
double x = origin.x - (origin.y - minY)/getSlope();
|
||||
result = Base::Vector3d(x,minY,0.0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Base::Vector3d LineSet::getPatternStartPoint(TechDrawGeometry::BaseGeom* g, double &offset, double scale)
|
||||
{
|
||||
Base::Vector3d result = getOrigin();
|
||||
Base::Vector3d atomStart = findAtomStart();
|
||||
Base::Vector3d thisStart = calcApparentStart(g);
|
||||
double angle = getAngle();
|
||||
double patternLength = scale * getPatternLength();
|
||||
Base::Vector3d lineOrigin;
|
||||
double distX,distY;
|
||||
int overlayIndex = 0;
|
||||
//interval & offset are not patternscaled
|
||||
|
||||
//figure out which line this is in the overlay
|
||||
if (angle == 0.0) { //odd case - horizontal line
|
||||
distY = (thisStart.y - atomStart.y); //this is patternscaled
|
||||
overlayIndex = (int) round(distY/(scale * getIntervalY())); //this is +/-
|
||||
lineOrigin = getOrigin() + distY * Base::Vector3d(0.0,1.0,0.0); //move u/d
|
||||
} else {
|
||||
distX = (thisStart.x - atomStart.x); //this is patternscaled
|
||||
overlayIndex = (int) round(distX/(scale * getIntervalX()));
|
||||
lineOrigin = getOrigin() + overlayIndex * (scale * getInterval()) * getUnitOrtho();
|
||||
}
|
||||
|
||||
lineOrigin = lineOrigin + (overlayIndex * (scale * getOffset())) * getUnitDir(); //move along line
|
||||
|
||||
//is lineOrigin on line of g? should be within fp error
|
||||
Base::Vector3d gStart(g->getStartPoint().x,
|
||||
g->getStartPoint().y,
|
||||
0.0);
|
||||
Base::Vector3d gEnd(g->getEndPoint().x,
|
||||
g->getEndPoint().y,
|
||||
0.0);
|
||||
double lenStartOrg = (gStart - lineOrigin).Length();
|
||||
double lenEndOrg = (gEnd - lineOrigin).Length();
|
||||
double lenStartEnd = (gStart - gEnd).Length();
|
||||
if ( (lenStartOrg <= lenStartEnd) &&
|
||||
(lenEndOrg <= lenStartEnd) ) { //origin is in g
|
||||
result = lineOrigin;
|
||||
offset = 0.0;
|
||||
} else {
|
||||
//find a point where pattern repeats within g
|
||||
double patsStartOrg = lenStartOrg/patternLength; //# pattern repeats from lineOrigin to start
|
||||
double patsEndOrg = lenEndOrg/patternLength;
|
||||
if (lenStartOrg < lenEndOrg) { //origin is before start
|
||||
double c = ceil(patsStartOrg);
|
||||
if (c <= patsEndOrg) { //c is an integer pattern count in [patsStartOrg,patsEndOrg]
|
||||
result = lineOrigin + c*patternLength*getUnitDir();
|
||||
offset = 0.0;
|
||||
} else {
|
||||
// //ugly case - partial pattern
|
||||
result = gStart;
|
||||
offset = patsStartOrg - (int)patsStartOrg; //fraction of a patternLength
|
||||
offset = offset * patternLength;
|
||||
}
|
||||
} else if (lenStartOrg > lenEndOrg) { //origin is after end
|
||||
double c = ceil(patsEndOrg);
|
||||
if (c <= patsStartOrg) { //c is an integer pattern count in [patsStartOrg,patsEndOrg]
|
||||
result = lineOrigin - c*patternLength*getUnitDir();
|
||||
offset = 0.0;
|
||||
} else {
|
||||
result = gStart;
|
||||
offset = ceil(patsStartOrg) - patsStartOrg; //fraction of a patternLength patstartorg to repeat point
|
||||
offset = offset * patternLength;
|
||||
}
|
||||
} else {
|
||||
Base::Console().Log("ERROR - HL::getPatternStart - something has gone wrong!\n");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
PATLineSpec::PATLineSpec()
|
||||
@@ -307,7 +429,52 @@ bool PATLineSpec::isDashed(void)
|
||||
return result;
|
||||
}
|
||||
|
||||
//! X component of distance between lines
|
||||
double PATLineSpec::getIntervalX(void)
|
||||
{
|
||||
if (getAngle() == 0.0) {
|
||||
return 0.0;
|
||||
} else if ((getAngle() == 90.0) || (getAngle() == -90.0)) {
|
||||
return getInterval();
|
||||
} else {
|
||||
double perpAngle = fabs(getAngle() - 90.0);
|
||||
return fabs(getInterval() / cos(perpAngle * M_PI/180.0));
|
||||
}
|
||||
}
|
||||
|
||||
//! Y component of distance between lines
|
||||
double PATLineSpec::getIntervalY(void)
|
||||
{
|
||||
if (getAngle() == 0.0) {
|
||||
return getInterval();
|
||||
} else if ((getAngle() == 90.0) || (getAngle() == -90.0)) {
|
||||
return 0.0;
|
||||
} else {
|
||||
double perpAngle = fabs(getAngle() - 90.0);
|
||||
return fabs(getInterval() * tan(perpAngle * M_PI/180.0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//********************************************************
|
||||
|
||||
double DashSpec::length(void)
|
||||
{
|
||||
double result = 0.0;
|
||||
for (auto& c: get()) {
|
||||
result += fabs(c);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DashSpec DashSpec::reversed(void)
|
||||
{
|
||||
std::vector<double> p = get();
|
||||
std::reverse(p.begin(),p.end());
|
||||
DashSpec result(p);
|
||||
return result;
|
||||
}
|
||||
|
||||
void DashSpec::dump(char* title)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
@@ -62,6 +62,7 @@ public:
|
||||
int size(void) {return m_parms.size();}
|
||||
double length(void);
|
||||
void dump(char* title);
|
||||
DashSpec reversed(void);
|
||||
|
||||
private:
|
||||
std::vector<double> m_parms;
|
||||
@@ -80,8 +81,12 @@ public:
|
||||
double getAngle(void) {return m_angle;}
|
||||
Base::Vector3d getOrigin(void) {return m_origin;}
|
||||
double getInterval(void) {return m_interval;}
|
||||
double getIntervalX(void);
|
||||
double getIntervalY(void);
|
||||
double getOffset(void) {return m_offset;}
|
||||
std::vector<double> getDashParms(void) {return m_dashParms;}
|
||||
double getSlope(void);
|
||||
double getLength(void) {return m_dashParms.length(); }
|
||||
DashSpec getDashParms(void) {return m_dashParms;}
|
||||
|
||||
static std::vector<PATLineSpec> getSpecsForPattern(std::string& parmFile, std::string& parmName);
|
||||
static bool findPatternStart(std::ifstream& inFile, std::string& parmName);
|
||||
@@ -115,14 +120,26 @@ public:
|
||||
void setGeoms(std::vector<TechDrawGeometry::BaseGeom*> g) {m_geoms = g;}
|
||||
void setBBox(Bnd_Box bb) {m_box = bb;}
|
||||
|
||||
PATLineSpec getPATLineSpec(void) { return m_hatchLine; }
|
||||
double getOffset(void) { return m_hatchLine.getOffset(); }
|
||||
double getAngle(void) { return m_hatchLine.getAngle(); }
|
||||
DashSpec getDashSpec(void) { return m_hatchLine.getDashParms();}
|
||||
std::vector<TopoDS_Edge> getEdges(void) { return m_edges; }
|
||||
TopoDS_Edge getEdge(int i) {return m_edges.at(i);}
|
||||
std::vector<TopoDS_Edge> getEdges(void) { return m_edges; }
|
||||
TopoDS_Edge getEdge(int i) {return m_edges.at(i);}
|
||||
std::vector<TechDrawGeometry::BaseGeom*> getGeoms(void) { return m_geoms; }
|
||||
Base::Vector3d calcApparentStart(TechDrawGeometry::BaseGeom* g);
|
||||
|
||||
PATLineSpec getPATLineSpec(void) { return m_hatchLine; }
|
||||
double getOffset(void) { return m_hatchLine.getOffset(); } //delta X offset
|
||||
double getAngle(void) { return m_hatchLine.getAngle(); }
|
||||
Base::Vector3d getOrigin(void) { return m_hatchLine.getOrigin(); }
|
||||
double getInterval(void) {return m_hatchLine.getInterval(); } //space between lines
|
||||
double getIntervalX(void) { return m_hatchLine.getIntervalX(); } //interval X-component
|
||||
double getIntervalY(void) { return m_hatchLine.getIntervalY(); } //interval Y-component
|
||||
double getSlope(void) { return m_hatchLine.getSlope(); }
|
||||
double getPatternLength(void) { return m_hatchLine.getLength(); }
|
||||
Base::Vector3d getUnitDir(void);
|
||||
Base::Vector3d getUnitOrtho(void);
|
||||
DashSpec getDashSpec(void) { return m_hatchLine.getDashParms();}
|
||||
Base::Vector3d calcApparentStart(TechDrawGeometry::BaseGeom* g);
|
||||
Base::Vector3d findAtomStart(void);
|
||||
Base::Vector3d getLineOrigin(void); //point corresponding to pattern origin for this line (O + n*intervalX)
|
||||
Base::Vector3d getPatternStartPoint(TechDrawGeometry::BaseGeom* g, double &offset, double scale = 1.0);
|
||||
|
||||
Bnd_Box getBBox(void) {return m_box;}
|
||||
double getMinX(void);
|
||||
|
||||
@@ -69,13 +69,13 @@ using namespace TechDraw;
|
||||
|
||||
QGIFace::QGIFace(int index) :
|
||||
projIndex(index),
|
||||
m_colDefFill(Qt::white), //Qt::transparent? paper colour?
|
||||
m_colDefFill(Qt::white),
|
||||
m_styleDef(Qt::SolidPattern),
|
||||
m_styleSelect(Qt::SolidPattern)
|
||||
{
|
||||
m_segCount = 0;
|
||||
setFillMode(NoFill);
|
||||
isHatched(false);
|
||||
// setFlag(QGraphicsItem::ItemClipsChildrenToShape,true);
|
||||
setFlag(QGraphicsItem::ItemClipsChildrenToShape,false);
|
||||
|
||||
//setStyle(Qt::NoPen); //don't draw face lines, just fill for debugging
|
||||
@@ -97,6 +97,8 @@ QGIFace::QGIFace(int index) :
|
||||
|
||||
m_svgCol = SVGCOLDEFAULT;
|
||||
m_fillScale = 1.0;
|
||||
|
||||
getParameters();
|
||||
}
|
||||
|
||||
QGIFace::~QGIFace()
|
||||
@@ -115,7 +117,7 @@ void QGIFace::draw()
|
||||
m_fillStyle = m_styleDef;
|
||||
m_styleNormal = m_fillStyle;
|
||||
for (auto& ls: m_lineSets) {
|
||||
lineSetToFillItem(ls);
|
||||
lineSetToFillItems(ls);
|
||||
}
|
||||
}
|
||||
} else if ((m_mode == FromFile) ||
|
||||
@@ -231,96 +233,236 @@ void QGIFace::addLineSet(LineSet ls)
|
||||
m_lineSets.push_back(ls);
|
||||
}
|
||||
|
||||
void QGIFace::lineSetToFillItem(LineSet ls)
|
||||
void QGIFace::lineSetToFillItems(LineSet ls)
|
||||
{
|
||||
m_segCount = 0;
|
||||
QPen pen = setGeomPen();
|
||||
for (auto& g: ls.getGeoms()) {
|
||||
QGraphicsLineItem* fillItem = geomToLine(g);
|
||||
QPen geomPen = setGeomPen(ls.getDashSpec());
|
||||
if (ls.isDashed()) {
|
||||
double offset = calcOffset(g,ls); //offset in graphics coords(?)
|
||||
geomPen.setDashOffset(offset);
|
||||
// geomPen.setDashOffset(offset/getXForm()); //try to account for QGraphicsView Zoom level
|
||||
double offset = 0.0;
|
||||
Base::Vector3d pStart = ls.getPatternStartPoint(g, offset,m_fillScale);
|
||||
offset = Rez::guiX(offset);
|
||||
Base::Vector3d gStart(g->getStartPoint().x,
|
||||
g->getStartPoint().y,
|
||||
0.0);
|
||||
Base::Vector3d gEnd(g->getEndPoint().x,
|
||||
g->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(g, remain, ls);
|
||||
shortItem->setPen(pen);
|
||||
m_fillItems.push_back(shortItem);
|
||||
}
|
||||
} else { //not dashed
|
||||
QGraphicsPathItem* fillItem = geomToLine(g, 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;
|
||||
}
|
||||
fillItem->setPen(geomPen);
|
||||
}
|
||||
}
|
||||
|
||||
QGraphicsLineItem* QGIFace::geomToLine(TechDrawGeometry::BaseGeom* base)
|
||||
QGraphicsPathItem* QGIFace::lineFromPoints(Base::Vector3d start, Base::Vector3d end, DashSpec ds)
|
||||
{
|
||||
QGraphicsLineItem* fillItem = new QGraphicsLineItem(this);
|
||||
fillItem->setLine(Rez::guiX(base->getStartPoint().x),
|
||||
Rez::guiX(-base->getStartPoint().y),
|
||||
Rez::guiX(base->getEndPoint().x),
|
||||
Rez::guiX(-base->getEndPoint().y));
|
||||
QGraphicsPathItem* fillItem = new QGraphicsPathItem(this);
|
||||
fillItem->setPath(dashedPPath(decodeDashSpec(ds),
|
||||
Rez::guiX(start),
|
||||
Rez::guiX(end)));
|
||||
return fillItem;
|
||||
}
|
||||
|
||||
QGraphicsPathItem* QGIFace::geomToLine(TechDrawGeometry::BaseGeom* 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(TechDrawGeometry::BaseGeom* 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;
|
||||
}
|
||||
|
||||
QPen QGIFace::setGeomPen(DashSpec ourSpec)
|
||||
QPen QGIFace::setGeomPen(void)
|
||||
{
|
||||
QPen result;
|
||||
result.setWidthF(Rez::guiX(m_geomWeight));
|
||||
// result.setWidthF(1.0);
|
||||
result.setColor(m_geomColor);
|
||||
if (ourSpec.empty()) {
|
||||
result.setStyle(Qt::SolidLine);
|
||||
} else {
|
||||
result.setStyle(Qt::CustomDashLine);
|
||||
result.setDashPattern(decodeDashSpec(ourSpec));
|
||||
}
|
||||
result.setStyle(Qt::SolidLine);
|
||||
return result;
|
||||
}
|
||||
|
||||
double QGIFace::calcOffset(TechDrawGeometry::BaseGeom* g,LineSet ls)
|
||||
{
|
||||
Base::Vector3d startPoint(g->getStartPoint().x,g->getStartPoint().y,0.0);
|
||||
Base::Vector3d appStart = ls.calcApparentStart(g);
|
||||
double distToStart = (startPoint - appStart).Length();
|
||||
double patternLength = ls.getDashSpec().length();
|
||||
|
||||
double penWidth = Rez::guiX(m_geomWeight);
|
||||
// double penWidth = 1.0;
|
||||
distToStart = Rez::guiX(distToStart); //distance in scene units/pixels?
|
||||
patternLength = Rez::guiX(patternLength) * penWidth; //pattern as it will be rendered by QPen (length*weight)
|
||||
double patternReps = distToStart / patternLength;
|
||||
double remain = patternReps - floor(patternReps); //fraction of a pattern
|
||||
double result = patternLength * remain;
|
||||
return result;
|
||||
}
|
||||
|
||||
//!convert from PAT style "-1,0,-1,+1" in mm to Qt style "mark,space,mark,space" in penWidths
|
||||
// the actual dash pattern/offset varies according to lineWeight, GraphicsView zoom level, scene unit size (and printer scale?).
|
||||
// haven't figured out the actual algorithm.
|
||||
// in Qt a dash length of l (8) with a pen of width w (2) yields a dash of length l*w (16), but this is only part of the equation.
|
||||
QVector<qreal> QGIFace::decodeDashSpec(DashSpec patDash)
|
||||
//!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;
|
||||
}
|
||||
double unitLength = penWidth;
|
||||
// double unitLength = 1.0;
|
||||
std::vector<double> result;
|
||||
for (auto& d: patDash.get()) {
|
||||
double strokeLength;
|
||||
if (DrawUtil::fpCompare(d,0.0)) { //pat dot
|
||||
strokeLength = unitLength;
|
||||
} else if (Rez::guiX(d) < 0) { //pat space
|
||||
strokeLength = fabs(Rez::guiX(d)) / unitLength;
|
||||
} else { //pat dash
|
||||
strokeLength = Rez::guiX(d) / unitLength;
|
||||
strokeLength = penWidth;
|
||||
} else { //pat mark/space
|
||||
strokeLength = Rez::guiX(d);
|
||||
}
|
||||
// //try to keep the pattern the same when View scales
|
||||
// strokeLength = strokeLength/getXForm();
|
||||
// Base::Console().Message("TRACE - QGIF - d: %.3f strokeLength: %.3f\n",d,strokeLength);
|
||||
result.push_back(strokeLength);
|
||||
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++;
|
||||
}
|
||||
|
||||
return QVector<qreal>::fromStdVector( result );
|
||||
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 result;
|
||||
double length = 0.0;
|
||||
for (auto& d: dv) {
|
||||
length += fabs(d);
|
||||
}
|
||||
if (offset > length) {
|
||||
result = 0.0;
|
||||
} else {
|
||||
result = length - offset;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//! get zoom level (scale) from QGraphicsView
|
||||
// not used currently
|
||||
double QGIFace::getXForm(void)
|
||||
{
|
||||
//try to keep the pattern the same when View scales
|
||||
@@ -351,8 +493,8 @@ void QGIFace::makeMark(double x, double y)
|
||||
QGICMark* cmItem = new QGICMark(-1);
|
||||
cmItem->setParentItem(this);
|
||||
cmItem->setPos(x,y);
|
||||
cmItem->setThick(0.5);
|
||||
cmItem->setSize(2.0);
|
||||
cmItem->setThick(1.0);
|
||||
cmItem->setSize(40.0);
|
||||
cmItem->setZValue(ZVALUE::VERTEX);
|
||||
}
|
||||
|
||||
@@ -463,6 +605,14 @@ void QGIFace::setLineWeight(double w) {
|
||||
m_geomWeight = w;
|
||||
}
|
||||
|
||||
void QGIFace::getParameters(void)
|
||||
{
|
||||
Base::Reference<ParameterGrp> hGrp = App::GetApplication().GetUserParameter()
|
||||
.GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/PAT");
|
||||
|
||||
m_maxSeg = hGrp->GetInt("MaxSeg",10000l);
|
||||
}
|
||||
|
||||
|
||||
QRectF QGIFace::boundingRect() const
|
||||
{
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <QByteArray>
|
||||
#include <QBrush>
|
||||
#include <QPixmap>
|
||||
//#include <QVector>
|
||||
|
||||
#include <Mod/TechDraw/App/HatchLine.h>
|
||||
#include <Mod/TechDraw/App/Geometry.h>
|
||||
@@ -108,9 +109,11 @@ public:
|
||||
void addLineSet(LineSet ls);
|
||||
void clearFillItems(void);
|
||||
|
||||
void lineSetToFillItem(LineSet ls);
|
||||
QGraphicsLineItem* geomToLine(TechDrawGeometry::BaseGeom* base);
|
||||
double calcOffset(TechDrawGeometry::BaseGeom* g,LineSet ls);
|
||||
void lineSetToFillItems(LineSet ls);
|
||||
QGraphicsPathItem* geomToLine(TechDrawGeometry::BaseGeom* base, LineSet ls);
|
||||
QGraphicsPathItem* geomToOffsetLine(TechDrawGeometry::BaseGeom* base, double offset, LineSet ls);
|
||||
QGraphicsPathItem* geomToStubbyLine(TechDrawGeometry::BaseGeom* base, double offset, LineSet ls);
|
||||
QGraphicsPathItem* lineFromPoints(Base::Vector3d start, Base::Vector3d end, DashSpec ds);
|
||||
|
||||
//bitmap texture fill parms method
|
||||
QPixmap textureFromBitmap(std::string fileSpec);
|
||||
@@ -119,8 +122,12 @@ public:
|
||||
protected:
|
||||
void makeMark(double x, double y);
|
||||
double getXForm(void);
|
||||
void getParameters(void);
|
||||
|
||||
|
||||
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(TechDrawGeometry::BaseGeom* g,LineSet ls);
|
||||
int projIndex; //index of face in Projection. -1 for SectionFace.
|
||||
QGCustomRect *m_rect;
|
||||
|
||||
@@ -133,11 +140,13 @@ protected:
|
||||
bool m_isHatched;
|
||||
QGIFace::fillMode m_mode;
|
||||
|
||||
QPen setGeomPen(DashSpec ds);
|
||||
QVector<qreal> decodeDashSpec(DashSpec d);
|
||||
std::vector<QGraphicsLineItem*> m_fillItems;
|
||||
QPen setGeomPen(void);
|
||||
std::vector<double> decodeDashSpec(DashSpec d);
|
||||
std::vector<QGraphicsPathItem*> m_fillItems;
|
||||
std::vector<LineSet> m_lineSets;
|
||||
std::vector<DashSpec> m_dashSpecs;
|
||||
long int m_segCount;
|
||||
long int m_maxSeg;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
Reference in New Issue
Block a user