/*************************************************************************** * Copyright (c) 2013 Luke Parry * * * * 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 # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include // # include # include # include # include # include # include # include # include # include #include #endif // #ifndef _PreComp_ #include # include # include # include # include # include # include "GeometryObject.h" //#include using namespace TechDrawGeometry; struct EdgePoints { gp_Pnt v1, v2; TopoDS_Edge edge; }; GeometryObject::GeometryObject() : brep_hlr(0), Tolerance(0.05f), Scale(1.f) { } GeometryObject::~GeometryObject() { clear(); } void GeometryObject::setTolerance(double value) { Tolerance = value; } void GeometryObject::setScale(double value) { Scale = value; } void GeometryObject::clear() { for(std::vector::iterator it = edgeGeom.begin(); it != edgeGeom.end(); ++it) { delete *it; *it = 0; } for(std::vector::iterator it = faceGeom.begin(); it != faceGeom.end(); ++it) { delete *it; *it = 0; } for(std::vector::iterator it = vertexGeom.begin(); it != vertexGeom.end(); ++it) { delete *it; *it = 0; } vertexGeom.clear(); vertexReferences.clear(); faceGeom.clear(); faceReferences.clear(); edgeGeom.clear(); edgeReferences.clear(); } void GeometryObject::drawFace (const bool visible, const int iface, Handle_HLRBRep_Data & DS, TopoDS_Shape& Result) const { // add all the edges for this face(iface) to Result HLRBRep_FaceIterator Itf; for (Itf.InitEdge(DS->FDataArray().ChangeValue(iface)); Itf.MoreEdge(); Itf.NextEdge()) { int ie = Itf.Edge(); // if (std::find(used.begin(),used.end(),ie) == used.end()) { //only use an edge once HLRBRep_EdgeData& edf = DS->EDataArray().ChangeValue(ie); if(edf.Status().NbVisiblePart() > 0) { drawEdge(edf, Result, visible); } // double first = edf.Geometry().FirstParameter(); // double last = edf.Geometry().LastParameter(); // gp_Pnt p0 = edf.Geometry().Value3D(first); // gp_Pnt p1 = edf.Geometry().Value3D(last); // qDebug()< &projFaces) const { if(face.IsNull()) throw Base::Exception("Projected shape is null"); gp_Pnt supportCentre = findCentroid(support, direction, xaxis); // TODO: We used to invert Y twice here, make sure that wasn't intentional gp_Trsf mat; mat.SetMirror(gp_Ax2(supportCentre, gp_Dir(0, 1, 0))); gp_Trsf matScale; matScale.SetScale(supportCentre, Scale); mat.Multiply(matScale); BRepBuilderAPI_Transform mkTrfScale(face, mat); gp_Ax2 transform; transform = gp_Ax2(supportCentre, gp_Dir(direction.x, direction.y, direction.z), gp_Dir(xaxis.x, xaxis.y, xaxis.z)); HLRBRep_Algo *brep_hlr = new HLRBRep_Algo(); brep_hlr->Add(mkTrfScale.Shape()); HLRAlgo_Projector projector( transform ); brep_hlr->Projector(projector); brep_hlr->Update(); brep_hlr->Hide(); Base::Console().Log("GeometryObject::projectSurfaces - projecting face\n"); // Extract Faces std::vector projFaceRefs; extractFaces(brep_hlr, mkTrfScale.Shape(), true, WithSmooth, projFaces, projFaceRefs); delete brep_hlr; } Base::BoundBox3d GeometryObject::boundingBoxOfBspline(const BSpline *spline) const { Base::BoundBox3d bb; for (std::vector::const_iterator segItr( spline->segments.begin() ); segItr != spline->segments.end(); ++segItr) { switch (segItr->poles) { case 0: // Degenerate, but safe ignore break; case 2: // Degenerate - straight line bb.Add(Base::Vector3d( segItr->pnts[1].fX, segItr->pnts[1].fY, 0 )); // fall through case 1: // Degenerate - just a point bb.Add(Base::Vector3d( segItr->pnts[0].fX, segItr->pnts[0].fY, 0 )); break; case 3: { double px[3] = { segItr->pnts[0].fX, segItr->pnts[1].fX, segItr->pnts[2].fX }, py[3] = { segItr->pnts[0].fY, segItr->pnts[1].fY, segItr->pnts[2].fY }, slns[4] = { 0, 1 }; // Consider the segment's end points // if's are to prevent problems with divide-by-0 if ((2 * px[1] - px[0] - px[2]) == 0) { slns[2] = -1; } else { slns[2] = (px[1] - px[0]) / (2 * px[1] - px[0] - px[2]); } if ((2 * py[1] - py[0] - py[2]) == 0) { slns[3] = -1; } else { slns[3] = (py[1] - py[0]) / (2 * py[1] - py[0] - py[2]); } // evaluate B(t) at the endpoints and zeros for (int s(0); s < 4; ++s) { double t( slns[s] ); if (t < 0 || t > 1) { continue; } double tx( px[0] * (1 - t) * (1 - t) + px[1] * 2 * (1 - t) * t + px[2] * t * t ), ty( py[0] * (1 - t) * (1 - t) + py[1] * 2 * (1 - t) * t + py[2] * t * t ); bb.Add( Base::Vector3d(tx, ty, 0) ); } } break; case 4: { double px[4] = { segItr->pnts[0].fX, segItr->pnts[1].fX, segItr->pnts[2].fX, segItr->pnts[3].fX }, py[4] = { segItr->pnts[0].fY, segItr->pnts[1].fY, segItr->pnts[2].fY, segItr->pnts[3].fY }, // If B(t) is the cubic Bezier, find t where B'(t) == 0 // // For control points P0-P3, B'(t) works out to be: // B'(t) = t^2 * (-3P0 + 9P1 - 9P2 + 3P3) + // t * (6P0 - 12P1 + 6P2) + // 3 * (P1 - P0) // // So, we use the quadratic formula! ax = -3 * px[0] + 9 * px[1] - 9 * px[2] + 3 * px[3], ay = -3 * py[0] + 9 * py[1] - 9 * py[2] + 3 * py[3], bx = 6 * px[0] - 12 * px[1] + 6 * px[2], by = 6 * py[0] - 12 * py[1] + 6 * py[2], cx = 3 * px[1] - 3 * px[0], cy = 3 * py[1] - 3 * py[0], slns[6] = { 0, 1 }; // Consider the segment's end points // if's are to prevent problems with divide-by-0 and NaN if ( (2 * ax) == 0 || (bx * bx - 4 * ax * cx) < 0 ) { slns[2] = -1; slns[3] = -1; } else { slns[2] = (-bx + sqrt(bx * bx - 4 * ax * cx)) / (2 * ax); slns[3] = (-bx - sqrt(bx * bx - 4 * ax * cx)) / (2 * ax); } if ((2 * ay) == 0 || (by * by - 4 * ay * cy) < 0 ) { slns[4] = -1; slns[5] = -1; } else { slns[4] = (-by + sqrt(by * by - 4 * ay * cy)) / (2 * ay); slns[5] = (-by - sqrt(by * by - 4 * ay * cy)) / (2 * ay); } // evaluate B(t) at the endpoints and zeros for (int s(0); s < 6; ++s) { double t( slns[s] ); if (t < 0 || t > 1) { continue; } double tx( px[0] * (1 - t) * (1 - t) * (1 - t) + px[1] * 3 * (1 - t) * (1 - t) * t + px[2] * 3 * (1 - t) * t * t + px[3] * t * t * t ), ty( py[0] * (1 - t) * (1 - t) * (1 - t) + py[1] * 3 * (1 - t) * (1 - t) * t + py[2] * 3 * (1 - t) * t * t + py[3] * t * t * t ); bb.Add( Base::Vector3d(tx, ty, 0) ); } } break; default: throw Base::Exception("Invalid degree bezier segment in GeometryObject::calcBoundingBox"); } } return bb; } Base::BoundBox3d GeometryObject::boundingBoxOfAoe(const Ellipse *aoe, double start, double end, bool cw) const { // Using the ellipse form: // (xc, yc) = centre of ellipse // phi = angle of ellipse major axis off X axis // a, b = half of major, minor axes // // x(theta) = xc + a*cos(theta)*cos(phi) - b*sin(theta)*sin(phi) // y(theta) = yc + a*cos(theta)*sin(phi) + b*sin(theta)*cos(phi) double a (aoe->major / 2.0), b (aoe->minor / 2.0), phi (aoe->angle), xc (aoe->center.fX), yc (aoe->center.fY); if (a == 0 || b == 0) { // Degenerate case - TODO: handle as line instead of throwing throw Base::Exception("Ellipse with invalid major axis in GeometryObject::boundingBoxOfAoe()"); } // Calculate points of interest for the bounding box. These are points // where d(x)/d(theta) and d(y)/d(theta) = 0 (where the x and y extremes // of the ellipse would be if it were complete), and arc endpoints. double testAngles[6] = { atan(tan(phi) * (-b / a)), testAngles[0] + M_PI }; if (tan(phi) == 0) { testAngles[2] = M_PI / 2.0; testAngles[3] = 3.0 * M_PI / 2.0; } else { testAngles[2] = atan((1.0 / tan(phi)) * (b / a)); testAngles[3] = testAngles[2] + M_PI; } testAngles[4] = start; testAngles[5] = end; // Add extremes to bounding box, if they are within the arc Base::BoundBox3d bb; for (int ai(0); ai < 6; ++ai) { double theta(testAngles[ai]); if (isWithinArc(theta, start, end, cw) ) { bb.Add( Base::Vector3d(xc + a*cos(theta)*cos(phi) - b*sin(theta)*sin(phi), yc + a*cos(theta)*sin(phi) - b*sin(theta)*cos(phi), 0) ); } } return bb; } bool GeometryObject::isWithinArc(double theta, double first, double last, bool cw) const { if (fabs(last - first) >= 2 * M_PI) { return true; } // Put params within [0, 2*pi) - not totally sure this is necessary theta = fmod(theta, 2 * M_PI); if (theta < 0) { theta += 2 * M_PI; } first = fmod(first, 2 * M_PI); if (first < 0) { first += 2 * M_PI; } last = fmod(last, 2 * M_PI); if (last < 0) { last += 2 * M_PI; } if (cw) { if (first > last) { return theta <= first && theta >= last; } else { return theta <= first || theta >= last; } } else { if (first > last) { return theta >= first || theta <= last; } else { return theta >= first && theta <= last; } } } Base::BoundBox3d GeometryObject::calcBoundingBox() const { Base::BoundBox3d bbox; // First calculate bounding box based on vertices for(std::vector::const_iterator it( vertexGeom.begin() ); it != vertexGeom.end(); ++it) { bbox.Add( Base::Vector3d((*it)->pnt.fX, (*it)->pnt.fY, 0.) ); } // Now, consider geometry where vertices don't define bounding box eg circles for (std::vector::const_iterator it( edgeGeom.begin() ); it != edgeGeom.end(); ++it) { switch ((*it)->geomType) { case CIRCLE: { Circle *c = static_cast(*it); bbox.Add( Base::BoundBox3d(-c->radius + c->center.fX, -c->radius + c->center.fY, 0, c->radius + c->center.fX, c->radius + c->center.fY, 0) ); } break; case ARCOFCIRCLE: { AOC *arc = static_cast(*it); // Endpoints of arc bbox.Add( Base::Vector3d(arc->radius * cos(arc->startAngle), arc->radius * sin(arc->startAngle), 0.0) ); bbox.Add( Base::Vector3d(arc->radius * cos(arc->endAngle), arc->radius * sin(arc->endAngle), 0.0) ); // Extreme X and Y values if they're within the arc for (double theta = 0.0; theta < 6.5; theta += M_PI / 2.0) { if (isWithinArc(theta, arc->startAngle, arc->endAngle, arc->cw)) { bbox.Add( Base::Vector3d(arc->radius * cos(theta), arc->radius * sin(theta), 0.0) ); } } } break; case ELLIPSE: { bbox.Add( boundingBoxOfAoe(static_cast(*it)) ); } break; case ARCOFELLIPSE: { AOE *aoe = static_cast(*it); double start = aoe->startAngle, end = aoe->endAngle; bool cw = aoe->cw; bbox.Add( boundingBoxOfAoe(static_cast(*it), start, end, cw) ); } break; case BSPLINE: { bbox.Add( boundingBoxOfBspline(static_cast(*it)) ); } break; case GENERIC: { // this case ends up just drawing line segments between points Generic *gen = static_cast(*it); for (std::vector::const_iterator segIt = gen->points.begin(); segIt != gen->points.end(); ++segIt) { bbox.Add( Base::Vector3d(segIt->fX, segIt->fY, 0) ); } } break; default: throw Base::Exception("Unknown geomType in GeometryObject::calcBoundingBox()"); } } return bbox; } //! only ever called from fvp::getCompleteEdge DrawingGeometry::BaseGeom * GeometryObject::projectEdge(const TopoDS_Shape &edge, const TopoDS_Shape &support, const Base::Vector3d &direction, const Base::Vector3d &projXAxis) const { if(edge.IsNull()) throw Base::Exception("Projected edge is null"); // Invert y function using support to calculate bounding box gp_Pnt supportCentre = findCentroid(support, direction, projXAxis); gp_Trsf mat; mat.SetMirror(gp_Ax2(supportCentre, gp_Dir(0, 1, 0))); gp_Trsf matScale; matScale.SetScale(supportCentre, Scale); mat.Multiply(matScale); BRepBuilderAPI_Transform mkTrfScale(edge, mat); const TopoDS_Edge &refEdge = TopoDS::Edge(mkTrfScale.Shape()); gp_Ax2 transform; transform = gp_Ax2(supportCentre, gp_Dir(direction.x, direction.y, direction.z), gp_Dir(projXAxis.x, projXAxis.y, projXAxis.z)); BRepAdaptor_Curve refCurve(refEdge); HLRAlgo_Projector projector = HLRAlgo_Projector( transform ); projector.Scaled(true); if (refCurve.GetType() == GeomAbs_Line) { // Use the simpler algorithm for lines gp_Pnt p1 = refCurve.Value(refCurve.FirstParameter()); gp_Pnt p2 = refCurve.Value(refCurve.LastParameter()); // Project the points gp_Pnt2d pnt1, pnt2; projector.Project(p1, pnt1); projector.Project(p2, pnt2); DrawingGeometry::Generic *line = new DrawingGeometry::Generic(); line->points.push_back(Base::Vector2D(pnt1.X(), pnt1.Y())); line->points.push_back(Base::Vector2D(pnt2.X(), pnt2.Y())); return line; } else { HLRBRep_Curve curve; curve.Curve(refEdge); curve.Projector(&projector); DrawingGeometry::BaseGeom *result = 0; switch(HLRBRep_BCurveTool::GetType(curve.Curve())) { case GeomAbs_Line: { DrawingGeometry::Generic *line = new DrawingGeometry::Generic(); gp_Pnt2d pnt1 = curve.Value(curve.FirstParameter()); gp_Pnt2d pnt2 = curve.Value(curve.LastParameter()); line->points.push_back(Base::Vector2D(pnt1.X(), pnt1.Y())); line->points.push_back(Base::Vector2D(pnt2.X(), pnt2.Y())); result = line; }break; case GeomAbs_Circle: { DrawingGeometry::Circle *circle = new DrawingGeometry::Circle(); gp_Circ2d prjCirc = curve.Circle(); double f = curve.FirstParameter(); double l = curve.LastParameter(); gp_Pnt2d s = curve.Value(f); gp_Pnt2d e = curve.Value(l); if (fabs(l-f) > 1.0 && s.SquareDistance(e) < 0.001) { circle->radius = prjCirc.Radius(); circle->center = Base::Vector2D(prjCirc.Location().X(), prjCirc.Location().Y()); result = circle; } else { AOC *aoc = new AOC(); aoc->radius = prjCirc.Radius(); aoc->center = Base::Vector2D(prjCirc.Location().X(), prjCirc.Location().Y()); double ax = s.X() - aoc->center.fX; double ay = s.Y() - aoc->center.fY; double bx = e.X() - aoc->center.fX; double by = e.Y() - aoc->center.fY; aoc->startAngle = atan2(ay,ax); float range = atan2(-ay*bx+ax*by, ax*bx+ay*by); aoc->endAngle = aoc->startAngle + range; result = aoc; } } break; case GeomAbs_Ellipse: { gp_Elips2d prjEllipse = curve.Ellipse(); double f = curve.FirstParameter(); double l = curve.LastParameter(); gp_Pnt2d s = curve.Value(f); gp_Pnt2d e = curve.Value(l); if (fabs(l-f) > 1.0 && s.SquareDistance(e) < 0.001) { Ellipse *ellipse = new Ellipse(); ellipse->major = prjEllipse.MajorRadius(); ellipse->minor = prjEllipse.MinorRadius(); ellipse->center = Base::Vector2D(prjEllipse.Location().X(), prjEllipse.Location().Y()); result = ellipse; // TODO: Finish implementing ellipsey stuff } else { // TODO implement this correctly AOE *aoe = new AOE(); aoe->major = prjEllipse.MajorRadius(); aoe->minor = prjEllipse.MinorRadius(); aoe->center = Base::Vector2D(prjEllipse.Location().X(), prjEllipse.Location().Y()); result = aoe; } } break; case GeomAbs_BSplineCurve: { // TODO: Project BSpline here? } break; default: break; } return result; } } /* TODO: Clean this up when faces are actually working properly... void debugEdge(const TopoDS_Edge &e) { gp_Pnt p0 = BRep_Tool::Pnt(TopExp::FirstVertex(e)); gp_Pnt p1 = BRep_Tool::Pnt(TopExp::LastVertex(e)); qDebug()< &projFaces, std::vector &faceRefs) const { #if MOD_TECHDRAW_HANDLE_FACES if(!myAlgo) return; Handle_HLRBRep_Data DS = myAlgo->DataStructure(); if (DS.IsNull()) { Base::Console().Log("TechDraw::GeometryObject::extractFaces - DS is Null\n"); return; } DS->Projector().Scaled(true); int f1 = 1; int f2 = DS->NbFaces(); /* This block seems to set f1 and f2 to indices using a HLRBRep_ShapeBounds * object based that's based on myAlgo, but DS is also based on myAlgo too, * so I don't think this is required. IR if (!S.IsNull()) { int e1 = 1; int e2 = DS->NbEdges(); Standard_Integer v1,v2; Standard_Integer index = myAlgo->Index(S); if(index == 0) { Base::Console().Log("TechDraw::GeometryObject::extractFaces - myAlgo->Index(S) == 0\n"); return; } myAlgo->ShapeBounds(index).Bounds(v1, v2, e1, e2, f1, f2); } */ TopTools_IndexedMapOfShape anfIndices; TopTools_IndexedMapOfShape& Faces = DS->FaceMap(); TopExp::MapShapes(S, TopAbs_FACE, anfIndices); BRep_Builder B; /* ----------------- Extract Faces ------------------ */ for (int iface = f1; iface <= f2; iface++) { // Why oh why does Hiding() == true mean that a face is visible... if (! DS->FDataArray().ChangeValue(iface).Hiding()) { continue; } TopoDS_Shape face; B.MakeCompound(TopoDS::Compound(face)); // Generate a set of new wires based on face // TODO: Do these end up with input face's geometry as a base? drawFace(visible, iface, DS, face); std::vector possibleFaceWires; createWire(face, possibleFaceWires); DrawingGeometry::Face *myFace = NULL; // Process each wire - if we can make at least one face with it, then // send it down the road toward rendering for (std::vector::iterator wireIt = possibleFaceWires.begin(); wireIt != possibleFaceWires.end(); ++wireIt) { // Try making a face out of the wire, before doing anything else with it BRepBuilderAPI_MakeFace testFace(*wireIt); if (testFace.IsDone()) { if (myFace == NULL) { myFace = new DrawingGeometry::Face(); } DrawingGeometry::Wire *genWire = new DrawingGeometry::Wire(); // See createWire regarding BRepTools_WireExplorer vs TopExp_Explorer BRepTools_WireExplorer explr(*wireIt); while (explr.More()) { BRep_Builder builder; TopoDS_Compound comp; builder.MakeCompound(comp); builder.Add(comp, explr.Current()); calculateGeometry(comp, Plain, genWire->geoms); explr.Next(); } myFace->wires.push_back(genWire); } } if (myFace != NULL) { projFaces.push_back(myFace); } // I'm pretty sure this doesn't do what it's intended to do. IR int idxFace; for (int i = 1; i <= anfIndices.Extent(); i++) { idxFace = Faces.FindIndex(anfIndices(iface)); if (idxFace != 0) { break; } } if(idxFace == 0) idxFace = -1; // If Face not found - select hidden // Push the found face index onto references stack faceRefs.push_back(idxFace); } DS->Projector().Scaled(false); #endif //#if MOD_TECHDRAW_HANDLE_FACES } bool GeometryObject::shouldDraw(const bool inFace, const int typ, HLRBRep_EdgeData& ed) { bool todraw = false; if(inFace) todraw = true; else if (typ == 3) todraw = ed.Rg1Line() && !ed.RgNLine(); //smooth + !contour? else if (typ == 4) todraw = ed.RgNLine(); //contour? else todraw =!ed.Rg1Line(); //!smooth? return todraw; } void GeometryObject::extractVerts(HLRBRep_Algo *myAlgo, const TopoDS_Shape &S, HLRBRep_EdgeData& ed, int ie, ExtractionType extractionType) { if(!myAlgo) return; Handle_HLRBRep_Data DS = myAlgo->DataStructure(); if (DS.IsNull()) return; DS->Projector().Scaled(true); TopTools_IndexedMapOfShape anIndices; TopTools_IndexedMapOfShape anvIndices; TopExp::MapShapes(S, TopAbs_EDGE, anIndices); TopExp::MapShapes(S, TopAbs_VERTEX, anvIndices); // Load the edge if(ie < 0) { } else { TopoDS_Shape shape = anIndices.FindKey(ie); TopoDS_Edge edge = TopoDS::Edge(shape); // Gather a list of points associated with this curve std::list edgePoints; TopExp_Explorer xp; xp.Init(edge,TopAbs_VERTEX); while(xp.More()) { edgePoints.push_back(xp.Current()); xp.Next(); } for(std::list::const_iterator it = edgePoints.begin(); it != edgePoints.end(); ++it) { // Should share topological data structure so can reference int iv = anvIndices.FindIndex(*it); // Index of the found vertex if(iv < 0) continue; // Check if vertex has already been addded std::vector::iterator vert; vert = std::find(vertexReferences.begin(), vertexReferences.end(), iv); if(vert == vertexReferences.end()) { // If the index wasnt found and is unique, the point is projected using the HLR Projector Algorithm gp_Pnt2d prjPnt; DS->Projector().Project(BRep_Tool::Pnt(TopoDS::Vertex(*it)), prjPnt); // Check if this point lies on a visible section of the projected curve double sta,end; float tolsta,tolend; // There will be multiple edges that form the total edge so collect these BRep_Builder B; TopoDS_Compound comp; B.MakeCompound(comp); TopoDS_Edge E; HLRAlgo_EdgeIterator It; for(It.InitVisible(ed.Status()); It.MoreVisible(); It.NextVisible()) { It.Visible(sta,tolsta,end,tolend); E = HLRBRep::MakeEdge(ed.Geometry(),sta,end); if (!E.IsNull()) { B.Add(comp,E); } } bool vertexVisible = false; TopExp_Explorer exp; exp.Init(comp,TopAbs_VERTEX); while(exp.More()) { gp_Pnt pnt = BRep_Tool::Pnt(TopoDS::Vertex(exp.Current())); gp_Pnt2d edgePnt(pnt.X(), pnt.Y()); if(edgePnt.SquareDistance(prjPnt) < Precision::Confusion()) { vertexVisible = true; break; } exp.Next(); } if(vertexVisible) { Vertex *myVert = new Vertex(prjPnt.X(), prjPnt.Y()); vertexGeom.push_back(myVert); vertexReferences.push_back(iv); } } } } } void GeometryObject::extractEdges(HLRBRep_Algo *myAlgo, const TopoDS_Shape &S, int type, bool visible, ExtractionType extractionType) { if (!myAlgo) return; Handle_HLRBRep_Data DS = myAlgo->DataStructure(); if (DS.IsNull()) return; DS->Projector().Scaled(true); int e1 = 1; int e2 = DS->NbEdges(); int f1 = 1; int f2 = DS->NbFaces(); if (!S.IsNull()) { int v1,v2; int index = myAlgo->Index(S); if(index == 0) { Base::Console().Log("TechDraw::GeometryObject::extractEdges - myAlgo->Index(S) == 0\n"); return; } myAlgo->ShapeBounds(index).Bounds(v1,v2,e1,e2,f1,f2); } HLRBRep_EdgeData* ed = &(DS->EDataArray().ChangeValue(e1 - 1)); // Get map of edges and faces from projected geometry TopTools_IndexedMapOfShape& Edges = DS->EdgeMap(); TopTools_IndexedMapOfShape anIndices; TopExp::MapShapes(S, TopAbs_EDGE, anIndices); for (int j = e1; j <= e2; j++) { ed++; if (ed->Selected() && !ed->Vertical()) { ed->Used(false); ed->HideCount(0); } else { ed->Used(true); } } BRep_Builder B; std::list notFound; /* ----------------- Extract Edges ------------------ */ for (int i = 1; i <= anIndices.Extent(); i++) { int ie = Edges.FindIndex(anIndices(i)); if (ie != 0) { HLRBRep_EdgeData& ed = DS->EDataArray().ChangeValue(ie); if(!ed.Used()) { if(true) { TopoDS_Shape result; B.MakeCompound(TopoDS::Compound(result)); drawEdge(ed, result, visible); // Extract and Project Vertices for current Edge extractVerts(myAlgo, S, ed, i, extractionType); int edgesAdded = calculateGeometry(result, extractionType, edgeGeom); // Push the edge references while(edgesAdded--) edgeReferences.push_back(i); } ed.Used(Standard_True); } } else { notFound.push_back(i); } } // Add any remaining edges that couldn't be found int edgeIdx = -1; // Negative index for edge references for (int ie = e1; ie <= e2; ie++) { // Co HLRBRep_EdgeData& ed = DS->EDataArray().ChangeValue(ie); if (!ed.Used()) { if(shouldDraw(false, type, ed)) { const TopoDS_Shape &shp = Edges.FindKey(ie); //Compares original shape to see if match if(!shp.IsNull()) { const TopoDS_Edge& edge = TopoDS::Edge(shp); BRepAdaptor_Curve adapt1(edge); for (std::list::iterator it= notFound.begin(); it!= notFound.end(); ++it){ BRepAdaptor_Curve adapt2(TopoDS::Edge(anIndices(*it))); if(isSameCurve(adapt1, adapt2)) { edgeIdx = *it; // notFound.erase(it); break; } } } TopoDS_Shape result; B.MakeCompound(TopoDS::Compound(result)); drawEdge(ed, result, visible); int edgesAdded = calculateGeometry(result, extractionType, edgeGeom); // Push the edge references while(edgesAdded--) edgeReferences.push_back(edgeIdx); } ed.Used(true); } } DS->Projector().Scaled(false); } /** * Note projected edges are broken up so start and end parameters differ. */ bool GeometryObject::isSameCurve(const BRepAdaptor_Curve &c1, const BRepAdaptor_Curve &c2) const { if(c1.GetType() != c2.GetType()) return false; #if 0 const gp_Pnt& p1S = c1.Value(c1.FirstParameter()); const gp_Pnt& p1E = c1.Value(c1.LastParameter()); const gp_Pnt& p2S = c2.Value(c2.FirstParameter()); const gp_Pnt& p2E = c2.Value(c2.LastParameter()); bool state = (p1S.IsEqual(p2S, Precision::Confusion()) && p1E.IsEqual(p2E, Precision::Confusion())); if( s || (p1S.IsEqual(p2E, Precision::Confusion()) && p1E.IsEqual(p2S, Precision::Confusion())) ){ switch(c1.GetType()) { case GeomAbs_Circle: { gp_Circ circ1 = c1.Circle(); gp_Circ circ2 = c2.Circle(); const gp_Pnt& p = circ1.Location(); const gp_Pnt& p2 = circ2.Location(); double radius1 = circ1.Radius(); double radius2 = circ2.Radius(); double f1 = c1.FirstParameter(); double f2 = c2.FirstParameter(); double l1 = c1.LastParameter(); double l2 = c2.LastParameter(); c1.Curve().Curve()-> if( p.IsEqual(p2,Precision::Confusion()) && radius2 - radius1 < Precision::Confusion()) { return true; } } break; default: break; } } #endif return false; } //only used by extractFaces void GeometryObject::createWire(const TopoDS_Shape &input, std::vector &wiresOut) const { //input is a compound of edges? there is edgesToWire logic in Part? if (input.IsNull()) { Base::Console().Log("TechDraw::GeometryObject::createWire input is NULL\n"); return; // There is no OpenCascade Geometry to be calculated } std::list edgeList; // make a list of all the edges in the input shape TopExp_Explorer edges(input, TopAbs_EDGE); while (edges.More()) { edgeList.push_back(TopoDS::Edge(edges.Current())); edges.Next(); } // Combine connected edges into wires. // BRepBuilderAPI_MakeWire has an annoying behaviour where the only [sane] // way to test whether an edge connects to a wire is to attempt adding // the edge. But, if the last added edge was not connected to the wire, // BRepBuilderAPI_MakeWire::Wire() will throw an exception. So, we need // to hang on to the last successfully added edge to "reset" scapegoat. // // ...and why do we need scapegoat? Because the resetting adds a duplicate // edge (which can be problematic down the road), but it's not easy to // remove the edge from the BRepBuilderAPI_MakeWire. bool lastAddFailed; TopoDS_Edge lastGoodAdd; while (edgeList.size() > 0) { // add and erase first edge BRepBuilderAPI_MakeWire scapegoat, mkWire; scapegoat.Add(edgeList.front()); mkWire.Add(edgeList.front()); lastAddFailed = false; lastGoodAdd = edgeList.front(); edgeList.pop_front(); // try to connect remaining edges to the wire, the wire is complete if no more egdes are connectible bool found; do { found = false; for (std::list::iterator pE = edgeList.begin(); pE != edgeList.end(); ++pE) { // Try adding edge - this doesn't necessarily add it scapegoat.Add(*pE); if (scapegoat.Error() != BRepBuilderAPI_DisconnectedWire) { mkWire.Add(*pE); // Edge added! Remember it, so we can reset scapegoat lastAddFailed = false; lastGoodAdd = *pE; // ...remove it from edgeList, edgeList.erase(pE); // ...and start searching for the next edge found = true; break; //exit for loop } else { lastAddFailed = true; } } } while (found); // See note above re: BRepBuilderAPI_MakeWire annoying behaviour if (lastAddFailed) { scapegoat.Add(lastGoodAdd); } if (scapegoat.Error() == BRepBuilderAPI_WireDone) { // BRepTools_WireExplorer finds 1st n connected edges, while // TopExp_Explorer finds all edges. Since we built mkWire using // TopExp_Explorer, and want to run BRepTools_WireExplorer over // it, we need to reorder the wire. ShapeFix_Wire fix; fix.Load(mkWire.Wire()); fix.FixReorder(); fix.Perform(); wiresOut.push_back(fix.Wire()); } else if(scapegoat.Error() == BRepBuilderAPI_DisconnectedWire) { Standard_Failure::Raise("Fatal error occurred in GeometryObject::createWire()"); } } } gp_Pnt GeometryObject::findCentroid(const TopoDS_Shape &shape, const Base::Vector3d &direction, const Base::Vector3d &xAxis) const { gp_Ax2 viewAxis; viewAxis = gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(direction.x, -direction.y, direction.z), gp_Dir(xAxis.x, -xAxis.y, xAxis.z)); // Y invert warning! gp_Trsf tempTransform; tempTransform.SetTransformation(viewAxis); BRepBuilderAPI_Transform builder(shape, tempTransform); Bnd_Box tBounds; BRepBndLib::Add(builder.Shape(), tBounds); tBounds.SetGap(0.0); Standard_Real xMin, yMin, zMin, xMax, yMax, zMax; tBounds.Get(xMin, yMin, zMin, xMax, yMax, zMax); Standard_Real x = (xMin + xMax) / 2.0, y = (yMin + yMax) / 2.0, z = (zMin + zMax) / 2.0; // Get centroid back into object space tempTransform.Inverted().Transforms(x, y, z); return gp_Pnt(x, y, z); } void GeometryObject::extractGeometry(const TopoDS_Shape &input, const Base::Vector3d &direction, bool extractHidden, const Base::Vector3d &xAxis) { // Clear previous Geometry and References that may have been stored clear(); ///TODO: Consider whether it would be possible/beneficial to cache some of this effort (eg don't do scale in OpenCASCADE land) IR TopoDS_Shape transShape; HLRBRep_Algo *brep_hlr = NULL; gp_Pnt inputCentre; try { inputCentre = findCentroid(input, direction, xAxis); } catch (...) { Base::Console().Log("GeometryObject::extractGeometry - findCentroid failed.\n"); return; } try { // Make tempTransform scale the object around it's centre point and // mirror about the Y axis gp_Trsf tempTransform; tempTransform.SetScale(inputCentre, Scale); gp_Trsf mirrorTransform; mirrorTransform.SetMirror( gp_Ax2(inputCentre, gp_Dir(0, 1, 0)) ); tempTransform.Multiply(mirrorTransform); // Apply that transform to the shape. This should preserve the centre. BRepBuilderAPI_Transform mkTrf(input, tempTransform); transShape = mkTrf.Shape(); brep_hlr = new HLRBRep_Algo(); brep_hlr->Add(transShape); // Project the shape into view space with the object's centroid // at the origin. gp_Ax2 viewAxis; viewAxis = gp_Ax2(inputCentre, gp_Dir(direction.x, direction.y, direction.z), gp_Dir(xAxis.x, xAxis.y, xAxis.z)); HLRAlgo_Projector projector( viewAxis ); brep_hlr->Projector(projector); brep_hlr->Update(); brep_hlr->Hide(); } catch (...) { Standard_Failure::Raise("GeometryObject::extractGeometry - error occurred while projecting shape"); } // extracting the result sets: //TODO: What is this? IR // need HLRBRep_HLRToShape aHLRToShape(shapes); // then TopoDS_Shape V = shapes.VCompound(); //V is a compound of edges // V = shapes.VCompound ();// hard edge visibly - real edges in original shape // V1 = shapes.Rg1LineVCompound();// Smoth edges visibly - "transition edges between two surfaces" // VN = shapes.RgNLineVCompound();// contour edges visibly - "sewn edges"? // VO = shapes.OutLineVCompound();// contours apparents visibly - ?edge in projection but not in original shape? // VI = shapes.IsoLineVCompound();// isoparamtriques visibly - ?constant u,v sort of like lat/long // H = shapes.HCompound ();// hard edge invisibly // H1 = shapes.Rg1LineHCompound();// Smoth edges invisibly // HN = shapes.RgNLineHCompound();// contour edges invisibly // HO = shapes.OutLineHCompound();// contours apparents invisibly // HI = shapes.IsoLineHCompound();// isoparamtriques invisibly // Extract Hidden Edges if(extractHidden) extractEdges(brep_hlr, transShape, 5, false, WithHidden);// Hard Edge // calculateGeometry(extractCompound(brep_hlr, invertShape, 2, false), WithHidden); // Outline // calculateGeometry(extractCompound(brep_hlr, invertShape, 3, false), (ExtractionType)(WithSmooth | WithHidden)); // Smooth // Extract Visible Edges extractEdges(brep_hlr, transShape, 5, true, WithSmooth); // Hard Edge //get endpoints of visible projected edges and add to vertexGeom with ref = -1 //this could get slow for big models? const std::vector &edgeGeom = getEdgeGeometry(); std::vector::const_iterator iEdge = edgeGeom.begin(); for (; iEdge != edgeGeom.end(); iEdge++) { if ((*iEdge)->extractType == DrawingGeometry::WithHidden) { //only use visible edges continue; } std::vector ends = (*iEdge)->findEndPoints(); if (!ends.empty()) { if (!findVertex(ends[0])) { Vertex* v0 = new Vertex(ends[0]); v0->extractType = DrawingGeometry::Plain; vertexGeom.push_back(v0); vertexReferences.push_back(-1); } if (!findVertex(ends[1])) { Vertex* v1 = new Vertex(ends[1]); v1->extractType = DrawingGeometry::Plain; vertexGeom.push_back(v1); vertexReferences.push_back(-1); } } } // calculateGeometry(extractCompound(brep_hlr, invertShape, 2, true), Plain); // Outline // calculateGeometry(extractCompound(brep_hlr, invertShape, 3, true), WithSmooth); // Smooth Edge // Extract Faces //algorithm,shape,visible/hidden,smooth edges(show flat/curve transition,facewires,index of face in shape? extractFaces(brep_hlr, transShape, true, WithSmooth, faceGeom, faceReferences); // House Keeping delete brep_hlr; } //translate all the edges in "input" into BaseGeoms int GeometryObject::calculateGeometry(const TopoDS_Shape &input, const ExtractionType extractionType, std::vector &geom) const { if(input.IsNull()) { Base::Console().Log("TechDraw::GeometryObject::calculateGeometry input is NULL\n"); return 0; // There is no OpenCascade Geometry to be calculated } // build a mesh to explore the shape //BRepMesh::Mesh(input, Tolerance); //OCC has removed BRepMesh::Mesh() as of v6.8.0.oce-0.17-dev BRepMesh_IncrementalMesh(input, Tolerance); //making a mesh turns edges into multilines? int geomsAdded = 0; // Explore all edges of input and calculate base geometry representation TopExp_Explorer edges(input, TopAbs_EDGE); for (int i = 1 ; edges.More(); edges.Next(),i++) { const TopoDS_Edge& edge = TopoDS::Edge(edges.Current()); BRepAdaptor_Curve adapt(edge); switch(adapt.GetType()) { case GeomAbs_Circle: { double f = adapt.FirstParameter(); double l = adapt.LastParameter(); gp_Pnt s = adapt.Value(f); gp_Pnt e = adapt.Value(l); if (fabs(l-f) > 1.0 && s.SquareDistance(e) < 0.001) { Circle *circle = new Circle(adapt); circle->extractType = extractionType; geom.push_back(circle); } else { AOC *aoc = new AOC(adapt); aoc->extractType = extractionType; geom.push_back(aoc); } } break; case GeomAbs_Ellipse: { double f = adapt.FirstParameter(); double l = adapt.LastParameter(); gp_Pnt s = adapt.Value(f); gp_Pnt e = adapt.Value(l); if (fabs(l-f) > 1.0 && s.SquareDistance(e) < 0.001) { Ellipse *ellipse = new Ellipse(adapt); ellipse->extractType = extractionType; geom.push_back(ellipse); } else { AOE *aoe = new AOE(adapt); aoe->extractType = extractionType; geom.push_back(aoe); } } break; case GeomAbs_BSplineCurve: { BSpline *bspline = 0; Generic* gen = NULL; try { bspline = new BSpline(adapt); bspline->extractType = extractionType; if (bspline->isLine()) { Base::Vector2D start,end; start = bspline->segments.front().pnts[0]; end = bspline->segments.back().pnts[1]; gen = new Generic(start,end); gen->extractType = extractionType; geom.push_back(gen); delete bspline; } else { geom.push_back(bspline); } break; } catch (Standard_Failure) { delete bspline; delete gen; bspline = 0; // Move onto generating a primitive } } default: { Generic *primitive = new Generic(adapt); primitive->extractType = extractionType; geom.push_back(primitive); } break; } geomsAdded++; } return geomsAdded; } //! does this GeometryObject already have this vertex bool GeometryObject::findVertex(Base::Vector2D v) { bool found = false; std::vector::iterator it = vertexGeom.begin(); for (; it != vertexGeom.end(); it++) { double dist = (v - (*it)->pnt).Length(); if (dist < Precision::Confusion()) { found = true; break; } } return found; }