TechDraw: Fix hatch drawing (#19458)
* TechDraw: Fix hatch drawing (#16353) * TechDraw: Fix hatch drawing in Tech View
This commit is contained in:
@@ -317,10 +317,14 @@ std::vector<LineSet> DrawGeomHatch::getTrimmedLines(DrawViewPart* source,
|
||||
Bnd_Box bBox;
|
||||
BRepBndLib::AddOptimal(face, bBox);
|
||||
bBox.SetGap(0.0);
|
||||
gp_Vec translateVector(hatchOffset.x, hatchOffset.y, 0.);
|
||||
auto cornerMin = bBox.CornerMin().Translated(-translateVector);
|
||||
auto cornerMax = bBox.CornerMax().Translated(-translateVector);
|
||||
bBox = Bnd_Box(cornerMin, cornerMax);
|
||||
|
||||
for (auto& ls: lineSets) {
|
||||
PATLineSpec hl = ls.getPATLineSpec();
|
||||
std::vector<TopoDS_Edge> candidates = DrawGeomHatch::makeEdgeOverlay(hl, bBox, scale); //completely cover face bbox with lines
|
||||
std::vector<TopoDS_Edge> candidates = DrawGeomHatch::makeEdgeOverlay(hl, bBox, scale, hatchRotation); //completely cover face bbox with lines
|
||||
|
||||
//make Compound for this linespec
|
||||
BRep_Builder builder;
|
||||
@@ -331,14 +335,6 @@ std::vector<LineSet> DrawGeomHatch::getTrimmedLines(DrawViewPart* source,
|
||||
}
|
||||
|
||||
TopoDS_Shape grid = gridComp;
|
||||
if (hatchRotation != 0.0) {
|
||||
double hatchRotationRad = hatchRotation * M_PI / 180.0;
|
||||
gp_Ax1 gridAxis(gp_Pnt(0.0, 0.0, 0.0), gp_Vec(gp::OZ().Direction()));
|
||||
gp_Trsf xGridRotate;
|
||||
xGridRotate.SetRotation(gridAxis, hatchRotationRad);
|
||||
BRepBuilderAPI_Transform mkTransRotate(grid, xGridRotate, true);
|
||||
grid = mkTransRotate.Shape();
|
||||
}
|
||||
gp_Trsf xGridTranslate;
|
||||
xGridTranslate.SetTranslation(DrawUtil::to<gp_Vec>(hatchOffset));
|
||||
BRepBuilderAPI_Transform mkTransTranslate(grid, xGridTranslate, true);
|
||||
@@ -386,114 +382,104 @@ std::vector<LineSet> DrawGeomHatch::getTrimmedLines(DrawViewPart* source,
|
||||
}
|
||||
|
||||
/* static */
|
||||
std::vector<TopoDS_Edge> DrawGeomHatch::makeEdgeOverlay(PATLineSpec hatchLine, Bnd_Box bBox, double scale)
|
||||
std::vector<TopoDS_Edge> DrawGeomHatch::makeEdgeOverlay(PATLineSpec hatchLine, Bnd_Box bBox, double scale, double rotation)
|
||||
{
|
||||
constexpr double RightAngleDegrees{90.0};
|
||||
constexpr double HalfCircleDegrees{180.0};
|
||||
std::vector<TopoDS_Edge> result;
|
||||
const size_t MaxNumberOfEdges = Preferences::getPreferenceGroup("PAT")->GetInt("MaxSeg", 10000l);
|
||||
|
||||
std::vector<TopoDS_Edge> result;
|
||||
double minX, maxX, minY, maxY, minZ, maxZ;
|
||||
bBox.Get(minX, minY, minZ, maxX, maxY, maxZ);
|
||||
//make the overlay bigger to cover rotations. might need to be bigger than 2x.
|
||||
double widthX = maxX - minX;
|
||||
double widthY = maxY - minY;
|
||||
double width = std::max(widthX, widthY);
|
||||
Base::Vector3d topLeft(minX, maxY, 0.);
|
||||
Base::Vector3d topRight(maxX, maxY, 0.);
|
||||
Base::Vector3d bottomLeft(minX, minY, 0.);
|
||||
Base::Vector3d bottomRight(maxX, minY, 0.);
|
||||
|
||||
double centerX = (minX + maxX) / 2;
|
||||
minX = centerX - width;
|
||||
maxX = centerX + width;
|
||||
double centerY = (minY + maxY) / 2;
|
||||
minY = centerY - width;
|
||||
maxY = centerY + width;
|
||||
Base::Vector3d origin = hatchLine.getOrigin() * scale;
|
||||
double interval = hatchLine.getInterval() * scale;
|
||||
double offset = hatchLine.getOffset() * scale;
|
||||
double angle = hatchLine.getAngle() + rotation;
|
||||
origin.RotateZ(rotation * M_PI / 180.);
|
||||
|
||||
Base::Vector3d origin = hatchLine.getOrigin();
|
||||
double interval = hatchLine.getIntervalX() * scale;
|
||||
double angle = hatchLine.getAngle();
|
||||
if (scale == 0. || interval == 0.)
|
||||
return {};
|
||||
|
||||
//only dealing with angles -180:180 for now
|
||||
if (angle > RightAngleDegrees) {
|
||||
angle = -(HalfCircleDegrees - angle);
|
||||
} else if (angle < -RightAngleDegrees) {
|
||||
angle = (HalfCircleDegrees + angle);
|
||||
Base::Vector3d hatchDirection(cos(angle * M_PI / 180.), sin(angle * M_PI / 180.), 0.);
|
||||
Base::Vector3d hatchPerpendicular(-hatchDirection.y, hatchDirection.x, 0.);
|
||||
Base::Vector3d hatchIntervalAndOffset = offset * hatchDirection + interval * hatchPerpendicular;
|
||||
|
||||
std::array<double, 4> orthogonalProjections = {
|
||||
(topLeft - origin).Dot(hatchPerpendicular / interval),
|
||||
(topRight - origin).Dot(hatchPerpendicular / interval),
|
||||
(bottomLeft - origin).Dot(hatchPerpendicular / interval),
|
||||
(bottomRight - origin).Dot(hatchPerpendicular / interval)
|
||||
};
|
||||
auto minMaxIterators = std::minmax_element(orthogonalProjections.begin(), orthogonalProjections.end());
|
||||
int firstRepeatIndex = ceil(*minMaxIterators.first);
|
||||
int lastRepeatIndex = floor(*minMaxIterators.second);
|
||||
|
||||
std::vector<double> dashParams = hatchLine.getDashParms().get();
|
||||
double globalDashStep = 0.;
|
||||
if (dashParams.empty()) {
|
||||
// we define a single dash with length equal to twice the diagonal of the bounding box
|
||||
double diagonalLength = (topRight - bottomLeft).Length();
|
||||
dashParams.push_back(2. * diagonalLength);
|
||||
globalDashStep = diagonalLength;
|
||||
}
|
||||
double slope = hatchLine.getSlope();
|
||||
|
||||
if (angle == 0.0) { //odd case 1: horizontal lines
|
||||
interval = hatchLine.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, 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 {
|
||||
for (auto& x : dashParams) {
|
||||
x *= scale;
|
||||
globalDashStep += std::abs(x);
|
||||
}
|
||||
} else if (angle == RightAngleDegrees ||
|
||||
angle == -RightAngleDegrees) { //odd case 2: vertical lines
|
||||
interval = hatchLine.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;
|
||||
}
|
||||
if (globalDashStep == 0.) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// make repeats
|
||||
for (int i = 0; i < repeatTotal; i++) {
|
||||
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);
|
||||
// we handle hatch as a set of parallel lines made of dashes, here we loop on each line
|
||||
for (int i = firstRepeatIndex ; i <= lastRepeatIndex ; ++i) {
|
||||
Base::Vector3d currentOrigin = origin + static_cast<double>(i) * hatchIntervalAndOffset;
|
||||
|
||||
int firstDashIndex, lastDashIndex;
|
||||
if (std::abs(hatchDirection.x) > std::abs(hatchDirection.y)) { // we compute intersections with minX and maxX
|
||||
firstDashIndex = (hatchDirection.x > 0.)
|
||||
? std::floor((minX - currentOrigin.x) / (globalDashStep * hatchDirection.x))
|
||||
: std::floor((maxX - currentOrigin.x) / (globalDashStep * hatchDirection.x));
|
||||
lastDashIndex = (hatchDirection.x > 0.)
|
||||
? std::ceil((maxX - currentOrigin.x) / (globalDashStep * hatchDirection.x))
|
||||
: std::ceil((minX - currentOrigin.x) / (globalDashStep * hatchDirection.x));
|
||||
}
|
||||
//TODO: check if this makes 2-3 extra lines. might be some "left" lines on "right" side of vv
|
||||
} else if (angle > 0) { //oblique (bottom left -> top right)
|
||||
//ex: 60, 0,0, 0,4.0, 25, -25
|
||||
// Base::Console().Message("TRACE - DGH-makeEdgeOverlay - making angle > 0\n");
|
||||
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) 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 + 1;
|
||||
|
||||
//make repeats
|
||||
for (int i = 0; i < repeatTotal; i++) {
|
||||
Base::Vector3d newStart(leftStartX + (float(i) * interval), minY, 0);
|
||||
Base::Vector3d newEnd (leftEndX + (float(i) * interval), maxY, 0);
|
||||
TopoDS_Edge newLine = makeLine(newStart, newEnd);
|
||||
result.push_back(newLine);
|
||||
else { // we compute intersections with minY and maxY
|
||||
firstDashIndex = (hatchDirection.y > 0.)
|
||||
? std::floor((minY - currentOrigin.y) / (globalDashStep * hatchDirection.y))
|
||||
: std::floor((maxY - currentOrigin.y) / (globalDashStep * hatchDirection.y));
|
||||
lastDashIndex = (hatchDirection.y > 0.)
|
||||
? std::ceil((maxY - currentOrigin.y) / (globalDashStep * hatchDirection.y))
|
||||
: std::ceil((minY - currentOrigin.y) / (globalDashStep * hatchDirection.y));
|
||||
}
|
||||
} else { //oblique (bottom right -> top left)
|
||||
// ex: -60, 0,0, 0,4.0, 25.0, -12.5, 12.5, -6
|
||||
// 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) 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 + 1;
|
||||
|
||||
// make repeats
|
||||
for (int i = 0; i < repeatTotal; i++) {
|
||||
Base::Vector3d newStart(leftStartX + float(i)*interval, minY, 0);
|
||||
Base::Vector3d newEnd(leftEndX + float(i)*interval, maxY, 0);
|
||||
TopoDS_Edge newLine = makeLine(newStart, newEnd);
|
||||
result.push_back(newLine);
|
||||
for (int j = firstDashIndex ; j < lastDashIndex ; ++j) {
|
||||
Base::Vector3d current = currentOrigin + static_cast<double>(j) * globalDashStep * hatchDirection;
|
||||
for (auto dashParamsIterator = dashParams.begin() ; dashParamsIterator != dashParams.end() ; ++dashParamsIterator) {
|
||||
double len = *dashParamsIterator;
|
||||
Base::Vector3d next = current + std::abs(len) * hatchDirection;
|
||||
if (len > 0. && (current.x >= minX || next.x >= minX) && (current.x <= maxX || next.x <= maxX)
|
||||
&& (current.y >= minY || next.y >= minY) && (current.y <= maxY || next.y <= maxY)) {
|
||||
TopoDS_Edge newLine = makeLine(current, next);
|
||||
result.push_back(newLine);
|
||||
}
|
||||
std::swap(current, next);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.size() > MaxNumberOfEdges) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TopoDS_Edge DrawGeomHatch::makeLine(Base::Vector3d s, Base::Vector3d e)
|
||||
TopoDS_Edge DrawGeomHatch::makeLine(const Base::Vector3d& s, const Base::Vector3d& e)
|
||||
{
|
||||
gp_Pnt start(s.x, s.y, 0.0);
|
||||
gp_Pnt end(e.x, e.y, 0.0);
|
||||
@@ -527,7 +513,7 @@ std::vector<LineSet> DrawGeomHatch::getFaceOverlay(int iFace)
|
||||
|
||||
for (auto& ls: m_lineSets) {
|
||||
PATLineSpec hl = ls.getPATLineSpec();
|
||||
std::vector<TopoDS_Edge> candidates = DrawGeomHatch::makeEdgeOverlay(hl, bBox, ScalePattern.getValue());
|
||||
std::vector<TopoDS_Edge> candidates = DrawGeomHatch::makeEdgeOverlay(hl, bBox, ScalePattern.getValue(), PatternRotation.getValue());
|
||||
std::vector<TechDraw::BaseGeomPtr> resultGeoms;
|
||||
for (auto& e: candidates) {
|
||||
TechDraw::BaseGeomPtr base = BaseGeom::baseFactory(e);
|
||||
|
||||
@@ -94,8 +94,8 @@ public:
|
||||
Base::Vector3d hatchOffset = Base::Vector3d(0.0, 0.0, 0.0));
|
||||
|
||||
static std::vector<TopoDS_Edge> makeEdgeOverlay(PATLineSpec hatchLine, Bnd_Box bBox,
|
||||
double scale);
|
||||
static TopoDS_Edge makeLine(Base::Vector3d start, Base::Vector3d end);
|
||||
double scale, double rotation);
|
||||
static TopoDS_Edge makeLine(const Base::Vector3d& start, const Base::Vector3d& end);
|
||||
static std::vector<PATLineSpec> getDecodedSpecsFromFile(std::string fileSpec, std::string myPattern);
|
||||
static TopoDS_Face extractFace(DrawViewPart* source, int iface );
|
||||
static std::string prefGeomHatchFile();
|
||||
|
||||
@@ -110,21 +110,8 @@ Base::Vector3d LineSet::getUnitDir()
|
||||
|
||||
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;
|
||||
return Base::Vector3d(-unit.y, unit.x, 0.0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -57,37 +57,10 @@ 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);
|
||||
}
|
||||
//geom is a tdGeometry representation of 1 line in the pattern //not dashed
|
||||
QGraphicsPathItem* fillItem = simpleLine(geom);
|
||||
fillItem->setPen(pen);
|
||||
m_fillItems.push_back(fillItem);
|
||||
|
||||
if (m_segCount > m_maxSeg) {
|
||||
Base::Console().Warning("PAT segment count exceeded: %ld\n", m_segCount);
|
||||
@@ -124,6 +97,22 @@ QGraphicsPathItem* PATPathMaker::geomToLine(TechDraw::BaseGeomPtr base, LineSet
|
||||
return fillItem;
|
||||
}
|
||||
|
||||
/// create a simple line from geometry
|
||||
QGraphicsPathItem* PATPathMaker::simpleLine(TechDraw::BaseGeomPtr base)
|
||||
{
|
||||
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(std::vector<double>(),
|
||||
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)
|
||||
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
|
||||
protected:
|
||||
QGraphicsPathItem* geomToLine(TechDraw::BaseGeomPtr base, TechDraw::LineSet& ls);
|
||||
QGraphicsPathItem* simpleLine(TechDraw::BaseGeomPtr base);
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user