diff --git a/src/Mod/TechDraw/App/DrawGeomHatch.cpp b/src/Mod/TechDraw/App/DrawGeomHatch.cpp index 4b05873783..0d71cebbcd 100644 --- a/src/Mod/TechDraw/App/DrawGeomHatch.cpp +++ b/src/Mod/TechDraw/App/DrawGeomHatch.cpp @@ -289,7 +289,7 @@ std::vector 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 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 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 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++) { diff --git a/src/Mod/TechDraw/App/HatchLine.cpp b/src/Mod/TechDraw/App/HatchLine.cpp index 6ebe3738af..c1d84d2772 100644 --- a/src/Mod/TechDraw/App/HatchLine.cpp +++ b/src/Mod/TechDraw/App/HatchLine.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #endif #include @@ -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 p = get(); + std::reverse(p.begin(),p.end()); + DashSpec result(p); + return result; +} + void DashSpec::dump(char* title) { std::stringstream ss; diff --git a/src/Mod/TechDraw/App/HatchLine.h b/src/Mod/TechDraw/App/HatchLine.h index 9ce3c44a7b..1d3c31eb1a 100644 --- a/src/Mod/TechDraw/App/HatchLine.h +++ b/src/Mod/TechDraw/App/HatchLine.h @@ -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 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 getDashParms(void) {return m_dashParms;} + double getSlope(void); + double getLength(void) {return m_dashParms.length(); } + DashSpec getDashParms(void) {return m_dashParms;} static std::vector 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 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 getEdges(void) { return m_edges; } - TopoDS_Edge getEdge(int i) {return m_edges.at(i);} + std::vector getEdges(void) { return m_edges; } + TopoDS_Edge getEdge(int i) {return m_edges.at(i);} std::vector 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); diff --git a/src/Mod/TechDraw/Gui/QGIFace.cpp b/src/Mod/TechDraw/Gui/QGIFace.cpp index e3fc49871d..332e330711 100644 --- a/src/Mod/TechDraw/Gui/QGIFace.cpp +++ b/src/Mod/TechDraw/Gui/QGIFace.cpp @@ -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 QGIFace::decodeDashSpec(DashSpec patDash) +//!convert from mm to scene units +std::vector 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 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 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 QGIFace::offsetDash(const std::vector dv, const double offset) +{ + std::vector 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::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 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 hGrp = App::GetApplication().GetUserParameter() + .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/PAT"); + + m_maxSeg = hGrp->GetInt("MaxSeg",10000l); +} + QRectF QGIFace::boundingRect() const { diff --git a/src/Mod/TechDraw/Gui/QGIFace.h b/src/Mod/TechDraw/Gui/QGIFace.h index 0b06d060cf..af83794c44 100644 --- a/src/Mod/TechDraw/Gui/QGIFace.h +++ b/src/Mod/TechDraw/Gui/QGIFace.h @@ -29,6 +29,7 @@ #include #include #include +//#include #include #include @@ -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 offsetDash(const std::vector dv, const double offset); + QPainterPath dashedPPath(const std::vector dv, const Base::Vector3d start, const Base::Vector3d end); + double dashRemain(const std::vector 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 decodeDashSpec(DashSpec d); - std::vector m_fillItems; + QPen setGeomPen(void); + std::vector decodeDashSpec(DashSpec d); + std::vector m_fillItems; std::vector m_lineSets; std::vector m_dashSpecs; + long int m_segCount; + long int m_maxSeg; private: