From 71c9dc3e0efb9c9a384f99dc1a59527c9a842fce Mon Sep 17 00:00:00 2001 From: Abdullah Tahiri Date: Tue, 7 Dec 2021 14:17:46 +0100 Subject: [PATCH] Sketcher: EditModeCoinManager/DrawSkechHandler refactoring ====================================================== Creation of EditModeCoinManager class and helpers. In a nutshell: - EditModeCoinManager gets most of the content of struct EditData - Drawing is partly outsourced to EditModeCoinManager - EditModeCoinManager gets a nested Observer class to deal with parameters - A struct DrawingParameters is created to store all parameters used for drawing - EditModeCoinManager assume responsibility for determining the drawing size of the Axes - Preselection detection responsibility is moved to EditModeCoinManager. - Generation of constraint nodes and constraint drawing is moved to EditModeCoinManager. - Constraint generation parameters are refactored into ConstraintParameters. - Text rendering functions are moved to EditModeCoinManager. - Move HDPI resolution responsibility from VPSketch to EditModeCoinManager - Move responsibility to create the scenograph for edit mode to EditModeCoinManager - Move full updateColor responsibility to EditModeCoinManager - Allows for mapping N logical layers (LayerId of GeometryFacade) to M coin Layers (MgetPreselectPoint(); - int CrvId = sketchgui->getPreselectCurve(); - int CrsId = sketchgui->getPreselectCross(); + int VtId = getPreselectPoint(); + int CrvId = getPreselectCurve(); + int CrsId = getPreselectCross(); if (allowedSelTypes & (SelRoot | SelVertexOrRoot) && CrsId == 0) { selIdPair.GeoId = Sketcher::GeoEnum::RtPnt; selIdPair.PosId = Sketcher::PointPos::start; @@ -1809,8 +1809,8 @@ public: virtual bool releaseButton(Base::Vector2d onSketchPos) { - int VtId = sketchgui->getPreselectPoint(); - int CrsId = sketchgui->getPreselectCross(); + int VtId = getPreselectPoint(); + int CrsId = getPreselectCross(); std::stringstream ss; int GeoId_temp; Sketcher::PointPos PosId_temp; diff --git a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp index 98e9ed2abd..ea6ebafac3 100644 --- a/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp +++ b/src/Mod/Sketcher/Gui/CommandCreateGeo.cpp @@ -289,7 +289,7 @@ public: setPositionText(onSketchPos, text); EditCurve[1] = onSketchPos; - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr2, onSketchPos, onSketchPos - EditCurve[0])) { renderSuggestConstraintsCursor(sugConstr2); return; @@ -306,7 +306,7 @@ public: } else { EditCurve[1] = onSketchPos; - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); Mode = STATUS_End; } return true; @@ -353,7 +353,7 @@ public: tryAutoRecomputeIfNotSolve(static_cast(sketchgui->getObject())); EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true); if(continuousMode){ @@ -484,7 +484,7 @@ public: EditCurve[4] = EditCurve[0]; } - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.0,0.0))) { renderSuggestConstraintsCursor(sugConstr2); return; @@ -512,7 +512,7 @@ public: EditCurve[2] = onSketchPos; EditCurve[1] = Base::Vector2d(onSketchPos.x ,EditCurve[0].y); EditCurve[3] = Base::Vector2d(EditCurve[0].x,onSketchPos.y); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); Mode = STATUS_End; } else if (constructionMethod == CenterAndCorner) { @@ -521,7 +521,7 @@ public: EditCurve[2] = onSketchPos; EditCurve[3] = Base::Vector2d(onSketchPos.x,EditCurve[0].y); EditCurve[4] = EditCurve[0]; - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); Mode = STATUS_End; } } @@ -659,7 +659,7 @@ public: // This code enables the continuous creation mode. Mode=STATUS_SEEK_First; EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); EditCurve.resize(5); applyCursor(); /* this is ok not to call to purgeHandler @@ -855,7 +855,7 @@ public: text.sprintf(" (%.1fR %.1fX %.1fY)", radius, lengthX, lengthY); setPositionText(onSketchPos, text); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f, 0.f))) { renderSuggestConstraintsCursor(sugConstr2); return; @@ -1027,7 +1027,7 @@ public: // This code enables the continuous creation mode. Mode = STATUS_SEEK_First; EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); EditCurve.resize(37); applyCursor(); /* this is ok not to call to purgeHandler @@ -1366,7 +1366,7 @@ public: EditCurve[1] = EditCurve[0] + EditCurve[1]; } - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); float length = (EditCurve[1] - EditCurve[0]).Length(); float angle = (EditCurve[1] - EditCurve[0]).GetAngle(Base::Vector2d(1.f,0.f)); @@ -1447,7 +1447,7 @@ public: EditCurve[30] = CenterPoint; EditCurve[31] = EditCurve[0]; - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); SbString text; text.sprintf(" (%.1fR,%.1fdeg)", std::abs(arcRadius), arcAngle * 180 / M_PI); @@ -1508,7 +1508,7 @@ public: unsetCursor(); resetPositionText(); EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true); @@ -1525,7 +1525,7 @@ public: firstPosId=Sketcher::PointPos::none; previousPosId=Sketcher::PointPos::none; EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); EditCurve.resize(2); applyCursor(); /* this is ok not to call to purgeHandler @@ -1542,14 +1542,14 @@ public: Mode = STATUS_Do; - if (sketchgui->getPreselectPoint() != -1 && firstPosId != Sketcher::PointPos::none) { + if (getPreselectPoint() != -1 && firstPosId != Sketcher::PointPos::none) { int GeoId; Sketcher::PointPos PosId; - sketchgui->getSketchObject()->getGeoVertexIndex(sketchgui->getPreselectPoint(),GeoId,PosId); + sketchgui->getSketchObject()->getGeoVertexIndex(getPreselectPoint(),GeoId,PosId); if (sketchgui->getSketchObject()->arePointsCoincident(GeoId,PosId,firstCurve,firstPosId)) Mode = STATUS_Close; } - else if (sketchgui->getPreselectCross() == 0 && firstPosId != Sketcher::PointPos::none) { + else if (getPreselectCross() == 0 && firstPosId != Sketcher::PointPos::none) { // close line started at root point if (sketchgui->getSketchObject()->arePointsCoincident(-1,Sketcher::PointPos::start,firstCurve,firstPosId)) Mode = STATUS_Close; @@ -1672,7 +1672,7 @@ public: resetPositionText(); EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true); @@ -1689,7 +1689,7 @@ public: firstPosId=Sketcher::PointPos::none; previousPosId=Sketcher::PointPos::none; EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); EditCurve.resize(2); applyCursor(); /* this is ok not to call to purgeHandler @@ -1790,7 +1790,7 @@ public: previousPosId=Sketcher::PointPos::none; firstsegment=true; EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); EditCurve.resize(2); applyCursor(); } @@ -1948,7 +1948,7 @@ public: text.sprintf(" (%.1fR,%.1fdeg)", radius, angle * 180 / M_PI); setPositionText(onSketchPos, text); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f))) { renderSuggestConstraintsCursor(sugConstr2); return; @@ -1973,7 +1973,7 @@ public: text.sprintf(" (%.1fR,%.1fdeg)", radius, arcAngle * 180 / M_PI); setPositionText(onSketchPos, text); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.0,0.0))) { renderSuggestConstraintsCursor(sugConstr3); return; @@ -2014,7 +2014,7 @@ public: startAngle += arcAngle; } - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); applyCursor(); Mode = STATUS_End; } @@ -2071,7 +2071,7 @@ public: // This code enables the continuous creation mode. Mode=STATUS_SEEK_First; EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); EditCurve.resize(2); applyCursor(); /* this is ok not to call to purgeHandler @@ -2179,7 +2179,7 @@ public: text.sprintf(" (%.1fR,%.1fdeg)", (float) radius, (float) lineAngle * 180 / M_PI); setPositionText(onSketchPos, text); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f))) { renderSuggestConstraintsCursor(sugConstr2); return; @@ -2249,7 +2249,7 @@ public: text.sprintf(" (%.1fR,%.1fdeg)", (float) radius, (float) arcAngle * 180 / M_PI); setPositionText(onSketchPos, text); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.0,0.0), AutoConstraint::CURVE)) { renderSuggestConstraintsCursor(sugConstr3); @@ -2283,7 +2283,7 @@ public: else { EditCurve.resize(30); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); applyCursor(); Mode = STATUS_End; } @@ -2340,7 +2340,7 @@ public: // This code enables the continuous creation mode. Mode=STATUS_SEEK_First; EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); EditCurve.resize(2); applyCursor(); /* this is ok not to call to purgeHandler @@ -2538,7 +2538,7 @@ public: text.sprintf(" (%.1fR)", radius); setPositionText(onSketchPos, text); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr2, onSketchPos, onSketchPos - EditCurve[0], AutoConstraint::CURVE)) { renderSuggestConstraintsCursor(sugConstr2); @@ -2604,7 +2604,7 @@ public: // This code enables the continuous creation mode. Mode=STATUS_SEEK_First; EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); EditCurve.resize(34); applyCursor(); /* this is ok not to call to purgeHandler @@ -2761,7 +2761,7 @@ public: text.sprintf(" (%.1fR,%.1fR)", semiMajorRadius,semiMajorRadius); setPositionText(onSketchPos, text); - sketchgui->drawEdit(editCurve); + drawEdit(editCurve); // Suggestions for ellipse and curves are disabled because many tangent constraints // need an intermediate point or line. if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f), @@ -2778,7 +2778,7 @@ public: text.sprintf(" (%.1fR,%.1fR)", a, b); setPositionText(onSketchPos, text); - sketchgui->drawEdit(editCurve); + drawEdit(editCurve); if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.f,0.f), AutoConstraint::CURVE)) { renderSuggestConstraintsCursor(sugConstr3); @@ -2802,7 +2802,7 @@ public: text.sprintf(" (%.1fR,%.1fR)", semiMajorRadius,semiMajorRadius); setPositionText(onSketchPos, text); - sketchgui->drawEdit(editCurve); + drawEdit(editCurve); if (seekAutoConstraint(sugConstr2, onSketchPos, onSketchPos - centroid, AutoConstraint::CURVE)) { renderSuggestConstraintsCursor(sugConstr2); @@ -2817,7 +2817,7 @@ public: text.sprintf(" (%.1fR,%.1fR)", a, b); setPositionText(onSketchPos, text); - sketchgui->drawEdit(editCurve); + drawEdit(editCurve); if (seekAutoConstraint(sugConstr3, onSketchPos, onSketchPos - centroid, AutoConstraint::CURVE)) { renderSuggestConstraintsCursor(sugConstr3); @@ -3395,7 +3395,7 @@ private: mode = STATUS_SEEK_PERIAPSIS; } editCurve.clear(); - sketchgui->drawEdit(editCurve); + drawEdit(editCurve); ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); bool continuousMode = hGrp->GetBool("ContinuousCreationMode",true); @@ -3531,7 +3531,7 @@ public: text.sprintf(" (%.1fR,%.1fR)", radius,radius); setPositionText(onSketchPos, text); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr2, onSketchPos, onSketchPos - centerPoint, AutoConstraint::CURVE)) { renderSuggestConstraintsCursor(sugConstr2); @@ -3562,7 +3562,7 @@ public: text.sprintf(" (%.1fR,%.1fR)", a, b); setPositionText(onSketchPos, text); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.f,0.f))) { renderSuggestConstraintsCursor(sugConstr3); return; @@ -3601,7 +3601,7 @@ public: text.sprintf(" (%.1fR,%.1fR,%.1fdeg)", a, b, arcAngle * 180 / M_PI); setPositionText(onSketchPos, text); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr4, onSketchPos, Base::Vector2d(0.f,0.f))) { renderSuggestConstraintsCursor(sugConstr4); return; @@ -3758,7 +3758,7 @@ public: // This code enables the continuous creation mode. Mode=STATUS_SEEK_First; EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); EditCurve.resize(34); applyCursor(); /* this is ok not to call to purgeHandler @@ -3851,7 +3851,7 @@ public: text.sprintf(" (%.1fR,%.1fR)", radius,radius); setPositionText(onSketchPos, text); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f), AutoConstraint::CURVE)) { renderSuggestConstraintsCursor(sugConstr2); @@ -3883,7 +3883,7 @@ public: setPositionText(onSketchPos, text); } - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.f,0.f))) { renderSuggestConstraintsCursor(sugConstr3); return; @@ -3933,7 +3933,7 @@ public: arcAngle=0.; } - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr4, onSketchPos, Base::Vector2d(0.f,0.f))) { renderSuggestConstraintsCursor(sugConstr4); return; @@ -4101,7 +4101,7 @@ public: // This code enables the continuous creation mode. Mode = STATUS_SEEK_First; EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); EditCurve.resize(34); applyCursor(); /* It is ok not to call to purgeHandler @@ -4196,7 +4196,7 @@ public: text.sprintf(" (F%.1f)", radius); setPositionText(onSketchPos, text); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f))) { renderSuggestConstraintsCursor(sugConstr2); return; @@ -4228,7 +4228,7 @@ public: text.sprintf(" (F%.1f)", focal); setPositionText(onSketchPos, text); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr3, onSketchPos, Base::Vector2d(0.f,0.f))) { renderSuggestConstraintsCursor(sugConstr3); @@ -4274,7 +4274,7 @@ public: arcAngle=0.; } - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr4, onSketchPos, Base::Vector2d(0.f,0.f))) { renderSuggestConstraintsCursor(sugConstr4); return; @@ -4400,7 +4400,7 @@ public: // This code enables the continuous creation mode. Mode = STATUS_SEEK_First; EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); EditCurve.resize(34); applyCursor(); /* It is ok not to call to purgeHandler @@ -4638,7 +4638,7 @@ public: EditCurve[EditCurve.size()-1] = onSketchPos; - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); float length = (EditCurve[EditCurve.size()-1] - EditCurve[EditCurve.size()-2]).Length(); float angle = (EditCurve[EditCurve.size()-1] - EditCurve[EditCurve.size()-2]).GetAngle(Base::Vector2d(1.f,0.f)); @@ -4875,7 +4875,7 @@ public: // This code enables the continuous creation mode. Mode = STATUS_SEEK_FIRST_CONTROLPOINT; EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); EditCurve.resize(2); applyCursor(); @@ -4928,7 +4928,7 @@ public: // This code disregards existing data and enables the continuous creation mode. Mode = STATUS_SEEK_FIRST_CONTROLPOINT; EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); EditCurve.resize(2); applyCursor(); @@ -5200,7 +5200,7 @@ public: text.sprintf(" (%.1fR,%.1fdeg)", (float) radius, (float) lineAngle * 180 / M_PI); setPositionText(onSketchPos, text); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (Mode == STATUS_SEEK_Second) { if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f), AutoConstraint::CURVE)) { @@ -5240,7 +5240,7 @@ public: else { EditCurve.resize(N); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); applyCursor(); Mode = STATUS_End; } @@ -5297,7 +5297,7 @@ public: // This code enables the continuous creation mode. Mode=STATUS_SEEK_First; EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); EditCurve.resize(2); applyCursor(); /* this is ok not to call to purgeHandler @@ -5694,7 +5694,7 @@ public: virtual bool releaseButton(Base::Vector2d onSketchPos) { bool construction=false; - int VtId = sketchgui->getPreselectPoint(); + int VtId = getPreselectPoint(); if (Mode == STATUS_SEEK_First && VtId != -1) { int GeoId; Sketcher::PointPos PosId=Sketcher::PointPos::none; @@ -5755,7 +5755,7 @@ public: return true; } - int GeoId = sketchgui->getPreselectCurve(); + int GeoId = getPreselectCurve(); if (GeoId > -1) { const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(GeoId); if (geom->getTypeId().isDerivedFrom(Part::GeomBoundedCurve::getClassTypeId())) { @@ -6080,7 +6080,7 @@ public: { Q_UNUSED(onSketchPos); - int GeoId = sketchgui->getPreselectCurve(); + int GeoId = getPreselectCurve(); if (GeoId > -1) { auto sk = static_cast(sketchgui->getObject()); @@ -6106,12 +6106,12 @@ public: EditMarkers.emplace_back( end.x, end.y); } - sketchgui->drawEditMarkers(EditMarkers, 2); // maker augmented by two sizes (see supported marker sizes) + drawEditMarkers(EditMarkers, 2); // maker augmented by two sizes (see supported marker sizes) } } else { EditMarkers.resize(0); - sketchgui->drawEditMarkers(EditMarkers, 2); + drawEditMarkers(EditMarkers, 2); } } @@ -6123,7 +6123,7 @@ public: virtual bool releaseButton(Base::Vector2d onSketchPos) { - int GeoId = sketchgui->getPreselectCurve(); + int GeoId = getPreselectCurve(); if (GeoId > -1) { const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(GeoId); if (geom->getTypeId().isDerivedFrom(Part::GeomTrimmedCurve::getClassTypeId()) || @@ -6144,7 +6144,7 @@ public: } EditMarkers.resize(0); - sketchgui->drawEditMarkers(EditMarkers); + drawEditMarkers(EditMarkers); } else // exit the trimming tool if the user clicked on empty space sketchgui->purgeHandler(); // no code after this line, Handler get deleted in ViewProvider @@ -6295,7 +6295,7 @@ public: ExtendFromStart = onSketchPos.Distance(startPoint) < onSketchPos.Distance(endPoint); Increment = ExtendFromStart ? projection.Length() : projection.Length() - recenteredLine.Length(); } - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); } else if (geom->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { const Part::GeomArcOfCircle *arc = static_cast(geom); @@ -6358,9 +6358,9 @@ public: double angle = modStartAngle + i * modArcAngle/30.0; EditCurve[i] = Base::Vector2d(center.x + radius * cos(angle), center.y + radius * sin(angle)); } - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); } - int curveId = sketchgui->getPreselectCurve(); + int curveId = getPreselectCurve(); if (BaseGeoId != curveId && seekAutoConstraint(SugConstr, onSketchPos, Base::Vector2d(0.f,0.f))) { renderSuggestConstraintsCursor(SugConstr); return; @@ -6378,7 +6378,7 @@ public: { Q_UNUSED(onSketchPos); if (Mode == STATUS_SEEK_First) { - BaseGeoId = sketchgui->getPreselectCurve(); + BaseGeoId = getPreselectCurve(); if (BaseGeoId > -1) { const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(BaseGeoId); if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { @@ -6429,7 +6429,7 @@ public: Mode=STATUS_SEEK_First; filterGate->setDisabled(false); EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); EditCurve.resize(2); applyCursor(); /* this is ok not to call to purgeHandler @@ -6560,7 +6560,7 @@ public: virtual bool releaseButton(Base::Vector2d onSketchPos) { - int GeoId = sketchgui->getPreselectCurve(); + int GeoId = getPreselectCurve(); if (GeoId >= 0) { const Part::Geometry *geom = sketchgui->getSketchObject()->getGeometry(GeoId); if (geom->getTypeId() == Part::GeomLineSegment::getClassTypeId() @@ -6687,7 +6687,7 @@ public: virtual void activated(ViewProviderSketch *sketchgui) { - sketchgui->setAxisPickStyle(false); + setAxisPickStyle(false); Gui::MDIView *mdi = Gui::Application::Instance->activeDocument()->getActiveView(); Gui::View3DInventorViewer *viewer; viewer = static_cast(mdi)->getViewer(); @@ -6703,7 +6703,8 @@ public: virtual void deactivated(ViewProviderSketch *sketchgui) { - sketchgui->setAxisPickStyle(true); + Q_UNUSED(sketchgui); + setAxisPickStyle(true); } virtual void mouseMove(Base::Vector2d onSketchPos) @@ -6869,9 +6870,10 @@ public: Gui::Selection().rmvSelectionGate(); } + virtual void activated(ViewProviderSketch *sketchgui) { - sketchgui->setAxisPickStyle(false); + setAxisPickStyle(false); Gui::MDIView *mdi = Gui::Application::Instance->activeDocument()->getActiveView(); Gui::View3DInventorViewer *viewer; viewer = static_cast(mdi)->getViewer(); @@ -6887,7 +6889,8 @@ public: virtual void deactivated(ViewProviderSketch *sketchgui) { - sketchgui->setAxisPickStyle(true); + Q_UNUSED(sketchgui); + setAxisPickStyle(true); } virtual void mouseMove(Base::Vector2d onSketchPos) @@ -7090,7 +7093,7 @@ public: text.sprintf(" (%.1fR %.1fL)", r, sqrt(dx * dx + dy * dy)); setPositionText(onSketchPos, text); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(dx, dy), AutoConstraint::VERTEX_NO_TANGENCY)) { renderSuggestConstraintsCursor(sugConstr2); return; @@ -7234,7 +7237,7 @@ public: // This code enables the continuous creation mode. Mode = STATUS_SEEK_First; EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); EditCurve.resize(35); applyCursor(); /* this is ok not to call to purgeHandler @@ -7359,7 +7362,7 @@ public: text.sprintf(" (%.1fR %.1fdeg)", radius, angle ); setPositionText(onSketchPos, text); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr2, onSketchPos, Base::Vector2d(0.f,0.f))) { renderSuggestConstraintsCursor(sugConstr2); return; @@ -7427,7 +7430,7 @@ public: // This code enables the continuous creation mode. Mode=STATUS_SEEK_First; EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); EditCurve.resize(Corners+1); applyCursor(); /* this is ok not to call to purgeHandler diff --git a/src/Mod/Sketcher/Gui/CommandSketcherBSpline.cpp b/src/Mod/Sketcher/Gui/CommandSketcherBSpline.cpp index 47eea14e40..8bb99a7bf7 100644 --- a/src/Mod/Sketcher/Gui/CommandSketcherBSpline.cpp +++ b/src/Mod/Sketcher/Gui/CommandSketcherBSpline.cpp @@ -82,12 +82,11 @@ void ActivateBSplineHandler(Gui::Document *doc,DrawSketchHandler *handler) } } -void ShowRestoreInformationLayer(SketcherGui::ViewProviderSketch* vp, char * visibleelementname) +void ShowRestoreInformationLayer(const char * visibleelementname) { ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General"); bool status = hGrp->GetBool(visibleelementname, true); hGrp->SetBool(visibleelementname, !status); - vp->showRestoreInformationLayer(); } // Show/Hide B-spline degree @@ -111,9 +110,7 @@ void CmdSketcherBSplineDegree::activated(int iMsg) { Q_UNUSED(iMsg); - Gui::Document * doc = getActiveGuiDocument(); - SketcherGui::ViewProviderSketch* vp = static_cast(doc->getInEdit()); - ShowRestoreInformationLayer(vp, "BSplineDegreeVisible"); + ShowRestoreInformationLayer("BSplineDegreeVisible"); } bool CmdSketcherBSplineDegree::isActive(void) @@ -142,9 +139,7 @@ void CmdSketcherBSplinePolygon::activated(int iMsg) { Q_UNUSED(iMsg); - Gui::Document * doc = getActiveGuiDocument(); - SketcherGui::ViewProviderSketch* vp = static_cast(doc->getInEdit()); - ShowRestoreInformationLayer(vp, "BSplineControlPolygonVisible"); + ShowRestoreInformationLayer("BSplineControlPolygonVisible"); } bool CmdSketcherBSplinePolygon::isActive(void) @@ -173,9 +168,7 @@ void CmdSketcherBSplineComb::activated(int iMsg) { Q_UNUSED(iMsg); - Gui::Document * doc = getActiveGuiDocument(); - SketcherGui::ViewProviderSketch* vp = static_cast(doc->getInEdit()); - ShowRestoreInformationLayer(vp, "BSplineCombVisible"); + ShowRestoreInformationLayer("BSplineCombVisible"); } bool CmdSketcherBSplineComb::isActive(void) @@ -204,9 +197,7 @@ void CmdSketcherBSplineKnotMultiplicity::activated(int iMsg) { Q_UNUSED(iMsg); - Gui::Document * doc = getActiveGuiDocument(); - SketcherGui::ViewProviderSketch* vp = static_cast(doc->getInEdit()); - ShowRestoreInformationLayer(vp, "BSplineKnotMultiplicityVisible"); + ShowRestoreInformationLayer("BSplineKnotMultiplicityVisible"); } bool CmdSketcherBSplineKnotMultiplicity::isActive(void) @@ -235,9 +226,7 @@ void CmdSketcherBSplinePoleWeight::activated(int iMsg) { Q_UNUSED(iMsg); - Gui::Document* doc = getActiveGuiDocument(); - SketcherGui::ViewProviderSketch* vp = static_cast(doc->getInEdit()); - ShowRestoreInformationLayer(vp, "BSplinePoleWeightVisible"); + ShowRestoreInformationLayer("BSplinePoleWeightVisible"); } bool CmdSketcherBSplinePoleWeight::isActive(void) diff --git a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp index 96a359c6f0..a8aa4ae1e2 100644 --- a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp +++ b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp @@ -1350,7 +1350,7 @@ public: setPositionText(endpoint, text); EditCurve[1] = endpoint; - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr1, endpoint, Base::Vector2d(0.0, 0.0), AutoConstraint::VERTEX)) { renderSuggestConstraintsCursor(sugConstr1); return; @@ -1362,7 +1362,7 @@ public: virtual bool pressButton(Base::Vector2d) { if (Mode == STATUS_SEEK_First) { - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); Mode = STATUS_End; } return true; @@ -1415,7 +1415,7 @@ public: tryAutoRecomputeIfNotSolve(static_cast(sketchgui->getObject())); EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); // no code after this line, Handler gets deleted in ViewProvider sketchgui->purgeHandler(); @@ -1899,7 +1899,7 @@ public: setPositionText(endpoint, text); EditCurve[1] = endpoint; - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); if (seekAutoConstraint(sugConstr1, endpoint, Base::Vector2d(0.0, 0.0), AutoConstraint::VERTEX)) { renderSuggestConstraintsCursor(sugConstr1); @@ -1913,7 +1913,7 @@ public: virtual bool pressButton(Base::Vector2d) { if (Mode == STATUS_SEEK_First) { - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); Mode = STATUS_End; } return true; @@ -1952,7 +1952,7 @@ public: tryAutoRecomputeIfNotSolve(static_cast(sketchgui->getObject())); EditCurve.clear(); - sketchgui->drawEdit(EditCurve); + drawEdit(EditCurve); // no code after this line, Handler is deleted in ViewProvider sketchgui->purgeHandler(); diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp b/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp index 5857bdf7f7..5a27a206ca 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp +++ b/src/Mod/Sketcher/Gui/DrawSketchHandler.cpp @@ -59,6 +59,56 @@ using namespace SketcherGui; using namespace Sketcher; +/************************************ Attorney *******************************************/ + +inline void ViewProviderSketchDrawSketchHandlerAttorney::setPositionText(ViewProviderSketch &vp, const Base::Vector2d &Pos, const SbString &txt) +{ + vp.setPositionText(Pos,txt); +} + +inline void ViewProviderSketchDrawSketchHandlerAttorney::setPositionText(ViewProviderSketch &vp, const Base::Vector2d &Pos) +{ + vp.setPositionText(Pos); +} + +inline void ViewProviderSketchDrawSketchHandlerAttorney::resetPositionText(ViewProviderSketch &vp) +{ + vp.resetPositionText(); +} + +inline void ViewProviderSketchDrawSketchHandlerAttorney::drawEdit(ViewProviderSketch &vp, const std::vector &EditCurve) +{ + vp.drawEdit(EditCurve); +} + +inline void ViewProviderSketchDrawSketchHandlerAttorney::drawEditMarkers(ViewProviderSketch &vp, const std::vector &EditMarkers, unsigned int augmentationlevel) +{ + vp.drawEditMarkers(EditMarkers, augmentationlevel); +} + +inline void ViewProviderSketchDrawSketchHandlerAttorney::setAxisPickStyle(ViewProviderSketch &vp, bool on) +{ + vp.setAxisPickStyle(on); +} + +inline int ViewProviderSketchDrawSketchHandlerAttorney::getPreselectPoint(const ViewProviderSketch &vp) +{ + return vp.getPreselectPoint(); +} + +inline int ViewProviderSketchDrawSketchHandlerAttorney::getPreselectCurve(const ViewProviderSketch &vp) +{ + return vp.getPreselectCurve(); +} + +inline int ViewProviderSketchDrawSketchHandlerAttorney::getPreselectCross(const ViewProviderSketch &vp) +{ + return vp.getPreselectCross(); +} + +/**************************** DrawSketchHandler *******************************************/ + + //************************************************************************** // Construction/Destruction @@ -69,8 +119,8 @@ DrawSketchHandler::~DrawSketchHandler() {} void DrawSketchHandler::quit(void) { assert(sketchgui); - sketchgui->drawEdit(std::vector()); - sketchgui->drawEditMarkers(std::vector()); + drawEdit(std::vector()); + drawEditMarkers(std::vector()); resetPositionText(); Gui::Selection().rmvSelectionGate(); @@ -314,9 +364,9 @@ int DrawSketchHandler::seekAutoConstraint(std::vector &suggested Base::Vector3d hitShapeDir = Base::Vector3d(0,0,0); // direction of hit shape (if it is a line, the direction of the line) // Get Preselection - int preSelPnt = sketchgui->getPreselectPoint(); - int preSelCrv = sketchgui->getPreselectCurve(); - int preSelCrs = sketchgui->getPreselectCross(); + int preSelPnt = getPreselectPoint(); + int preSelCrv = getPreselectCurve(); + int preSelCrs = getPreselectCross(); int GeoId = GeoEnum::GeoUndef; Sketcher::PointPos PosId = Sketcher::PointPos::none; @@ -691,16 +741,46 @@ void DrawSketchHandler::renderSuggestConstraintsCursor(std::vectorsetPositionText(Pos, text); + ViewProviderSketchDrawSketchHandlerAttorney::setPositionText(*sketchgui, Pos, text); } void DrawSketchHandler::setPositionText(const Base::Vector2d &Pos) { - sketchgui->setPositionText(Pos); + ViewProviderSketchDrawSketchHandlerAttorney::setPositionText(*sketchgui,Pos); } void DrawSketchHandler::resetPositionText(void) { - sketchgui->resetPositionText(); + ViewProviderSketchDrawSketchHandlerAttorney::resetPositionText(*sketchgui); +} + +void DrawSketchHandler::drawEdit(const std::vector &EditCurve) +{ + ViewProviderSketchDrawSketchHandlerAttorney::drawEdit(*sketchgui, EditCurve); +} + +void DrawSketchHandler::drawEditMarkers(const std::vector &EditMarkers, unsigned int augmentationlevel) +{ + ViewProviderSketchDrawSketchHandlerAttorney::drawEditMarkers(*sketchgui, EditMarkers, augmentationlevel); +} + +void DrawSketchHandler::setAxisPickStyle(bool on) +{ + ViewProviderSketchDrawSketchHandlerAttorney::setAxisPickStyle(*sketchgui, on); +} + +int DrawSketchHandler::getPreselectPoint(void) const +{ + return ViewProviderSketchDrawSketchHandlerAttorney::getPreselectPoint(*sketchgui); +} + +int DrawSketchHandler::getPreselectCurve(void) const +{ + return ViewProviderSketchDrawSketchHandlerAttorney::getPreselectCurve(*sketchgui); +} + +int DrawSketchHandler::getPreselectCross(void) const +{ + return ViewProviderSketchDrawSketchHandlerAttorney::getPreselectCross(*sketchgui); } diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandler.h b/src/Mod/Sketcher/Gui/DrawSketchHandler.h index 19fcaae94e..679c81f113 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandler.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandler.h @@ -39,6 +39,28 @@ namespace SketcherGui { class ViewProviderSketch; +/** + * In order to enforce a certain degree of encapsulation and promote a not + * too tight coupling, while still allowing well defined collaboration, + * DrawSketchHandler accesses ViewProviderSketch via this Attorney class + */ +class ViewProviderSketchDrawSketchHandlerAttorney { +private: + static inline void setPositionText(ViewProviderSketch &vp, const Base::Vector2d &Pos, const SbString &txt); + static inline void setPositionText(ViewProviderSketch &vp, const Base::Vector2d &Pos); + static inline void resetPositionText(ViewProviderSketch &vp); + static inline void drawEdit(ViewProviderSketch &vp, const std::vector &EditCurve); + static inline void drawEditMarkers(ViewProviderSketch &vp, const std::vector &EditMarkers, unsigned int augmentationlevel = 0); + static inline void setAxisPickStyle(ViewProviderSketch &vp, bool on); + + static inline int getPreselectPoint(const ViewProviderSketch &vp); + static inline int getPreselectCurve(const ViewProviderSketch &vp); + static inline int getPreselectCross(const ViewProviderSketch &vp); + + friend class DrawSketchHandler; +}; + + // A Simple data type to hold basic information for suggested constraints struct AutoConstraint { @@ -56,6 +78,17 @@ struct AutoConstraint /** Handler to create new sketch geometry * This class has to be reimplemented to create geometry in the * sketcher while its in editing. + * + * DrawSketchHandler takes over the responsibility of drawing edit temporal curves and + * markers necessary to enable visual feedback to the user, as well as the UI interaction during + * such edits. This is its exclusive responsibility under the Single Responsibility Principle. + * + * A plethora of speciliased handlers derive from DrawSketchHandler for each specialised editing (see + * for example all the handlers for creation of new geometry). These derived classes do * not * have + * direct access to the ViewProviderSketchDrawSketchHandlerAttorney. This is intended to keep coupling + * under control. However, generic functionality requiring access to the Attorney can be implemented + * in DrawSketchHandler and used from its derived classes by virtue of the inheritance. This promotes a + * concentrating the coupling in a single point (and code reuse). */ class SketcherGuiExport DrawSketchHandler { @@ -98,7 +131,7 @@ protected: /** * Sets a cursor for 3D inventor view. * pixmap as a cursor image in device independent pixels. - * + * * \param autoScale - set this to false if pixmap already scaled for HiDPI **/ void setCursor(const QPixmap &pixmap, int x,int y, bool autoScale=true); @@ -113,6 +146,15 @@ protected: qreal devicePixelRatio(); void setCrosshairCursor(const char* svgName); + void drawEdit(const std::vector &EditCurve); + void drawEditMarkers(const std::vector &EditMarkers, unsigned int augmentationlevel = 0); + void setAxisPickStyle(bool on); + + int getPreselectPoint(void) const; + int getPreselectCurve(void) const; + int getPreselectCross(void) const; + + /** * Returns constraints icons scaled to width. **/ diff --git a/src/Mod/Sketcher/Gui/EditModeCoinManager.cpp b/src/Mod/Sketcher/Gui/EditModeCoinManager.cpp new file mode 100644 index 0000000000..5d14344187 --- /dev/null +++ b/src/Mod/Sketcher/Gui/EditModeCoinManager.cpp @@ -0,0 +1,829 @@ +/*************************************************************************** + * Copyright (c) 2021 Abdullah Tahiri * + * * + * 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 +#endif // #ifndef _PreComp_ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include + +#include "SoZoomTranslation.h" +#include "SoDatumLabel.h" + +#include "EditModeInformationOverlayCoinConverter.h" + +#include "EditModeGeometryCoinConverter.h" + +#include "ViewProviderSketch.h" + +#include "ViewProviderSketchCoinAttorney.h" + +#include "EditModeGeometryCoinManager.h" +#include "EditModeConstraintCoinManager.h" + +#include "EditModeCoinManager.h" + +using namespace SketcherGui; +using namespace Sketcher; + +//**************************** ParameterObserver nested class ****************************** +EditModeCoinManager::ParameterObserver::ParameterObserver(EditModeCoinManager &client): Client(client) +{ + initParameters(); + subscribeToParameters(); +} + +EditModeCoinManager::ParameterObserver::~ParameterObserver() +{ + unsubscribeToParameters(); +} + +void EditModeCoinManager::ParameterObserver::initParameters() +{ + // static map to avoid substantial if/else branching + // + // key->first => String of parameter, + // key->second => Update function to be called for the parameter, + str2updatefunction = { + {"SegmentsPerGeometry", + [this](const std::string & param){updateCurvedEdgeCountSegmentsParameter(param);}}, + {"BSplineDegreeVisible", + [this](const std::string & param){updateOverlayVisibilityParameter(param);}}, + {"BSplineControlPolygonVisible", + [this](const std::string & param){updateOverlayVisibilityParameter(param);}}, + {"BSplineCombVisible", + [this](const std::string & param){updateOverlayVisibilityParameter(param);}}, + {"BSplineKnotMultiplicityVisible", + [this](const std::string & param){updateOverlayVisibilityParameter(param);}}, + {"BSplinePoleWeightVisible", + [this](const std::string & param){updateOverlayVisibilityParameter(param);}}, + {"TopRenderGeometryId", + [this](const std::string & param){updateLineRenderingOrderParameters(param);}}, + {"MidRenderGeometryId", + [this](const std::string & param){updateLineRenderingOrderParameters(param);}}, + {"HideUnits", + [this](const std::string & param){updateConstraintPresentationParameters(param);}}, + {"ShowDimensionalName", + [this](const std::string & param){updateConstraintPresentationParameters(param);}}, + {"DimensionalStringFormat", + [this](const std::string & param){updateConstraintPresentationParameters(param);}}, + {"ViewScalingFactor", + [this](const std::string & param){updateElementSizeParameters(param);}}, + {"MarkerSize", + [this](const std::string & param){updateElementSizeParameters(param);}}, + {"EditSketcherFontSize", + [this](const std::string & param){updateElementSizeParameters(param);}}, + {"CreateLineColor", + [this, drawingParameters = Client.drawingParameters](const std::string & param){updateColor(drawingParameters.CreateCurveColor, param);}}, + {"EditedVertexColor", + [this, drawingParameters = Client.drawingParameters](const std::string & param){updateColor(drawingParameters.VertexColor, param);}}, + {"EditedEdgeColor", + [this, drawingParameters = Client.drawingParameters](const std::string & param){updateColor(drawingParameters.CurveColor, param);}}, + {"ConstructionColor", + [this, drawingParameters = Client.drawingParameters](const std::string & param){updateColor(drawingParameters.CurveDraftColor, param);}}, + {"InternalAlignedGeoColor", + [this, drawingParameters = Client.drawingParameters](const std::string & param){updateColor(drawingParameters.InternalAlignedGeoColor, param);}}, + {"FullyConstraintElementColor", + [this, drawingParameters = Client.drawingParameters](const std::string & param){updateColor(drawingParameters.FullyConstraintElementColor, param);}}, + {"FullyConstraintConstructionElementColor", + [this, drawingParameters = Client.drawingParameters](const std::string & param){updateColor(drawingParameters.FullyConstraintConstructionElementColor, param);}}, + {"FullyConstraintInternalAlignmentColor", + [this, drawingParameters = Client.drawingParameters](const std::string & param){updateColor(drawingParameters.FullyConstraintInternalAlignmentColor, param);}}, + {"FullyConstraintConstructionPointColor", + [this, drawingParameters = Client.drawingParameters](const std::string & param){updateColor(drawingParameters.FullyConstraintConstructionPointColor, param);}}, + {"FullyConstraintElementColor", + [this, drawingParameters = Client.drawingParameters](const std::string & param){updateColor(drawingParameters.FullyConstraintElementColor, param);}}, + {"InvalidSketchColor", + [this, drawingParameters = Client.drawingParameters](const std::string & param){updateColor(drawingParameters.InvalidSketchColor, param);}}, + {"FullyConstrainedColor", + [this, drawingParameters = Client.drawingParameters](const std::string & param){updateColor(drawingParameters.FullyConstrainedColor, param);}}, + {"ConstrainedDimColor", + [this, drawingParameters = Client.drawingParameters](const std::string & param){updateColor(drawingParameters.ConstrDimColor, param);}}, + {"ConstrainedIcoColor", + [this, drawingParameters = Client.drawingParameters](const std::string & param){updateColor(drawingParameters.ConstrIcoColor, param);}}, + {"NonDrivingConstrDimColor", + [this, drawingParameters = Client.drawingParameters](const std::string & param){updateColor(drawingParameters.NonDrivingConstrDimColor, param);}}, + {"ExprBasedConstrDimColor", + [this, drawingParameters = Client.drawingParameters](const std::string & param){updateColor(drawingParameters.ExprBasedConstrDimColor, param);}}, + {"DeactivatedConstrDimColor", + [this, drawingParameters = Client.drawingParameters](const std::string & param){updateColor(drawingParameters.DeactivatedConstrDimColor, param);}}, + {"ExternalColor", + [this, drawingParameters = Client.drawingParameters](const std::string & param){updateColor(drawingParameters.CurveExternalColor, param);}}, + {"HighlightColor", + [this, drawingParameters = Client.drawingParameters](const std::string & param){updateColor(drawingParameters.PreselectColor, param);}}, + {"SelectionColor", + [this, drawingParameters = Client.drawingParameters](const std::string & param){updateColor(drawingParameters.SelectColor, param);}}, + }; + + for( auto & val : str2updatefunction){ + auto string = val.first; + auto function = val.second; + + function(string); + } +} + +void EditModeCoinManager::ParameterObserver::updateCurvedEdgeCountSegmentsParameter(const std::string & parametername) +{ + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); + int stdcountsegments = hGrp->GetInt(parametername.c_str(), 50); + // value cannot be smaller than 6 + if (stdcountsegments < 6) + stdcountsegments = 6; + + Client.drawingParameters.curvedEdgeCountSegments = stdcountsegments; +} + +void EditModeCoinManager::ParameterObserver::updateLineRenderingOrderParameters(const std::string & parametername) +{ + (void) parametername; + + ParameterGrp::handle hGrpp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General"); + + Client.drawingParameters.topRenderingGeometry = DrawingParameters::GeometryRendering (hGrpp->GetInt("TopRenderGeometryId",1)); + Client.drawingParameters.midRenderingGeometry = DrawingParameters::GeometryRendering (hGrpp->GetInt("MidRenderGeometryId",2)); +} + +void EditModeCoinManager::ParameterObserver::updateConstraintPresentationParameters(const std::string & parametername) +{ + (void) parametername; + + ParameterGrp::handle hGrpskg = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); + + Client.constraintParameters.bHideUnits = hGrpskg->GetBool("HideUnits", false); + Client.constraintParameters.bShowDimensionalName = hGrpskg->GetBool("ShowDimensionalName", false); + Client.constraintParameters.sDimensionalStringFormat = QString::fromStdString(hGrpskg->GetASCII("DimensionalStringFormat", "%N = %V")); +} + +template +void EditModeCoinManager::ParameterObserver::updateOverlayVisibilityParameter(const std::string & parametername) +{ + ParameterGrp::handle hGrpsk = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General"); + + if constexpr (visibilityparameter == OverlayVisibilityParameter::BSplineDegree) + Client.overlayParameters.bSplineDegreeVisible = hGrpsk->GetBool(parametername.c_str(), true); + else if constexpr (visibilityparameter == OverlayVisibilityParameter::BSplineControlPolygonVisible) + Client.overlayParameters.bSplineControlPolygonVisible = hGrpsk->GetBool(parametername.c_str(), true); + else if constexpr (visibilityparameter == OverlayVisibilityParameter::BSplineCombVisible) + Client.overlayParameters.bSplineCombVisible = hGrpsk->GetBool(parametername.c_str(), true); + else if constexpr (visibilityparameter == OverlayVisibilityParameter::BSplineKnotMultiplicityVisible) + Client.overlayParameters.bSplineKnotMultiplicityVisible = hGrpsk->GetBool(parametername.c_str(), true); + else if constexpr (visibilityparameter == OverlayVisibilityParameter::BSplinePoleWeightVisible) + Client.overlayParameters.bSplinePoleWeightVisible = hGrpsk->GetBool(parametername.c_str(), true); + + Client.overlayParameters.visibleInformationChanged = true; +} + +void EditModeCoinManager::ParameterObserver::updateElementSizeParameters(const std::string & parametername) +{ + (void) parametername; + + //Add scaling to Constraint icons + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); + + double viewScalingFactor = hGrp->GetFloat("ViewScalingFactor", 1.0); + viewScalingFactor = Base::clamp(viewScalingFactor, 0.5, 5.0); + + int markersize = hGrp->GetInt("MarkerSize", 7); + + int defaultFontSizePixels = Client.defaultApplicationFontSizePixels(); // returns height in pixels, not points + + int sketcherfontSize = hGrp->GetInt("EditSketcherFontSize", defaultFontSizePixels); + + int dpi = Client.getApplicationLogicalDPIX(); + + // simple scaling factor for hardcoded pixel values in the Sketcher + Client.drawingParameters.pixelScalingFactor = viewScalingFactor * dpi / 96; // 96 ppi is the standard pixel density for which pixel quantities were calculated + + // Coin documentation indicates the size of a font is: + // SoSFFloat SoFont::size Size of font. Defaults to 10.0. + // + // For 2D rendered bitmap fonts (like for SoText2), this value is the height of a character in screen pixels. For 3D text, this value is the world-space coordinates height of a character in the current units setting (see documentation for SoUnits node). + // + // However, with hdpi monitors, the coin font labels do not respect the size passed in pixels: + // https://forum.freecadweb.org/viewtopic.php?f=3&t=54347&p=467610#p467610 + // https://forum.freecadweb.org/viewtopic.php?f=10&t=49972&start=40#p467471 + // + // Because I (abdullah) have 96 dpi logical, 82 dpi physical, and I see a 35px font setting for a "1" in a datum label as 34px, + // and I see kilsore and Elyas screenshots showing 41px and 61px in higher resolution monitors for the same configuration, I think + // that coin pixel size has to be corrected by the logical dpi of the monitor. The rationale is that: a) it obviously needs dpi + // correction, b) with physical dpi, the ratio of representation between kilsore and me is too far away. + // + // This means that the following correction does not have a documented basis, but appears necessary so that the Sketcher is usable in + // HDPI monitors. + + Client.drawingParameters.coinFontSize = std::lround(sketcherfontSize * 96.0f / dpi); + Client.drawingParameters.constraintIconSize = std::lround(0.8 * sketcherfontSize); + + // For marker size the global default is used. + // + // Rationale: + // -> Other WBs use the default value as is + // -> If a user has a HDPI, he will eventually change the value for the other WBs + // -> If we correct the value here in addition, we would get two times a resize + Client.drawingParameters.markerSize = markersize; + + Client.updateInventorNodeSizes(); +} + +void EditModeCoinManager::ParameterObserver::updateColor(SbColor &sbcolor, const std::string ¶metername) +{ + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); + + float transparency = 0.f; + unsigned long color = (unsigned long)(sbcolor.getPackedValue()); + color = hGrp->GetUnsigned(parametername.c_str(), color); + sbcolor.setPackedValue((uint32_t)color, transparency); +} + +void EditModeCoinManager::ParameterObserver::subscribeToParameters() +{ + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); + hGrp->Attach(this); + + ParameterGrp::handle hGrpsk = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General"); + hGrpsk->Attach(this); + + ParameterGrp::handle hGrpp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General"); + hGrpp->Attach(this); + + ParameterGrp::handle hGrpskg = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); + hGrpskg->Attach(this); + +} + +void EditModeCoinManager::ParameterObserver::unsubscribeToParameters() +{ + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); + hGrp->Detach(this); + + ParameterGrp::handle hGrpsk = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General"); + hGrpsk->Detach(this); + + ParameterGrp::handle hGrpp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General"); + hGrpp->Detach(this); + + ParameterGrp::handle hGrpskg = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); + hGrpskg->Detach(this); +} + +void EditModeCoinManager::ParameterObserver::OnChange(Base::Subject &rCaller, const char * sReason) +{ + (void) rCaller; + + auto key = str2updatefunction.find(sReason); + if( key != str2updatefunction.end() ) { + auto string = key->first; + auto function = key->second; + + function(string); + + Client.redrawViewProvider(); // redraw with non-temporal geometry + } +} + +//**************************** EditModeCoinManager class ****************************** + +EditModeCoinManager::EditModeCoinManager(ViewProviderSketch &vp):viewProvider(vp) { + + pEditModeConstraintCoinManager = std::make_unique(viewProvider, + drawingParameters, + geometryLayerParameters, + constraintParameters, + editModeScenegraphNodes, + coinMapping); + + pEditModeGeometryCoinManager = std::make_unique( viewProvider, + drawingParameters, + geometryLayerParameters, + analysisResults, + editModeScenegraphNodes, + coinMapping); + // Create Edit Mode Scenograph + createEditModeInventorNodes(); + + // Create parameter observer and initialise watched parameters + pObserver = std::make_unique(*this); +} + +EditModeCoinManager::~EditModeCoinManager() +{ + Gui::coinRemoveAllChildren(editModeScenegraphNodes.EditRoot); + ViewProviderSketchCoinAttorney::removeNodeFromRoot(viewProvider, editModeScenegraphNodes.EditRoot); + editModeScenegraphNodes.EditRoot->unref(); +} + +/***** Temporary edit curves and markers *****/ + +void EditModeCoinManager::drawEditMarkers(const std::vector &EditMarkers, unsigned int augmentationlevel) +{ + // determine marker size + int augmentedmarkersize = drawingParameters.markerSize; + + auto supportedsizes = Gui::Inventor::MarkerBitmaps::getSupportedSizes("CIRCLE_LINE"); + + auto defaultmarker = std::find(supportedsizes.begin(), supportedsizes.end(), drawingParameters.markerSize); + + if(defaultmarker != supportedsizes.end()) { + auto validAugmentationLevels = std::distance(defaultmarker,supportedsizes.end()); + + if(augmentationlevel >= validAugmentationLevels) + augmentationlevel = validAugmentationLevels - 1; + + augmentedmarkersize = *std::next(defaultmarker, augmentationlevel); + } + + editModeScenegraphNodes.EditMarkerSet->markerIndex.startEditing(); + editModeScenegraphNodes.EditMarkerSet->markerIndex = Gui::Inventor::MarkerBitmaps::getMarkerIndex("CIRCLE_LINE", augmentedmarkersize); + + // add the points to set + editModeScenegraphNodes.EditMarkersCoordinate->point.setNum(EditMarkers.size()); + editModeScenegraphNodes.EditMarkersMaterials->diffuseColor.setNum(EditMarkers.size()); + SbVec3f *verts = editModeScenegraphNodes.EditMarkersCoordinate->point.startEditing(); + SbColor *color = editModeScenegraphNodes.EditMarkersMaterials->diffuseColor.startEditing(); + + int i=0; // setting up the line set + for (std::vector::const_iterator it = EditMarkers.begin(); it != EditMarkers.end(); ++it,i++) { + verts[i].setValue(it->x, it->y, drawingParameters.zEdit); + color[i] = drawingParameters.InformationColor; + } + + editModeScenegraphNodes.EditMarkersCoordinate->point.finishEditing(); + editModeScenegraphNodes.EditMarkersMaterials->diffuseColor.finishEditing(); + editModeScenegraphNodes.EditMarkerSet->markerIndex.finishEditing(); +} + +void EditModeCoinManager::drawEdit(const std::vector &EditCurve) +{ + editModeScenegraphNodes.EditCurveSet->numVertices.setNum(1); + editModeScenegraphNodes.EditCurvesCoordinate->point.setNum(EditCurve.size()); + editModeScenegraphNodes.EditCurvesMaterials->diffuseColor.setNum(EditCurve.size()); + SbVec3f *verts = editModeScenegraphNodes.EditCurvesCoordinate->point.startEditing(); + int32_t *index = editModeScenegraphNodes.EditCurveSet->numVertices.startEditing(); + SbColor *color = editModeScenegraphNodes.EditCurvesMaterials->diffuseColor.startEditing(); + + int i=0; // setting up the line set + for (std::vector::const_iterator it = EditCurve.begin(); it != EditCurve.end(); ++it,i++) { + verts[i].setValue(it->x,it->y, drawingParameters.zEdit); + color[i] = drawingParameters.CreateCurveColor; + } + + index[0] = EditCurve.size(); + editModeScenegraphNodes.EditCurvesCoordinate->point.finishEditing(); + editModeScenegraphNodes.EditCurveSet->numVertices.finishEditing(); + editModeScenegraphNodes.EditCurvesMaterials->diffuseColor.finishEditing(); +} + +void EditModeCoinManager::setPositionText(const Base::Vector2d &Pos, const SbString &text) +{ + editModeScenegraphNodes.textX->string = text; + editModeScenegraphNodes.textPos->translation = SbVec3f(Pos.x, Pos.y, drawingParameters.zText); +} + +void EditModeCoinManager::setPositionText(const Base::Vector2d &Pos) +{ + SbString text; + text.sprintf(" (%.1f,%.1f)", Pos.x, Pos.y); + setPositionText(Pos,text); +} + +void EditModeCoinManager::resetPositionText(void) +{ + editModeScenegraphNodes.textX->string = ""; +} + +void EditModeCoinManager::setAxisPickStyle(bool on) +{ + if (on) + editModeScenegraphNodes.pickStyleAxes->style = SoPickStyle::SHAPE; + else + editModeScenegraphNodes.pickStyleAxes->style = SoPickStyle::UNPICKABLE; +} + +EditModeCoinManager::PreselectionResult EditModeCoinManager::detectPreselection(SoPickedPoint * Point, const SbVec2s &cursorPos) +{ + EditModeCoinManager::PreselectionResult result; + + if(!Point) + return result; + + //Base::Console().Log("Point pick\n"); + SoPath *path = Point->getPath(); + SoNode *tail = path->getTail(); // Tail is directly the node containing points and curves + + for(int l = 0; l < geometryLayerParameters.CoinLayers; l++) { + // checking for a hit in the points + if (tail == editModeScenegraphNodes.PointSet[l]) { + const SoDetail *point_detail = Point->getDetail(editModeScenegraphNodes.PointSet[l]); + if (point_detail && point_detail->getTypeId() == SoPointDetail::getClassTypeId()) { + // get the index + int pindex = static_cast(point_detail)->getCoordinateIndex(); + result.PointIndex = coinMapping.getPointVertexId(pindex, l); // returns -1 for root, global VertexId for the rest of vertices. + + if (result.PointIndex == -1) + result.Cross = PreselectionResult::Axes::RootPoint; + + return result; + } + } + + // checking for a hit in the curves + if (tail == editModeScenegraphNodes.CurveSet[l]) { + const SoDetail *curve_detail = Point->getDetail(editModeScenegraphNodes.CurveSet[l]); + if (curve_detail && curve_detail->getTypeId() == SoLineDetail::getClassTypeId()) { + // get the index + int curveIndex = static_cast(curve_detail)->getLineIndex(); + result.GeoIndex = coinMapping.getCurveGeoId(curveIndex, l); + + return result; + } + } + } + // checking for a hit in the axes + if (tail == editModeScenegraphNodes.RootCrossSet) { + const SoDetail *cross_detail = Point->getDetail(editModeScenegraphNodes.RootCrossSet); + if (cross_detail && cross_detail->getTypeId() == SoLineDetail::getClassTypeId()) { + // get the index (reserve index 0 for root point) + int CrossIndex = static_cast(cross_detail)->getLineIndex(); + + if(CrossIndex == 0) + result.Cross = PreselectionResult::Axes::HorizontalAxis; + else if(CrossIndex == 1) + result.Cross = PreselectionResult::Axes::VerticalAxis; + + return result; + } + } + // checking if a constraint is hit + result.ConstrIndices = pEditModeConstraintCoinManager->detectPreselectionConstr(Point, cursorPos); + + return result; +} + +SoGroup* EditModeCoinManager::getSelectedConstraints() +{ + SoGroup* group = new SoGroup(); + group->ref(); + + for (int i=0; i < editModeScenegraphNodes.constrGroup->getNumChildren(); i++) { + if (ViewProviderSketchCoinAttorney::isConstraintSelected(viewProvider, i)) { + SoSeparator *sep = pEditModeConstraintCoinManager->getConstraintIdSeparator(i); + if (sep) + group->addChild(sep); + } + } + + return group; +} + +/***** update coin nodes *****/ + +void EditModeCoinManager::processGeometryConstraintsInformationOverlay(const GeoListFacade & geolistfacade, bool rebuildinformationlayer) +{ + overlayParameters.rebuildInformationLayer = rebuildinformationlayer; + + pEditModeGeometryCoinManager->processGeometry(geolistfacade); + + updateOverlayParameters(); + + processGeometryInformationOverlay(geolistfacade); + + updateAxesLength(); + + updateGridExtent(); + + pEditModeConstraintCoinManager->processConstraints(geolistfacade); +} + +void EditModeCoinManager::updateOverlayParameters() +{ + if ( (analysisResults.combRepresentationScale > (2 * overlayParameters.currentBSplineCombRepresentationScale)) || + (analysisResults.combRepresentationScale < (overlayParameters.currentBSplineCombRepresentationScale / 2))) + overlayParameters.currentBSplineCombRepresentationScale = analysisResults.combRepresentationScale ; +} + +void EditModeCoinManager::processGeometryInformationOverlay(const GeoListFacade & geolistfacade) +{ + if(overlayParameters.rebuildInformationLayer) { + // every time we start with empty information overlay + Gui::coinRemoveAllChildren(editModeScenegraphNodes.infoGroup); + } + + auto ioconv = EditModeInformationOverlayCoinConverter(editModeScenegraphNodes.infoGroup, overlayParameters, drawingParameters); + + // geometry information layer for bsplines, as they need a second round now that max curvature is known + for (auto geoid : analysisResults.bsplineGeoIds) { + const Part::Geometry *geo = geolistfacade.getGeometryFromGeoId(geoid); + + ioconv.convert(geo, geoid); + } + + overlayParameters.visibleInformationChanged = false; // just updated +} + +void EditModeCoinManager::updateAxesLength() +{ + editModeScenegraphNodes.RootCrossCoordinate->point.set1Value(0,SbVec3f(-analysisResults.boundingBoxMagnitudeOrder, 0.0f, drawingParameters.zCross)); + editModeScenegraphNodes.RootCrossCoordinate->point.set1Value(1,SbVec3f(analysisResults.boundingBoxMagnitudeOrder, 0.0f, drawingParameters.zCross)); + editModeScenegraphNodes.RootCrossCoordinate->point.set1Value(2,SbVec3f(0.0f, -analysisResults.boundingBoxMagnitudeOrder, drawingParameters.zCross)); + editModeScenegraphNodes.RootCrossCoordinate->point.set1Value(3,SbVec3f(0.0f, analysisResults.boundingBoxMagnitudeOrder, drawingParameters.zCross)); +} + +void EditModeCoinManager::updateGridExtent() +{ + float dMagF = analysisResults.boundingBoxMagnitudeOrder; + + ViewProviderSketchCoinAttorney::updateGridExtent(viewProvider,-dMagF, dMagF, -dMagF, dMagF); +} + +void EditModeCoinManager::updateColor() +{ + auto geolistfacade = ViewProviderSketchCoinAttorney::getGeoListFacade(viewProvider); + + updateColor(geolistfacade); +} + +void EditModeCoinManager::updateColor(const GeoListFacade & geolistfacade) +{ + bool sketchinvalid = ViewProviderSketchCoinAttorney::isSketchInvalid(viewProvider); + + pEditModeGeometryCoinManager->updateGeometryColor(geolistfacade, sketchinvalid); + + // update constraint color + + auto constraints = ViewProviderSketchCoinAttorney::getConstraints(viewProvider); + + if(ViewProviderSketchCoinAttorney::haveConstraintsInvalidGeometry(viewProvider)) + return; + + pEditModeConstraintCoinManager->updateConstraintColor(constraints); +} + +void EditModeCoinManager::createEditModeInventorNodes() +{ + // 1 - Create the edit root node + editModeScenegraphNodes.EditRoot = new SoSeparator; + editModeScenegraphNodes.EditRoot->ref(); // Node is unref in the destructor of EditModeCoinManager + editModeScenegraphNodes.EditRoot->setName("Sketch_EditRoot"); + ViewProviderSketchCoinAttorney::addNodeToRoot(viewProvider, editModeScenegraphNodes.EditRoot); + editModeScenegraphNodes.EditRoot->renderCaching = SoSeparator::OFF ; + + // Create Geometry Coin nodes ++++++++++++++++++++++++++++++++++++++ + pEditModeGeometryCoinManager->createEditModeInventorNodes(); + + // stuff for the RootCross lines +++++++++++++++++++++++++++++++++++++++ + SoGroup* crossRoot = new Gui::SoSkipBoundingGroup; + editModeScenegraphNodes.pickStyleAxes = new SoPickStyle(); + editModeScenegraphNodes.pickStyleAxes->style = SoPickStyle::SHAPE; + crossRoot->addChild(editModeScenegraphNodes.pickStyleAxes); + editModeScenegraphNodes.EditRoot->addChild(crossRoot); + auto MtlBind = new SoMaterialBinding; + MtlBind->setName("RootCrossMaterialBinding"); + MtlBind->value = SoMaterialBinding::PER_FACE; + crossRoot->addChild(MtlBind); + + editModeScenegraphNodes.RootCrossDrawStyle = new SoDrawStyle; + editModeScenegraphNodes.RootCrossDrawStyle->setName("RootCrossDrawStyle"); + editModeScenegraphNodes.RootCrossDrawStyle->lineWidth = 2 * drawingParameters.pixelScalingFactor; + crossRoot->addChild(editModeScenegraphNodes.RootCrossDrawStyle); + + editModeScenegraphNodes.RootCrossMaterials = new SoMaterial; + editModeScenegraphNodes.RootCrossMaterials->setName("RootCrossMaterials"); + editModeScenegraphNodes.RootCrossMaterials->diffuseColor.set1Value(0, drawingParameters.CrossColorH); + editModeScenegraphNodes.RootCrossMaterials->diffuseColor.set1Value(1, drawingParameters.CrossColorV); + crossRoot->addChild(editModeScenegraphNodes.RootCrossMaterials); + + editModeScenegraphNodes.RootCrossCoordinate = new SoCoordinate3; + editModeScenegraphNodes.RootCrossCoordinate->setName("RootCrossCoordinate"); + crossRoot->addChild(editModeScenegraphNodes.RootCrossCoordinate); + + editModeScenegraphNodes.RootCrossSet = new SoLineSet; + editModeScenegraphNodes.RootCrossSet->setName("RootCrossLineSet"); + crossRoot->addChild(editModeScenegraphNodes.RootCrossSet); + + // stuff for the EditCurves +++++++++++++++++++++++++++++++++++++++ + SoSeparator* editCurvesRoot = new SoSeparator; + editModeScenegraphNodes.EditRoot->addChild(editCurvesRoot); + editModeScenegraphNodes.EditCurvesMaterials = new SoMaterial; + editModeScenegraphNodes.EditCurvesMaterials->setName("EditCurvesMaterials"); + editCurvesRoot->addChild(editModeScenegraphNodes.EditCurvesMaterials); + + editModeScenegraphNodes.EditCurvesCoordinate = new SoCoordinate3; + editModeScenegraphNodes.EditCurvesCoordinate->setName("EditCurvesCoordinate"); + editCurvesRoot->addChild(editModeScenegraphNodes.EditCurvesCoordinate); + + editModeScenegraphNodes.EditCurvesDrawStyle = new SoDrawStyle; + editModeScenegraphNodes.EditCurvesDrawStyle->setName("EditCurvesDrawStyle"); + editModeScenegraphNodes.EditCurvesDrawStyle->lineWidth = 3 * drawingParameters.pixelScalingFactor; + editCurvesRoot->addChild(editModeScenegraphNodes.EditCurvesDrawStyle); + + editModeScenegraphNodes.EditCurveSet = new SoLineSet; + editModeScenegraphNodes.EditCurveSet->setName("EditCurveLineSet"); + editCurvesRoot->addChild(editModeScenegraphNodes.EditCurveSet); + + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); + float transparency; + SbColor cursorTextColor(0,0,1); + cursorTextColor.setPackedValue((uint32_t)hGrp->GetUnsigned("CursorTextColor", cursorTextColor.getPackedValue()), transparency); + + // stuff for the EditMarkers +++++++++++++++++++++++++++++++++++++++ + SoSeparator* editMarkersRoot = new SoSeparator; + editModeScenegraphNodes.EditRoot->addChild(editMarkersRoot); + editModeScenegraphNodes.EditMarkersMaterials = new SoMaterial; + editModeScenegraphNodes.EditMarkersMaterials->setName("EditMarkersMaterials"); + editMarkersRoot->addChild(editModeScenegraphNodes.EditMarkersMaterials); + + editModeScenegraphNodes.EditMarkersCoordinate = new SoCoordinate3; + editModeScenegraphNodes.EditMarkersCoordinate->setName("EditMarkersCoordinate"); + editMarkersRoot->addChild(editModeScenegraphNodes.EditMarkersCoordinate); + + editModeScenegraphNodes.EditMarkersDrawStyle = new SoDrawStyle; + editModeScenegraphNodes.EditMarkersDrawStyle->setName("EditMarkersDrawStyle"); + editModeScenegraphNodes.EditMarkersDrawStyle->pointSize = 8 * drawingParameters.pixelScalingFactor; + editMarkersRoot->addChild(editModeScenegraphNodes.EditMarkersDrawStyle); + + editModeScenegraphNodes.EditMarkerSet = new SoMarkerSet; + editModeScenegraphNodes.EditMarkerSet->setName("EditMarkerSet"); + editModeScenegraphNodes.EditMarkerSet->markerIndex = Gui::Inventor::MarkerBitmaps::getMarkerIndex("CIRCLE_LINE", drawingParameters.markerSize); + editMarkersRoot->addChild(editModeScenegraphNodes.EditMarkerSet); + + // stuff for the edit coordinates ++++++++++++++++++++++++++++++++++++++ + SoSeparator *Coordsep = new SoSeparator(); + SoPickStyle* ps = new SoPickStyle(); + ps->style.setValue(SoPickStyle::UNPICKABLE); + Coordsep->addChild(ps); + Coordsep->setName("CoordSeparator"); + // no caching for frequently-changing data structures + Coordsep->renderCaching = SoSeparator::OFF; + + SoMaterial *CoordTextMaterials = new SoMaterial; + CoordTextMaterials->setName("CoordTextMaterials"); + CoordTextMaterials->diffuseColor = cursorTextColor; + Coordsep->addChild(CoordTextMaterials); + + SoFont *font = new SoFont(); + font->size.setValue(drawingParameters.coinFontSize); + + Coordsep->addChild(font); + + editModeScenegraphNodes.textPos = new SoTranslation(); + Coordsep->addChild(editModeScenegraphNodes.textPos); + + editModeScenegraphNodes.textX = new SoText2(); + editModeScenegraphNodes.textX->justification = SoText2::LEFT; + editModeScenegraphNodes.textX->string = ""; + Coordsep->addChild(editModeScenegraphNodes.textX); + editModeScenegraphNodes.EditRoot->addChild(Coordsep); + + // coin nodes for the constraints +++++++++++++++++++++++++++++++++++++++++++++++++++ + pEditModeConstraintCoinManager->createEditModeInventorNodes(); + + // group node for the Geometry information visual +++++++++++++++++++++++++++++++++++ + MtlBind = new SoMaterialBinding; + MtlBind->setName("InformationMaterialBinding"); + MtlBind->value = SoMaterialBinding::OVERALL ; + editModeScenegraphNodes.EditRoot->addChild(MtlBind); + + // use small line width for the information visual + editModeScenegraphNodes.InformationDrawStyle = new SoDrawStyle; + editModeScenegraphNodes.InformationDrawStyle->setName("InformationDrawStyle"); + editModeScenegraphNodes.InformationDrawStyle->lineWidth = 1 * drawingParameters.pixelScalingFactor; + editModeScenegraphNodes.EditRoot->addChild(editModeScenegraphNodes.InformationDrawStyle); + + // add the group where all the information entity has its SoSeparator + editModeScenegraphNodes.infoGroup = new SoGroup(); + editModeScenegraphNodes.infoGroup->setName("InformationGroup"); + editModeScenegraphNodes.EditRoot->addChild(editModeScenegraphNodes.infoGroup); +} + +void EditModeCoinManager::redrawViewProvider() +{ + viewProvider.draw(false,false); +} + +/************************ Delegated constraint public interface **********/ + +// public function that triggers drawing of most constraint icons +void EditModeCoinManager::drawConstraintIcons() +{ + pEditModeConstraintCoinManager->drawConstraintIcons(); +} + +void EditModeCoinManager::drawConstraintIcons(const GeoList & geolist) +{ + pEditModeConstraintCoinManager->drawConstraintIcons(geolist); +} + +void EditModeCoinManager::updateVirtualSpace() +{ + pEditModeConstraintCoinManager->updateVirtualSpace(); +} + +/************************ Resizing of coin nodes ************************/ + +int EditModeCoinManager::defaultApplicationFontSizePixels() const { + return ViewProviderSketchCoinAttorney::defaultApplicationFontSizePixels(viewProvider); +} + +int EditModeCoinManager::getApplicationLogicalDPIX() const { + return ViewProviderSketchCoinAttorney::getApplicationLogicalDPIX(viewProvider); +} + +void EditModeCoinManager::updateInventorNodeSizes() +{ + for(int l = 0; l < geometryLayerParameters.CoinLayers; l++) { + editModeScenegraphNodes.PointsDrawStyle[l]->pointSize = 8 * drawingParameters.pixelScalingFactor; + editModeScenegraphNodes.PointSet[l]->markerIndex = Gui::Inventor::MarkerBitmaps::getMarkerIndex("CIRCLE_FILLED", drawingParameters.markerSize); + editModeScenegraphNodes.CurvesDrawStyle[l]->lineWidth = 3 * drawingParameters.pixelScalingFactor; + } + + editModeScenegraphNodes.RootCrossDrawStyle->lineWidth = 2 * drawingParameters.pixelScalingFactor; + editModeScenegraphNodes.EditCurvesDrawStyle->lineWidth = 3 * drawingParameters.pixelScalingFactor; + editModeScenegraphNodes.EditMarkersDrawStyle->pointSize = 8 * drawingParameters.pixelScalingFactor; + editModeScenegraphNodes.EditMarkerSet->markerIndex = Gui::Inventor::MarkerBitmaps::getMarkerIndex("CIRCLE_LINE", drawingParameters.markerSize); + editModeScenegraphNodes.ConstraintDrawStyle->lineWidth = 1 * drawingParameters.pixelScalingFactor; + editModeScenegraphNodes.InformationDrawStyle->lineWidth = 1 * drawingParameters.pixelScalingFactor; + + pEditModeConstraintCoinManager->rebuildConstraintNodes(); +} + +/************************ Edit node access ************************/ + +SoSeparator* EditModeCoinManager::getRootEditNode() +{ + return editModeScenegraphNodes.EditRoot; +} diff --git a/src/Mod/Sketcher/Gui/EditModeCoinManager.h b/src/Mod/Sketcher/Gui/EditModeCoinManager.h new file mode 100644 index 0000000000..c58869f447 --- /dev/null +++ b/src/Mod/Sketcher/Gui/EditModeCoinManager.h @@ -0,0 +1,293 @@ +/*************************************************************************** + * Copyright (c) 2021 Abdullah Tahiri * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef SKETCHERGUI_EditModeCoinManager_H +#define SKETCHERGUI_EditModeCoinManager_H + +#include +#include + +#include +#include + +#include "EditModeCoinManagerParameters.h" + +#include + +class SbVec3f; +class SoRayPickAction; +class SoPickedPoint; +class SbVec3s; + +namespace Base { + template< typename T > + class Vector3; + + class Vector2d; + + class Placement; +} + +namespace Part { + class Geometry; +} + +namespace Sketcher { + class Constraint; + class PropertyConstraintList; +}; + +namespace SketcherGui { + +class ViewProviderSketch; +class EditModeConstraintCoinManager; +class EditModeGeometryCoinManager; + +using GeoList = Sketcher::GeoList; +using GeoListFacade = Sketcher::GeoListFacade; + +/** @brief Helper class for managing the Coin nodes of ViewProviderSketch. + * @details + * + * Given the substantial amount of code involved in coin node management, EditModeCoinManager + * further delegates on other specialised helper classes. Some of them share the + * ViewProviderSketchCoinAttorney, which defines the maximum coupling and minimum encapsulation. + * + * The most important such delegates are: EditModeGeometryCoinManager and EditModeConstraintCoinManager. + * + * EditModeCoinManager takes over the responsibility of creating the Coin (Inventor) scenograph + * and modifying it, including all the drawing of geometry, constraints and overlay layer. This + * is an exclusive responsibility under the Single Responsibility Principle. + * + * EditModeCoinManager exposes a public interface to be used by ViewProviderSketch. Where, + * EditModeCoinManager needs special access to facilities of ViewProviderSketch in order to fulfil + * its responsibility, this access is defined by ViewProviderSketchCoinAttorney. + * + * EditModeCoinManager is responsible, under the Single Responsibility Principle, to manage the coin + * EditRoot node. This node is ref-ed on creation and unref-ed on destruction to ensure that its lifetime + * matches the one of EditModecoinManager. + * + * EditRoot is added on request to pcRoot by ViewProviderSketch. The node pcRoot belongs, under the Single + * Responsibility Principle, to ViewProviderSketch. EditModeCoinManager delegates addition and removal of + * child notes of EditRoot to specialised helper classes. + * + * EditModeCoinManager is designed to define the span of time in which ViewProviderSketch is in edit mode. + * + * In addition to the scenograph, EditModeCoinManager is responsible for keeping any necessary mapping between + * indices used at ViewProviderSketch level, and internal indexing used by EditModeCoinManager and its subclasses. + */ +class SketcherGuiExport EditModeCoinManager +{ + /** @brief Class for monitoring changes in parameters affecting drawing and coin node generation + * @details + * + * This nested class is a helper responsible for attaching to the parameters relevant for + * EditModeCoinManager and its helpers, initialising the EditModeCoinManager to the current configuration + * and handle in real time any change to their values. + */ + class ParameterObserver : public ParameterGrp::ObserverType + { + private: + enum class OverlayVisibilityParameter { + BSplineDegree, + BSplineControlPolygonVisible, + BSplineCombVisible, + BSplineKnotMultiplicityVisible, + BSplinePoleWeightVisible + }; + + public: + ParameterObserver(EditModeCoinManager & client); + ~ParameterObserver(); + + void subscribeToParameters(); + + void unsubscribeToParameters(); + + /** Observer for parameter group. */ + void OnChange(Base::Subject &rCaller, const char * sReason) override; + + private: + void initParameters(); + void updateCurvedEdgeCountSegmentsParameter(const std::string & parametername); + void updateLineRenderingOrderParameters(const std::string & parametername); + void updateConstraintPresentationParameters(const std::string & parametername); + void updateElementSizeParameters(const std::string & parametername); + void updateColor(SbColor &sbcolor, const std::string ¶metername); + + template + void updateOverlayVisibilityParameter(const std::string & parametername); + + private: + std::map> str2updatefunction; + EditModeCoinManager &Client; + }; + +public: + /** @brief This struct defines the information provided to other classes about preselection. + * + * @details + * + * PointIndex: Only Positive values corresponding to VertexId (are positive for both normal and external geometry) + * GeoIndex: Same values as GeoId of GeoElementId, except for axes which are not included. -1 represents an invalid curve. + * + * In other words valid values are 0,1,2,... for normal geometry and -3,-4,-5,... for external geometry + * + * Cross: Axes and RootPoint values as defined in the enum class. + * + */ + struct PreselectionResult { + enum SpecialValues { + InvalidPoint = -1, + InvalidCurve = -1, + ExternalCurve = -3 + }; + + enum class Axes { + None = -1, + RootPoint = 0, + HorizontalAxis = 1, + VerticalAxis = 2 + }; + + int PointIndex = InvalidPoint; + int GeoIndex = InvalidCurve; // valid values are 0,1,2,... for normal geometry and -3,-4,-5,... for external geometry + Axes Cross = Axes::None; + std::set ConstrIndices; + + inline void clear() { + PointIndex = InvalidPoint; + GeoIndex = InvalidCurve; + Cross = Axes::None; + ConstrIndices.clear(); + } + }; + +public: + explicit EditModeCoinManager(ViewProviderSketch &vp); + ~EditModeCoinManager(); + + /** @name Temporary edit curves and markers */ + //@{ + void drawEditMarkers(const std::vector &EditMarkers, unsigned int augmentationlevel); + void drawEdit(const std::vector &EditCurve); + void setPositionText(const Base::Vector2d &Pos, const SbString &txt); + void setPositionText(const Base::Vector2d &Pos); + void resetPositionText(void); + void setAxisPickStyle(bool on); + //@} + + /** @name handle preselection and selection of points */ + //@{ + PreselectionResult detectPreselection(SoPickedPoint * Point, const SbVec2s &cursorPos); + /// The client is responsible for unref-ing the SoGroup to release the memory. + SoGroup* getSelectedConstraints(); + //@} + + /** @name update coin nodes*/ + void processGeometryConstraintsInformationOverlay(const GeoListFacade & geolistfacade, bool rebuildinformationlayer); + + void updateVirtualSpace(); + + /// Draw all constraint icons + /*! Except maybe the radius and lock ones? */ + void drawConstraintIcons(); + + // This specific overload is to use a specific geometry list, which may be a temporal one + void drawConstraintIcons(const GeoList & geolist); + //@} + + /** @name coin node access*/ + SoSeparator* getRootEditNode(); + //@} + + /** @name update coin colors*/ + //@{ + void updateColor(); + void updateColor(const GeoListFacade & geolistfacade); // overload to be used with temporal geometry. + //@} + + + /** @name change coin visualisation and behaviour*/ + //@{ + void updateGridExtent(); + //@} + +private: + // This function populates the coin nodes with the information of the current geometry + void processGeometry(const GeoListFacade & geolistfacade); + + // This function populates the geometry information layer of coin. It requires the analysis information + // gathered during the processGeometry step, so it is not possible to run both in parallel. + void processGeometryInformationOverlay(const GeoListFacade & geolistfacade); + + // updates the Axes length to extend beyond the calculated bounding box magnitude + void updateAxesLength(); + + // updates the parameters to be used for the Overlay information layer + void updateOverlayParameters(); + + void updateGeometryColor(const GeoListFacade & geolistfacade, bool issketchinvalid); + + // causes the ViewProvider to draw + void redrawViewProvider(); + + int defaultApplicationFontSizePixels() const; + + int getApplicationLogicalDPIX() const; + + void updateInventorNodeSizes(); + + /** @name coin nodes creation*/ + void createEditModeInventorNodes(); + //@} + +private: + /// Reference to ViewProviderSketch in order to access the public and the Attorney Interface + ViewProviderSketch & viewProvider; + /// Observer to track all the needed parameters. + std::unique_ptr pObserver; + + DrawingParameters drawingParameters; + AnalysisResults analysisResults; + OverlayParameters overlayParameters; + ConstraintParameters constraintParameters; + GeometryLayerParameters geometryLayerParameters; + + /// The pointers to Coin Scenegraph + EditModeScenegraphNodes editModeScenegraphNodes; + + /// Mapping between external and internal indices + CoinMapping coinMapping; + + // Coin Helpers + std::unique_ptr pEditModeConstraintCoinManager; + std::unique_ptr pEditModeGeometryCoinManager; +}; + + +} // namespace SketcherGui + + +#endif // SKETCHERGUI_EditModeCoinManager_H + diff --git a/src/Mod/Sketcher/Gui/EditModeCoinManagerParameters.cpp b/src/Mod/Sketcher/Gui/EditModeCoinManagerParameters.cpp new file mode 100644 index 0000000000..ff1aaf0ddc --- /dev/null +++ b/src/Mod/Sketcher/Gui/EditModeCoinManagerParameters.cpp @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (c) 2021 Abdullah Tahiri * + * * + * 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_ + +#endif // #ifndef _PreComp_ + +#include "EditModeCoinManagerParameters.h" + +using namespace SketcherGui; + +SbColor DrawingParameters::InformationColor (0.0f,1.0f,0.0f); // #00FF00 -> ( 0,255, 0) +SbColor DrawingParameters::CreateCurveColor (0.8f,0.8f,0.8f); // #CCCCCC -> (204,204,204) +SbColor DrawingParameters::CrossColorH (0.8f,0.4f,0.4f); // #CC6666 -> (204,102,102) +SbColor DrawingParameters::CrossColorV (0.47f,1.0f,0.51f); // #83FF83 -> (120,255,131) +SbColor DrawingParameters::InvalidSketchColor (1.0f,0.42f,0.0f); // #FF6D00 -> (255,109, 0) +SbColor DrawingParameters::FullyConstrainedColor (0.0f,1.0f,0.0f); // #00FF00 -> ( 0,255, 0) +SbColor DrawingParameters::FullyConstraintInternalAlignmentColor (0.87f,0.87f,0.78f); // #DEDEC8 -> (222,222,200) +SbColor DrawingParameters::InternalAlignedGeoColor (0.7f,0.7f,0.5f); // #B2B27F -> (178,178,127) +SbColor DrawingParameters::FullyConstraintConstructionPointColor (1.0f,0.58f,0.50f); // #FF9580 -> (255,149,128) +SbColor DrawingParameters::VertexColor (1.0f,0.149f,0.0f); // #FF2600 -> (255, 38, 0) +SbColor DrawingParameters::FullyConstraintElementColor (0.50f,0.81f,0.62f); // #80D0A0 -> (128,208,160) +SbColor DrawingParameters::CurveColor (1.0f,1.0f,1.0f); // #FFFFFF -> (255,255,255) +SbColor DrawingParameters::PreselectColor (0.88f,0.88f,0.0f); // #E1E100 -> (225,225, 0) +SbColor DrawingParameters::SelectColor (0.11f,0.68f,0.11f); // #1CAD1C -> ( 28,173, 28) +SbColor DrawingParameters::PreselectSelectedColor (0.36f,0.48f,0.11f); // #5D7B1C -> ( 93,123, 28) +SbColor DrawingParameters::CurveExternalColor (0.8f,0.2f,0.6f); // #CC3399 -> (204, 51,153) +SbColor DrawingParameters::CurveDraftColor (0.0f,0.0f,0.86f); // #0000DC -> ( 0, 0,220) +SbColor DrawingParameters::FullyConstraintConstructionElementColor (0.56f,0.66f,0.99f); // #8FA9FD -> (143,169,253) + +SbColor DrawingParameters::ConstrDimColor (1.0f,0.149f,0.0f); // #FF2600 -> (255, 38, 0) +SbColor DrawingParameters::ConstrIcoColor (1.0f,0.149f,0.0f); // #FF2600 -> (255, 38, 0) +SbColor DrawingParameters::NonDrivingConstrDimColor (0.0f,0.149f,1.0f); // #0026FF -> ( 0, 38,255) +SbColor DrawingParameters::ExprBasedConstrDimColor (1.0f,0.5f,0.149f); // #FF7F26 -> (255, 127,38) +SbColor DrawingParameters::DeactivatedConstrDimColor (0.8f,0.8f,0.8f); // #CCCCCC -> (204,204,204) + +QColor DrawingParameters::constrIcoColor( (int)(DrawingParameters::ConstrIcoColor [0] * 255.0f), + (int)(DrawingParameters::ConstrIcoColor[1] * 255.0f), + (int)(DrawingParameters::ConstrIcoColor[2] * 255.0f)); + +QColor DrawingParameters::nonDrivingConstrIcoColor( (int)(DrawingParameters::NonDrivingConstrDimColor[0] * 255.0f), + (int)(DrawingParameters::NonDrivingConstrDimColor[1] * 255.0f), + (int)(DrawingParameters::NonDrivingConstrDimColor[2] * 255.0f)); + +QColor DrawingParameters::constrIconSelColor ( (int)(DrawingParameters::SelectColor[0] * 255.0f), + (int)(DrawingParameters::SelectColor[1] * 255.0f), + (int)(DrawingParameters::SelectColor[2] * 255.0f)); + +QColor DrawingParameters::constrIconPreselColor ( (int)(DrawingParameters::PreselectColor[0] * 255.0f), + (int)(DrawingParameters::PreselectColor[1] * 255.0f), + (int)(DrawingParameters::PreselectColor[2] * 255.0f)); + +QColor DrawingParameters::constrIconDisabledColor ( (int)(DrawingParameters::DeactivatedConstrDimColor[0] * 255.0f), + (int)(DrawingParameters::DeactivatedConstrDimColor[1] * 255.0f), + (int)(DrawingParameters::DeactivatedConstrDimColor[2] * 255.0f)); + +const MultiFieldId MultiFieldId::Invalid = MultiFieldId(); diff --git a/src/Mod/Sketcher/Gui/EditModeCoinManagerParameters.h b/src/Mod/Sketcher/Gui/EditModeCoinManagerParameters.h new file mode 100644 index 0000000000..8b0d075b02 --- /dev/null +++ b/src/Mod/Sketcher/Gui/EditModeCoinManagerParameters.h @@ -0,0 +1,417 @@ +/*************************************************************************** + * Copyright (c) 2021 Abdullah Tahiri * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef SKETCHERGUI_EditModeCoinManagerParameters_H +#define SKETCHERGUI_EditModeCoinManagerParameters_H + +#ifndef _PreComp_ +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif // #ifndef _PreComp_ + +#include + +#include +#include + +#include + +#include +#include + + +namespace Part { + class Geometry; +} + +namespace SketcherGui { + +/** @brief Struct for storing local drawing parameters + * + * Parameters based on user preferenced are auto loaded by EditCoinManager observer nested class. + */ +struct DrawingParameters { + /// Defines the number of segments to use to draw a curved edge + int curvedEdgeCountSegments; + + /** @name Rendering Heights - virtual height introduced in the scenegraph to determine what is drawn on top of what*/ + //@{ + const float zEdit = 0.001f; // Height used by temporal edit curves + const float zCross = 0.001f; // Height used by the Axes + const float zInfo = 0.004f; // Height used by the Overlay information layer + const float zLowLines = 0.005f; // Height used for bottom rendered lines + const float zMidLines = 0.006f; // Height used for in-the-middle rendered lines + const float zHighLines = 0.007f; // Height used for on top rendered lines + const float zHighLine = 0.008f; // Height for highlighted lines (selected/preselected) + const float zConstr = 0.009f; // Height for rendering constraints + const float zLowPoints = 0.010f; // Height used for bottom rendered points + const float zHighPoints = 0.011f; // Height used for in-the-middle rendered points + const float zHighlight = 0.012f; // Height for highlighted points (selected/preselected) + const float zText = 0.012f; // Height for rendered text + //@} + + /// Different categories of geometries that can be selected by the user to be rendered on top, in the middle or in the bottom + enum class GeometryRendering { + NormalGeometry = 1, + Construction = 2, + ExternalGeometry = 3 + }; + + /** @name Rendering Order - defining what should be rendered on top and in the middle*/ + //@{ + GeometryRendering topRenderingGeometry = GeometryRendering::NormalGeometry; + GeometryRendering midRenderingGeometry = GeometryRendering::Construction; + //@} + + /** @name Rendering Coin Colors **/ + //@{ + static SbColor InformationColor; // Information Overlay Color + static SbColor CreateCurveColor; // Color for Edit Curves during creation + static SbColor CrossColorH; // Color for the Horizontal Axis + static SbColor CrossColorV; // Color for the Vertical Axis + static SbColor InvalidSketchColor; // Color for rendering an invalid sketch + static SbColor FullyConstrainedColor; // Color for a fully constrained sketch + static SbColor FullyConstraintInternalAlignmentColor; // Color for fully constrained internal alignment geometry + static SbColor InternalAlignedGeoColor; // Color for non-fully constrained internal alignment geometry + static SbColor FullyConstraintConstructionPointColor; // Color for fully constrained construction points + static SbColor VertexColor; // Color for vertices + static SbColor FullyConstraintElementColor; // Color for a fully constrained element + static SbColor CurveColor; // Color for curves + static SbColor PreselectColor; // Color used for pre-selection + static SbColor PreselectSelectedColor; // Color used for pre-selection when geometry is already selected + static SbColor SelectColor; // Color used for selected geometry + static SbColor CurveExternalColor; // Color used for external geometry + static SbColor CurveDraftColor; // Color used for construction geometry + static SbColor FullyConstraintConstructionElementColor; // Color used for a fully constrained construction element + static SbColor ConstrDimColor; // Color used for a dimensional constraints + static SbColor ConstrIcoColor; // Color used for constraint icons + static SbColor NonDrivingConstrDimColor; // Color used for non-driving (reference) dimensional constraints + static SbColor ExprBasedConstrDimColor; // Color used for expression based dimensional constraints + static SbColor DeactivatedConstrDimColor; // Color used for deactivated dimensional constraints + //@} + + /** @name Rendering Icon colors **/ + //@{ + static QColor constrIcoColor; // Icon color for constraints + static QColor nonDrivingConstrIcoColor; // Icon color for references (non-driving constraints) + static QColor constrIconSelColor; // Icon color for selected constraints + static QColor constrIconPreselColor; // Icon color for preselected constraints + static QColor constrIconDisabledColor; // Icon color for disabled constraints + //@} + + /** @name Rendering sizes (also to support HDPI monitors) **/ + //@{ + double pixelScalingFactor = 1.0; // Scaling factor to be used for pixels + int coinFontSize = 17; // Font size to be used by coin + int constraintIconSize = 15; // Size of constraint icons + int markerSize = 7; // Size used for markers + //@} +}; + +/** @brief Struct for storing references to the scenegraph nodes necessary for geometry layers + */ +struct GeometryLayerNodes { + /** @name Point nodes*/ + //@{ + std::vector & PointsMaterials; // The materials for the points/vertices + std::vector& PointsCoordinate; // The coordinates of the points/vertices + //@} + + /** @name Curve nodes*/ + //@{ + std::vector & CurvesMaterials; // The materials for the curves + std::vector & CurvesCoordinate; // The coordinates of the segments of the curves + std::vector & CurveSet; // The set of curves + //@} +}; + +/** @brief + * Helper class to store together a field index of a coin multifield object and the coin geometry layer to + * which it belongs. + * + * @details + * + * @warning the layer is * not * the logical layer (the one of GeometryFacade), but the coin layer. See GeometryLayerParameters. + * + * Overloaded operators and specialisation of std::less enable it to be used in containers including ordered + * containers. + */ +class MultiFieldId { +public: + explicit constexpr MultiFieldId(int fieldindex = -1, int layerid = 0): fieldIndex(fieldindex), + layerId(layerid){} + + MultiFieldId(const MultiFieldId & ) = default; + MultiFieldId & operator=(const MultiFieldId &) = default; + + MultiFieldId(MultiFieldId && ) = default; + MultiFieldId & operator=(MultiFieldId &&) = default; + + inline bool operator==(const MultiFieldId& obj) const + { + return this->fieldIndex == obj.fieldIndex && this->layerId == obj.layerId; + } + + inline bool operator!=(const MultiFieldId& obj) const + { + return this->fieldIndex != obj.fieldIndex || this->layerId != obj.layerId; + } + + int fieldIndex = -1; + int layerId = 0; + + static const MultiFieldId Invalid; +}; + + +} // namespace SketcherGui + +namespace std +{ + template<> struct less + { + bool operator() (const SketcherGui::MultiFieldId& lhs, const SketcherGui::MultiFieldId& rhs) const + { + return (lhs.layerId != rhs.layerId)?(lhs.layerId < rhs.layerId):(static_cast(lhs.fieldIndex) < static_cast(rhs.fieldIndex)); + } + }; +} // namespace std + + +namespace SketcherGui { + +/** @brief + * Helper class to store geometry layers configuration + * + * CoinLayers is the number of * Coin * Geometry Layers. This + * is *not* necessarily the number of Geometry layers (logical layers). + * + * Logical layers (as stored in GeometryFacade) define a grouping of geometries. + * However, this does not mean that they are represented in coin in different layers. + * Only when there is a reason for such mapping is done so. For example, when the + * geometry have different drawing parameters. + * + * This means that there may be: + * 1. A N:N relationship between logical layers and coin layers. + * 2. A M:N relationship between logical layers and coin layers (M logicalLayer2CoinLayer; +}; + +/** @brief Struct to hold the results of analysis performed on geometry +*/ +struct AnalysisResults { // TODO: This needs to be refactored + double combRepresentationScale = 0; // used for information overlay (BSpline comb) + float boundingBoxMagnitudeOrder = 0; // used for grid extension + std::vector bsplineGeoIds; // used for information overlay +}; + +/** @brief Struct adapted to store the parameters necessary to create and update + * the information overlay layer. + */ +struct OverlayParameters { + bool rebuildInformationLayer = false; + bool visibleInformationChanged = true; + double currentBSplineCombRepresentationScale = 0; + + // Parameters (auto loaded by EditCoinManager observer nested class) + bool bSplineDegreeVisible; + bool bSplineControlPolygonVisible; + bool bSplineCombVisible; + bool bSplineKnotMultiplicityVisible; + bool bSplinePoleWeightVisible; +}; + +/** @brief Struct adapted to store the parameters necessary to create and update + * constraints. + */ +struct ConstraintParameters { + bool bHideUnits; // whether units should be hidden or not + bool bShowDimensionalName; // whether the name of dimensional constraints should be shown or not + QString sDimensionalStringFormat; // how to code strings of dimensional constraints +}; + +/** @brief Helper struct adapted to store the pointer to edit mode scenegraph objects. + */ +struct EditModeScenegraphNodes { + /** @name Point nodes*/ + //@{ + SoSeparator * EditRoot; + SmSwitchboard * PointsGroup; + std::vector PointsMaterials; + std::vector PointsCoordinate; + std::vector PointsDrawStyle; + std::vector PointSet; + //@} + + /** @name Curve nodes*/ + //@{ + SmSwitchboard * CurvesGroup; + std::vector CurvesMaterials; + std::vector CurvesCoordinate; + std::vector CurvesDrawStyle; + std::vector CurveSet; + //@} + + /** @name Axes nodes*/ + /// @warning Root Point is added together with the Point nodes above + //@{ + SoMaterial *RootCrossMaterials; + SoCoordinate3 *RootCrossCoordinate; + SoLineSet *RootCrossSet; + SoDrawStyle *RootCrossDrawStyle; + //@} + + /** @name Temporal edit curve nodes - For geometry creation */ + //@{ + SoMaterial *EditCurvesMaterials; + SoCoordinate3 *EditCurvesCoordinate; + SoLineSet *EditCurveSet; + SoDrawStyle *EditCurvesDrawStyle; + SoPickStyle *pickStyleAxes; + //@} + + /** @name Temporal edit markers nodes- For operation rendering, such as trimming green circles*/ + //@{ + SoMaterial *EditMarkersMaterials; + SoCoordinate3 *EditMarkersCoordinate; + SoMarkerSet *EditMarkerSet; + SoDrawStyle *EditMarkersDrawStyle; + //@} + + /** @name Temporal edit text nodes*/ + //@{ + SoText2 *textX; + SoTranslation *textPos; + //@} + + /** @name Constraint nodes*/ + //@{ + SmSwitchboard *constrGroup; + SoDrawStyle *ConstraintDrawStyle; + //@} + + /** @name Information Overlay Layer nodes*/ + //@{ + SoGroup *infoGroup; + SoDrawStyle *InformationDrawStyle; + //@} +}; + +/** @brief Helper struct containing index conversions (mappings) between + * {GeoId, PointPos} and MF indices per layer, and VertexId and MF indices per layer. + * + * These are updated with every draw of the scenegraph. + */ +struct CoinMapping { + + void clear() { + CurvIdToGeoId.clear(); + PointIdToGeoId.clear(); + GeoElementId2SetId.clear(); + PointIdToVertexId.clear(); + }; + + /// given the MF index of a curve and the coin layer in which it is drawn returns the GeoId of the curve + int getCurveGeoId(int curveindex, int layerindex) {return CurvIdToGeoId[layerindex][curveindex];} + /// given the MF index of a point and the coin layer in which it is drawn returns the GeoId of the point + int getPointGeoId(int pointindex, int layerindex) {return PointIdToGeoId[layerindex][pointindex];} + /// given the MF index of a point and the coin layer in which it is drawn returns the VertexId of the point + int getPointVertexId(int pointindex, int layerindex) {return PointIdToVertexId[layerindex][pointindex];} + + /// given the {GeoId, PointPos} of a curve or point, returns MultiFieldId containing the MF index and the coin layer of the curve or point + MultiFieldId getIndexLayer(int geoid, Sketcher::PointPos pos) { + auto indexit = GeoElementId2SetId.find(Sketcher::GeoElementId(geoid, pos)); + + if (indexit != GeoElementId2SetId.end()) { + return indexit->second; + } + + return MultiFieldId::Invalid; + } + + /// given the VertexId of a point, returns MultiFieldId containing the MF index and the coin layer of the point + MultiFieldId getIndexLayer(int vertexId) { + + for(size_t l=0; l> CurvIdToGeoId; // conversion of SoLineSet index to GeoId + std::vector> PointIdToGeoId; // conversion of SoCoordinate3 index to GeoId + + //* This maps an MF index (second index) of a point within a coin layer (first index) to a global VertexId */ + std::vector> PointIdToVertexId; + + /// This maps GeoElementId index {GeoId, PointPos} to a {coin layer and MF index} of a curve or point. + std::map GeoElementId2SetId; +}; + + +} // namespace SketcherGui + +#endif // SKETCHERGUI_EditModeCoinManagerParameters_H + diff --git a/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp new file mode 100644 index 0000000000..6f0ece3642 --- /dev/null +++ b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp @@ -0,0 +1,2385 @@ +/*************************************************************************** + * Copyright (c) 2021 Abdullah Tahiri * + * * + * 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 +#endif // #ifndef _PreComp_ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include + +#include "SoZoomTranslation.h" +#include "SoDatumLabel.h" + +#include "EditModeInformationOverlayCoinConverter.h" + +#include "EditModeGeometryCoinConverter.h" + +#include "EditModeCoinManager.h" + +#include "ViewProviderSketch.h" + +#include "ViewProviderSketchCoinAttorney.h" + +#include "EditModeConstraintCoinManager.h" + +using namespace SketcherGui; +using namespace Sketcher; + +//**************************** EditModeConstraintCoinManager class ****************************** + +EditModeConstraintCoinManager::EditModeConstraintCoinManager( ViewProviderSketch &vp, + DrawingParameters & drawingParams, + GeometryLayerParameters & geometryLayerParams, + ConstraintParameters & constraintParams, + EditModeScenegraphNodes & editModeScenegraph, + CoinMapping & coinMap): + viewProvider(vp), + drawingParameters(drawingParams), + geometryLayerParameters(geometryLayerParams), + constraintParameters(constraintParams), + editModeScenegraphNodes(editModeScenegraph), + coinMapping(coinMap) +{} + +EditModeConstraintCoinManager::~EditModeConstraintCoinManager() +{} + +void EditModeConstraintCoinManager::updateVirtualSpace() +{ + const std::vector &constrlist = ViewProviderSketchCoinAttorney::getConstraints(viewProvider); + + bool isshownvirtualspace = ViewProviderSketchCoinAttorney::isShownVirtualSpace(viewProvider); + + if(constrlist.size() == vConstrType.size()) { + + editModeScenegraphNodes.constrGroup->enable.setNum(constrlist.size()); + + SbBool *sws = editModeScenegraphNodes.constrGroup->enable.startEditing(); + + for (size_t i = 0; i < constrlist.size(); i++) + sws[i] = !(constrlist[i]->isInVirtualSpace != isshownvirtualspace); // XOR of constraint mode and VP mode + + + editModeScenegraphNodes.constrGroup->enable.finishEditing(); + } +} + +void EditModeConstraintCoinManager::processConstraints(const GeoListFacade & geolistfacade) +{ + const auto &constrlist = ViewProviderSketchCoinAttorney::getConstraints(viewProvider); + + // After an undo/redo it can happen that we have an empty geometry list but a non-empty constraint list + // In this case just ignore the constraints. (See bug #0000421) + if (geolistfacade.geomlist.size() <= 2 && !constrlist.empty()) { + rebuildConstraintNodes(geolistfacade); + return; + } + + int extGeoCount = geolistfacade.getExternalCount(); + int intGeoCount = geolistfacade.getInternalCount(); + + // reset point if the constraint type has changed +Restart: + // check if a new constraint arrived + if (constrlist.size() != vConstrType.size()) + rebuildConstraintNodes(geolistfacade); + + assert(int(constrlist.size()) == editModeScenegraphNodes.constrGroup->getNumChildren()); + assert(int(vConstrType.size()) == editModeScenegraphNodes.constrGroup->getNumChildren()); + + // update the virtual space + updateVirtualSpace(); + + auto getNormal = [] (const GeoListFacade & geolistfacade, const int geoid, const Base::Vector3d & pointoncurve) { + auto geom = geolistfacade.getGeometryFromGeoId(geoid); + auto curve = dynamic_cast(geom); + + Base::Vector3d normal; + if(!(curve && curve->normalAt(pointoncurve, normal))) { + normal = Base::Vector3d(1,0,0); + } + + return normal; + }; + + // go through the constraints and update the position + int i = 0; + for (std::vector::const_iterator it=constrlist.begin(); + it != constrlist.end(); ++it, i++) { + // check if the type has changed + if ((*it)->Type != vConstrType[i]) { + // clearing the type vector will force a rebuild of the visual nodes + vConstrType.clear(); + //TODO: The 'goto' here is unsafe as it can happen that we cause an endless loop (see bug #0001956). + goto Restart; + } + try{//because calculateNormalAtPoint, used in there, can throw + // root separator for this constraint + SoSeparator *sep = static_cast(editModeScenegraphNodes.constrGroup->getChild(i)); + const Constraint *Constr = *it; + + if(Constr->First < -extGeoCount || Constr->First >= intGeoCount + || (Constr->Second!=GeoEnum::GeoUndef + && (Constr->Second < -extGeoCount || Constr->Second >= intGeoCount)) + || (Constr->Third!=GeoEnum::GeoUndef + && (Constr->Third < -extGeoCount || Constr->Third >= intGeoCount))) + { + // Constraint can refer to non-existent geometry during undo/redo + continue; + } + + // distinguish different constraint types to build up + switch (Constr->Type) { + case Block: + case Horizontal: // write the new position of the Horizontal constraint Same as vertical position. + case Vertical: // write the new position of the Vertical constraint + { + assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); + bool alignment = Constr->Type!=Block && Constr->Second != GeoEnum::GeoUndef; + + // get the geometry + const Part::Geometry *geo = geolistfacade.getGeometryFromGeoId(Constr->First); + + if (!alignment) { + // Vertical & Horiz can only be a GeomLineSegment, but Blocked can be anything. + Base::Vector3d midpos; + Base::Vector3d dir; + Base::Vector3d norm; + + if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + const Part::GeomLineSegment *lineSeg = static_cast(geo); + + // calculate the half distance between the start and endpoint + midpos = ((lineSeg->getEndPoint()+lineSeg->getStartPoint())/2); + + //Get a set of vectors perpendicular and tangential to these + dir = (lineSeg->getEndPoint()-lineSeg->getStartPoint()).Normalize(); + + norm = Base::Vector3d(-dir.y,dir.x,0); + } + else if (geo->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()) { + const Part::GeomBSplineCurve *bsp = static_cast(geo); + midpos = Base::Vector3d(0,0,0); + + std::vector poles = bsp->getPoles(); + + // Move center of gravity towards start not to collide with bspline degree information. + double ws = 1.0 / poles.size(); + double w = 1.0; + + for (std::vector::iterator it = poles.begin(); it != poles.end(); ++it) { + midpos += w*(*it); + w -= ws; + } + + midpos /= poles.size(); + + dir = (bsp->getEndPoint() - bsp->getStartPoint()).Normalize(); + norm = Base::Vector3d(-dir.y,dir.x,0); + } + else { + double ra=0,rb=0; + double angle,angleplus=0.;//angle = rotation of object as a whole; angleplus = arc angle (t parameter for ellipses). + if (geo->getTypeId() == Part::GeomCircle::getClassTypeId()) { + const Part::GeomCircle *circle = static_cast(geo); + ra = circle->getRadius(); + angle = M_PI/4; + midpos = circle->getCenter(); + } else if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle *arc = static_cast(geo); + ra = arc->getRadius(); + double startangle, endangle; + arc->getRange(startangle, endangle, /*emulateCCW=*/true); + angle = (startangle + endangle)/2; + midpos = arc->getCenter(); + } else if (geo->getTypeId() == Part::GeomEllipse::getClassTypeId()) { + const Part::GeomEllipse *ellipse = static_cast(geo); + ra = ellipse->getMajorRadius(); + rb = ellipse->getMinorRadius(); + Base::Vector3d majdir = ellipse->getMajorAxisDir(); + angle = atan2(majdir.y, majdir.x); + angleplus = M_PI/4; + midpos = ellipse->getCenter(); + } else if (geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) { + const Part::GeomArcOfEllipse *aoe = static_cast(geo); + ra = aoe->getMajorRadius(); + rb = aoe->getMinorRadius(); + double startangle, endangle; + aoe->getRange(startangle, endangle, /*emulateCCW=*/true); + Base::Vector3d majdir = aoe->getMajorAxisDir(); + angle = atan2(majdir.y, majdir.x); + angleplus = (startangle + endangle)/2; + midpos = aoe->getCenter(); + } else if (geo->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId()) { + const Part::GeomArcOfHyperbola *aoh = static_cast(geo); + ra = aoh->getMajorRadius(); + rb = aoh->getMinorRadius(); + double startangle, endangle; + aoh->getRange(startangle, endangle, /*emulateCCW=*/true); + Base::Vector3d majdir = aoh->getMajorAxisDir(); + angle = atan2(majdir.y, majdir.x); + angleplus = (startangle + endangle)/2; + midpos = aoh->getCenter(); + } else if (geo->getTypeId() == Part::GeomArcOfParabola::getClassTypeId()) { + const Part::GeomArcOfParabola *aop = static_cast(geo); + ra = aop->getFocal(); + double startangle, endangle; + aop->getRange(startangle, endangle, /*emulateCCW=*/true); + Base::Vector3d majdir = - aop->getXAxisDir(); + angle = atan2(majdir.y, majdir.x); + angleplus = (startangle + endangle)/2; + midpos = aop->getFocus(); + } else + break; + + if( geo->getTypeId() == Part::GeomEllipse::getClassTypeId() || + geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() || + geo->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ){ + + Base::Vector3d majDir, minDir, rvec; + majDir = Base::Vector3d(cos(angle),sin(angle),0);//direction of major axis of ellipse + minDir = Base::Vector3d(-majDir.y,majDir.x,0);//direction of minor axis of ellipse + rvec = (ra*cos(angleplus)) * majDir + (rb*sin(angleplus)) * minDir; + midpos += rvec; + rvec.Normalize(); + norm = rvec; + dir = Base::Vector3d(-rvec.y,rvec.x,0);//DeepSOIC: I'm not sure what dir is supposed to mean. + } + else { + norm = Base::Vector3d(cos(angle),sin(angle),0); + dir = Base::Vector3d(-norm.y,norm.x,0); + midpos += ra*norm; + } + } + + Base::Vector3d relpos = seekConstraintPosition(midpos, norm, dir, 2.5, editModeScenegraphNodes.constrGroup->getChild(i)); + + auto translation = static_cast(sep->getChild(static_cast(ConstraintNodePosition::FirstTranslationIndex))); + + translation->abPos = SbVec3f(midpos.x, midpos.y, drawingParameters.zConstr); //Absolute Reference + + //Reference Position that is scaled according to zoom + translation->translation = SbVec3f(relpos.x, relpos.y, 0); + } + else { + assert(Constr->Second >= -extGeoCount && Constr->Second < intGeoCount); + assert(Constr->FirstPos != Sketcher::PointPos::none && Constr->SecondPos != Sketcher::PointPos::none); + + Base::Vector3d midpos1, dir1, norm1; + Base::Vector3d midpos2, dir2, norm2; + + midpos1 = geolistfacade.getPoint(Constr->First, Constr->FirstPos); + midpos2 = geolistfacade.getPoint(Constr->Second, Constr->SecondPos); + + dir1 = (midpos2-midpos1).Normalize(); + dir2 = -dir1; + norm1 = Base::Vector3d(-dir1.y,dir1.x,0.); + norm2 = norm1; + + Base::Vector3d relpos1 = seekConstraintPosition(midpos1, norm1, dir1, 4.0, editModeScenegraphNodes.constrGroup->getChild(i)); + + auto translation = static_cast(sep->getChild(static_cast(ConstraintNodePosition::FirstTranslationIndex))); + + translation->abPos = SbVec3f(midpos1.x, midpos1.y, drawingParameters.zConstr); + translation->translation = SbVec3f(relpos1.x, relpos1.y, 0); + + Base::Vector3d relpos2 = seekConstraintPosition(midpos2, norm2, dir2, 4.0, editModeScenegraphNodes.constrGroup->getChild(i)); + + Base::Vector3d secondPos = midpos2 - midpos1; + + translation = static_cast(sep->getChild(static_cast(ConstraintNodePosition::SecondTranslationIndex))); + + translation->abPos = SbVec3f(secondPos.x, secondPos.y, drawingParameters.zConstr); + translation->translation = SbVec3f(relpos2.x -relpos1.x, relpos2.y -relpos1.y, 0); + } + } + break; + case Perpendicular: + { + assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); + assert(Constr->Second >= -extGeoCount && Constr->Second < intGeoCount); + // get the geometry + const Part::Geometry *geo1 = geolistfacade.getGeometryFromGeoId(Constr->First); + const Part::Geometry *geo2 = geolistfacade.getGeometryFromGeoId(Constr->Second); + + Base::Vector3d midpos1, dir1, norm1; + Base::Vector3d midpos2, dir2, norm2; + bool twoIcons = false;//a very local flag. It's set to true to indicate that the second dir+norm are valid and should be used + + + if (Constr->Third != GeoEnum::GeoUndef || //perpty via point + Constr->FirstPos != Sketcher::PointPos::none) { //endpoint-to-curve or endpoint-to-endpoint perpty + + int ptGeoId; + Sketcher::PointPos ptPosId; + do {//dummy loop to use break =) Maybe goto? + ptGeoId = Constr->First; + ptPosId = Constr->FirstPos; + if (ptPosId != Sketcher::PointPos::none) break; + ptGeoId = Constr->Second; + ptPosId = Constr->SecondPos; + if (ptPosId != Sketcher::PointPos::none) break; + ptGeoId = Constr->Third; + ptPosId = Constr->ThirdPos; + if (ptPosId != Sketcher::PointPos::none) break; + assert(0);//no point found! + } while (false); + + midpos1 = geolistfacade.getPoint(ptGeoId, ptPosId); + + norm1 = getNormal(geolistfacade, Constr->Second, midpos1); + + // TODO: Check the method above. This was the old one making use of the solver. + //norm1 = getSolvedSketch().calculateNormalAtPoint(Constr->Second, midpos1.x, midpos1.y); + + norm1.Normalize(); + dir1 = norm1; dir1.RotateZ(-M_PI/2.0); + + } else if (Constr->FirstPos == Sketcher::PointPos::none) { + + if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + const Part::GeomLineSegment *lineSeg1 = static_cast(geo1); + midpos1 = ((lineSeg1->getEndPoint()+lineSeg1->getStartPoint())/2); + dir1 = (lineSeg1->getEndPoint()-lineSeg1->getStartPoint()).Normalize(); + norm1 = Base::Vector3d(-dir1.y,dir1.x,0.); + } else if (geo1->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle *arc = static_cast(geo1); + double startangle, endangle, midangle; + arc->getRange(startangle, endangle, /*emulateCCW=*/true); + midangle = (startangle + endangle)/2; + norm1 = Base::Vector3d(cos(midangle),sin(midangle),0); + dir1 = Base::Vector3d(-norm1.y,norm1.x,0); + midpos1 = arc->getCenter() + arc->getRadius() * norm1; + } else if (geo1->getTypeId() == Part::GeomCircle::getClassTypeId()) { + const Part::GeomCircle *circle = static_cast(geo1); + norm1 = Base::Vector3d(cos(M_PI/4),sin(M_PI/4),0); + dir1 = Base::Vector3d(-norm1.y,norm1.x,0); + midpos1 = circle->getCenter() + circle->getRadius() * norm1; + } else + break; + + if (geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + const Part::GeomLineSegment *lineSeg2 = static_cast(geo2); + midpos2 = ((lineSeg2->getEndPoint()+lineSeg2->getStartPoint())/2); + dir2 = (lineSeg2->getEndPoint()-lineSeg2->getStartPoint()).Normalize(); + norm2 = Base::Vector3d(-dir2.y,dir2.x,0.); + } else if (geo2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle *arc = static_cast(geo2); + double startangle, endangle, midangle; + arc->getRange(startangle, endangle, /*emulateCCW=*/true); + midangle = (startangle + endangle)/2; + norm2 = Base::Vector3d(cos(midangle),sin(midangle),0); + dir2 = Base::Vector3d(-norm2.y,norm2.x,0); + midpos2 = arc->getCenter() + arc->getRadius() * norm2; + } else if (geo2->getTypeId() == Part::GeomCircle::getClassTypeId()) { + const Part::GeomCircle *circle = static_cast(geo2); + norm2 = Base::Vector3d(cos(M_PI/4),sin(M_PI/4),0); + dir2 = Base::Vector3d(-norm2.y,norm2.x,0); + midpos2 = circle->getCenter() + circle->getRadius() * norm2; + } else + break; + twoIcons = true; + } + + Base::Vector3d relpos1 = seekConstraintPosition(midpos1, norm1, dir1, 4.0, editModeScenegraphNodes.constrGroup->getChild(i)); + + auto translation = static_cast(sep->getChild(static_cast(ConstraintNodePosition::FirstTranslationIndex))); + + translation->abPos = SbVec3f(midpos1.x, midpos1.y, drawingParameters.zConstr); + translation->translation = SbVec3f(relpos1.x, relpos1.y, 0); + + if (twoIcons) { + Base::Vector3d relpos2 = seekConstraintPosition(midpos2, norm2, dir2, 4.0, editModeScenegraphNodes.constrGroup->getChild(i)); + + Base::Vector3d secondPos = midpos2 - midpos1; + auto translation = static_cast(sep->getChild(static_cast(ConstraintNodePosition::SecondTranslationIndex))); + translation->abPos = SbVec3f(secondPos.x, secondPos.y, drawingParameters.zConstr); + translation->translation = SbVec3f(relpos2.x -relpos1.x, relpos2.y -relpos1.y, 0); + } + + } + break; + case Parallel: + case Equal: + { + assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); + assert(Constr->Second >= -extGeoCount && Constr->Second < intGeoCount); + // get the geometry + const Part::Geometry *geo1 = geolistfacade.getGeometryFromGeoId(Constr->First); + const Part::Geometry *geo2 = geolistfacade.getGeometryFromGeoId(Constr->Second); + + Base::Vector3d midpos1, dir1, norm1; + Base::Vector3d midpos2, dir2, norm2; + if (geo1->getTypeId() != Part::GeomLineSegment::getClassTypeId() || + geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId()) { + if (Constr->Type == Equal) { + double r1a=0,r1b=0,r2a=0,r2b=0; + double angle1,angle1plus=0., angle2, angle2plus=0.;//angle1 = rotation of object as a whole; angle1plus = arc angle (t parameter for ellipses). + if (geo1->getTypeId() == Part::GeomCircle::getClassTypeId()) { + const Part::GeomCircle *circle = static_cast(geo1); + r1a = circle->getRadius(); + angle1 = M_PI/4; + midpos1 = circle->getCenter(); + } else if (geo1->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle *arc = static_cast(geo1); + r1a = arc->getRadius(); + double startangle, endangle; + arc->getRange(startangle, endangle, /*emulateCCW=*/true); + angle1 = (startangle + endangle)/2; + midpos1 = arc->getCenter(); + } else if (geo1->getTypeId() == Part::GeomEllipse::getClassTypeId()) { + const Part::GeomEllipse *ellipse = static_cast(geo1); + r1a = ellipse->getMajorRadius(); + r1b = ellipse->getMinorRadius(); + Base::Vector3d majdir = ellipse->getMajorAxisDir(); + angle1 = atan2(majdir.y, majdir.x); + angle1plus = M_PI/4; + midpos1 = ellipse->getCenter(); + } else if (geo1->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) { + const Part::GeomArcOfEllipse *aoe = static_cast(geo1); + r1a = aoe->getMajorRadius(); + r1b = aoe->getMinorRadius(); + double startangle, endangle; + aoe->getRange(startangle, endangle, /*emulateCCW=*/true); + Base::Vector3d majdir = aoe->getMajorAxisDir(); + angle1 = atan2(majdir.y, majdir.x); + angle1plus = (startangle + endangle)/2; + midpos1 = aoe->getCenter(); + } else if (geo1->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId()) { + const Part::GeomArcOfHyperbola *aoh = static_cast(geo1); + r1a = aoh->getMajorRadius(); + r1b = aoh->getMinorRadius(); + double startangle, endangle; + aoh->getRange(startangle, endangle, /*emulateCCW=*/true); + Base::Vector3d majdir = aoh->getMajorAxisDir(); + angle1 = atan2(majdir.y, majdir.x); + angle1plus = (startangle + endangle)/2; + midpos1 = aoh->getCenter(); + } else if (geo1->getTypeId() == Part::GeomArcOfParabola::getClassTypeId()) { + const Part::GeomArcOfParabola *aop = static_cast(geo1); + r1a = aop->getFocal(); + double startangle, endangle; + aop->getRange(startangle, endangle, /*emulateCCW=*/true); + Base::Vector3d majdir = - aop->getXAxisDir(); + angle1 = atan2(majdir.y, majdir.x); + angle1plus = (startangle + endangle)/2; + midpos1 = aop->getFocus(); + } else + break; + + if (geo2->getTypeId() == Part::GeomCircle::getClassTypeId()) { + const Part::GeomCircle *circle = static_cast(geo2); + r2a = circle->getRadius(); + angle2 = M_PI/4; + midpos2 = circle->getCenter(); + } else if (geo2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle *arc = static_cast(geo2); + r2a = arc->getRadius(); + double startangle, endangle; + arc->getRange(startangle, endangle, /*emulateCCW=*/true); + angle2 = (startangle + endangle)/2; + midpos2 = arc->getCenter(); + } else if (geo2->getTypeId() == Part::GeomEllipse::getClassTypeId()) { + const Part::GeomEllipse *ellipse = static_cast(geo2); + r2a = ellipse->getMajorRadius(); + r2b = ellipse->getMinorRadius(); + Base::Vector3d majdir = ellipse->getMajorAxisDir(); + angle2 = atan2(majdir.y, majdir.x); + angle2plus = M_PI/4; + midpos2 = ellipse->getCenter(); + } else if (geo2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) { + const Part::GeomArcOfEllipse *aoe = static_cast(geo2); + r2a = aoe->getMajorRadius(); + r2b = aoe->getMinorRadius(); + double startangle, endangle; + aoe->getRange(startangle, endangle, /*emulateCCW=*/true); + Base::Vector3d majdir = aoe->getMajorAxisDir(); + angle2 = atan2(majdir.y, majdir.x); + angle2plus = (startangle + endangle)/2; + midpos2 = aoe->getCenter(); + } else if (geo2->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId()) { + const Part::GeomArcOfHyperbola *aoh = static_cast(geo2); + r2a = aoh->getMajorRadius(); + r2b = aoh->getMinorRadius(); + double startangle, endangle; + aoh->getRange(startangle, endangle, /*emulateCCW=*/true); + Base::Vector3d majdir = aoh->getMajorAxisDir(); + angle2 = atan2(majdir.y, majdir.x); + angle2plus = (startangle + endangle)/2; + midpos2 = aoh->getCenter(); + } else if (geo2->getTypeId() == Part::GeomArcOfParabola::getClassTypeId()) { + const Part::GeomArcOfParabola *aop = static_cast(geo2); + r2a = aop->getFocal(); + double startangle, endangle; + aop->getRange(startangle, endangle, /*emulateCCW=*/true); + Base::Vector3d majdir = -aop->getXAxisDir(); + angle2 = atan2(majdir.y, majdir.x); + angle2plus = (startangle + endangle)/2; + midpos2 = aop->getFocus(); + } else + break; + + if( geo1->getTypeId() == Part::GeomEllipse::getClassTypeId() || + geo1->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() || + geo1->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ){ + + Base::Vector3d majDir, minDir, rvec; + majDir = Base::Vector3d(cos(angle1),sin(angle1),0);//direction of major axis of ellipse + minDir = Base::Vector3d(-majDir.y,majDir.x,0);//direction of minor axis of ellipse + rvec = (r1a*cos(angle1plus)) * majDir + (r1b*sin(angle1plus)) * minDir; + midpos1 += rvec; + rvec.Normalize(); + norm1 = rvec; + dir1 = Base::Vector3d(-rvec.y,rvec.x,0);//DeepSOIC: I'm not sure what dir is supposed to mean. + } + else { + norm1 = Base::Vector3d(cos(angle1),sin(angle1),0); + dir1 = Base::Vector3d(-norm1.y,norm1.x,0); + midpos1 += r1a*norm1; + } + + + if( geo2->getTypeId() == Part::GeomEllipse::getClassTypeId() || + geo2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() || + geo2->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId()) { + + Base::Vector3d majDir, minDir, rvec; + majDir = Base::Vector3d(cos(angle2),sin(angle2),0);//direction of major axis of ellipse + minDir = Base::Vector3d(-majDir.y,majDir.x,0);//direction of minor axis of ellipse + rvec = (r2a*cos(angle2plus)) * majDir + (r2b*sin(angle2plus)) * minDir; + midpos2 += rvec; + rvec.Normalize(); + norm2 = rvec; + dir2 = Base::Vector3d(-rvec.y,rvec.x,0); + } + else { + norm2 = Base::Vector3d(cos(angle2),sin(angle2),0); + dir2 = Base::Vector3d(-norm2.y,norm2.x,0); + midpos2 += r2a*norm2; + } + + } else // Parallel can only apply to a GeomLineSegment + break; + } else { + const Part::GeomLineSegment *lineSeg1 = static_cast(geo1); + const Part::GeomLineSegment *lineSeg2 = static_cast(geo2); + + // calculate the half distance between the start and endpoint + midpos1 = ((lineSeg1->getEndPoint()+lineSeg1->getStartPoint())/2); + midpos2 = ((lineSeg2->getEndPoint()+lineSeg2->getStartPoint())/2); + //Get a set of vectors perpendicular and tangential to these + dir1 = (lineSeg1->getEndPoint()-lineSeg1->getStartPoint()).Normalize(); + dir2 = (lineSeg2->getEndPoint()-lineSeg2->getStartPoint()).Normalize(); + norm1 = Base::Vector3d(-dir1.y,dir1.x,0.); + norm2 = Base::Vector3d(-dir2.y,dir2.x,0.); + } + + Base::Vector3d relpos1 = seekConstraintPosition(midpos1, norm1, dir1, 4.0, editModeScenegraphNodes.constrGroup->getChild(i)); + Base::Vector3d relpos2 = seekConstraintPosition(midpos2, norm2, dir2, 4.0, editModeScenegraphNodes.constrGroup->getChild(i)); + + auto translation = static_cast(sep->getChild(static_cast(ConstraintNodePosition::FirstTranslationIndex))); + + translation->abPos = SbVec3f(midpos1.x, midpos1.y, drawingParameters.zConstr); //Absolute Reference + + //Reference Position that is scaled according to zoom + translation->translation = SbVec3f(relpos1.x, relpos1.y, 0); + + Base::Vector3d secondPos = midpos2 - midpos1; + + translation = static_cast(sep->getChild(static_cast(ConstraintNodePosition::SecondTranslationIndex))); + + translation->abPos = SbVec3f(secondPos.x, secondPos.y, drawingParameters.zConstr); //Absolute Reference + + //Reference Position that is scaled according to zoom + translation->translation = SbVec3f(relpos2.x - relpos1.x, relpos2.y -relpos1.y, 0); + + } + break; + case Distance: + case DistanceX: + case DistanceY: + { + assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); + + Base::Vector3d pnt1(0.,0.,0.), pnt2(0.,0.,0.); + if (Constr->SecondPos != Sketcher::PointPos::none) { // point to point distance + pnt1 = geolistfacade.getPoint(Constr->First, Constr->FirstPos); + pnt2 = geolistfacade.getPoint(Constr->Second, Constr->SecondPos); + } else if (Constr->Second != GeoEnum::GeoUndef) { // point to line distance + pnt1 = geolistfacade.getPoint(Constr->First, Constr->FirstPos); + + const Part::Geometry *geo = geolistfacade.getGeometryFromGeoId(Constr->Second); + if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + const Part::GeomLineSegment *lineSeg = static_cast(geo); + Base::Vector3d l2p1 = lineSeg->getStartPoint(); + Base::Vector3d l2p2 = lineSeg->getEndPoint(); + // calculate the projection of p1 onto line2 + pnt2.ProjectToLine(pnt1-l2p1, l2p2-l2p1); + pnt2 += pnt1; + } else + break; + } else if (Constr->FirstPos != Sketcher::PointPos::none) { + pnt2 = geolistfacade.getPoint(Constr->First, Constr->FirstPos); + } else if (Constr->First != GeoEnum::GeoUndef) { + const Part::Geometry *geo = geolistfacade.getGeometryFromGeoId(Constr->First); + if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + const Part::GeomLineSegment *lineSeg = static_cast(geo); + pnt1 = lineSeg->getStartPoint(); + pnt2 = lineSeg->getEndPoint(); + } else + break; + } else + break; + + SoDatumLabel *asciiText = static_cast(sep->getChild(static_cast(ConstraintNodePosition::DatumLabelIndex))); + + // Get presentation string (w/o units if option is set) + asciiText->string = SbString( getPresentationString(Constr).toUtf8().constData() ); + + if (Constr->Type == Distance) + asciiText->datumtype = SoDatumLabel::DISTANCE; + else if (Constr->Type == DistanceX) + asciiText->datumtype = SoDatumLabel::DISTANCEX; + else if (Constr->Type == DistanceY) + asciiText->datumtype = SoDatumLabel::DISTANCEY; + + // Assign the Datum Points + asciiText->pnts.setNum(2); + SbVec3f *verts = asciiText->pnts.startEditing(); + + verts[0] = SbVec3f (pnt1.x, pnt1.y, drawingParameters.zConstr); + verts[1] = SbVec3f (pnt2.x, pnt2.y, drawingParameters.zConstr); + + asciiText->pnts.finishEditing(); + + //Assign the Label Distance + asciiText->param1 = Constr->LabelDistance; + asciiText->param2 = Constr->LabelPosition; + } + break; + case PointOnObject: + case Tangent: + case SnellsLaw: + { + assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); + assert(Constr->Second >= -extGeoCount && Constr->Second < intGeoCount); + + Base::Vector3d pos, relPos; + if ( Constr->Type == PointOnObject || + Constr->Type == SnellsLaw || + (Constr->Type == Tangent && Constr->Third != GeoEnum::GeoUndef) || //Tangency via point + (Constr->Type == Tangent && Constr->FirstPos != Sketcher::PointPos::none) //endpoint-to-curve or endpoint-to-endpoint tangency + ) { + + //find the point of tangency/point that is on object + //just any point among first/second/third should be OK + int ptGeoId; + Sketcher::PointPos ptPosId; + do {//dummy loop to use break =) Maybe goto? + ptGeoId = Constr->First; + ptPosId = Constr->FirstPos; + if (ptPosId != Sketcher::PointPos::none) break; + ptGeoId = Constr->Second; + ptPosId = Constr->SecondPos; + if (ptPosId != Sketcher::PointPos::none) break; + ptGeoId = Constr->Third; + ptPosId = Constr->ThirdPos; + if (ptPosId != Sketcher::PointPos::none) break; + assert(0);//no point found! + } while (false); + + pos = geolistfacade.getPoint(ptGeoId, ptPosId); + auto norm = getNormal(geolistfacade, Constr->Second, pos); + + // TODO: Check substitution + // Base::Vector3d norm = getSolvedSketch().calculateNormalAtPoint(Constr->Second, pos.x, pos.y); + norm.Normalize(); + Base::Vector3d dir = norm; dir.RotateZ(-M_PI/2.0); + + relPos = seekConstraintPosition(pos, norm, dir, 2.5, editModeScenegraphNodes.constrGroup->getChild(i)); + + auto translation = static_cast(sep->getChild(static_cast(ConstraintNodePosition::FirstTranslationIndex))); + + translation->abPos = SbVec3f(pos.x, pos.y, drawingParameters.zConstr); //Absolute Reference + translation->translation = SbVec3f(relPos.x, relPos.y, 0); + } + else if (Constr->Type == Tangent) { + // get the geometry + const Part::Geometry *geo1 = geolistfacade.getGeometryFromGeoId(Constr->First); + const Part::Geometry *geo2 = geolistfacade.getGeometryFromGeoId(Constr->Second); + + if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId() && + geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + const Part::GeomLineSegment *lineSeg1 = static_cast(geo1); + const Part::GeomLineSegment *lineSeg2 = static_cast(geo2); + // tangency between two lines + Base::Vector3d midpos1 = ((lineSeg1->getEndPoint()+lineSeg1->getStartPoint())/2); + Base::Vector3d midpos2 = ((lineSeg2->getEndPoint()+lineSeg2->getStartPoint())/2); + Base::Vector3d dir1 = (lineSeg1->getEndPoint()-lineSeg1->getStartPoint()).Normalize(); + Base::Vector3d dir2 = (lineSeg2->getEndPoint()-lineSeg2->getStartPoint()).Normalize(); + Base::Vector3d norm1 = Base::Vector3d(-dir1.y,dir1.x,0.f); + Base::Vector3d norm2 = Base::Vector3d(-dir2.y,dir2.x,0.f); + + Base::Vector3d relpos1 = seekConstraintPosition(midpos1, norm1, dir1, 4.0, editModeScenegraphNodes.constrGroup->getChild(i)); + Base::Vector3d relpos2 = seekConstraintPosition(midpos2, norm2, dir2, 4.0, editModeScenegraphNodes.constrGroup->getChild(i)); + + auto translation = static_cast(sep->getChild(static_cast(ConstraintNodePosition::FirstTranslationIndex))); + + translation->abPos = SbVec3f(midpos1.x, midpos1.y, drawingParameters.zConstr); //Absolute Reference + translation->translation = SbVec3f(relpos1.x, relpos1.y, 0); + + Base::Vector3d secondPos = midpos2 - midpos1; + + translation = static_cast(sep->getChild(static_cast(ConstraintNodePosition::SecondTranslationIndex))); + + translation->abPos = SbVec3f(secondPos.x, secondPos.y, drawingParameters.zConstr); //Absolute Reference + translation->translation = SbVec3f(relpos2.x -relpos1.x, relpos2.y -relpos1.y, 0); + + break; + } + else if (geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + std::swap(geo1,geo2); + } + + if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + const Part::GeomLineSegment *lineSeg = static_cast(geo1); + Base::Vector3d dir = (lineSeg->getEndPoint() - lineSeg->getStartPoint()).Normalize(); + Base::Vector3d norm(-dir.y, dir.x, 0); + if (geo2->getTypeId()== Part::GeomCircle::getClassTypeId()) { + const Part::GeomCircle *circle = static_cast(geo2); + // tangency between a line and a circle + float length = (circle->getCenter() - lineSeg->getStartPoint())*dir; + + pos = lineSeg->getStartPoint() + dir * length; + relPos = norm * 1; //TODO Huh? + } + else if (geo2->getTypeId()== Part::GeomEllipse::getClassTypeId() || + geo2->getTypeId()== Part::GeomArcOfEllipse::getClassTypeId()) { + + Base::Vector3d center; + if(geo2->getTypeId()== Part::GeomEllipse::getClassTypeId()){ + const Part::GeomEllipse *ellipse = static_cast(geo2); + center=ellipse->getCenter(); + } else { + const Part::GeomArcOfEllipse *aoc = static_cast(geo2); + center=aoc->getCenter(); + } + + // tangency between a line and an ellipse + float length = (center - lineSeg->getStartPoint())*dir; + + pos = lineSeg->getStartPoint() + dir * length; + relPos = norm * 1; + } + else if (geo2->getTypeId()== Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle *arc = static_cast(geo2); + // tangency between a line and an arc + float length = (arc->getCenter() - lineSeg->getStartPoint())*dir; + + pos = lineSeg->getStartPoint() + dir * length; + relPos = norm * 1; //TODO Huh? + } + } + + if (geo1->getTypeId()== Part::GeomCircle::getClassTypeId() && + geo2->getTypeId()== Part::GeomCircle::getClassTypeId()) { + const Part::GeomCircle *circle1 = static_cast(geo1); + const Part::GeomCircle *circle2 = static_cast(geo2); + // tangency between two cicles + Base::Vector3d dir = (circle2->getCenter() - circle1->getCenter()).Normalize(); + pos = circle1->getCenter() + dir * circle1->getRadius(); + relPos = dir * 1; + } + else if (geo2->getTypeId()== Part::GeomCircle::getClassTypeId()) { + std::swap(geo1,geo2); + } + + if (geo1->getTypeId()== Part::GeomCircle::getClassTypeId() && + geo2->getTypeId()== Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomCircle *circle = static_cast(geo1); + const Part::GeomArcOfCircle *arc = static_cast(geo2); + // tangency between a circle and an arc + Base::Vector3d dir = (arc->getCenter() - circle->getCenter()).Normalize(); + pos = circle->getCenter() + dir * circle->getRadius(); + relPos = dir * 1; + } + else if (geo1->getTypeId()== Part::GeomArcOfCircle::getClassTypeId() && + geo2->getTypeId()== Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle *arc1 = static_cast(geo1); + const Part::GeomArcOfCircle *arc2 = static_cast(geo2); + // tangency between two arcs + Base::Vector3d dir = (arc2->getCenter() - arc1->getCenter()).Normalize(); + pos = arc1->getCenter() + dir * arc1->getRadius(); + relPos = dir * 1; + } + auto translation = static_cast(sep->getChild(static_cast(ConstraintNodePosition::FirstTranslationIndex))); + + translation->abPos = SbVec3f(pos.x, pos.y, drawingParameters.zConstr); //Absolute Reference + translation->translation = SbVec3f(relPos.x, relPos.y, 0); + } + } + break; + case Symmetric: + { + assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); + assert(Constr->Second >= -extGeoCount && Constr->Second < intGeoCount); + + Base::Vector3d pnt1 = geolistfacade.getPoint(Constr->First, Constr->FirstPos); + Base::Vector3d pnt2 = geolistfacade.getPoint(Constr->Second, Constr->SecondPos); + + SbVec3f p1(pnt1.x, pnt1.y, drawingParameters.zConstr); + SbVec3f p2(pnt2.x, pnt2.y, drawingParameters.zConstr); + SbVec3f dir = (p2-p1); + dir.normalize(); + SbVec3f norm (-dir[1],dir[0],0); + + SoDatumLabel *asciiText = static_cast(sep->getChild(static_cast(ConstraintNodePosition::DatumLabelIndex))); + asciiText->datumtype = SoDatumLabel::SYMMETRIC; + + asciiText->pnts.setNum(2); + SbVec3f *verts = asciiText->pnts.startEditing(); + + verts[0] = p1; + verts[1] = p2; + + asciiText->pnts.finishEditing(); + + auto translation = static_cast(sep->getChild(static_cast(ConstraintNodePosition::FirstTranslationIndex))); + + translation->translation = (p1 + p2)/2; + } + break; + case Angle: + { + assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); + assert((Constr->Second >= -extGeoCount && Constr->Second < intGeoCount) || + Constr->Second == GeoEnum::GeoUndef); + + SbVec3f p0; + double startangle,range,endangle; + if (Constr->Second != GeoEnum::GeoUndef) { + Base::Vector3d dir1, dir2; + if(Constr->Third == GeoEnum::GeoUndef) { //angle between two lines + const Part::Geometry *geo1 = geolistfacade.getGeometryFromGeoId(Constr->First); + const Part::Geometry *geo2 = geolistfacade.getGeometryFromGeoId(Constr->Second); + if (geo1->getTypeId() != Part::GeomLineSegment::getClassTypeId() || + geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId()) + break; + const Part::GeomLineSegment *lineSeg1 = static_cast(geo1); + const Part::GeomLineSegment *lineSeg2 = static_cast(geo2); + + bool flip1 = (Constr->FirstPos == PointPos::end); + bool flip2 = (Constr->SecondPos == PointPos::end); + dir1 = (flip1 ? -1. : 1.) * (lineSeg1->getEndPoint()-lineSeg1->getStartPoint()); + dir2 = (flip2 ? -1. : 1.) * (lineSeg2->getEndPoint()-lineSeg2->getStartPoint()); + Base::Vector3d pnt1 = flip1 ? lineSeg1->getEndPoint() : lineSeg1->getStartPoint(); + Base::Vector3d pnt2 = flip2 ? lineSeg2->getEndPoint() : lineSeg2->getStartPoint(); + + // line-line intersection + { + double det = dir1.x*dir2.y - dir1.y*dir2.x; + if ((det > 0 ? det : -det) < 1e-10) { + // lines are coincident (or parallel) and in this case the center + // of the point pairs with the shortest distance is used + Base::Vector3d p1[2], p2[2]; + p1[0] = lineSeg1->getStartPoint(); + p1[1] = lineSeg1->getEndPoint(); + p2[0] = lineSeg2->getStartPoint(); + p2[1] = lineSeg2->getEndPoint(); + double length = DBL_MAX; + for (int i=0; i <= 1; i++) { + for (int j=0; j <= 1; j++) { + double tmp = (p2[j]-p1[i]).Length(); + if (tmp < length) { + length = tmp; + p0.setValue((p2[j].x+p1[i].x)/2,(p2[j].y+p1[i].y)/2,0); + } + } + } + } + else { + double c1 = dir1.y*pnt1.x - dir1.x*pnt1.y; + double c2 = dir2.y*pnt2.x - dir2.x*pnt2.y; + double x = (dir1.x*c2 - dir2.x*c1)/det; + double y = (dir1.y*c2 - dir2.y*c1)/det; + p0 = SbVec3f(x,y,0); + } + } + + range = Constr->getValue(); // WYSIWYG + startangle = atan2(dir1.y,dir1.x); + } + else {//angle-via-point + Base::Vector3d p = geolistfacade.getPoint(Constr->Third, Constr->ThirdPos); + p0 = SbVec3f(p.x, p.y, 0); + dir1 = getNormal(geolistfacade, Constr->First, p); + // TODO: Check + // dir1 = getSolvedSketch().calculateNormalAtPoint(Constr->First, p.x, p.y); + dir1.RotateZ(-M_PI/2);//convert to vector of tangency by rotating + dir2 = getNormal(geolistfacade, Constr->Second, p); + // TODO: Check + // dir2 = getSolvedSketch().calculateNormalAtPoint(Constr->Second, p.x, p.y); + dir2.RotateZ(-M_PI/2); + + startangle = atan2(dir1.y,dir1.x); + range = atan2(dir1.x*dir2.y-dir1.y*dir2.x, + dir1.x*dir2.x+dir1.y*dir2.y); + } + + endangle = startangle + range; + + } else if (Constr->First != GeoEnum::GeoUndef) { + const Part::Geometry *geo = geolistfacade.getGeometryFromGeoId(Constr->First); + if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + const Part::GeomLineSegment *lineSeg = static_cast(geo); + p0 = Base::convertTo((lineSeg->getEndPoint()+lineSeg->getStartPoint())/2); + + Base::Vector3d dir = lineSeg->getEndPoint()-lineSeg->getStartPoint(); + startangle = 0.; + range = atan2(dir.y,dir.x); + endangle = startangle + range; + } + else if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle *arc = static_cast(geo); + p0 = Base::convertTo(arc->getCenter()); + + arc->getRange(startangle, endangle,/*emulateCCWXY=*/true); + range = endangle - startangle; + } + else { + break; + } + } else + break; + + SoDatumLabel *asciiText = static_cast(sep->getChild(static_cast(ConstraintNodePosition::DatumLabelIndex))); + asciiText->string = SbString(getPresentationString(Constr).toUtf8().constData()); + asciiText->datumtype = SoDatumLabel::ANGLE; + asciiText->param1 = Constr->LabelDistance; + asciiText->param2 = startangle; + asciiText->param3 = range; + + asciiText->pnts.setNum(2); + SbVec3f *verts = asciiText->pnts.startEditing(); + + verts[0] = p0; + + asciiText->pnts.finishEditing(); + + } + break; + case Diameter: + { + assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); + + Base::Vector3d pnt1(0.,0.,0.), pnt2(0.,0.,0.); + if (Constr->First != GeoEnum::GeoUndef) { + const Part::Geometry *geo = geolistfacade.getGeometryFromGeoId(Constr->First); + + if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle *arc = static_cast(geo); + double radius = arc->getRadius(); + double angle = (double) Constr->LabelPosition; + if (angle == 10) { + double startangle, endangle; + arc->getRange(startangle, endangle, /*emulateCCW=*/true); + angle = (startangle + endangle)/2; + } + Base::Vector3d center = arc->getCenter(); + pnt1 = center - radius * Base::Vector3d(cos(angle),sin(angle),0.); + pnt2 = center + radius * Base::Vector3d(cos(angle),sin(angle),0.); + } + else if (geo->getTypeId() == Part::GeomCircle::getClassTypeId()) { + const Part::GeomCircle *circle = static_cast(geo); + double radius = circle->getRadius(); + double angle = (double) Constr->LabelPosition; + if (angle == 10) { + angle = 0; + } + Base::Vector3d center = circle->getCenter(); + pnt1 = center - radius * Base::Vector3d(cos(angle),sin(angle),0.); + pnt2 = center + radius * Base::Vector3d(cos(angle),sin(angle),0.); + } + else + break; + } else + break; + + SbVec3f p1(pnt1.x, pnt1.y, drawingParameters.zConstr); + SbVec3f p2(pnt2.x, pnt2.y, drawingParameters.zConstr); + + SoDatumLabel *asciiText = static_cast(sep->getChild(static_cast(ConstraintNodePosition::DatumLabelIndex))); + + // Get display string with units hidden if so requested + asciiText->string = SbString( getPresentationString(Constr).toUtf8().constData() ); + + asciiText->datumtype = SoDatumLabel::DIAMETER; + asciiText->param1 = Constr->LabelDistance; + asciiText->param2 = Constr->LabelPosition; + + asciiText->pnts.setNum(2); + SbVec3f *verts = asciiText->pnts.startEditing(); + + verts[0] = p1; + verts[1] = p2; + + asciiText->pnts.finishEditing(); + } + break; + case Weight: + case Radius: + { + assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); + + Base::Vector3d pnt1(0.,0.,0.), pnt2(0.,0.,0.); + + if (Constr->First != GeoEnum::GeoUndef) { + const Part::Geometry *geo = geolistfacade.getGeometryFromGeoId(Constr->First); + + if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { + const Part::GeomArcOfCircle *arc = static_cast(geo); + double radius = arc->getRadius(); + double angle = (double) Constr->LabelPosition; + if (angle == 10) { + double startangle, endangle; + arc->getRange(startangle, endangle, /*emulateCCW=*/true); + angle = (startangle + endangle)/2; + } + pnt1 = arc->getCenter(); + pnt2 = pnt1 + radius * Base::Vector3d(cos(angle),sin(angle),0.); + } + else if (geo->getTypeId() == Part::GeomCircle::getClassTypeId()) { + const Part::GeomCircle *circle = static_cast(geo); + auto gf = GeometryFacade::getFacade(geo); + + double radius; + + radius = circle->getRadius(); + + double angle = (double) Constr->LabelPosition; + if (angle == 10) { + angle = 0; + } + pnt1 = circle->getCenter(); + pnt2 = pnt1 + radius * Base::Vector3d(cos(angle),sin(angle),0.); + } + else + break; + } else + break; + + SbVec3f p1(pnt1.x, pnt1.y, drawingParameters.zConstr); + SbVec3f p2(pnt2.x, pnt2.y, drawingParameters.zConstr); + + SoDatumLabel *asciiText = static_cast(sep->getChild(static_cast(ConstraintNodePosition::DatumLabelIndex))); + + // Get display string with units hidden if so requested + if(Constr->Type == Weight) + asciiText->string = SbString( QString::number(Constr->getValue()).toStdString().c_str()); + else + asciiText->string = SbString( getPresentationString(Constr).toUtf8().constData() ); + + asciiText->datumtype = SoDatumLabel::RADIUS; + asciiText->param1 = Constr->LabelDistance; + asciiText->param2 = Constr->LabelPosition; + + asciiText->pnts.setNum(2); + SbVec3f *verts = asciiText->pnts.startEditing(); + + verts[0] = p1; + verts[1] = p2; + + asciiText->pnts.finishEditing(); + } + break; + case Coincident: // nothing to do for coincident + case None: + case InternalAlignment: + case NumConstraintTypes: + break; + } + + } catch (Base::Exception &e) { + Base::Console().Error("Exception during draw: %s\n", e.what()); + } catch (...){ + Base::Console().Error("Exception during draw: unknown\n"); + } + + } +} + +Base::Vector3d EditModeConstraintCoinManager::seekConstraintPosition(const Base::Vector3d &origPos, + const Base::Vector3d &norm, + const Base::Vector3d &dir, float step, + const SoNode *constraint) +{ + + auto rp = ViewProviderSketchCoinAttorney::getRayPickAction(viewProvider); + + float scaled_step = step * ViewProviderSketchCoinAttorney::getScaleFactor(viewProvider); + + int multiplier = 0; + Base::Vector3d relPos, freePos; + bool isConstraintAtPosition = true; + while (isConstraintAtPosition && multiplier < 10) { + // Calculate new position of constraint + relPos = norm * 0.5f + dir * multiplier; + freePos = origPos + relPos * scaled_step; + + rp->setRadius(0.1f); + rp->setPickAll(true); + rp->setRay(SbVec3f(freePos.x, freePos.y, -1.f), SbVec3f(0, 0, 1) ); + //problem + rp->apply(editModeScenegraphNodes.constrGroup); // We could narrow it down to just the SoGroup containing the constraints + + // returns a copy of the point + SoPickedPoint *pp = rp->getPickedPoint(); + const SoPickedPointList ppl = rp->getPickedPointList(); + + if (ppl.getLength() <= 1 && pp) { + SoPath *path = pp->getPath(); + int length = path->getLength(); + SoNode *tailFather1 = path->getNode(length-2); + SoNode *tailFather2 = path->getNode(length-3); + + // checking if a constraint is the same as the one selected + if (tailFather1 == constraint || tailFather2 == constraint) + isConstraintAtPosition = false; + } + else { + isConstraintAtPosition = false; + } + + multiplier *= -1; // search in both sides + if (multiplier >= 0) + multiplier++; // Increment the multiplier + } + if (multiplier == 10) + relPos = norm * 0.5f; // no free position found + return relPos * step; +} + +void EditModeConstraintCoinManager::updateConstraintColor(const std::vector &constraints) +{ + // Because coincident constraints are selected using the point color, we need to edit the point materials. + + std::vector PtNum; + std::vector pcolor; // point color + std::vector CurvNum; + std::vector color; // curve color + + for(int l=0; ldiffuseColor.getNum()); + pcolor.push_back(editModeScenegraphNodes.PointsMaterials[l]->diffuseColor.startEditing()); + CurvNum.push_back(editModeScenegraphNodes.CurvesMaterials[l]->diffuseColor.getNum()); + color.push_back(editModeScenegraphNodes.CurvesMaterials[l]->diffuseColor.startEditing()); + } + + int maxNumberOfConstraints = std::min(editModeScenegraphNodes.constrGroup->getNumChildren(), static_cast(constraints.size())); + + // colors of the constraints + for (int i = 0; i < maxNumberOfConstraints; i++) { + SoSeparator *s = static_cast(editModeScenegraphNodes.constrGroup->getChild(i)); + + // Check Constraint Type + Sketcher::Constraint* constraint = constraints[i]; + ConstraintType type = constraint->Type; + bool hasDatumLabel = (type == Sketcher::Angle || + type == Sketcher::Radius || + type == Sketcher::Diameter || + type == Sketcher::Weight || + type == Sketcher::Symmetric || + type == Sketcher::Distance || + type == Sketcher::DistanceX || + type == Sketcher::DistanceY); + + // Non DatumLabel Nodes will have a material excluding coincident + bool hasMaterial = false; + + SoMaterial *m = 0; + if (!hasDatumLabel && type != Sketcher::Coincident && type != Sketcher::InternalAlignment) { + hasMaterial = true; + m = static_cast(s->getChild(static_cast(ConstraintNodePosition::MaterialIndex))); + } + + auto selectpoint = [this, pcolor, PtNum](int geoid, Sketcher::PointPos pos){ + if(geoid >= 0) { + auto multifieldIndex = coinMapping.getIndexLayer(geoid, pos); + + if (multifieldIndex != MultiFieldId::Invalid) { + int index = multifieldIndex.fieldIndex; + int layer = multifieldIndex.layerId; + if(layer < static_cast(PtNum.size()) && index >= 0 && index < PtNum[layer]) { + pcolor[layer][index] = drawingParameters.SelectColor; + } + } + } + }; + + auto selectline = [this, color, CurvNum](int geoid){ + if(geoid >= 0) { + auto multifieldIndex = coinMapping.getIndexLayer(geoid, Sketcher::PointPos::none); + + if (multifieldIndex != MultiFieldId::Invalid) { + int index = multifieldIndex.fieldIndex; + int layer = multifieldIndex.layerId; + if(layer < static_cast(CurvNum.size()) && index >= 0 && index < CurvNum[layer]) { + color[layer][index] = drawingParameters.SelectColor; + } + } + } + }; + + + if (ViewProviderSketchCoinAttorney::isConstraintSelected(viewProvider, i)) { + if (hasDatumLabel) { + SoDatumLabel *l = static_cast(s->getChild(static_cast(ConstraintNodePosition::DatumLabelIndex))); + l->textColor = drawingParameters.SelectColor; + } else if (hasMaterial) { + m->diffuseColor = drawingParameters.SelectColor; + } else if (type == Sketcher::Coincident) { + selectpoint(constraint->First, constraint->FirstPos); + selectpoint(constraint->Second, constraint->SecondPos); + } else if (type == Sketcher::InternalAlignment) { + switch(constraint->AlignmentType) { + case EllipseMajorDiameter: + case EllipseMinorDiameter: + case HyperbolaMajor: + case HyperbolaMinor: + { + selectline(constraint->First); + + } + break; + case EllipseFocus1: + case EllipseFocus2: + case HyperbolaFocus: + case ParabolaFocus: + case BSplineControlPoint: + case BSplineKnotPoint: + { + selectpoint(constraint->First, constraint->FirstPos); + } + break; + default: + break; + } + } + } else if (ViewProviderSketchCoinAttorney::isConstraintPreselected(viewProvider,i)) { + if (hasDatumLabel) { + SoDatumLabel *l = static_cast(s->getChild(static_cast(ConstraintNodePosition::DatumLabelIndex))); + l->textColor = drawingParameters.PreselectColor; + } else if (hasMaterial) { + m->diffuseColor = drawingParameters.PreselectColor; + } + } + else { + if (hasDatumLabel) { + SoDatumLabel *l = static_cast(s->getChild(static_cast(ConstraintNodePosition::DatumLabelIndex))); + + l->textColor = constraint->isActive ? + ViewProviderSketchCoinAttorney::constraintHasExpression(viewProvider, i) ? + drawingParameters.ExprBasedConstrDimColor + :(constraint->isDriving ? + drawingParameters.ConstrDimColor + : drawingParameters.NonDrivingConstrDimColor) + :drawingParameters.DeactivatedConstrDimColor; + + } else if (hasMaterial) { + m->diffuseColor = constraint->isActive ? + (constraint->isDriving ? + drawingParameters.ConstrDimColor + :drawingParameters.NonDrivingConstrDimColor) + :drawingParameters.DeactivatedConstrDimColor; + } + } + } + + for(int l=0; ldiffuseColor.finishEditing(); + editModeScenegraphNodes.CurvesMaterials[l]->diffuseColor.finishEditing(); + } +} + +void EditModeConstraintCoinManager::rebuildConstraintNodes(void) +{ + auto geolistfacade = ViewProviderSketchCoinAttorney::getGeoListFacade(viewProvider); + + rebuildConstraintNodes(geolistfacade); +} + +void EditModeConstraintCoinManager::rebuildConstraintNodes(const GeoListFacade & geolistfacade) +{ + const std::vector &constrlist = ViewProviderSketchCoinAttorney::getConstraints(viewProvider); + + // clean up + Gui::coinRemoveAllChildren(editModeScenegraphNodes.constrGroup); + + vConstrType.clear(); + + // Get sketch normal + Base::Vector3d RN(0,0,1); + + // move to position of Sketch + Base::Placement Plz = ViewProviderSketchCoinAttorney::getEditingPlacement(viewProvider); + Base::Rotation tmp(Plz.getRotation()); + tmp.multVec(RN,RN); + Plz.setRotation(tmp); + + SbVec3f norm(RN.x, RN.y, RN.z); + + rebuildConstraintNodes(geolistfacade, constrlist, norm); +} + +void EditModeConstraintCoinManager::rebuildConstraintNodes(const GeoListFacade & geolistfacade, const std::vector constrlist, SbVec3f norm) +{ + + for (std::vector::const_iterator it=constrlist.begin(); it != constrlist.end(); ++it) { + // root separator for one constraint + SoSeparator *sep = new SoSeparator(); + sep->ref(); + // no caching for frequently-changing data structures + sep->renderCaching = SoSeparator::OFF; + + // every constrained visual node gets its own material for preselection and selection + SoMaterial *mat = new SoMaterial; + mat->ref(); + mat->diffuseColor = (*it)->isActive ? + ((*it)->isDriving ? + drawingParameters.ConstrDimColor + :drawingParameters.NonDrivingConstrDimColor) + :drawingParameters.DeactivatedConstrDimColor; + + + // distinguish different constraint types to build up + switch ((*it)->Type) { + case Distance: + case DistanceX: + case DistanceY: + case Radius: + case Diameter: + case Weight: + case Angle: + { + SoDatumLabel *text = new SoDatumLabel(); + text->norm.setValue(norm); + text->string = ""; + text->textColor = (*it)->isActive ? + ((*it)->isDriving ? + drawingParameters.ConstrDimColor + :drawingParameters.NonDrivingConstrDimColor) + :drawingParameters.DeactivatedConstrDimColor; + text->size.setValue(drawingParameters.coinFontSize); + text->lineWidth = 2 * drawingParameters.pixelScalingFactor; + text->useAntialiasing = false; + SoAnnotation *anno = new SoAnnotation(); + anno->renderCaching = SoSeparator::OFF; + anno->addChild(text); + // #define CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL 0 + sep->addChild(text); + editModeScenegraphNodes.constrGroup->addChild(anno); + vConstrType.push_back((*it)->Type); + // nodes not needed + sep->unref(); + mat->unref(); + continue; // jump to next constraint + } + break; + case Horizontal: + case Vertical: + case Block: + { + // #define CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL 0 + sep->addChild(mat); + // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION 1 + sep->addChild(new SoZoomTranslation()); + // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_ICON 2 + sep->addChild(new SoImage()); + // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_CONSTRAINTID 3 + sep->addChild(new SoInfo()); + // #define CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION 4 + sep->addChild(new SoZoomTranslation()); + // #define CONSTRAINT_SEPARATOR_INDEX_SECOND_ICON 5 + sep->addChild(new SoImage()); + // #define CONSTRAINT_SEPARATOR_INDEX_SECOND_CONSTRAINTID 6 + sep->addChild(new SoInfo()); + + // remember the type of this constraint node + vConstrType.push_back((*it)->Type); + } + break; + case Coincident: // no visual for coincident so far + vConstrType.push_back(Coincident); + break; + case Parallel: + case Perpendicular: + case Equal: + { + // #define CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL 0 + sep->addChild(mat); + // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION 1 + sep->addChild(new SoZoomTranslation()); + // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_ICON 2 + sep->addChild(new SoImage()); + // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_CONSTRAINTID 3 + sep->addChild(new SoInfo()); + // #define CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION 4 + sep->addChild(new SoZoomTranslation()); + // #define CONSTRAINT_SEPARATOR_INDEX_SECOND_ICON 5 + sep->addChild(new SoImage()); + // #define CONSTRAINT_SEPARATOR_INDEX_SECOND_CONSTRAINTID 6 + sep->addChild(new SoInfo()); + + // remember the type of this constraint node + vConstrType.push_back((*it)->Type); + } + break; + case PointOnObject: + case Tangent: + case SnellsLaw: + { + // #define CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL 0 + sep->addChild(mat); + // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION 1 + sep->addChild(new SoZoomTranslation()); + // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_ICON 2 + sep->addChild(new SoImage()); + // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_CONSTRAINTID 3 + sep->addChild(new SoInfo()); + + if ((*it)->Type == Tangent) { + const Part::Geometry *geo1 = geolistfacade.getGeometryFromGeoId((*it)->First); + const Part::Geometry *geo2 = geolistfacade.getGeometryFromGeoId((*it)->Second); + if (!geo1 || !geo2) { + Base::Console().Warning("Tangent constraint references non-existing geometry\n"); + } + else if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId() && + geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + // #define CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION 4 + sep->addChild(new SoZoomTranslation()); + // #define CONSTRAINT_SEPARATOR_INDEX_SECOND_ICON 5 + sep->addChild(new SoImage()); + // #define CONSTRAINT_SEPARATOR_INDEX_SECOND_CONSTRAINTID 6 + sep->addChild(new SoInfo()); + } + } + + vConstrType.push_back((*it)->Type); + } + break; + case Symmetric: + { + SoDatumLabel *arrows = new SoDatumLabel(); + arrows->norm.setValue(norm); + arrows->string = ""; + arrows->textColor = drawingParameters.ConstrDimColor; + arrows->lineWidth = 2 * drawingParameters.pixelScalingFactor; + + // #define CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL 0 + sep->addChild(arrows); + // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION 1 + sep->addChild(new SoTranslation()); + // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_ICON 2 + sep->addChild(new SoImage()); + // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_CONSTRAINTID 3 + sep->addChild(new SoInfo()); + + vConstrType.push_back((*it)->Type); + } + break; + case InternalAlignment: + { + vConstrType.push_back((*it)->Type); + } + break; + default: + vConstrType.push_back((*it)->Type); + } + + editModeScenegraphNodes.constrGroup->addChild(sep); + // decrement ref counter again + sep->unref(); + mat->unref(); + } +} + +QString EditModeConstraintCoinManager::getPresentationString(const Constraint *constraint) +{ + QString nameStr; // name parameter string + QString valueStr; // dimensional value string + QString presentationStr; // final return string + QString unitStr; // the actual unit string + QString baseUnitStr; // the expected base unit string + double factor; // unit scaling factor, currently not used + Base::UnitSystem unitSys; // current unit system + + if(!constraint->isActive) + return QString::fromLatin1(" "); + + // Get the current name parameter string of the constraint + nameStr = QString::fromStdString(constraint->Name); + + // Get the current value string including units + valueStr = constraint->getPresentationValue().getUserString(factor, unitStr); + + // Hide units if user has requested it, is being displayed in the base + // units, and the schema being used has a clear base unit in the first + // place. Otherwise, display units. + if(constraintParameters.bHideUnits && constraint->Type != Sketcher::Angle) + { + // Only hide the default length unit. Right now there is not an easy way + // to get that from the Unit system so we have to manually add it here. + // Hopefully this can be added in the future so this code won't have to + // be updated if a new units schema is added. + unitSys = Base::UnitsApi::getSchema(); + + // If this is a supported unit system then define what the base unit is. + switch (unitSys) + { + case Base::UnitSystem::SI1: + case Base::UnitSystem::MmMin: + baseUnitStr = QString::fromLatin1("mm"); + break; + + case Base::UnitSystem::SI2: + baseUnitStr = QString::fromLatin1("m"); + break; + + case Base::UnitSystem::ImperialDecimal: + baseUnitStr = QString::fromLatin1("in"); + break; + + case Base::UnitSystem::Centimeters: + baseUnitStr = QString::fromLatin1("cm"); + break; + + default: + // Nothing to do + break; + } + + if( !baseUnitStr.isEmpty() ) + { + // expected unit string matches actual unit string. remove. + if( QString::compare(baseUnitStr, unitStr)==0 ) + { + // Example code from: Mod/TechDraw/App/DrawViewDimension.cpp:372 + QRegExp rxUnits(QString::fromUtf8(" \\D*$")); //space + any non digits at end of string + valueStr.remove(rxUnits); //getUserString(defaultDecimals) without units + } + } + } + + if (constraint->Type == Sketcher::Diameter){ + valueStr.prepend(QChar(216)); // Diameter sign + } + else if (constraint->Type == Sketcher::Radius){ + valueStr.prepend(QChar(82)); // Capital letter R + } + + /** + Create the representation string from the user defined format string + Format options are: + %N - the constraint name parameter + %V - the value of the dimensional constraint, including any unit characters + */ + if (constraintParameters.bShowDimensionalName && !nameStr.isEmpty()) + { + if (constraintParameters.sDimensionalStringFormat.contains(QLatin1String("%V")) || + constraintParameters.sDimensionalStringFormat.contains(QLatin1String("%N"))) + { + presentationStr = constraintParameters.sDimensionalStringFormat; + presentationStr.replace(QLatin1String("%N"), nameStr); + presentationStr.replace(QLatin1String("%V"), valueStr); + } + else + { + // user defined format string does not contain any valid parameter, using default format "%N = %V" + presentationStr = nameStr + QLatin1String(" = ") + valueStr; + } + + return presentationStr; + } + + return valueStr; +} + +std::set EditModeConstraintCoinManager::detectPreselectionConstr(const SoPickedPoint *Point, + const SbVec2s &cursorPos) +{ + std::set constrIndices; + SoPath *path = Point->getPath(); + + // Get the constraints' tail + SoNode *tailFather2 = path->getNode(path->getLength()-3); + + if (tailFather2 != editModeScenegraphNodes.constrGroup) + return constrIndices; + + + SoNode *tail = path->getTail(); + SoNode *tailFather = path->getNode(path->getLength()-2); + + for (int i=0; i < editModeScenegraphNodes.constrGroup->getNumChildren(); ++i) { + if (editModeScenegraphNodes.constrGroup->getChild(i) == tailFather) { + SoSeparator *sep = static_cast(tailFather); + if (sep->getNumChildren() > static_cast(ConstraintNodePosition::FirstConstraintIdIndex)) { + SoInfo *constrIds = NULL; + if (tail == sep->getChild(static_cast(ConstraintNodePosition::FirstIconIndex))) { + // First icon was hit + constrIds = static_cast(sep->getChild(static_cast(ConstraintNodePosition::FirstConstraintIdIndex))); + } + else { + // Assume second icon was hit + if ( static_cast(ConstraintNodePosition::SecondConstraintIdIndex) < sep->getNumChildren()) { + constrIds = static_cast(sep->getChild(static_cast(ConstraintNodePosition::SecondConstraintIdIndex))); + } + } + + if (constrIds) { + QString constrIdsStr = QString::fromLatin1(constrIds->string.getValue().getString()); + if (combinedConstrBoxes.count(constrIdsStr) && dynamic_cast(tail)) { + // If it's a combined constraint icon + + // Screen dimensions of the icon + SbVec3s iconSize = getDisplayedSize(static_cast(tail)); + // Center of the icon + //SbVec2f iconCoords = viewer->screenCoordsOfPath(path); + + // The use of the Path to get the screen coordinates to get the icon center coordinates + // does not work. + // + // This implementation relies on the use of ZoomTranslation to get the absolute and relative + // positions of the icons. + // + // In the case of second icons (the same constraint has two icons at two different positions), + // the translation vectors have to be added, as the second ZoomTranslation operates on top of + // the first. + // + // Coordinates are projected on the sketch plane and then to the screen in the interval [0 1] + // Then this result is converted to pixels using the scale factor. + + SbVec3f absPos; + SbVec3f trans; + + auto translation = static_cast(static_cast(tailFather)->getChild( static_cast(ConstraintNodePosition::FirstTranslationIndex))); + + absPos = translation->abPos.getValue(); + + trans = translation->translation.getValue(); + + if (tail != sep->getChild(static_cast(ConstraintNodePosition::FirstIconIndex))) { + + auto translation2 = static_cast(static_cast(tailFather)->getChild( static_cast(ConstraintNodePosition::SecondTranslationIndex))); + + absPos += translation2->abPos.getValue(); + + trans += translation2->translation.getValue(); + } + + // TODO: Is this calculation actually sound? Why the absolute position is not scaled and the translation is? Review. + SbVec3f constrPos = absPos + trans*ViewProviderSketchCoinAttorney::getScaleFactor(viewProvider); + + SbVec2f iconCoords = ViewProviderSketchCoinAttorney::getScreenCoordinates(viewProvider, SbVec2f(constrPos[0],constrPos[1])); + + // cursorPos is SbVec2s in screen coordinates coming from SoEvent in mousemove + // + // Coordinates of the mouse cursor on the icon, origin at top-left for Qt + // but bottom-left for OIV. + // The coordinates are needed in Qt format, i.e. from top to bottom. + int iconX = cursorPos[0] - iconCoords[0] + iconSize[0]/2, + iconY = cursorPos[1] - iconCoords[1] + iconSize[1]/2; + iconY = iconSize[1] - iconY; + + for (ConstrIconBBVec::iterator b = combinedConstrBoxes[constrIdsStr].begin(); + b != combinedConstrBoxes[constrIdsStr].end(); ++b) { + +#ifdef FC_DEBUG + // Useful code to debug coordinates and bounding boxes that does not need to be compiled in for + // any debug operations. + + /*Base::Console().Log("Abs(%f,%f),Trans(%f,%f),Coords(%d,%d),iCoords(%f,%f),icon(%d,%d),isize(%d,%d),boundingbox([%d,%d],[%d,%d])\n", absPos[0],absPos[1],trans[0], trans[1], cursorPos[0], cursorPos[1], iconCoords[0], iconCoords[1], iconX, iconY, iconSize[0], iconSize[1], b->first.topLeft().x(),b->first.topLeft().y(),b->first.bottomRight().x(),b->first.bottomRight().y());*/ +#endif + + if (b->first.contains(iconX, iconY)) { + // We've found a bounding box that contains the mouse pointer! + for (std::set::iterator k = b->second.begin(); k != b->second.end(); ++k) + constrIndices.insert(*k); + } + } + } + else { + // It's a constraint icon, not a combined one + QStringList constrIdStrings = constrIdsStr.split(QString::fromLatin1(",")); + while (!constrIdStrings.empty()) + constrIndices.insert(constrIdStrings.takeAt(0).toInt()); + } + } + } + else { + // other constraint icons - eg radius... + constrIndices.clear(); + constrIndices.insert(i); + } + break; + } + } + + return constrIndices; +} + +SbVec3s EditModeConstraintCoinManager::getDisplayedSize(const SoImage *iconPtr) const +{ +#if (COIN_MAJOR_VERSION >= 3) + SbVec3s iconSize = iconPtr->image.getValue().getSize(); +#else + SbVec2s size; + int nc; + const unsigned char * bytes = iconPtr->image.getValue(size, nc); + SbImage img (bytes, size, nc); + SbVec3s iconSize = img.getSize(); +#endif + if (iconPtr->width.getValue() != -1) + iconSize[0] = iconPtr->width.getValue(); + if (iconPtr->height.getValue() != -1) + iconSize[1] = iconPtr->height.getValue(); + return iconSize; +} + +// public function that triggers drawing of most constraint icons +void EditModeConstraintCoinManager::drawConstraintIcons() +{ + auto geolist = ViewProviderSketchCoinAttorney::getGeoList(viewProvider); + + drawConstraintIcons(geolist); +} + +void EditModeConstraintCoinManager::drawConstraintIcons(const GeoList & geolist) +{ + const std::vector &constraints = ViewProviderSketchCoinAttorney::getConstraints(viewProvider); + + int constrId = 0; + + std::vector iconQueue; + + for (std::vector::const_iterator it=constraints.begin(); + it != constraints.end(); ++it, ++constrId) { + + // Check if Icon Should be created + bool multipleIcons = false; + + QString icoType = iconTypeFromConstraint(*it); + if(icoType.isEmpty()) + continue; + + switch((*it)->Type) { + + case Tangent: + { // second icon is available only for colinear line segments + const Part::Geometry *geo1 = geolist.getGeometryFromGeoId((*it)->First); + const Part::Geometry *geo2 = geolist.getGeometryFromGeoId((*it)->Second); + if (geo1 && geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId() && + geo2 && geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { + multipleIcons = true; + } + } + break; + case Horizontal: + case Vertical: + { // second icon is available only for point alignment + if ((*it)->Second != GeoEnum::GeoUndef && + (*it)->FirstPos != Sketcher::PointPos::none && + (*it)->SecondPos != Sketcher::PointPos::none) { + multipleIcons = true; + } + } + break; + case Parallel: + multipleIcons = true; + break; + case Perpendicular: + // second icon is available only when there is no common point + if ((*it)->FirstPos == Sketcher::PointPos::none && (*it)->Third == GeoEnum::GeoUndef) + multipleIcons = true; + break; + case Equal: + multipleIcons = true; + break; + default: + break; + } + + // Double-check that we can safely access the Inventor nodes + if (constrId >= editModeScenegraphNodes.constrGroup->getNumChildren()) { + Base::Console().Warning("Can't update constraint icons because view is not in sync with sketch\n"); + break; + } + + // Find the Constraint Icon SoImage Node + SoSeparator *sep = static_cast(editModeScenegraphNodes.constrGroup->getChild(constrId)); + int numChildren = sep->getNumChildren(); + + SbVec3f absPos; + // Somewhat hacky - we use SoZoomTranslations for most types of icon, + // but symmetry icons use SoTranslations... + SoTranslation *translationPtr = static_cast(sep->getChild(static_cast(ConstraintNodePosition::FirstTranslationIndex))); + + if(dynamic_cast(translationPtr)) + absPos = static_cast(translationPtr)->abPos.getValue(); + else + absPos = translationPtr->translation.getValue(); + + SoImage *coinIconPtr = dynamic_cast(sep->getChild(static_cast(ConstraintNodePosition::FirstIconIndex))); + SoInfo *infoPtr = static_cast(sep->getChild(static_cast(ConstraintNodePosition::FirstConstraintIdIndex))); + + constrIconQueueItem thisIcon; + thisIcon.type = icoType; + thisIcon.constraintId = constrId; + thisIcon.position = absPos; + thisIcon.destination = coinIconPtr; + thisIcon.infoPtr = infoPtr; + thisIcon.visible = (*it)->isInVirtualSpace == ViewProviderSketchCoinAttorney::isShownVirtualSpace(viewProvider); + + if ((*it)->Type==Symmetric) { + Base::Vector3d startingpoint = geolist.getPoint((*it)->First, (*it)->FirstPos); + Base::Vector3d endpoint = geolist.getPoint((*it)->Second,(*it)->SecondPos); + + SbVec3f pos0(startingpoint.x,startingpoint.y,startingpoint.z); + SbVec3f pos1(endpoint.x,endpoint.y,endpoint.z); + + thisIcon.iconRotation = ViewProviderSketchCoinAttorney::getRotation(viewProvider, pos0, pos1); + } + else { + thisIcon.iconRotation = 0; + } + + if (multipleIcons) { + if((*it)->Name.empty()) + thisIcon.label = QString::number(constrId + 1); + else + thisIcon.label = QString::fromUtf8((*it)->Name.c_str()); + iconQueue.push_back(thisIcon); + + // Note that the second translation is meant to be applied after the first. + // So, to get the position of the second icon, we add the two translations together + // + // See note ~30 lines up. + if (numChildren > static_cast(ConstraintNodePosition::SecondConstraintIdIndex)) { + translationPtr = static_cast(sep->getChild(static_cast(ConstraintNodePosition::SecondTranslationIndex))); + if(dynamic_cast(translationPtr)) + thisIcon.position += static_cast(translationPtr)->abPos.getValue(); + else + thisIcon.position += translationPtr->translation.getValue(); + + thisIcon.destination = dynamic_cast(sep->getChild(static_cast(ConstraintNodePosition::SecondIconIndex))); + thisIcon.infoPtr = static_cast(sep->getChild(static_cast(ConstraintNodePosition::SecondConstraintIdIndex))); + } + } + else { + if ((*it)->Name.empty()) + thisIcon.label = QString(); + else + thisIcon.label = QString::fromUtf8((*it)->Name.c_str()); + } + + iconQueue.push_back(thisIcon); + } + + combineConstraintIcons(iconQueue); +} + +void EditModeConstraintCoinManager::combineConstraintIcons(IconQueue iconQueue) +{ + // getScaleFactor gives us a ratio of pixels per some kind of real units + float maxDistSquared = pow(ViewProviderSketchCoinAttorney::getScaleFactor(viewProvider), 2); + + // There's room for optimisation here; we could reuse the combined icons... + combinedConstrBoxes.clear(); + + while(!iconQueue.empty()) { + // A group starts with an item popped off the back of our initial queue + IconQueue thisGroup; + thisGroup.push_back(iconQueue.back()); + constrIconQueueItem init = iconQueue.back(); + iconQueue.pop_back(); + + // we group only icons not being Symmetry icons, because we want those on the line + // and only icons that are visible + if(init.type != QString::fromLatin1("Constraint_Symmetric") && init.visible){ + + IconQueue::iterator i = iconQueue.begin(); + + + while(i != iconQueue.end()) { + if((*i).visible) { + bool addedToGroup = false; + + for(IconQueue::iterator j = thisGroup.begin(); + j != thisGroup.end(); ++j) { + float distSquared = pow(i->position[0]-j->position[0],2) + pow(i->position[1]-j->position[1],2); + if(distSquared <= maxDistSquared && (*i).type != QString::fromLatin1("Constraint_Symmetric")) { + // Found an icon in iconQueue that's close enough to + // a member of thisGroup, so move it into thisGroup + thisGroup.push_back(*i); + i = iconQueue.erase(i); + addedToGroup = true; + break; + } + } + + if(addedToGroup) { + if(i == iconQueue.end()) + // We just got the last icon out of iconQueue + break; + else + // Start looking through the iconQueue again, in case + // we have an icon that's now close enough to thisGroup + i = iconQueue.begin(); + } else + ++i; + } + else // if !visible we skip it + i++; + } + + } + + if(thisGroup.size() == 1) { + drawTypicalConstraintIcon(thisGroup[0]); + } + else { + drawMergedConstraintIcons(thisGroup); + } + } +} + +void EditModeConstraintCoinManager::drawMergedConstraintIcons(IconQueue iconQueue) +{ + for(IconQueue::iterator i = iconQueue.begin(); i != iconQueue.end(); ++i) { + clearCoinImage(i->destination); + } + + QImage compositeIcon; + SoImage *thisDest = iconQueue[0].destination; + SoInfo *thisInfo = iconQueue[0].infoPtr; + + // Tracks all constraint IDs that are combined into this icon + QString idString; + int lastVPad = 0; + + QStringList labels; + std::vector ids; + QString thisType; + QColor iconColor; + QList labelColors; + int maxColorPriority; + double iconRotation; + + ConstrIconBBVec boundingBoxes; + while(!iconQueue.empty()) { + IconQueue::iterator i = iconQueue.begin(); + + labels.clear(); + labels.append(i->label); + + ids.clear(); + ids.push_back(i->constraintId); + + thisType = i->type; + iconColor = constrColor(i->constraintId); + labelColors.clear(); + labelColors.append(iconColor); + iconRotation= i->iconRotation; + + maxColorPriority = constrColorPriority(i->constraintId); + + if(idString.length()) + idString.append(QString::fromLatin1(",")); + idString.append(QString::number(i->constraintId)); + + i = iconQueue.erase(i); + while(i != iconQueue.end()) { + if(i->type != thisType) { + ++i; + continue; + } + + labels.append(i->label); + ids.push_back(i->constraintId); + labelColors.append(constrColor(i->constraintId)); + + if(constrColorPriority(i->constraintId) > maxColorPriority) { + maxColorPriority = constrColorPriority(i->constraintId); + iconColor= constrColor(i->constraintId); + } + + idString.append(QString::fromLatin1(",") + + QString::number(i->constraintId)); + + i = iconQueue.erase(i); + } + + // To be inserted into edit->combinedConstBoxes + std::vector boundingBoxesVec; + int oldHeight = 0; + + // Render the icon here. + if(compositeIcon.isNull()) { + compositeIcon = renderConstrIcon(thisType, + iconColor, + labels, + labelColors, + iconRotation, + &boundingBoxesVec, + &lastVPad); + } else { + int thisVPad; + QImage partialIcon = renderConstrIcon(thisType, + iconColor, + labels, + labelColors, + iconRotation, + &boundingBoxesVec, + &thisVPad); + + // Stack vertically for now. Down the road, it might make sense + // to figure out the best orientation automatically. + oldHeight = compositeIcon.height(); + + // This is overkill for the currently used (20 July 2014) font, + // since it always seems to have the same vertical pad, but this + // might not always be the case. The 3 pixel buffer might need + // to vary depending on font size too... + oldHeight -= std::max(lastVPad - 3, 0); + + compositeIcon = compositeIcon.copy(0, 0, + std::max(partialIcon.width(), + compositeIcon.width()), + partialIcon.height() + + compositeIcon.height()); + + QPainter qp(&compositeIcon); + qp.drawImage(0, oldHeight, partialIcon); + + lastVPad = thisVPad; + } + + // Add bounding boxes for the icon we just rendered to boundingBoxes + std::vector::iterator id = ids.begin(); + std::set nextIds; + for(std::vector::iterator bb = boundingBoxesVec.begin(); + bb != boundingBoxesVec.end(); ++bb) { + nextIds.clear(); + + if(bb == boundingBoxesVec.begin()) { + // The first bounding box is for the icon at left, so assign + // all IDs for that type of constraint to the icon. + for(std::vector::iterator j = ids.begin(); j != ids.end(); ++j) + nextIds.insert(*j); + } + else { + nextIds.insert(*(id++)); + } + + ConstrIconBB newBB(bb->adjusted(0, oldHeight, 0, oldHeight), + nextIds); + + boundingBoxes.push_back(newBB); + } + } + + combinedConstrBoxes[idString] = boundingBoxes; + thisInfo->string.setValue(idString.toLatin1().data()); + sendConstraintIconToCoin(compositeIcon, thisDest); +} + + +/// Note: labels, labelColors, and boundingBoxes are all +/// assumed to be the same length. +QImage EditModeConstraintCoinManager::renderConstrIcon(const QString &type, + const QColor &iconColor, + const QStringList &labels, + const QList &labelColors, + double iconRotation, + std::vector *boundingBoxes, + int *vPad) +{ + // Constants to help create constraint icons + QString joinStr = QString::fromLatin1(", "); + + QPixmap pxMap; + std::stringstream constraintName; + constraintName << type.toLatin1().data() << drawingParameters.constraintIconSize; // allow resizing by embedding size + if (! Gui::BitmapFactory().findPixmapInCache(constraintName.str().c_str(), pxMap)) { + pxMap = Gui::BitmapFactory().pixmapFromSvg(type.toLatin1().data(),QSizeF(drawingParameters.constraintIconSize, drawingParameters.constraintIconSize)); + Gui::BitmapFactory().addPixmapToCache(constraintName.str().c_str(), pxMap); // Cache for speed, avoiding pixmapFromSvg + } + QImage icon = pxMap.toImage(); + + QFont font = ViewProviderSketchCoinAttorney::getApplicationFont(viewProvider); + font.setPixelSize(static_cast(1.0 * drawingParameters.constraintIconSize)); + font.setBold(true); + QFontMetrics qfm = QFontMetrics(font); + + int labelWidth = qfm.boundingRect(labels.join(joinStr)).width(); + // See Qt docs on qRect::bottom() for explanation of the +1 + int pxBelowBase = qfm.boundingRect(labels.join(joinStr)).bottom() + 1; + + if(vPad) + *vPad = pxBelowBase; + + QTransform rotation; + rotation.rotate(iconRotation); + + QImage roticon = icon.transformed(rotation); + QImage image = roticon.copy(0, 0, roticon.width() + labelWidth, + roticon.height() + pxBelowBase); + + // Make a bounding box for the icon + if(boundingBoxes) + boundingBoxes->push_back(QRect(0, 0, roticon.width(), roticon.height())); + + // Render the Icons + QPainter qp(&image); + qp.setCompositionMode(QPainter::CompositionMode_SourceIn); + qp.fillRect(roticon.rect(), iconColor); + + // Render constraint label if necessary + if (!labels.join(QString()).isEmpty()) { + qp.setCompositionMode(QPainter::CompositionMode_SourceOver); + qp.setFont(font); + + int cursorOffset = 0; + + //In Python: "for label, color in zip(labels, labelColors):" + QStringList::const_iterator labelItr; + QString labelStr; + QList::const_iterator colorItr; + QRect labelBB; + for(labelItr = labels.begin(), colorItr = labelColors.begin(); + labelItr != labels.end() && colorItr != labelColors.end(); + ++labelItr, ++colorItr) { + + qp.setPen(*colorItr); + + if(labelItr + 1 == labels.end()) // if this is the last label + labelStr = *labelItr; + else + labelStr = *labelItr + joinStr; + + // Note: text can sometimes draw to the left of the starting + // position, eg italic fonts. Check QFontMetrics + // documentation for more info, but be mindful if the + // icon.width() is ever very small (or removed). + qp.drawText(icon.width() + cursorOffset, icon.height(), labelStr); + + if(boundingBoxes) { + labelBB = qfm.boundingRect(labelStr); + labelBB.moveTo(icon.width() + cursorOffset, + icon.height() - qfm.height() + pxBelowBase); + boundingBoxes->push_back(labelBB); + } + + cursorOffset += Gui::QtTools::horizontalAdvance(qfm, labelStr); + } + } + + return image; +} + +void EditModeConstraintCoinManager::drawTypicalConstraintIcon(const constrIconQueueItem &i) +{ + QColor color = constrColor(i.constraintId); + + QImage image = renderConstrIcon(i.type, + color, + QStringList(i.label), + QList() << color, + i.iconRotation); + + i.infoPtr->string.setValue(QString::number(i.constraintId).toLatin1().data()); + sendConstraintIconToCoin(image, i.destination); +} + +QString EditModeConstraintCoinManager::iconTypeFromConstraint(Constraint *constraint) +{ + /*! TODO: Consider pushing this functionality up into Constraint + * + Abdullah: Please, don't. An icon is visualisation information and + does not belong in App, but in Gui. Rather consider refactoring it + in a separate class dealing with visualisation of constraints.*/ + + switch(constraint->Type) { + case Horizontal: + return QString::fromLatin1("Constraint_Horizontal"); + case Vertical: + return QString::fromLatin1("Constraint_Vertical"); + case PointOnObject: + return QString::fromLatin1("Constraint_PointOnObject"); + case Tangent: + return QString::fromLatin1("Constraint_Tangent"); + case Parallel: + return QString::fromLatin1("Constraint_Parallel"); + case Perpendicular: + return QString::fromLatin1("Constraint_Perpendicular"); + case Equal: + return QString::fromLatin1("Constraint_EqualLength"); + case Symmetric: + return QString::fromLatin1("Constraint_Symmetric"); + case SnellsLaw: + return QString::fromLatin1("Constraint_SnellsLaw"); + case Block: + return QString::fromLatin1("Constraint_Block"); + default: + return QString(); + } +} + +void EditModeConstraintCoinManager::sendConstraintIconToCoin(const QImage &icon, SoImage *soImagePtr) +{ + SoSFImage icondata = SoSFImage(); + + Gui::BitmapFactory().convert(icon, icondata); + + SbVec2s iconSize(icon.width(), icon.height()); + + int four = 4; + soImagePtr->image.setValue(iconSize, 4, icondata.getValue(iconSize, four)); + + //Set Image Alignment to Center + soImagePtr->vertAlignment = SoImage::HALF; + soImagePtr->horAlignment = SoImage::CENTER; +} + +void EditModeConstraintCoinManager::clearCoinImage(SoImage *soImagePtr) +{ + soImagePtr->setToDefaults(); +} + +QColor EditModeConstraintCoinManager::constrColor(int constraintId) +{ + const auto constraints = ViewProviderSketchCoinAttorney::getConstraints(viewProvider); + + if (ViewProviderSketchCoinAttorney::isConstraintPreselected(viewProvider,constraintId)) + return drawingParameters.constrIconPreselColor; + else if (ViewProviderSketchCoinAttorney::isConstraintSelected(viewProvider, constraintId)) + return drawingParameters.constrIconSelColor; + else if(!constraints[constraintId]->isActive) + return drawingParameters.constrIconDisabledColor; + else if(!constraints[constraintId]->isDriving) + return drawingParameters.nonDrivingConstrIcoColor; + else + return drawingParameters.constrIcoColor; + +} + +int EditModeConstraintCoinManager::constrColorPriority(int constraintId) +{ + if (ViewProviderSketchCoinAttorney::isConstraintPreselected(viewProvider,constraintId)) + return 3; + else if (ViewProviderSketchCoinAttorney::isConstraintSelected(viewProvider, constraintId)) + return 2; + else + return 1; +} + +SoSeparator * EditModeConstraintCoinManager::getConstraintIdSeparator(int i) +{ + return dynamic_cast(editModeScenegraphNodes.constrGroup->getChild(i)); +} + +void EditModeConstraintCoinManager::createEditModeInventorNodes() +{ + // group node for the Constraint visual +++++++++++++++++++++++++++++++++++ + SoMaterialBinding *MtlBind = new SoMaterialBinding; + MtlBind->setName("ConstraintMaterialBinding"); + MtlBind->value = SoMaterialBinding::OVERALL ; + editModeScenegraphNodes.EditRoot->addChild(MtlBind); + + // use small line width for the Constraints + editModeScenegraphNodes.ConstraintDrawStyle = new SoDrawStyle; + editModeScenegraphNodes.ConstraintDrawStyle->setName("ConstraintDrawStyle"); + editModeScenegraphNodes.ConstraintDrawStyle->lineWidth = 1 * drawingParameters.pixelScalingFactor; + editModeScenegraphNodes.EditRoot->addChild(editModeScenegraphNodes.ConstraintDrawStyle); + + // add the group where all the constraints has its SoSeparator + editModeScenegraphNodes.constrGroup = new SmSwitchboard(); + editModeScenegraphNodes.constrGroup->setName("ConstraintGroup"); + editModeScenegraphNodes.EditRoot->addChild(editModeScenegraphNodes.constrGroup); +} diff --git a/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.h b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.h new file mode 100644 index 0000000000..ac17243330 --- /dev/null +++ b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.h @@ -0,0 +1,253 @@ +/*************************************************************************** + * Copyright (c) 2021 Abdullah Tahiri * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef SKETCHERGUI_EditModeConstraintCoinManager_H +#define SKETCHERGUI_EditModeConstraintCoinManager_H + +#include +#include + +#include +#include + +#include + +#include "EditModeCoinManagerParameters.h" + +class SbVec3f; +class SoRayPickAction; +class SoPickedPoint; +class SbVec3s; + +namespace Base { + template< typename T > + class Vector3; + + class Vector2d; + + class Placement; +} + +namespace Part { + class Geometry; +} + +namespace Sketcher { + class Constraint; + class PropertyConstraintList; +}; + +namespace SketcherGui { + +class ViewProviderSketch; + +using GeoList = Sketcher::GeoList; +using GeoListFacade = Sketcher::GeoListFacade; + +/** @brief Class for managing the Edit mode coin nodes of ViewProviderSketch relating to constraints. + * @details To be documented. + * + */ +class SketcherGuiExport EditModeConstraintCoinManager +{ +private: + /// Coin Node indices for constraints + enum class ConstraintNodePosition { + MaterialIndex = 0, + DatumLabelIndex = 0, + FirstTranslationIndex = 1, + FirstIconIndex = 2, + FirstConstraintIdIndex = 3, + SecondTranslationIndex = 4, + SecondIconIndex = 5, + SecondConstraintIdIndex = 6 + }; +public: + explicit EditModeConstraintCoinManager( ViewProviderSketch &vp, + DrawingParameters & drawingParams, + GeometryLayerParameters & geometryLayerParams, + ConstraintParameters & constraintParams, + EditModeScenegraphNodes & editModeScenegraph, + CoinMapping & coinMap); + ~EditModeConstraintCoinManager(); + + + /** @name update coin nodes*/ + // geometry list to be used for constraints, which may be a temporal geometry + void processConstraints(const GeoListFacade & geolistfacade); + + void updateVirtualSpace(); + + /// Draw all constraint icons + /*! Except maybe the radius and lock ones? */ + void drawConstraintIcons(); + + // This specific overload is to use a specific geometry list, which may be a temporal one + void drawConstraintIcons(const GeoList & geolist); + //@} + + /** @name update coin colors*/ + //@{ + void updateConstraintColor(const std::vector & constraints); + //@} + + /** @name coin nodes creation*/ + void rebuildConstraintNodes(void); + //@} + + std::set detectPreselectionConstr( const SoPickedPoint *Point, + const SbVec2s &cursorPos); + + SoSeparator * getConstraintIdSeparator(int i); + + void createEditModeInventorNodes(); + +private: + void rebuildConstraintNodes(const GeoListFacade & geolistfacade); // with specific geometry + + void rebuildConstraintNodes(const GeoListFacade & geolistfacade, const std::vector constrlist, SbVec3f norm); + + /// finds a free position for placing a constraint icon + Base::Vector3d seekConstraintPosition(const Base::Vector3d &origPos, + const Base::Vector3d &norm, + const Base::Vector3d &dir, float step, + const SoNode *constraint); + + /// Return display string for constraint including hiding units if + //requested. + QString getPresentationString(const Sketcher::Constraint *constraint); + + /// Returns the size that Coin should display the indicated image at + SbVec3s getDisplayedSize(const SoImage *) const; + + /** @name Protected helpers for drawing constraint icons*/ + //@{ + QString iconTypeFromConstraint(Sketcher::Constraint *constraint); + + /// Returns a QColor object appropriate for constraint with given id + /*! In the case of combined icons, the icon color is chosen based on + * the constraint with the highest priority from constrColorPriority() + */ + QColor constrColor(int constraintId); + /// Used by drawMergedConstraintIcons to decide what color to make icons + /*! See constrColor() */ + int constrColorPriority(int constraintId); + + // TODO: Review and refactor where these structs and types relating constraints + // should actually go. + + // helper data structures for the constraint rendering + std::vector vConstrType; + + // For each of the combined constraint icons drawn, also create a vector + // of bounding boxes and associated constraint IDs, to go from the icon's + // pixel coordinates to the relevant constraint IDs. + // + // The outside map goes from a string representation of a set of constraint + // icons (like the one used by the constraint IDs we insert into the Coin + // rendering tree) to a vector of those bounding boxes paired with relevant + // constraint IDs. + + using ConstrIconBB = std::pair >; + using ConstrIconBBVec = std::vector; + + std::map combinedConstrBoxes; + + + /// Internal type used for drawing constraint icons + struct constrIconQueueItem { + /// Type of constraint the icon represents. Eg: "small/Constraint_PointOnObject_sm" + QString type; + + /// Internal constraint ID number + /// These map to results of getSketchObject()->Constraints.getValues() + int constraintId; + + /// Label to be rendered with this icon, if any + QString label; + + /// Absolute coordinates of the constraint icon + SbVec3f position; + + /// Pointer to the SoImage object where the icon should be written + SoImage *destination; + + /// Pointer to SoInfo object where we store the constraint IDs that the icon refers to + SoInfo *infoPtr; + + /// Angle to rotate an icon + double iconRotation; + + bool visible; + }; + + using IconQueue = std::vector; + + void combineConstraintIcons(IconQueue iconQueue); + + /// Renders an icon for a single constraint and sends it to Coin + void drawTypicalConstraintIcon(const constrIconQueueItem &i); + + /// Combines multiple constraint icons and sends them to Coin + void drawMergedConstraintIcons(IconQueue iconQueue); + + /// Helper for drawMergedConstraintIcons and drawTypicalConstraintIcon + QImage renderConstrIcon(const QString &type, + const QColor &iconColor, + const QStringList &labels, + const QList &labelColors, + double iconRotation, + //! Gets populated with bounding boxes (in icon + //! image coordinates) for the icon at left, then + //! labels for different constraints. + std::vector *boundingBoxes = NULL, + //! If not NULL, gets set to the number of pixels + //! that the text extends below the icon base. + int *vPad = NULL); + + /// Copies a QImage constraint icon into a SoImage* + /*! Used by drawTypicalConstraintIcon() and drawMergedConstraintIcons() */ + void sendConstraintIconToCoin(const QImage &icon, SoImage *soImagePtr); + + /// Essentially a version of sendConstraintIconToCoin, with a blank icon + void clearCoinImage(SoImage *soImagePtr); + //@} + +private: + ViewProviderSketch & viewProvider; + + DrawingParameters & drawingParameters; + GeometryLayerParameters & geometryLayerParameters; + ConstraintParameters & constraintParameters; + + EditModeScenegraphNodes & editModeScenegraphNodes; + + CoinMapping & coinMapping; +}; + + +} // namespace SketcherGui + + +#endif // SKETCHERGUI_EditModeConstraintCoinManager_H + diff --git a/src/Mod/Sketcher/Gui/EditModeGeometryCoinConverter.cpp b/src/Mod/Sketcher/Gui/EditModeGeometryCoinConverter.cpp new file mode 100644 index 0000000000..aa797cf277 --- /dev/null +++ b/src/Mod/Sketcher/Gui/EditModeGeometryCoinConverter.cpp @@ -0,0 +1,388 @@ +/*************************************************************************** + * Copyright (c) 2021 Abdullah Tahiri * + * * + * 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 +#endif // #ifndef _PreComp_ + +#include +#include +#include + +#include + +#include "EditModeCoinManagerParameters.h" + +#include "Mod/Sketcher/App/Constraint.h" + +#include "EditModeGeometryCoinConverter.h" + + +using namespace SketcherGui; + +EditModeGeometryCoinConverter::EditModeGeometryCoinConverter( GeometryLayerNodes & geometrylayernodes, + DrawingParameters & drawingparameters, + GeometryLayerParameters& geometryLayerParams, + CoinMapping & coinMap ): + geometryLayerNodes(geometrylayernodes), + drawingParameters(drawingparameters), + geometryLayerParameters(geometryLayerParams), + coinMapping(coinMap) +{} + +void EditModeGeometryCoinConverter::convert(const Sketcher::GeoListFacade & geolistfacade) +{ + + // measurements + bsplineGeoIds.clear(); + + // end information layer + Coords.clear(); + Points.clear(); + Index.clear(); + + coinMapping.clear(); + + pointCounter.clear(); + curveCounter.clear(); + + for(int l=0; l 0) // insert the first segment of the curve into the map + coinMapping.GeoElementId2SetId.emplace( std::piecewise_construct, + std::forward_as_tuple(geoId, Sketcher::PointPos::none), + std::forward_as_tuple(static_cast(coinMapping.CurvIdToGeoId[coinLayer].size()), coinLayer)); + + for(int i = 0; i < numberCurves; i++) + coinMapping.CurvIdToGeoId[coinLayer].push_back(geoId); + }; + + for (size_t i = 0 ; i < geolistfacade.geomlist.size()- 2; i++) { + + const auto GeoId = geolistfacade.getGeoIdFromGeomListIndex(i); + const auto geom = geolistfacade.getGeometryFacadeFromGeoId(GeoId); + const auto type = geom->getGeometry()->getTypeId(); + auto layerId = geom->getGeometryLayerId(); + + auto coinLayer = geometryLayerParameters.getCoinLayer(layerId); + + if (type == Part::GeomPoint::getClassTypeId()) { // add a point + convert< Part::GeomPoint, + EditModeGeometryCoinConverter::PointsMode::InsertSingle, + EditModeGeometryCoinConverter::CurveMode::NoCurve, + EditModeGeometryCoinConverter::AnalyseMode::BoundingBoxMagnitude>(geom, GeoId); + setTracking(GeoId, coinLayer, EditModeGeometryCoinConverter::PointsMode::InsertSingle, 0); + } + else if (type == Part::GeomLineSegment::getClassTypeId()) { // add a line + convert< Part::GeomLineSegment, + EditModeGeometryCoinConverter::PointsMode::InsertStartEnd, + EditModeGeometryCoinConverter::CurveMode::StartEndPointsOnly, + EditModeGeometryCoinConverter::AnalyseMode::BoundingBoxMagnitude>(geom, GeoId); + setTracking(GeoId, coinLayer, EditModeGeometryCoinConverter::PointsMode::InsertStartEnd, 1); + } + else if (type.isDerivedFrom(Part::GeomConic::getClassTypeId())) { // add a closed curve conic + convert< Part::GeomConic, + EditModeGeometryCoinConverter::PointsMode::InsertMidOnly, + EditModeGeometryCoinConverter::CurveMode::ClosedCurve, + EditModeGeometryCoinConverter::AnalyseMode::BoundingBoxMagnitude>(geom, GeoId); + setTracking(GeoId, coinLayer, EditModeGeometryCoinConverter::PointsMode::InsertMidOnly, 1); + } + else if (type.isDerivedFrom(Part::GeomArcOfConic::getClassTypeId())) { // add an arc of conic + convert< Part::GeomArcOfConic, + EditModeGeometryCoinConverter::PointsMode::InsertStartEndMid, + EditModeGeometryCoinConverter::CurveMode::OpenCurve, + EditModeGeometryCoinConverter::AnalyseMode::BoundingBoxMagnitude>(geom, GeoId); + setTracking(GeoId, coinLayer, EditModeGeometryCoinConverter::PointsMode::InsertStartEndMid, 1); + } + else if (type == Part::GeomBSplineCurve::getClassTypeId()) { // add a bspline (a bounded curve that is not a conic) + convert< Part::GeomBSplineCurve, + EditModeGeometryCoinConverter::PointsMode::InsertStartEnd, + EditModeGeometryCoinConverter::CurveMode::OpenCurve, + EditModeGeometryCoinConverter::AnalyseMode::BoundingBoxMagnitudeAndBSplineCurvature>(geom, GeoId); + setTracking(GeoId, coinLayer, EditModeGeometryCoinConverter::PointsMode::InsertStartEnd, 1); + bsplineGeoIds.push_back(GeoId); + } + } + + for(int l=0 ; l < geometryLayerParameters.CoinLayers ; l++) { + + // Coin Nodes Editing + geometryLayerNodes.CurvesCoordinate[l]->point.setNum(Coords[l].size()); + geometryLayerNodes.CurveSet[l]->numVertices.setNum(Index[l].size()); + geometryLayerNodes.CurvesMaterials[l]->diffuseColor.setNum(Index[l].size()); + geometryLayerNodes.PointsCoordinate[l]->point.setNum(Points[l].size()); + geometryLayerNodes.PointsMaterials[l]->diffuseColor.setNum(Points[l].size()); + + SbVec3f *verts = geometryLayerNodes.CurvesCoordinate[l]->point.startEditing(); + int32_t *index = geometryLayerNodes.CurveSet[l]->numVertices.startEditing(); + SbVec3f *pverts = geometryLayerNodes.PointsCoordinate[l]->point.startEditing(); + + int i=0; // setting up the line set + for (std::vector::const_iterator it = Coords[l].begin(); it != Coords[l].end(); ++it,i++) + verts[i].setValue(it->x,it->y,drawingParameters.zLowLines); + + i=0; // setting up the indexes of the line set + for (std::vector::const_iterator it = Index[l].begin(); it != Index[l].end(); ++it,i++) + index[i] = *it; + + i=0; // setting up the point set + for (std::vector::const_iterator it = Points[l].begin(); it != Points[l].end(); ++it,i++) + pverts[i].setValue(it->x,it->y,drawingParameters.zLowPoints); + + geometryLayerNodes.CurvesCoordinate[l]->point.finishEditing(); + geometryLayerNodes.CurveSet[l]->numVertices.finishEditing(); + geometryLayerNodes.PointsCoordinate[l]->point.finishEditing(); + } +} + +template < typename GeoType, + EditModeGeometryCoinConverter::PointsMode pointmode, + EditModeGeometryCoinConverter::CurveMode curvemode, + EditModeGeometryCoinConverter::AnalyseMode analysemode > +void EditModeGeometryCoinConverter::convert(const Sketcher::GeometryFacade * geometryfacade, [[maybe_unused]] int geoid) { + auto geo = static_cast(geometryfacade->getGeometry()); + auto layerId = geometryfacade->getGeometryLayerId(); + + auto coinLayer = geometryLayerParameters.getCoinLayer(layerId); + + auto addPoint = [&dMg = boundingBoxMaxMagnitude] (auto & pushvector, Base::Vector3d point) { + + if constexpr (analysemode == AnalyseMode::BoundingBoxMagnitude || analysemode == AnalyseMode::BoundingBoxMagnitudeAndBSplineCurvature) { + dMg = dMg>std::abs(point.x)?dMg:std::abs(point.x); + dMg = dMg>std::abs(point.y)?dMg:std::abs(point.y); + pushvector.push_back(point); + } + }; + + // Points + if constexpr (pointmode == PointsMode::InsertSingle) { + addPoint(Points[coinLayer], geo->getPoint()); + } + else if constexpr (pointmode == PointsMode::InsertStartEnd) { + addPoint(Points[coinLayer], geo->getStartPoint()); + addPoint(Points[coinLayer], geo->getEndPoint()); + } + else if constexpr (pointmode == PointsMode::InsertStartEndMid) { + // All in this group are Trimmed Curves (see Geometry.h) + addPoint(Points[coinLayer], geo->getStartPoint(/*emulateCCW=*/true)); + addPoint(Points[coinLayer], geo->getEndPoint(/*emulateCCW=*/true)); + addPoint(Points[coinLayer], geo->getCenter()); + } + else if constexpr (pointmode == PointsMode::InsertMidOnly) { + addPoint(Points[coinLayer], geo->getCenter()); + } + + // Curves + if constexpr (curvemode == CurveMode::StartEndPointsOnly) { + addPoint(Coords[coinLayer], geo->getStartPoint()); + addPoint(Coords[coinLayer], geo->getEndPoint()); + Index[coinLayer].push_back(2); + } + else if constexpr (curvemode == CurveMode::ClosedCurve) { + double segment = (geo->getLastParameter() - geo->getFirstParameter()) / drawingParameters.curvedEdgeCountSegments; + + for (int i=0; i < drawingParameters.curvedEdgeCountSegments; i++) { + Base::Vector3d pnt = geo->value(i*segment); + addPoint(Coords[coinLayer], pnt); + } + + Base::Vector3d pnt = geo->value(0); + addPoint(Coords[coinLayer], pnt); + + Index[coinLayer].push_back(drawingParameters.curvedEdgeCountSegments+1); + } + else if constexpr (curvemode == CurveMode::OpenCurve) { + + double segment = (geo->getLastParameter() - geo->getFirstParameter()) / drawingParameters.curvedEdgeCountSegments; + + for (int i=0; i < drawingParameters.curvedEdgeCountSegments; i++) { + Base::Vector3d pnt = geo->value(geo->getFirstParameter() + i*segment); + addPoint(Coords[coinLayer], pnt); + } + + Base::Vector3d pnt = geo->value(geo->getLastParameter()); + addPoint(Coords[coinLayer], pnt); + + Index[coinLayer].push_back(drawingParameters.curvedEdgeCountSegments+1); + + if constexpr (analysemode == AnalyseMode::BoundingBoxMagnitudeAndBSplineCurvature) { + //*************************************************************************************************************** + // global information gathering for geometry information layer + + std::vector poles = geo->getPoles(); + + Base::Vector3d midp = Base::Vector3d(0,0,0); + + for (std::vector::iterator it = poles.begin(); it != poles.end(); ++it) { + midp += (*it); + } + + midp /= poles.size(); + + double firstparam = geo->getFirstParameter(); + double lastparam = geo->getLastParameter(); + + const int ndiv = poles.size()>4?poles.size()*16:64; + double step = (lastparam - firstparam ) / (ndiv -1); + + std::vector paramlist(ndiv); + std::vector pointatcurvelist(ndiv); + std::vector curvaturelist(ndiv); + std::vector normallist(ndiv); + + double maxcurv = 0; + double maxdisttocenterofmass = 0; + + for (int i = 0; i < ndiv; i++) { + paramlist[i] = firstparam + i * step; + pointatcurvelist[i] = geo->pointAtParameter(paramlist[i]); + + try { + curvaturelist[i] = geo->curvatureAt(paramlist[i]); + } + catch(Base::CADKernelError &e) { + // it is "just" a visualisation matter OCC could not calculate the curvature + // terminating here would mean that the other shapes would not be drawn. + // Solution: Report the issue and set dummy curvature to 0 + e.ReportException(); + Base::Console().Error("Curvature graph for B-Spline with GeoId=%d could not be calculated.\n", geoid); // TODO: Fix identification of curve. + curvaturelist[i] = 0; + } + + if (curvaturelist[i] > maxcurv) + maxcurv = curvaturelist[i]; + + double tempf = ( pointatcurvelist[i] - midp ).Length(); + + if (tempf > maxdisttocenterofmass) + maxdisttocenterofmass = tempf; + + } + + double temprepscale = 0; + if (maxcurv > 0) + temprepscale = (0.5 * maxdisttocenterofmass) / maxcurv; // just a factor to make a comb reasonably visible + + if (temprepscale > combrepscale) + combrepscale = temprepscale; + + } + + } + +} + +float EditModeGeometryCoinConverter::getBoundingBoxMaxMagnitude() +{ + return boundingBoxMaxMagnitude; +} + +double EditModeGeometryCoinConverter::getCombRepresentationScale() +{ + return combrepscale; +} diff --git a/src/Mod/Sketcher/Gui/EditModeGeometryCoinConverter.h b/src/Mod/Sketcher/Gui/EditModeGeometryCoinConverter.h new file mode 100644 index 0000000000..ae23634440 --- /dev/null +++ b/src/Mod/Sketcher/Gui/EditModeGeometryCoinConverter.h @@ -0,0 +1,163 @@ +/*************************************************************************** + * Copyright (c) 2021 Abdullah Tahiri * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef SKETCHERGUI_GeometryCoinConverter_H +#define SKETCHERGUI_GeometryCoinConverter_H + +#include + +namespace Base { + template< typename T > + class Vector3; + + class Vector2d; +} + +namespace Sketcher { + enum ConstraintType : int; + enum class PointPos : int; +} + +namespace Part { + class Geometry; +} + +namespace SketcherGui { + struct GeometryLayerNodes; + struct DrawingParameters; + class GeometryLayerParameters; + struct CoinMapping; + +/** @brief Class for creating the Geometry layer into coin nodes + * @details + * Responsibility: + * To create and update GeometryLayer nodes provided as constructor parameter + * for the provided geometry, taking into account the drawing parameters provided as + * constructor parameters. + * + * Interface: + * A single entry point convert(), performing the following flow: + * + * [Geometry] => Analysis => construct drawing elements => Create mappings GeoId coin => populate coin nodes + * + * Analysis performs analysis such as maximum boundingbox magnitude of all geometries and maximum curvature of BSplines + */ +class EditModeGeometryCoinConverter { +// These internal private classes are used to parametrize the conversion of geometry into points and line sets (see template method convert) +private: + enum class PointsMode { + InsertSingle, + InsertStartEnd, + InsertStartEndMid, + InsertMidOnly + }; + + enum class CurveMode { + NoCurve, + StartEndPointsOnly, + ClosedCurve, + OpenCurve + }; + + enum class AnalyseMode { + BoundingBoxMagnitude, + BoundingBoxMagnitudeAndBSplineCurvature + }; + +public: + /** Constructs an GeometryCoinConverter responsible for + * generating the points and line sets for drawing the geometry + * defined by a GeometryLayer into the coin nodes provided by + * GeometryLayerNodes. + * + * @param geometrylayernodes: The coin nodes to be populated with + * the geometry + * + * @param drawingparameters: Parameters for drawing the overlay information + */ + EditModeGeometryCoinConverter( GeometryLayerNodes & geometrylayernodes, + DrawingParameters & drawingparameters, + GeometryLayerParameters& geometryLayerParams, + CoinMapping & coinMap ); + + /** + * converts the geometry defined by GeometryLayer into the coin nodes. + * + * @param geometry: the geometry to be processed + */ + void convert(const Sketcher::GeoListFacade & geolistfacade); + + /** + * returns the maximum of the vertical and horizontal magnitudes of the + * coordinates of the points and lines added to coin by this layer (local responsibility). + */ + float getBoundingBoxMaxMagnitude(); + + /** + * returns the Comb representation scale that should be used to represent + * the B-Splines of this layer (local responsibility). + */ + double getCombRepresentationScale(); + + /** + * returns the GeoIds of BSpline geometries + */ + auto getBSplineGeoIds(){ return std::move(bsplineGeoIds);} + +private: + template < typename GeoType, PointsMode pointmode, CurveMode curvemode, AnalyseMode analysemode > + void convert(const Sketcher::GeometryFacade * geometryfacade, [[maybe_unused]] int geoId); + +private: + GeometryLayerNodes & geometryLayerNodes; + + std::vector> Coords; + std::vector> Points; + std::vector> Index; + + // temporal counters, one per layer + std::vector pointCounter; + std::vector curveCounter; + + // temporal global vertex counter + int vertexCounter = 0; + + // Parameters + DrawingParameters & drawingParameters; + GeometryLayerParameters& geometryLayerParameters; + // Mappings coin geoId + CoinMapping & coinMapping; + + // measurements + float boundingBoxMaxMagnitude = 100; + double combrepscale = 0; // the repscale that would correspond to this comb based only on this calculation. + std::vector bsplineGeoIds; + +}; + + +} // namespace SketcherGui + + +#endif // SKETCHERGUI_GeometryCoinConverter_H + diff --git a/src/Mod/Sketcher/Gui/EditModeGeometryCoinManager.cpp b/src/Mod/Sketcher/Gui/EditModeGeometryCoinManager.cpp new file mode 100644 index 0000000000..8167a7d899 --- /dev/null +++ b/src/Mod/Sketcher/Gui/EditModeGeometryCoinManager.cpp @@ -0,0 +1,544 @@ +/*************************************************************************** + * Copyright (c) 2021 Abdullah Tahiri * + * * + * 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 +#endif // #ifndef _PreComp_ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include + +#include "SoZoomTranslation.h" +#include "SoDatumLabel.h" + +#include "EditModeInformationOverlayCoinConverter.h" + +#include "EditModeGeometryCoinConverter.h" + +#include "ViewProviderSketch.h" + +#include "ViewProviderSketchCoinAttorney.h" + +#include "EditModeConstraintCoinManager.h" + +#include "EditModeGeometryCoinManager.h" + +using namespace SketcherGui; +using namespace Sketcher; + +//**************************** EditModeGeometryCoinManager class ****************************** + +EditModeGeometryCoinManager::EditModeGeometryCoinManager( ViewProviderSketch &vp, + DrawingParameters & drawingParams, + GeometryLayerParameters & geometryLayerParams, + AnalysisResults & analysisResultStruct, + EditModeScenegraphNodes & editModeScenegraph, + CoinMapping & coinMap): + viewProvider(vp), + drawingParameters(drawingParams), + geometryLayerParameters(geometryLayerParams), + analysisResults(analysisResultStruct), + editModeScenegraphNodes(editModeScenegraph), + coinMapping(coinMap) +{} + +EditModeGeometryCoinManager::~EditModeGeometryCoinManager() +{} + +void EditModeGeometryCoinManager::processGeometry(const GeoListFacade & geolistfacade) +{ + // enable all layers + editModeScenegraphNodes.PointsGroup->enable.setNum(geometryLayerParameters.CoinLayers); + editModeScenegraphNodes.CurvesGroup->enable.setNum(geometryLayerParameters.CoinLayers); + SbBool *swsp = editModeScenegraphNodes.PointsGroup->enable.startEditing(); + SbBool *swsc = editModeScenegraphNodes.CurvesGroup->enable.startEditing(); + + for(int l=0; lenable.finishEditing(); + editModeScenegraphNodes.CurvesGroup->enable.finishEditing(); + + // Define the coin nodes that will be filled in with the geometry layers + GeometryLayerNodes geometrylayernodes { + editModeScenegraphNodes.PointsMaterials, + editModeScenegraphNodes.PointsCoordinate, + editModeScenegraphNodes.CurvesMaterials, + editModeScenegraphNodes.CurvesCoordinate, + editModeScenegraphNodes.CurveSet + }; + + // process geometry layers + EditModeGeometryCoinConverter gcconv(geometrylayernodes, drawingParameters, geometryLayerParameters, coinMapping); + + gcconv.convert(geolistfacade); + + // set cross coordinates + editModeScenegraphNodes.RootCrossSet->numVertices.set1Value(0,2); + editModeScenegraphNodes.RootCrossSet->numVertices.set1Value(1,2); + + analysisResults.combRepresentationScale = gcconv.getCombRepresentationScale(); + analysisResults.boundingBoxMagnitudeOrder = exp(ceil(log(std::abs(gcconv.getBoundingBoxMaxMagnitude())))); + analysisResults.bsplineGeoIds = gcconv.getBSplineGeoIds(); +} + +void EditModeGeometryCoinManager::updateGeometryColor(const GeoListFacade & geolistfacade, bool issketchinvalid) +{ + // Lambdas for convenience retrieval of geometry information + auto isConstructionGeom = [&geolistfacade](int GeoId) { + auto geom = geolistfacade.getGeometryFacadeFromGeoId(GeoId); + if (geom) + return geom->getConstruction(); + return false; + }; + + auto isDefinedGeomPoint = [&geolistfacade](int GeoId) { + auto geom = geolistfacade.getGeometryFacadeFromGeoId(GeoId); + if (geom) + return geom->isGeoType(Part::GeomPoint::getClassTypeId()) && !geom->getConstruction(); + return false; + }; + + auto isInternalAlignedGeom = [&geolistfacade](int GeoId) { + auto geom = geolistfacade.getGeometryFacadeFromGeoId(GeoId); + if (geom) { + return geom->isInternalAligned(); + } + return false; + }; + + auto isFullyConstraintElement = [&geolistfacade](int GeoId) { + auto geom = geolistfacade.getGeometryFacadeFromGeoId(GeoId); + + if(geom) { + if(geom->hasExtension(Sketcher::SolverGeometryExtension::getClassTypeId())) { + + auto solvext = std::static_pointer_cast( + geom->getExtension(Sketcher::SolverGeometryExtension::getClassTypeId()).lock()); + + return (solvext->getGeometry() == Sketcher::SolverGeometryExtension::FullyConstraint); + } + } + return false; + }; + + // Update Colors + + SbColor *crosscolor = editModeScenegraphNodes.RootCrossMaterials->diffuseColor.startEditing(); + + for(int l=0; ldiffuseColor.getNum(); + SbColor *pcolor = editModeScenegraphNodes.PointsMaterials[l]->diffuseColor.startEditing(); + int CurvNum = editModeScenegraphNodes.CurvesMaterials[l]->diffuseColor.getNum(); + SbColor *color = editModeScenegraphNodes.CurvesMaterials[l]->diffuseColor.startEditing(); + + SbVec3f *verts = editModeScenegraphNodes.CurvesCoordinate[l]->point.startEditing(); + SbVec3f *pverts = editModeScenegraphNodes.PointsCoordinate[l]->point.startEditing(); + + float x,y,z; + + // colors of the point set + if( issketchinvalid ) { + for (int i=0; i < PtNum; i++) + pcolor[i] = drawingParameters.InvalidSketchColor; + } + else if (ViewProviderSketchCoinAttorney::isSketchFullyConstrained(viewProvider)) { + for (int i=0; i < PtNum; i++) + pcolor[i] = drawingParameters.FullyConstrainedColor; + } + else { + for (int i=0; i < PtNum; i++) { + int GeoId = coinMapping.getPointGeoId(i, l); + + bool constrainedElement = isFullyConstraintElement(GeoId); + + if(isInternalAlignedGeom(GeoId)) { + if(constrainedElement) + pcolor[i] = drawingParameters.FullyConstraintInternalAlignmentColor; + else + pcolor[i] = drawingParameters.InternalAlignedGeoColor; + } + else { + if(!isDefinedGeomPoint(GeoId)) { + + if(constrainedElement) + pcolor[i] = drawingParameters.FullyConstraintConstructionPointColor; + else + pcolor[i] = drawingParameters.VertexColor; + } + else { // this is a defined GeomPoint + if(constrainedElement) + pcolor[i] = drawingParameters.FullyConstraintElementColor; + else + pcolor[i] = drawingParameters.CurveColor; + } + } + } + } + + // update rendering height of points + + auto getRenderHeight = [this](DrawingParameters::GeometryRendering renderingtype, float toprendering, float midrendering, float lowrendering) { + if(drawingParameters.topRenderingGeometry == renderingtype) + return toprendering; + else if(drawingParameters.midRenderingGeometry == renderingtype) + return midrendering; + else + return lowrendering; + }; + + float zNormPoint = getRenderHeight(DrawingParameters::GeometryRendering::NormalGeometry, + drawingParameters.zHighPoints, + drawingParameters.zLowPoints, + drawingParameters.zLowPoints); + + float zConstrPoint = getRenderHeight(DrawingParameters::GeometryRendering::Construction, + drawingParameters.zHighPoints, + drawingParameters.zLowPoints, + drawingParameters.zLowPoints); + + + for (int i=0; i < PtNum; i++) { // 0 is the origin + pverts[i].getValue(x,y,z); + auto geom = geolistfacade.getGeometryFacadeFromGeoId(coinMapping.getPointGeoId(i, l)); + if(geom && z < drawingParameters.zHighlight) { + if(geom->getConstruction()) + pverts[i].setValue(x,y,zConstrPoint); + else + pverts[i].setValue(x,y,zNormPoint); + } + } + + auto preselectpoint = ViewProviderSketchCoinAttorney::getPreselectPoint(viewProvider); + auto preselectcross = ViewProviderSketchCoinAttorney::getPreselectCross(viewProvider); + auto preselectcurve = ViewProviderSketchCoinAttorney::getPreselectCurve(viewProvider); + + MultiFieldId preselectpointmfid; + + if ( preselectcross == 0) { + if(l == 0) // cross only in layer 0 + pcolor[0] = drawingParameters.PreselectColor; + } + else if (preselectpoint != -1) { + preselectpointmfid = coinMapping.getIndexLayer(preselectpoint); + if (l == preselectpointmfid.layerId && preselectpointmfid.fieldIndex < PtNum) + pcolor[preselectpointmfid.fieldIndex] = drawingParameters.PreselectColor; + } + + ViewProviderSketchCoinAttorney::executeOnSelectionPointSet(viewProvider, + [pcolor, PtNum, preselectpointmfid, layerId = l, &coinMapping = coinMapping, drawingParameters = this->drawingParameters](const int i) { + auto pointindex = coinMapping.getIndexLayer(i); + if (layerId == pointindex.layerId && pointindex.fieldIndex < PtNum) { + pcolor[pointindex.fieldIndex] = (preselectpointmfid == pointindex) + ? drawingParameters.PreselectSelectedColor : drawingParameters.SelectColor; + } + }); + + // update colors and rendering height of the curves + + float zNormLine = getRenderHeight(DrawingParameters::GeometryRendering::NormalGeometry, + drawingParameters.zHighLines, + drawingParameters.zMidLines, + drawingParameters.zLowLines); + + float zConstrLine = getRenderHeight(DrawingParameters::GeometryRendering::Construction, + drawingParameters.zHighLines, + drawingParameters.zMidLines, + drawingParameters.zLowLines); + + float zExtLine = getRenderHeight(DrawingParameters::GeometryRendering::ExternalGeometry, + drawingParameters.zHighLines, + drawingParameters.zMidLines, + drawingParameters.zLowLines); + + int j=0; // vertexindex + + for (int i=0; i < CurvNum; i++) { + int GeoId = coinMapping.getCurveGeoId(i, l); + // CurvId has several vertices associated to 1 material + //edit->CurveSet->numVertices => [i] indicates number of vertex for line i. + int indexes = (editModeScenegraphNodes.CurveSet[l]->numVertices[i]); + + bool selected = ViewProviderSketchCoinAttorney::isCurveSelected(viewProvider, GeoId); + bool preselected = (preselectcurve == GeoId); + + bool constrainedElement = isFullyConstraintElement(GeoId); + + if (selected && preselected) { + color[i] = drawingParameters.PreselectSelectedColor; + for (int k=j; jdiffuseColor.finishEditing(); + editModeScenegraphNodes.PointsMaterials[l]->diffuseColor.finishEditing(); + editModeScenegraphNodes.CurvesCoordinate[l]->point.finishEditing(); + editModeScenegraphNodes.CurveSet[l]->numVertices.finishEditing(); + } + + editModeScenegraphNodes.RootCrossMaterials->diffuseColor.finishEditing(); +} + + +void EditModeGeometryCoinManager::createEditModeInventorNodes() +{ + // stuff for the points ++++++++++++++++++++++++++++++++++++++ + editModeScenegraphNodes.PointsGroup = new SmSwitchboard; + editModeScenegraphNodes.EditRoot->addChild(editModeScenegraphNodes.PointsGroup); + + auto concat = [](std::string string, int i) { + return string+std::to_string(i); + }; + + for(int i=0; i < geometryLayerParameters.CoinLayers; i++) { + SoSeparator * sep = new SoSeparator; + sep->ref(); + + auto somaterial = new SoMaterial; + editModeScenegraphNodes.PointsMaterials.push_back(somaterial); + editModeScenegraphNodes.PointsMaterials[i]->setName(concat("PointsMaterials_",i).c_str()); + sep->addChild(editModeScenegraphNodes.PointsMaterials[i]); + + SoMaterialBinding *MtlBind = new SoMaterialBinding; + MtlBind->setName(concat("PointsMaterialBinding",i).c_str()); + MtlBind->value = SoMaterialBinding::PER_VERTEX; + sep->addChild(MtlBind); + + auto coords = new SoCoordinate3; + editModeScenegraphNodes.PointsCoordinate.push_back(coords); + editModeScenegraphNodes.PointsCoordinate[i]->setName(concat("PointsCoordinate",i).c_str()); + sep->addChild(editModeScenegraphNodes.PointsCoordinate[i]); + + auto drawstyle = new SoDrawStyle; + editModeScenegraphNodes.PointsDrawStyle.push_back(drawstyle); + editModeScenegraphNodes.PointsDrawStyle[i]->setName(concat("PointsDrawStyle",i).c_str()); + editModeScenegraphNodes.PointsDrawStyle[i]->pointSize = 8 * drawingParameters.pixelScalingFactor; + sep->addChild(editModeScenegraphNodes.PointsDrawStyle[i]); + + auto pointset = new SoMarkerSet; + editModeScenegraphNodes.PointSet.push_back(pointset); + editModeScenegraphNodes.PointSet[i]->setName(concat("PointSet",i).c_str()); + editModeScenegraphNodes.PointSet[i]->markerIndex = Gui::Inventor::MarkerBitmaps::getMarkerIndex("CIRCLE_FILLED", drawingParameters.markerSize); + sep->addChild(editModeScenegraphNodes.PointSet[i]); + + editModeScenegraphNodes.PointsGroup->addChild(sep); + sep->unref(); + } + + // stuff for the Curves +++++++++++++++++++++++++++++++++++++++ + editModeScenegraphNodes.CurvesGroup = new SmSwitchboard; + editModeScenegraphNodes.EditRoot->addChild(editModeScenegraphNodes.CurvesGroup); + + for(int i=0; i < geometryLayerParameters.CoinLayers; i++) { + SoSeparator * sep = new SoSeparator; + sep->ref(); + + auto somaterial = new SoMaterial; + editModeScenegraphNodes.CurvesMaterials.push_back(somaterial); + editModeScenegraphNodes.CurvesMaterials[i]->setName(concat("CurvesMaterials",i).c_str()); + sep->addChild(editModeScenegraphNodes.CurvesMaterials[i]); + + auto MtlBind = new SoMaterialBinding; + MtlBind->setName(concat("CurvesMaterialsBinding",i).c_str()); + MtlBind->value = SoMaterialBinding::PER_FACE; + sep->addChild(MtlBind); + + auto coords = new SoCoordinate3; + editModeScenegraphNodes.CurvesCoordinate.push_back(coords); + editModeScenegraphNodes.CurvesCoordinate[i]->setName(concat("CurvesCoordinate",i).c_str()); + sep->addChild(editModeScenegraphNodes.CurvesCoordinate[i]); + + auto drawstyle = new SoDrawStyle; + editModeScenegraphNodes.CurvesDrawStyle.push_back(drawstyle); + editModeScenegraphNodes.CurvesDrawStyle[i]->setName(concat("CurvesDrawStyle",i).c_str()); + editModeScenegraphNodes.CurvesDrawStyle[i]->lineWidth = 3 * drawingParameters.pixelScalingFactor; + + /* Demo code to introduce a dashed line + if(i == 1) { + editModeScenegraphNodes.CurvesDrawStyle[i]->linePattern = 0x3CF2; + editModeScenegraphNodes.CurvesDrawStyle[i]->linePatternScaleFactor = 5; + }*/ + + sep->addChild(editModeScenegraphNodes.CurvesDrawStyle[i]); + + auto solineset = new SoLineSet; + editModeScenegraphNodes.CurveSet.push_back(solineset); + editModeScenegraphNodes.CurveSet[i]->setName(concat("CurvesLineSet",i).c_str()); + sep->addChild(editModeScenegraphNodes.CurveSet[i]); + + editModeScenegraphNodes.CurvesGroup->addChild(sep); + sep->unref(); + } + +} diff --git a/src/Mod/Sketcher/Gui/EditModeGeometryCoinManager.h b/src/Mod/Sketcher/Gui/EditModeGeometryCoinManager.h new file mode 100644 index 0000000000..2c9d6d3578 --- /dev/null +++ b/src/Mod/Sketcher/Gui/EditModeGeometryCoinManager.h @@ -0,0 +1,123 @@ +/*************************************************************************** + * Copyright (c) 2021 Abdullah Tahiri * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef SKETCHERGUI_EditModeGeometryCoinManager_H +#define SKETCHERGUI_EditModeGeometryCoinManager_H + +#include +#include + +#include +#include + +#include "EditModeCoinManagerParameters.h" + +#include + +class SbVec3f; +class SoRayPickAction; +class SoPickedPoint; +class SbVec3s; + +namespace Base { + template< typename T > + class Vector3; + + class Vector2d; + + class Placement; +} + +namespace Part { + class Geometry; +} + +namespace Sketcher { + class Constraint; + class PropertyConstraintList; +}; + +namespace SketcherGui { + +class ViewProviderSketch; +class EditModeConstraintCoinManager; + +using GeoList = Sketcher::GeoList; +using GeoListFacade = Sketcher::GeoListFacade; + +/** @brief Class for managing the Edit mode coin nodes of ViewProviderSketch relating to geometry. + * @details + * + * EditModeGeometryCoinManager is a helper of EditModeCoinManager specialised in geometry management. + * + * Three main functions are delegated to it: + * 1. Creation of Edit mode coin nodes to handle Geometry representation. + * 2. Converting Sketcher geometry into Coin information. + * 3. Updating the Geometry colors. + * + * Internally, EditModeGeometryCoinManager uses yet another class for geometry conversion, + * GeometryCoinConverter. + * + */ +class SketcherGuiExport EditModeGeometryCoinManager +{ + +public: + explicit EditModeGeometryCoinManager( ViewProviderSketch &vp, + DrawingParameters & drawingParams, + GeometryLayerParameters & geometryLayerParams, + AnalysisResults & analysisResultStruct, + EditModeScenegraphNodes & editModeScenegraph, + CoinMapping & coinMap); + ~EditModeGeometryCoinManager(); + + + // This function populates the coin nodes with the information of the current geometry + void processGeometry(const GeoListFacade & geolistfacade); + + void updateGeometryColor(const GeoListFacade & geolistfacade, bool issketchinvalid); + + /** @name coin nodes creation*/ + void createEditModeInventorNodes(); + //@} + + +private: + ViewProviderSketch & viewProvider; + + DrawingParameters & drawingParameters; + GeometryLayerParameters & geometryLayerParameters; + AnalysisResults & analysisResults; + + EditModeScenegraphNodes & editModeScenegraphNodes; + + CoinMapping & coinMapping; + +}; + + +} // namespace SketcherGui + + +#endif // SKETCHERGUI_EditModeGeometryCoinManager_H + diff --git a/src/Mod/Sketcher/Gui/EditModeInformationOverlayCoinConverter.cpp b/src/Mod/Sketcher/Gui/EditModeInformationOverlayCoinConverter.cpp new file mode 100644 index 0000000000..cabb75ba28 --- /dev/null +++ b/src/Mod/Sketcher/Gui/EditModeInformationOverlayCoinConverter.cpp @@ -0,0 +1,446 @@ +/*************************************************************************** + * Copyright (c) 2021 Abdullah Tahiri * + * * + * 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 +#endif // #ifndef _PreComp_ + +#include +#include +#include + +#include + +#include "EditModeCoinManagerParameters.h" + +#include "EditModeInformationOverlayCoinConverter.h" + + +using namespace SketcherGui; + +EditModeInformationOverlayCoinConverter::EditModeInformationOverlayCoinConverter( + SoGroup * infogroup, + OverlayParameters & overlayparameters, + DrawingParameters & drawingparameters): infoGroup(infogroup), + overlayParameters(overlayparameters), + drawingParameters(drawingparameters), + nodeId(0){ + +}; + +void EditModeInformationOverlayCoinConverter::convert(const Part::Geometry * geometry, int geoid) { + + // at this point all calculations relate to BSplineCurves + assert(geometry->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()); + + calculate(geometry, geoid); + calculate(geometry, geoid); + calculate(geometry, geoid); + calculate(geometry, geoid); + calculate(geometry, geoid); + + addUpdateNode(degree); + addUpdateNode(controlPolygon); + addUpdateNode(curvatureComb); + addUpdateNode(knotMultiplicity); + addUpdateNode(poleWeights); + +}; + +void EditModeInformationOverlayCoinConverter::addToInfoGroup(SoSwitch * sw) { + infoGroup->addChild(sw); + nodeId++; +} + +template < EditModeInformationOverlayCoinConverter::CalculationType calculation > +void EditModeInformationOverlayCoinConverter::calculate(const Part::Geometry * geometry, [[maybe_unused]] int geoid) { + const Part::GeomBSplineCurve *spline = static_cast(geometry); + + if constexpr (calculation == CalculationType::BSplineDegree ) { + clearCalculation(degree); + + std::vector poles = spline->getPoles(); + + degree.strings.clear(); + degree.positions.clear(); + + Base::Vector3d midp = Base::Vector3d(0,0,0); + + for (auto val : poles) + midp += val; + + midp /= poles.size(); + + degree.strings.emplace_back(std::to_string(spline->getDegree())); + degree.positions.emplace_back(midp); + } + else if constexpr (calculation == CalculationType::BSplineControlPolygon ) { + + clearCalculation(controlPolygon); + + std::vector poles = spline->getPoles(); + + controlPolygon.coordinates.clear(); + controlPolygon.indices.clear(); + + if (spline->isPeriodic()) + controlPolygon.coordinates.reserve(poles.size()+1); + else + controlPolygon.coordinates.reserve(poles.size()); + + for (auto & v : poles) + controlPolygon.coordinates.emplace_back(v); + + if (spline->isPeriodic()) + controlPolygon.coordinates.emplace_back(poles[0]); + + controlPolygon.indices.push_back(poles.size()); // single continuous poligon starting at index 0 + } + else if constexpr (calculation == CalculationType::BSplineCurvatureComb ) { + + clearCalculation(curvatureComb); + // curvature graph -------------------------------------------------------- + + // reimplementation of python source: + // https://github.com/tomate44/CurvesWB/blob/master/ParametricComb.py + // by FreeCAD user Chris_G + + std::vector poles = spline->getPoles(); + + double firstparam = spline->getFirstParameter(); + double lastparam = spline->getLastParameter(); + + const int ndiv = poles.size()>4?poles.size()*16:64; // heuristic of number of division to fill in + double step = (lastparam - firstparam) / (ndiv-1); + + std::vector pointatcurvelist; + std::vector curvaturelist; + std::vector normallist; + + pointatcurvelist.reserve(ndiv); + curvaturelist.reserve(ndiv); + normallist.reserve(ndiv); + + for(int i = 0; i < ndiv; i++) { + double param = firstparam + i * step; + pointatcurvelist.emplace_back(spline->value(param)); + + try { + curvaturelist.emplace_back(spline->curvatureAt(param)); + } + catch(Base::CADKernelError &e) { + // it is "just" a visualisation matter OCC could not calculate the curvature + // terminating here would mean that the other shapes would not be drawn. + // Solution: Report the issue and set dummy curvature to 0 + e.ReportException(); + Base::Console().Error("Curvature graph for B-Spline with GeoId=%d could not be calculated.\n", geoid); + curvaturelist.emplace_back(0); + } + + Base::Vector3d normal; + try { + spline->normalAt(param, normal); + normallist.emplace_back(normal); + } + catch(Base::Exception&) { + normallist.emplace_back(0,0,0); + } + } + + std::vector pointatcomblist; + pointatcomblist.reserve(ndiv); + + for(int i = 0; i < ndiv; i++) { + pointatcomblist.emplace_back(pointatcurvelist[i] - overlayParameters.currentBSplineCombRepresentationScale * curvaturelist[i] * normallist[i]); + } + + curvatureComb.coordinates.reserve(3*ndiv); // 2*ndiv +1 points of ndiv separate segments + ndiv points for last segment + curvatureComb.indices.reserve(ndiv+1); // ndiv separate segments of radials + 1 segment connecting at comb end + + for(int i = 0; i < ndiv; i++) { + // note emplace emplaces on the position BEFORE the iterator given. + curvatureComb.coordinates.emplace_back(pointatcurvelist[i].x, pointatcurvelist[i].y, drawingParameters.zInfo); // radials + curvatureComb.coordinates.emplace_back(pointatcomblist[i].x, pointatcomblist[i].y, drawingParameters.zInfo); // radials + + curvatureComb.indices.emplace_back(2); // line + } + + for(int i = 0; i < ndiv; i++) + curvatureComb.coordinates.emplace_back(pointatcomblist[i].x, pointatcomblist[i].y, drawingParameters.zInfo); // // comb endpoint closing segment + + curvatureComb.indices.emplace_back(ndiv); // Comb line + } + else if constexpr (calculation == CalculationType::BSplineKnotMultiplicity ) { + + clearCalculation(knotMultiplicity); + std::vector knots = spline->getKnots(); + std::vector mult = spline->getMultiplicities(); + + for(size_t i=0; ipointAtParameter(knots[i])); + + std::ostringstream stringStream; + stringStream << "(" << mult[i] << ")"; + + knotMultiplicity.strings.emplace_back( stringStream.str()); + } + } + else if constexpr (calculation == CalculationType::BSplinePoleWeight ) { + + clearCalculation(poleWeights); + std::vector poles = spline->getPoles(); + auto weights = spline->getWeights(); + + for(size_t i=0; i +void EditModeInformationOverlayCoinConverter::addUpdateNode(const Result & result) { + + if(overlayParameters.rebuildInformationLayer) + addNode(result); + else + updateNode(result); +} + +template < EditModeInformationOverlayCoinConverter::CalculationType calculation > +bool EditModeInformationOverlayCoinConverter::isVisible() { + if constexpr ( calculation == CalculationType::BSplineDegree ) { + return overlayParameters.bSplineDegreeVisible; + } + else if constexpr ( calculation == CalculationType::BSplineControlPolygon ) { + return overlayParameters.bSplineControlPolygonVisible; + } + else if constexpr ( calculation == CalculationType::BSplineCurvatureComb ) { + return overlayParameters.bSplineCombVisible; + } + else if constexpr ( calculation == CalculationType::BSplineKnotMultiplicity ) { + return overlayParameters.bSplineKnotMultiplicityVisible; + } + else if constexpr ( calculation == CalculationType::BSplinePoleWeight ) { + return overlayParameters.bSplinePoleWeightVisible; + } +} + +template < typename Result > +void EditModeInformationOverlayCoinConverter::setPolygon(const Result & result, SoLineSet *polygonlineset, SoCoordinate3 *polygoncoords) { + + polygoncoords->point.setNum(result.coordinates.size()); + polygonlineset->numVertices.setNum(result.indices.size()); + + int32_t *index = polygonlineset->numVertices.startEditing(); + SbVec3f *vts = polygoncoords->point.startEditing(); + + for(size_t i = 0; i < result.coordinates.size(); i++) + vts[i].setValue(result.coordinates[i].x, result.coordinates[i].y, drawingParameters.zInfo); + + for(size_t i = 0; i < result.indices.size(); i++) + index[i] = result.indices[i]; + + polygoncoords->point.finishEditing(); + polygonlineset->numVertices.finishEditing(); +} + +template < int line > +void EditModeInformationOverlayCoinConverter::setText(const std::string & string, SoText2 * text) { + + if constexpr (line == 1) { + text->string = SbString(string.c_str()); + } + else { + assert(line > 1); + SoMFString label; + for ( int l = 0; l < (line - 1) ; l++) + label.set1Value(l, SbString("")); + + label.set1Value(line-1, SbString(string.c_str())); + text->string = label; + } +} + + +template < typename Result > +void EditModeInformationOverlayCoinConverter::clearCalculation(Result & result) { + if constexpr (Result::visualisationType == VisualisationType::Text) { + result.positions.clear(); + result.strings.clear(); + } + else if constexpr (Result::visualisationType == VisualisationType::Polygon) { + result.coordinates.clear(); + result.indices.clear(); + } +} + +template < typename Result> +void EditModeInformationOverlayCoinConverter::addNode(const Result & result) { + + if constexpr (Result::visualisationType == VisualisationType::Text) { + + for(size_t i = 0; i < result.strings.size(); i++) { + + SoSwitch *sw = new SoSwitch(); + + sw->whichChild = isVisible()?SO_SWITCH_ALL:SO_SWITCH_NONE; + + SoSeparator *sep = new SoSeparator(); + sep->ref(); + // no caching for frequently-changing data structures + sep->renderCaching = SoSeparator::OFF; + + // every information visual node gets its own material for to-be-implemented preselection and selection + SoMaterial *mat = new SoMaterial; + mat->ref(); + mat->diffuseColor = drawingParameters.InformationColor; + + SoTranslation *translate = new SoTranslation; + + translate->translation.setValue(result.positions[i].x, result.positions[i].y, drawingParameters.zInfo); + + SoFont *font = new SoFont; + font->name.setValue("Helvetica"); + font->size.setValue(drawingParameters.coinFontSize); + + SoText2 *text = new SoText2; + + // since the first and last control point of a spline is also treated as knot and thus + // can also have a displayed multiplicity, we must assure the multiplicity is not visibly overwritten + // therefore be output the weight in a second line + // + // This could be made into a more generic form, but it is probably not worth the effort at this time. + if constexpr ( Result::calculationType == CalculationType::BSplinePoleWeight ) + setText<2>(result.strings[i], text); + else + setText(result.strings[i], text); + + sep->addChild(translate); + sep->addChild(mat); + sep->addChild(font); + sep->addChild(text); + + sw->addChild(sep); + + addToInfoGroup(sw); + sep->unref(); + mat->unref(); + } + } + else if constexpr (Result::visualisationType == VisualisationType::Polygon) { + + SoSwitch *sw = new SoSwitch(); + + // hGrpsk->GetBool("BSplineControlPolygonVisible", true) + sw->whichChild = isVisible()?SO_SWITCH_ALL:SO_SWITCH_NONE; + + SoSeparator *sep = new SoSeparator(); + sep->ref(); + // no caching for frequently-changing data structures + sep->renderCaching = SoSeparator::OFF; + + // every information visual node gets its own material for to-be-implemented preselection and selection + SoMaterial *mat = new SoMaterial; + mat->ref(); + mat->diffuseColor = drawingParameters.InformationColor; + + SoLineSet *polygonlineset = new SoLineSet; + SoCoordinate3 *polygoncoords = new SoCoordinate3; + + setPolygon(result, polygonlineset, polygoncoords); + + sep->addChild(mat); + sep->addChild(polygoncoords); + sep->addChild(polygonlineset); + + sw->addChild(sep); + + addToInfoGroup(sw); + sep->unref(); + mat->unref(); + } +} + +template < typename Result > +void EditModeInformationOverlayCoinConverter::updateNode(const Result & result) { + + if constexpr (Result::visualisationType == VisualisationType::Text ) { + + for(size_t i = 0; i < result.strings.size(); i++) { + SoSwitch *sw = static_cast(infoGroup->getChild(nodeId)); + + if (overlayParameters.visibleInformationChanged) + sw->whichChild = isVisible()?SO_SWITCH_ALL:SO_SWITCH_NONE; + + SoSeparator *sep = static_cast(sw->getChild(0)); + + static_cast(sep->getChild(static_cast(TextNodePosition::TextCoordinates)))->translation.setValue(result.positions[i].x, result.positions[i].y, drawingParameters.zInfo); + + // since the first and last control point of a spline is also treated as knot and thus + // can also have a displayed multiplicity, we must assure the multiplicity is not visibly overwritten + // therefore be output the weight in a second line + // + // This could be made into a more generic form, but it is probably not worth the effort at this time. + if constexpr ( Result::calculationType == CalculationType::BSplinePoleWeight ) + setText<2>(result.strings[i], static_cast(sep->getChild(static_cast(TextNodePosition::TextInformation)))); + else + setText(result.strings[i], static_cast(sep->getChild(static_cast(TextNodePosition::TextInformation)))); + + nodeId++; + } + + } + else if constexpr (Result::visualisationType == VisualisationType::Polygon) { + + SoSwitch *sw = static_cast(infoGroup->getChild(nodeId)); + + if(overlayParameters.visibleInformationChanged) + sw->whichChild = isVisible()?SO_SWITCH_ALL:SO_SWITCH_NONE; + + SoSeparator *sep = static_cast(sw->getChild(0)); + + SoCoordinate3 *polygoncoords = static_cast(sep->getChild(static_cast(PolygonNodePosition::PolygonCoordinates))); + + SoLineSet *polygonlineset = static_cast(sep->getChild(static_cast(PolygonNodePosition::PolygonLineSet))); + + setPolygon(result, polygonlineset, polygoncoords); + + nodeId++; + } +} diff --git a/src/Mod/Sketcher/Gui/EditModeInformationOverlayCoinConverter.h b/src/Mod/Sketcher/Gui/EditModeInformationOverlayCoinConverter.h new file mode 100644 index 0000000000..004a9e3531 --- /dev/null +++ b/src/Mod/Sketcher/Gui/EditModeInformationOverlayCoinConverter.h @@ -0,0 +1,202 @@ +/*************************************************************************** + * Copyright (c) 2021 Abdullah Tahiri * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef SKETCHERGUI_InformationOverlayCoinConverter_H +#define SKETCHERGUI_InformationOverlayCoinConverter_H + +#include + +namespace Base { + template< typename T > + class Vector3; + + class Vector2d; +} + +namespace Part { + class Geometry; +} + +namespace SketcherGui { + struct OverlayParameters; + struct DrawingParameters; + +/** @brief Class for creating the Overlay information layer + * @details + * + * Responsibility: + * To create and update the SoGroup provided as a constructor parameter, + * taking into account the drawing and overlay parameters provided as + * constructor parameters. + * + * Interface: + * A single entry point convert(), performing the following flow: + * + * [Geometry] => Calculate => addUpdateNode + * + * Calculate is responsible for generating information directly usable by Coin (but with standard types that + * would enable portability) in a predetermined internal Node structure format (e.g. StringNode, PolygonNode) + * that can generically be used by the addUpdateNode. + * + * addUpdateNode is responsible for creating or updating the node structure (depending on overlayParameters.rebuildInformationLayer) + * + * Supported: + * Currently it only supports information of Part::Geometry objects and implements calculations only for GeomBSplineCurve. + * + * Caveats: + * - This class relies on the order of creation to perform the update. Any parallel execution that does not deterministically + * maintain the order will result in undefined behaviour. This provides a reasonable tradeoff between complexity and the fact that + * currently the information layer is generally so small that no parallel execution would actually result in a performance gain. + * + */ +class EditModeInformationOverlayCoinConverter { +private: + + enum class CalculationType + { + BSplineDegree, + BSplineControlPolygon, + BSplineCurvatureComb, + BSplineKnotMultiplicity, + BSplinePoleWeight + }; + enum class VisualisationType + { + Text, + Polygon + }; + + using Vector3d = Base::Vector3; + +private: + // A Coin Node follows a VisualisationType, which defines how + // the information is represented, and which member must be + // filled into the struct for representation + // + // A struct containing the information to represent VisualisationType + // must be provided per CalculationType + // + // a struct Node template enables to define the VisualisationType and + // the CalculationType so that uniform treatment can be provided + template< VisualisationType vtype, CalculationType ctype > + struct Node { + static constexpr VisualisationType visualisationType = vtype; + static constexpr CalculationType calculationType = ctype; + }; + + template< CalculationType ctype > + struct NodeText : public Node { + std::vector strings; + std::vector positions; + }; + + template< CalculationType ctype > + struct NodePolygon: public Node { + std::vector coordinates; + std::vector indices; + }; + +private: + // Node Position in the Coin Scenograph for the different types of nodes + enum class TextNodePosition { + TextCoordinates = 0, + TextInformation = 3 + }; + + enum class PolygonNodePosition { + PolygonCoordinates = 1, + PolygonLineSet = 2 + }; + +public: + /** Constructs an InformationOverlayCoinConverter responsible for + * generating (calculating) the information of a full geometry layer + * overlay using the overlay and drawing parameters + * + * @param infogroup: The SoGroup to be populated with the coin nodes + * generated from the calculated information. + * + * @param overlayparameters: Parameters for controlling the overlay + * @param drawingparameters: Parameters for drawing the overlay information + */ + EditModeInformationOverlayCoinConverter( SoGroup * infogroup, + OverlayParameters & overlayparameters, + DrawingParameters & drawingparameters); + + /** + * extracts information from the geometry and converts it into an information overlay in the + * SoGroup provided in the constructor. + * + * @param geometry: the geometry to be processed + */ + void convert(const Part::Geometry * geometry, int geoid); + +private: + template < CalculationType calculation > + void calculate(const Part::Geometry * geometry, [[maybe_unused]] int geoid); + + template + void addUpdateNode(const Result & result); + + template < CalculationType calculation > + bool isVisible(); + + template < typename Result > + void setPolygon(const Result & result, SoLineSet *polygonlineset, SoCoordinate3 *polygoncoords); + + template < int line = 1 > + void setText(const std::string & string, SoText2 * text); + + void addToInfoGroup(SoSwitch * sw); + + template < typename Result > + void clearCalculation(Result & result); + + template < typename Result> + void addNode(const Result & result); + + template < typename Result > + void updateNode(const Result & result); + +private: + SoGroup * infoGroup; + OverlayParameters & overlayParameters; + DrawingParameters & drawingParameters; + + // Calculations + NodeText degree; + NodeText knotMultiplicity; + NodeText poleWeights; + NodePolygon controlPolygon; + NodePolygon curvatureComb; + + // Node Management + int nodeId; +}; + + +} // namespace SketcherGui + + +#endif // SKETCHERGUI_InformationOverlayCoinConverter_H + diff --git a/src/Mod/Sketcher/Gui/ShortcutListener.cpp b/src/Mod/Sketcher/Gui/ShortcutListener.cpp index 9db4a1f91d..1b795e8101 100644 --- a/src/Mod/Sketcher/Gui/ShortcutListener.cpp +++ b/src/Mod/Sketcher/Gui/ShortcutListener.cpp @@ -32,6 +32,13 @@ using namespace SketcherGui; +// ******************** ViewProvider attorney *********************************************// +inline void ViewProviderSketchShortcutListenerAttorney::deleteSelected(ViewProviderSketch & vp) +{ + vp.deleteSelected(); +}; + +// ******************** ShortcutListener *********************************************// ShortcutListener::ShortcutListener(ViewProviderSketch * vp) { pViewProvider = vp; @@ -50,7 +57,7 @@ bool ShortcutListener::eventFilter(QObject *obj, QEvent *event) { switch (kevent->key()) { case Qt::Key_Delete: kevent->accept(); - pViewProvider->deleteSelected(); // this takes a list of objects + ViewProviderSketchShortcutListenerAttorney::deleteSelected(*pViewProvider); return true; default: break; diff --git a/src/Mod/Sketcher/Gui/ShortcutListener.h b/src/Mod/Sketcher/Gui/ShortcutListener.h index 8fa1c31bdb..193bf17475 100644 --- a/src/Mod/Sketcher/Gui/ShortcutListener.h +++ b/src/Mod/Sketcher/Gui/ShortcutListener.h @@ -31,6 +31,13 @@ namespace SketcherGui { class ViewProviderSketch; +class ViewProviderSketchShortcutListenerAttorney { +private: + static inline void deleteSelected(ViewProviderSketch &vp); + + + friend class ShortcutListener; +}; class ShortcutListener: public QObject { diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index da378d8c1d..323cb80347 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -26,35 +26,15 @@ #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 @@ -64,7 +44,6 @@ # include # include # include -# include # include # include # include @@ -73,7 +52,6 @@ # include # include -# include #endif /// Here the FreeCAD includes sorted by Base,App,Gui...... @@ -108,6 +86,8 @@ #include #include #include +#include +#include #include "SoZoomTranslation.h" #include "SoDatumLabel.h" @@ -118,197 +98,184 @@ #include "TaskSketcherValidation.h" #include "Utils.h" #include "ViewProviderSketchGeometryExtension.h" -#include + +#include "EditModeCoinManager.h" FC_LOG_LEVEL_INIT("Sketch",true,true) -// The first is used to point at a SoDatumLabel for some -// constraints, and at a SoMaterial for others... -#define CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL 0 -#define CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION 1 -#define CONSTRAINT_SEPARATOR_INDEX_FIRST_ICON 2 -#define CONSTRAINT_SEPARATOR_INDEX_FIRST_CONSTRAINTID 3 -#define CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION 4 -#define CONSTRAINT_SEPARATOR_INDEX_SECOND_ICON 5 -#define CONSTRAINT_SEPARATOR_INDEX_SECOND_CONSTRAINTID 6 - -// Macros to define information layer node child positions within type -#define GEOINFO_BSPLINE_DEGREE_POS 0 -#define GEOINFO_BSPLINE_DEGREE_TEXT 3 -#define GEOINFO_BSPLINE_POLYGON 1 - using namespace SketcherGui; using namespace Sketcher; namespace bp = boost::placeholders; -SbColor ViewProviderSketch::VertexColor (1.0f,0.149f,0.0f); // #FF2600 -> (255, 38, 0) -SbColor ViewProviderSketch::CurveColor (1.0f,1.0f,1.0f); // #FFFFFF -> (255,255,255) -SbColor ViewProviderSketch::CurveDraftColor (0.0f,0.0f,0.86f); // #0000DC -> ( 0, 0,220) -SbColor ViewProviderSketch::CurveExternalColor (0.8f,0.2f,0.6f); // #CC3399 -> (204, 51,153) -SbColor ViewProviderSketch::CrossColorH (0.8f,0.4f,0.4f); // #CC6666 -> (204,102,102) -SbColor ViewProviderSketch::CrossColorV (0.47f,1.0f,0.51f); // #83FF83 -> (120,255,131) -SbColor ViewProviderSketch::FullyConstrainedColor (0.0f,1.0f,0.0f); // #00FF00 -> ( 0,255, 0) -SbColor ViewProviderSketch::ConstrDimColor (1.0f,0.149f,0.0f); // #FF2600 -> (255, 38, 0) -SbColor ViewProviderSketch::ConstrIcoColor (1.0f,0.149f,0.0f); // #FF2600 -> (255, 38, 0) -SbColor ViewProviderSketch::NonDrivingConstrDimColor (0.0f,0.149f,1.0f); // #0026FF -> ( 0, 38,255) -SbColor ViewProviderSketch::ExprBasedConstrDimColor (1.0f,0.5f,0.149f); // #FF7F26 -> (255, 127,38) -SbColor ViewProviderSketch::InformationColor (0.0f,1.0f,0.0f); // #00FF00 -> ( 0,255, 0) -SbColor ViewProviderSketch::PreselectColor (0.88f,0.88f,0.0f); // #E1E100 -> (225,225, 0) -SbColor ViewProviderSketch::SelectColor (0.11f,0.68f,0.11f); // #1CAD1C -> ( 28,173, 28) -SbColor ViewProviderSketch::PreselectSelectedColor (0.36f,0.48f,0.11f); // #5D7B1C -> ( 93,123, 28) -SbColor ViewProviderSketch::CreateCurveColor (0.8f,0.8f,0.8f); // #CCCCCC -> (204,204,204) -SbColor ViewProviderSketch::DeactivatedConstrDimColor (0.8f,0.8f,0.8f); // #CCCCCC -> (204,204,204) -SbColor ViewProviderSketch::InternalAlignedGeoColor (0.7f,0.7f,0.5f); // #B2B27F -> (178,178,127) -SbColor ViewProviderSketch::FullyConstraintElementColor (0.50f,0.81f,0.62f); // #80D0A0 -> (128,208,160) -SbColor ViewProviderSketch::FullyConstraintConstructionElementColor (0.56f,0.66f,0.99f); // #8FA9FD -> (143,169,253) -SbColor ViewProviderSketch::FullyConstraintInternalAlignmentColor (0.87f,0.87f,0.78f); // #DEDEC8 -> (222,222,200) -SbColor ViewProviderSketch::FullyConstraintConstructionPointColor (1.0f,0.58f,0.50f); // #FF9580 -> (255,149,128) -SbColor ViewProviderSketch::InvalidSketchColor (1.0f,0.42f,0.0f); // #FF6D00 -> (255,109, 0) -// Variables for holding previous click -SbTime ViewProviderSketch::prvClickTime; -SbVec2s ViewProviderSketch::prvClickPos; -SbVec2s ViewProviderSketch::prvCursorPos; -SbVec2s ViewProviderSketch::newCursorPos; +/************** ViewProviderSketch::ParameterObserver *********************/ -//************************************************************************** -// Edit data structure - -/// Data structure while editing the sketch -struct EditData { - EditData(): - sketchHandler(0), - buttonPress(false), - handleEscapeButton(false), - DragPoint(-1), - DragCurve(-1), - PreselectPoint(-1), - PreselectCurve(-1), - PreselectCross(-1), - MarkerSize(7), - coinFontSize(17), // this value is in pixels, 17 pixels - constraintIconSize(15), - pixelScalingFactor(1.0), - blockedPreselection(false), - FullyConstrained(false), - //ActSketch(0), // if you are wondering, it went to SketchObject, accessible via getSolvedSketch() and via SketchObject interface as appropriate - EditRoot(0), - PointsMaterials(0), - CurvesMaterials(0), - RootCrossMaterials(0), - EditCurvesMaterials(0), - EditMarkersMaterials(0), - PointsCoordinate(0), - CurvesCoordinate(0), - RootCrossCoordinate(0), - EditCurvesCoordinate(0), - EditMarkersCoordinate(0), - CurveSet(0), - RootCrossSet(0), - EditCurveSet(0), - EditMarkerSet(0), - PointSet(0), - textX(0), - textPos(0), - constrGroup(0), - infoGroup(0), - pickStyleAxes(0), - PointsDrawStyle(0), - CurvesDrawStyle(0), - RootCrossDrawStyle(0), - EditCurvesDrawStyle(0), - EditMarkersDrawStyle(0), - ConstraintDrawStyle(0), - InformationDrawStyle(0) - {} - - // pointer to the active handler for new sketch objects - DrawSketchHandler *sketchHandler; - bool buttonPress; - bool handleEscapeButton; - - // dragged point - int DragPoint; - // dragged curve - int DragCurve; - // dragged constraints - std::set DragConstraintSet; - - SbColor PreselectOldColor; - int PreselectPoint; - int PreselectCurve; - int PreselectCross; - int MarkerSize; - int coinFontSize; - int constraintIconSize; - double pixelScalingFactor; - std::set PreselectConstraintSet; - bool blockedPreselection; - bool FullyConstrained; - - // container to track our own selected parts - std::set SelPointSet; - std::set SelCurvSet; // also holds cross axes at -1 and -2 - std::set SelConstraintSet; - std::vector CurvIdToGeoId; // conversion of SoLineSet index to GeoId - std::vector PointIdToGeoId; // conversion of SoCoordinate3 index to GeoId - - // helper data structures for the constraint rendering - std::vector vConstrType; - - // For each of the combined constraint icons drawn, also create a vector - // of bounding boxes and associated constraint IDs, to go from the icon's - // pixel coordinates to the relevant constraint IDs. - // - // The outside map goes from a string representation of a set of constraint - // icons (like the one used by the constraint IDs we insert into the Coin - // rendering tree) to a vector of those bounding boxes paired with relevant - // constraint IDs. - std::map combinedConstrBoxes; - - // nodes for the visuals - SoSeparator *EditRoot; - SoMaterial *PointsMaterials; - SoMaterial *CurvesMaterials; - SoMaterial *RootCrossMaterials; - SoMaterial *EditCurvesMaterials; - SoMaterial *EditMarkersMaterials; - SoCoordinate3 *PointsCoordinate; - SoCoordinate3 *CurvesCoordinate; - SoCoordinate3 *RootCrossCoordinate; - SoCoordinate3 *EditCurvesCoordinate; - SoCoordinate3 *EditMarkersCoordinate; - SoLineSet *CurveSet; - SoLineSet *RootCrossSet; - SoLineSet *EditCurveSet; - SoMarkerSet *EditMarkerSet; - SoMarkerSet *PointSet; - - SoText2 *textX; - SoTranslation *textPos; - - SmSwitchboard *constrGroup; - SoGroup *infoGroup; - SoPickStyle *pickStyleAxes; - - SoDrawStyle * PointsDrawStyle; - SoDrawStyle * CurvesDrawStyle; - SoDrawStyle * RootCrossDrawStyle; - SoDrawStyle * EditCurvesDrawStyle; - SoDrawStyle * EditMarkersDrawStyle; - SoDrawStyle * ConstraintDrawStyle; - SoDrawStyle * InformationDrawStyle; -}; - - -// this function is used to simulate cyclic periodic negative geometry indices (for external geometry) -const Part::Geometry* GeoById(const std::vector GeoList, int Id) +ViewProviderSketch::ParameterObserver::ParameterObserver(ViewProviderSketch &client): Client(client) { - if (Id >= 0) - return GeoList[Id]; - else - return GeoList[GeoList.size()+Id]; } +ViewProviderSketch::ParameterObserver::~ParameterObserver() +{ + unsubscribeToParameters(); +} + +void ViewProviderSketch::ParameterObserver::updateBoolProperty(const std::string & string, App::Property * property, bool defaultvalue) +{ + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General"); + + auto boolprop = static_cast(property); + boolprop->setValue(hGrp->GetBool(string.c_str(), defaultvalue)); +} + +void ViewProviderSketch::ParameterObserver::updateColorProperty(const std::string & string, App::Property * property, float r, float g, float b) +{ + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); + + auto colorprop = static_cast(property); + + colorprop->setValue(r,g,b); + + App::Color elementAppColor = colorprop->getValue(); + unsigned long color = (unsigned long)(elementAppColor.getPackedValue()); + color = hGrp->GetUnsigned(string.c_str(), color); + elementAppColor.setPackedValue((uint32_t)color); + colorprop->setValue( elementAppColor); + +} + +void ViewProviderSketch::ParameterObserver::updateGridSize(const std::string & string, App::Property * property) +{ + (void) property; + (void) string; + + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General"); + + Client.GridSize.setValue(Base::Quantity::parse(QString::fromLatin1(hGrp->GetGroup("GridSize")->GetASCII("Hist0", "10.0").c_str())).getValue()); +} + +void ViewProviderSketch::ParameterObserver::updateEscapeKeyBehaviour(const std::string & string, App::Property * property) +{ + (void) property; + (void) string; + + ParameterGrp::handle hSketch = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); + Client.viewProviderParameters.handleEscapeButton = !hSketch->GetBool("LeaveSketchWithEscape", true); +} + +void ViewProviderSketch::ParameterObserver::updateAutoRecompute(const std::string & string, App::Property * property) +{ + (void) property; + (void) string; + + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); + Client.viewProviderParameters.autoRecompute = hGrp->GetBool("AutoRecompute",false); +} + +void ViewProviderSketch::ParameterObserver::updateRecalculateInitialSolutionWhileDragging(const std::string & string, App::Property * property) +{ + (void) property; + (void) string; + + ParameterGrp::handle hGrp2 = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); + + Client.viewProviderParameters.recalculateInitialSolutionWhileDragging = hGrp2->GetBool("RecalculateInitialSolutionWhileDragging",true); +} + +void ViewProviderSketch::ParameterObserver::subscribeToParameters() +{ + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General"); + hGrp->Attach(this); + + ParameterGrp::handle hGrpskg = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General"); + hGrpskg->Attach(this); + + ParameterGrp::handle hGrp2 = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); + hGrp2->Attach(this); +} + +void ViewProviderSketch::ParameterObserver::unsubscribeToParameters() +{ + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General"); + hGrp->Detach(this); + + ParameterGrp::handle hGrpskg = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General"); + hGrpskg->Detach(this); + + ParameterGrp::handle hGrp2 = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); + hGrp2->Detach(this); +} + +void ViewProviderSketch::ParameterObserver::initParameters() +{ + // once initialize the map with the properties + + parameterMap = { + {"HideDependent", + {[this](const std::string & string, App::Property * property){ updateBoolProperty(string, property, true);}, &Client.HideDependent }}, + {"ShowLinks", + {[this](const std::string & string, App::Property * property){ updateBoolProperty(string, property, true);}, &Client.ShowLinks }}, + {"ShowSupport", + {[this](const std::string & string, App::Property * property){ updateBoolProperty(string, property, true);}, &Client.ShowSupport }}, + {"RestoreCamera", + {[this](const std::string & string, App::Property * property){ updateBoolProperty(string, property, true);}, &Client.RestoreCamera }}, + {"ForceOrtho", + {[this](const std::string & string, App::Property * property){ updateBoolProperty(string, property, false);}, &Client.ForceOrtho }}, + {"SectionView", + {[this](const std::string & string, App::Property * property){ updateBoolProperty(string, property, false);}, &Client.SectionView }}, + {"ShowGrid", + {[this](const std::string & string, App::Property * property){ updateBoolProperty(string, property, false);}, &Client.ShowGrid }}, + {"GridSnap", + {[this](const std::string & string, App::Property * property){ updateBoolProperty(string, property, false);}, &Client.GridSnap }}, + {"AutoConstraints", + {[this](const std::string & string, App::Property * property){ updateBoolProperty(string, property, true);}, &Client.Autoconstraints }}, + {"AvoidRedundantAutoconstraints", + {[this](const std::string & string, App::Property * property){ updateBoolProperty(string, property, true);}, &Client.AvoidRedundant }}, + {"GridSize", + {[this](const std::string & string, App::Property * property){ updateGridSize(string, property);}, &Client.GridSize }}, + {"SketchEdgeColor", + {[this](const std::string & string, App::Property * property){ updateColorProperty(string, property, 1.f, 1.f, 1.f);}, &Client.LineColor }}, + {"SketchVertexColor", + {[this](const std::string & string, App::Property * property){ updateColorProperty(string, property, 1.f, 1.f, 1.f);}, &Client.PointColor }}, + {"updateEscapeKeyBehaviour", + {[this](const std::string & string, App::Property * property){ updateEscapeKeyBehaviour(string, property);}, nullptr }}, + {"AutoRecompute", + {[this](const std::string & string, App::Property * property){ updateAutoRecompute(string, property);}, nullptr }}, + {"RecalculateInitialSolutionWhileDragging", + {[this](const std::string & string, App::Property * property){ updateRecalculateInitialSolutionWhileDragging(string, property);}, nullptr }}, + }; + + for( auto & val : parameterMap){ + auto string = val.first; + auto update = std::get<0>(val.second); + auto property = std::get<1>(val.second); + + update(string, property); + } + +} + +void ViewProviderSketch::ParameterObserver::OnChange(Base::Subject &rCaller, const char * sReason) +{ + (void) rCaller; + + auto key = parameterMap.find(sReason); + if( key != parameterMap.end()) { + auto string = key->first; + auto update = std::get<0>(key->second); + auto property = std::get<1>(key->second); + + update(string, property); + } + +} + +/*************************** ViewProviderSketch **************************/ + +// Struct for holding previous click information +SbTime ViewProviderSketch::DoubleClick::prvClickTime; +SbVec2s ViewProviderSketch::DoubleClick::prvClickPos; //used by double-click-detector +SbVec2s ViewProviderSketch::DoubleClick::prvCursorPos; +SbVec2s ViewProviderSketch::DoubleClick::newCursorPos; + //************************************************************************** // Construction/Destruction @@ -319,12 +286,11 @@ PROPERTY_SOURCE_WITH_EXTENSIONS(SketcherGui::ViewProviderSketch, PartGui::ViewPr ViewProviderSketch::ViewProviderSketch() : SelectionObserver(false), - edit(0), Mode(STATUS_NONE), - visibleInformationChanged(true), - combrepscalehyst(0), - isShownVirtualSpace(false), - listener(0) + listener(0), + editCoinManager(nullptr), + pObserver(std::make_unique(*this)), + sketchHandler(nullptr) { PartGui::ViewProviderAttachExtension::initExtension(this); @@ -339,79 +305,24 @@ ViewProviderSketch::ViewProviderSketch() ADD_PROPERTY_TYPE(SectionView,(false),"Visibility automation",(App::PropertyType)(App::Prop_None),"If true, only objects (or part of) located behind the sketch plane are visible."); ADD_PROPERTY_TYPE(EditingWorkbench,("SketcherWorkbench"),"Visibility automation",(App::PropertyType)(App::Prop_None),"Name of the workbench to activate when editing this sketch."); - {//visibility automation: update defaults to follow preferences - ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General"); - this->HideDependent.setValue(hGrp->GetBool("HideDependent", true)); - this->ShowLinks.setValue(hGrp->GetBool("ShowLinks", true)); - this->ShowSupport.setValue(hGrp->GetBool("ShowSupport", true)); - this->RestoreCamera.setValue(hGrp->GetBool("RestoreCamera", true)); - this->ForceOrtho.setValue(hGrp->GetBool("ForceOrtho", false)); - this->SectionView.setValue(hGrp->GetBool("SectionView", false)); - - // well it is not visibility automation but a good place nevertheless - this->ShowGrid.setValue(hGrp->GetBool("ShowGrid", false)); - this->GridSize.setValue(Base::Quantity::parse(QString::fromLatin1(hGrp->GetGroup("GridSize")->GetASCII("Hist0", "10.0").c_str())).getValue()); - this->GridSnap.setValue(hGrp->GetBool("GridSnap", false)); - this->Autoconstraints.setValue(hGrp->GetBool("AutoConstraints", true)); - this->AvoidRedundant.setValue(hGrp->GetBool("AvoidRedundantAutoconstraints", true)); - this->GridAutoSize.setValue(false); //Grid size is managed by this class - } - - sPixmap = "Sketcher_Sketch"; - LineColor.setValue(1,1,1); - PointColor.setValue(1,1,1); + // Default values that will be overriden by preferences (if existing) PointSize.setValue(4); - zCross=0.001f; - zEdit=0.001f; - zInfo=0.004f; - zLowLines=0.005f; - //zLines=0.005f; // ZLines removed in favour of 3 height groups intended for NormalLines, ConstructionLines, ExternalLines - zMidLines=0.006f; - zHighLines=0.007f; // Lines that are somehow selected to be in the high position (higher than other line categories) - zHighLine=0.008f; // highlighted line (of any group) - zConstr=0.009f; // constraint not construction - //zPoints=0.010f; - zLowPoints = 0.010f; - zHighPoints = 0.011f; - zHighlight=0.012f; - zText=0.012f; + // visibility automation and other parameters: update parameter and property defaults to follow preferences + pObserver->initParameters(); + pObserver->subscribeToParameters(); + this->GridAutoSize.setValue(false); //Grid size is managed by this class - xInit=0; - yInit=0; - relative=false; - - unsigned long color; - ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); - - // edge color - App::Color edgeColor = LineColor.getValue(); - color = (unsigned long)(edgeColor.getPackedValue()); - color = hGrp->GetUnsigned("SketchEdgeColor", color); - edgeColor.setPackedValue((uint32_t)color); - LineColor.setValue(edgeColor); - - // vertex color - App::Color vertexColor = PointColor.getValue(); - color = (unsigned long)(vertexColor.getPackedValue()); - color = hGrp->GetUnsigned("SketchVertexColor", color); - vertexColor.setPackedValue((uint32_t)color); - PointColor.setValue(vertexColor); + sPixmap = "Sketcher_Sketch"; //rubberband selection - rubberband = new Gui::Rubberband(); + rubberband = std::make_unique(); - // Status message states: - - - subscribeToParameters(); } ViewProviderSketch::~ViewProviderSketch() { - delete rubberband; - unsubscribeToParameters(); } void ViewProviderSketch::slotUndoDocument(const Gui::Document& /*doc*/) @@ -443,15 +354,17 @@ void ViewProviderSketch::forceUpdateData() } } -// handler management *************************************************************** +/***************************** handler management ************************************/ + void ViewProviderSketch::activateHandler(DrawSketchHandler *newHandler) { - assert(edit); - assert(edit->sketchHandler == 0); - edit->sketchHandler = newHandler; + assert(editCoinManager); + assert(sketchHandler == nullptr); + + sketchHandler = std::unique_ptr(newHandler); Mode = STATUS_SKETCH_UseHandler; - edit->sketchHandler->sketchgui = this; - edit->sketchHandler->activated(this); + sketchHandler->sketchgui = this; + sketchHandler->activated(this); // make sure receiver has focus so immediately pressing Escape will be handled by // ViewProviderSketch::keyPressed() and dismiss the active handler, and not the entire @@ -462,17 +375,17 @@ void ViewProviderSketch::activateHandler(DrawSketchHandler *newHandler) void ViewProviderSketch::deactivateHandler() { - assert(edit); - if(edit->sketchHandler != 0){ + assert(isInEditMode()); + if(sketchHandler){ std::vector editCurve; editCurve.clear(); drawEdit(editCurve); // erase any line resetPositionText(); - edit->sketchHandler->deactivated(this); - edit->sketchHandler->unsetCursor(); - delete(edit->sketchHandler); + sketchHandler->deactivated(this); + sketchHandler->unsetCursor(); + sketchHandler = nullptr; } - edit->sketchHandler = 0; + Mode = STATUS_NONE; } @@ -493,11 +406,8 @@ void ViewProviderSketch::purgeHandler(void) void ViewProviderSketch::setAxisPickStyle(bool on) { - assert(edit); - if (on) - edit->pickStyleAxes->style = SoPickStyle::SHAPE; - else - edit->pickStyleAxes->style = SoPickStyle::UNPICKABLE; + assert(isInEditMode()); + editCoinManager->setAxisPickStyle(on); } // ********************************************************************************** @@ -509,56 +419,56 @@ bool ViewProviderSketch::keyPressed(bool pressed, int key) case SoKeyboardEvent::ESCAPE: { // make the handler quit but not the edit mode - if (edit && edit->sketchHandler) { + if (isInEditMode() && sketchHandler) { if (!pressed) - edit->sketchHandler->quit(); + sketchHandler->quit(); return true; } - if (edit && (edit->DragConstraintSet.empty() == false)) { + if (isInEditMode() && (drag.DragConstraintSet.empty() == false)) { if (!pressed) { - edit->DragConstraintSet.clear(); + drag.DragConstraintSet.clear(); } return true; } - if (edit && edit->DragCurve >= 0) { + if (isInEditMode() && drag.isDragCurveValid()) { if (!pressed) { - getSketchObject()->movePoint(edit->DragCurve, Sketcher::PointPos::none, Base::Vector3d(0,0,0), true); - edit->DragCurve = -1; + getSketchObject()->movePoint(drag.DragCurve, Sketcher::PointPos::none, Base::Vector3d(0,0,0), true); + drag.DragCurve = Drag::InvalidCurve; resetPositionText(); Mode = STATUS_NONE; } return true; } - if (edit && edit->DragPoint >= 0) { + if (isInEditMode() && drag.isDragPointValid()) { if (!pressed) { int GeoId; Sketcher::PointPos PosId; - getSketchObject()->getGeoVertexIndex(edit->DragPoint, GeoId, PosId); + getSketchObject()->getGeoVertexIndex(drag.DragPoint, GeoId, PosId); getSketchObject()->movePoint(GeoId, PosId, Base::Vector3d(0,0,0), true); - edit->DragPoint = -1; + drag.DragPoint = Drag::InvalidPoint; resetPositionText(); Mode = STATUS_NONE; } return true; } - if (edit) { + if (isInEditMode()) { // #0001479: 'Escape' key dismissing dialog cancels Sketch editing // If we receive a button release event but not a press event before // then ignore this one. - if (!pressed && !edit->buttonPress) + if (!pressed && !viewProviderParameters.buttonPress) return true; - edit->buttonPress = pressed; + viewProviderParameters.buttonPress = pressed; // More control over Sketcher edit mode Esc key behavior // https://forum.freecadweb.org/viewtopic.php?f=3&t=42207 - return edit->handleEscapeButton; + return viewProviderParameters.handleEscapeButton; } return false; } default: { - if (edit && edit->sketchHandler) - edit->sketchHandler->registerPressedKey(pressed,key); + if (isInEditMode() && sketchHandler) + sketchHandler->registerPressedKey(pressed,key); } } @@ -629,7 +539,7 @@ Base::Placement ViewProviderSketch::getEditingPlacement() const { return Base::Placement(doc->getEditingTransform()); } -void ViewProviderSketch::getCoordsOnSketchPlane(double &u, double &v,const SbVec3f &point, const SbVec3f &normal) +void ViewProviderSketch::getCoordsOnSketchPlane(const SbVec3f &point, const SbVec3f &normal, double &u, double &v) const { // Plane form Base::Vector3d R0(0,0,0),RN(0,0,1),RX(1,0,0),RY(0,1,0); @@ -660,7 +570,7 @@ void ViewProviderSketch::getCoordsOnSketchPlane(double &u, double &v,const SbVec bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVec2s &cursorPos, const Gui::View3DInventorViewer *viewer) { - assert(edit); + assert(isInEditMode()); // Calculate 3d point to the mouse position SbLine line; @@ -684,7 +594,7 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe } try { - getCoordsOnSketchPlane(x,y,pos,normal); + getCoordsOnSketchPlane(pos,normal,x,y); snapToGrid(x, y); } catch (const Base::DivisionByZeroError&) { @@ -698,19 +608,19 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe switch (Mode) { case STATUS_NONE:{ bool done=false; - if (edit->PreselectPoint != -1) { + if (preselection.isPreselectPointValid()) { //Base::Console().Log("start dragging, point:%d\n",this->DragPoint); Mode = STATUS_SELECT_Point; done = true; - } else if (edit->PreselectCurve != -1) { + } else if (preselection.isPreselectCurveValid()) { //Base::Console().Log("start dragging, point:%d\n",this->DragPoint); Mode = STATUS_SELECT_Edge; done = true; - } else if (edit->PreselectCross != -1) { + } else if (preselection.isCrossPreselected()) { //Base::Console().Log("start dragging, point:%d\n",this->DragPoint); Mode = STATUS_SELECT_Cross; done = true; - } else if (edit->PreselectConstraintSet.empty() != true) { + } else if (preselection.PreselectConstraintSet.empty() != true) { //Base::Console().Log("start dragging, point:%d\n",this->DragPoint); Mode = STATUS_SELECT_Constraint; done = true; @@ -720,21 +630,21 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe float dci = (float) QApplication::doubleClickInterval()/1000.0f; if (done && - SbVec2f(cursorPos - prvClickPos).length() < dblClickRadius && - (SbTime::getTimeOfDay() - prvClickTime).getValue() < dci) { + SbVec2f(cursorPos - DoubleClick::prvClickPos).length() < dblClickRadius && + (SbTime::getTimeOfDay() - DoubleClick::prvClickTime).getValue() < dci) { // Double Click Event Occurred editDoubleClicked(); // Reset Double Click Static Variables - prvClickTime = SbTime(); - prvClickPos = SbVec2s(-16000,-16000); //certainly far away from any clickable place, to avoid re-trigger of double-click if next click happens fast. + DoubleClick::prvClickTime = SbTime(); + DoubleClick::prvClickPos = SbVec2s(-16000,-16000); //certainly far away from any clickable place, to avoid re-trigger of double-click if next click happens fast. Mode = STATUS_NONE; } else { - prvClickTime = SbTime::getTimeOfDay(); - prvClickPos = cursorPos; - prvCursorPos = cursorPos; - newCursorPos = cursorPos; + DoubleClick::prvClickTime = SbTime::getTimeOfDay(); + DoubleClick::prvClickPos = cursorPos; + DoubleClick::prvCursorPos = cursorPos; + DoubleClick::newCursorPos = cursorPos; if (!done) Mode = STATUS_SKETCH_StartRubberBand; } @@ -742,7 +652,7 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe return done; } case STATUS_SKETCH_UseHandler: - return edit->sketchHandler->pressButton(Base::Vector2d(x,y)); + return sketchHandler->pressButton(Base::Vector2d(x,y)); default: return false; } @@ -754,20 +664,13 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe //Base::Console().Log("Select Point:%d\n",this->DragPoint); // Do selection std::stringstream ss; - ss << "Vertex" << edit->PreselectPoint + 1; + ss << "Vertex" << preselection.getPreselectionVertexIndex(); -#define SEL_PARAMS editDocName.c_str(),editObjName.c_str(),\ - (editSubName+ss.str()).c_str() - if (Gui::Selection().isSelected(SEL_PARAMS) ) { - Gui::Selection().rmvSelection(SEL_PARAMS); + if (isSelected(ss.str())) { + rmvSelection(ss.str()); } else { - Gui::Selection().addSelection2(SEL_PARAMS - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); - this->edit->DragPoint = -1; - this->edit->DragCurve = -1; - this->edit->DragConstraintSet.clear(); + addSelection2(ss.str(), pp->getPoint()[0], pp->getPoint()[1],pp->getPoint()[2]); + drag.resetIds(); } } Mode = STATUS_NONE; @@ -776,23 +679,18 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe if (pp) { //Base::Console().Log("Select Point:%d\n",this->DragPoint); std::stringstream ss; - if (edit->PreselectCurve >= 0) - ss << "Edge" << edit->PreselectCurve + 1; + if (preselection.isEdge()) + ss << "Edge" << preselection.getPreselectionEdgeIndex(); else // external geometry - ss << "ExternalEdge" << -edit->PreselectCurve - 2; + ss << "ExternalEdge" << preselection.getPreselectionExternalEdgeIndex(); // If edge already selected move from selection - if (Gui::Selection().isSelected(SEL_PARAMS) ) { - Gui::Selection().rmvSelection(SEL_PARAMS); + if (isSelected(ss.str()) ) { + rmvSelection(ss.str()); } else { // Add edge to the selection - Gui::Selection().addSelection2(SEL_PARAMS - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); - this->edit->DragPoint = -1; - this->edit->DragCurve = -1; - this->edit->DragConstraintSet.clear(); + addSelection2(ss.str(), pp->getPoint()[0], pp->getPoint()[1], pp->getPoint()[2]); + drag.resetIds(); } } Mode = STATUS_NONE; @@ -801,62 +699,56 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe if (pp) { //Base::Console().Log("Select Point:%d\n",this->DragPoint); std::stringstream ss; - switch(edit->PreselectCross){ - case 0: ss << "RootPoint" ; break; - case 1: ss << "H_Axis" ; break; - case 2: ss << "V_Axis" ; break; + switch(preselection.PreselectCross){ + case Preselection::Axes::RootPoint: ss << "RootPoint" ; break; + case Preselection::Axes::HorizontalAxis: ss << "H_Axis" ; break; + case Preselection::Axes::VerticalAxis: ss << "V_Axis" ; break; + default: break; } // If cross already selected move from selection - if (Gui::Selection().isSelected(SEL_PARAMS) ) { - Gui::Selection().rmvSelection(SEL_PARAMS); + if (isSelected(ss.str()) ) { + rmvSelection(ss.str()); } else { // Add cross to the selection - Gui::Selection().addSelection2(SEL_PARAMS - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); - this->edit->DragPoint = -1; - this->edit->DragCurve = -1; - this->edit->DragConstraintSet.clear(); + addSelection2(ss.str(), pp->getPoint()[0], pp->getPoint()[1], pp->getPoint()[2]); + drag.resetIds(); } } Mode = STATUS_NONE; return true; case STATUS_SELECT_Constraint: if (pp) { - auto sels = edit->PreselectConstraintSet; + auto sels = preselection.PreselectConstraintSet; for(int id : sels) { std::stringstream ss; ss << Sketcher::PropertyConstraintList::getConstraintName(id); // If the constraint already selected remove - if (Gui::Selection().isSelected(SEL_PARAMS) ) { - Gui::Selection().rmvSelection(SEL_PARAMS); + if (isSelected(ss.str()) ) { + rmvSelection(ss.str()); } else { // Add constraint to current selection - Gui::Selection().addSelection2(SEL_PARAMS - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); - this->edit->DragPoint = -1; - this->edit->DragCurve = -1; - this->edit->DragConstraintSet.clear(); + addSelection2(ss.str(), pp->getPoint()[0], pp->getPoint()[1], pp->getPoint()[2]); + drag.resetIds(); } } } Mode = STATUS_NONE; return true; case STATUS_SKETCH_DragPoint: - if (edit->DragPoint != -1) { + if (drag.isDragPointValid()) { int GeoId; Sketcher::PointPos PosId; - getSketchObject()->getGeoVertexIndex(edit->DragPoint, GeoId, PosId); + + getSketchObject()->getGeoVertexIndex(drag.DragPoint, GeoId, PosId); + if (GeoId != Sketcher::GeoEnum::GeoUndef && PosId != Sketcher::PointPos::none) { getDocument()->openCommand(QT_TRANSLATE_NOOP("Command", "Drag Point")); try { Gui::cmdAppObjectArgs(getObject(), "movePoint(%i,%i,App.Vector(%f,%f,0),%i)" - ,GeoId, static_cast(PosId), x-xInit, y-yInit, 0); + ,GeoId, static_cast(PosId), x-drag.xInit, y-drag.yInit, 0); + getDocument()->commitCommand(); tryAutoRecomputeIfNotSolve(getSketchObject()); @@ -866,16 +758,16 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe Base::Console().Error("Drag point: %s\n", e.what()); } } - setPreselectPoint(edit->DragPoint); - edit->DragPoint = -1; + setPreselectPoint(drag.DragPoint); + drag.DragPoint = Drag::InvalidPoint; //updateColor(); } resetPositionText(); Mode = STATUS_NONE; return true; case STATUS_SKETCH_DragCurve: - if (edit->DragCurve != -1) { - const Part::Geometry *geo = getSketchObject()->getGeometry(edit->DragCurve); + if (drag.isDragCurveValid()) { + const Part::Geometry *geo = getSketchObject()->getGeometry(drag.DragCurve); if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId() || geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId() || geo->getTypeId() == Part::GeomCircle::getClassTypeId() || @@ -886,10 +778,10 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe geo->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()) { getDocument()->openCommand(QT_TRANSLATE_NOOP("Command", "Drag Curve")); - auto geo = getSketchObject()->getGeometry(edit->DragCurve); + auto geo = getSketchObject()->getGeometry(drag.DragCurve); auto gf = GeometryFacade::getFacade(geo); - Base::Vector3d vec(x-xInit,y-yInit,0); + Base::Vector3d vec(x-drag.xInit,y-drag.yInit,0); // BSpline weights have a radius corresponding to the weight value // However, in order for them proportional to the B-Spline size, @@ -916,7 +808,8 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe try { Gui::cmdAppObjectArgs(getObject(), "movePoint(%i,%i,App.Vector(%f,%f,0),%i)" - ,edit->DragCurve, static_cast(Sketcher::PointPos::none), vec.x, vec.y, relative ? 1 : 0); + ,drag.DragCurve, static_cast(Sketcher::PointPos::none), vec.x, vec.y, drag.relative ? 1 : 0); + getDocument()->commitCommand(); tryAutoRecomputeIfNotSolve(getSketchObject()); @@ -926,23 +819,23 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe Base::Console().Error("Drag curve: %s\n", e.what()); } } - edit->PreselectCurve = edit->DragCurve; - edit->DragCurve = -1; + preselection.PreselectCurve = drag.DragCurve; + drag.DragCurve = Drag::InvalidCurve; //updateColor(); } resetPositionText(); Mode = STATUS_NONE; return true; case STATUS_SKETCH_DragConstraint: - if (edit->DragConstraintSet.empty() == false) { + if (drag.DragConstraintSet.empty() == false) { getDocument()->openCommand(QT_TRANSLATE_NOOP("Command", "Drag Constraint")); - auto idset = edit->DragConstraintSet; + auto idset = drag.DragConstraintSet; for(int id : idset) { moveConstraint(id, Base::Vector2d(x, y)); //updateColor(); } - edit->PreselectConstraintSet = edit->DragConstraintSet; - edit->DragConstraintSet.clear(); + preselection.PreselectConstraintSet = drag.DragConstraintSet; + drag.DragConstraintSet.clear(); getDocument()->commitCommand(); } Mode = STATUS_NONE; @@ -952,7 +845,7 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe Gui::Selection().clearSelection(); return true; case STATUS_SKETCH_UseRubberBand: - doBoxSelection(prvCursorPos, cursorPos, viewer); + doBoxSelection(DoubleClick::prvCursorPos, cursorPos, viewer); rubberband->setWorking(false); // a redraw is required in order to clear the rubberband @@ -961,7 +854,7 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe Mode = STATUS_NONE; return true; case STATUS_SKETCH_UseHandler: { - return edit->sketchHandler->releaseButton(Base::Vector2d(x,y)); + return sketchHandler->releaseButton(Base::Vector2d(x,y)); } case STATUS_NONE: default: @@ -975,16 +868,16 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe switch (Mode) { case STATUS_SKETCH_UseHandler: // make the handler quit - edit->sketchHandler->quit(); + sketchHandler->quit(); return true; case STATUS_NONE: { // A right click shouldn't change the Edit Mode - if (edit->PreselectPoint != -1) { + if (preselection.isPreselectPointValid()) { return true; - } else if (edit->PreselectCurve != -1) { + } else if (preselection.isPreselectCurveValid()) { return true; - } else if (edit->PreselectConstraintSet.empty() != true) { + } else if (preselection.PreselectConstraintSet.empty() != true) { return true; } else { Gui::MenuItem geom; @@ -1049,7 +942,7 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe if (it->size() > 4 && it->substr(0,4) == "Edge") { // Get the index of the object selected int GeoId = std::atoi(it->substr(4,4000).c_str()) - 1; - if (edit->PreselectCurve == GeoId) + if (preselection.PreselectCurve == GeoId) rightClickOnSelectedLine = true; } else { // The selection is not exclusively edges @@ -1089,20 +982,20 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe void ViewProviderSketch::editDoubleClicked(void) { - if (edit->PreselectPoint != -1) { - Base::Console().Log("double click point:%d\n",edit->PreselectPoint); + if (preselection.isPreselectPointValid()) { + Base::Console().Log("double click point:%d\n",preselection.PreselectPoint); } - else if (edit->PreselectCurve != -1) { - Base::Console().Log("double click edge:%d\n",edit->PreselectCurve); + else if (preselection.isPreselectCurveValid()) { + Base::Console().Log("double click edge:%d\n",preselection.PreselectCurve); } - else if (edit->PreselectCross != -1) { - Base::Console().Log("double click cross:%d\n",edit->PreselectCross); + else if (preselection.isCrossPreselected()) { + Base::Console().Log("double click cross:%d\n",preselection.PreselectCross); } - else if (edit->PreselectConstraintSet.empty() != true) { + else if (preselection.PreselectConstraintSet.empty() != true) { // Find the constraint const std::vector &constrlist = getSketchObject()->Constraints.getValues(); - auto sels = edit->PreselectConstraintSet; + auto sels = preselection.PreselectConstraintSet; for(int id : sels) { Constraint *Constr = constrlist[id]; @@ -1122,7 +1015,7 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor // maximum radius for mouse moves when selecting a geometry before switching to drag mode const int dragIgnoredDistance = 3; - if (!edit) + if (!isInEditMode()) return false; // ignore small moves after selection @@ -1132,7 +1025,7 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor case STATUS_SELECT_Constraint: case STATUS_SKETCH_StartRubberBand: short dx, dy; - (cursorPos - prvCursorPos).getValue(dx, dy); + (cursorPos - DoubleClick::prvCursorPos).getValue(dx, dy); if(std::abs(dx) < dragIgnoredDistance && std::abs(dy) < dragIgnoredDistance) return false; default: @@ -1145,7 +1038,7 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor double x,y; try { - getCoordsOnSketchPlane(x,y,line.getPosition(),line.getDirection()); + getCoordsOnSketchPlane(line.getPosition(),line.getDirection(),x,y); snapToGrid(x, y); } catch (const Base::DivisionByZeroError&) { @@ -1161,46 +1054,44 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor Mode != STATUS_SKETCH_DragConstraint && Mode != STATUS_SKETCH_UseRubberBand) { - boost::scoped_ptr pp(this->getPointOnRay(cursorPos, viewer)); - preselectChanged = detectPreselection(pp.get(), viewer, cursorPos); + std::unique_ptr Point(this->getPointOnRay(cursorPos, viewer)); + + preselectChanged = detectAndShowPreselection(Point.get(), cursorPos); } switch (Mode) { case STATUS_NONE: if (preselectChanged) { - this->drawConstraintIcons(); + editCoinManager->drawConstraintIcons(); this->updateColor(); return true; } return false; case STATUS_SELECT_Point: if (!getSolvedSketch().hasConflicts() && - edit->PreselectPoint != -1 && edit->DragPoint != edit->PreselectPoint) { + preselection.isPreselectPointValid() && drag.DragPoint != preselection.PreselectPoint) { Mode = STATUS_SKETCH_DragPoint; - edit->DragPoint = edit->PreselectPoint; + drag.DragPoint = preselection.PreselectPoint; int GeoId; Sketcher::PointPos PosId; - getSketchObject()->getGeoVertexIndex(edit->DragPoint, GeoId, PosId); + + getSketchObject()->getGeoVertexIndex(drag.DragPoint, GeoId, PosId); + if (GeoId != Sketcher::GeoEnum::GeoUndef && PosId != Sketcher::PointPos::none) { getSketchObject()->initTemporaryMove(GeoId, PosId, false); - relative = false; - xInit = 0; - yInit = 0; + drag.resetVector(); } } else { Mode = STATUS_NONE; } resetPreselectPoint(); - edit->PreselectCurve = -1; - edit->PreselectCross = -1; - edit->PreselectConstraintSet.clear(); return true; case STATUS_SELECT_Edge: if (!getSolvedSketch().hasConflicts() && - edit->PreselectCurve != -1 && edit->DragCurve != edit->PreselectCurve) { + preselection.isPreselectCurveValid() && drag.DragCurve != preselection.PreselectCurve) { Mode = STATUS_SKETCH_DragCurve; - edit->DragCurve = edit->PreselectCurve; - const Part::Geometry *geo = getSketchObject()->getGeometry(edit->DragCurve); + drag.DragCurve = preselection.PreselectCurve; + const Part::Geometry *geo = getSketchObject()->getGeometry(drag.DragCurve); // BSpline Control points are edge draggable only if their radius is movable // This is because dragging gives unwanted cosmetic results due to the scale ratio. @@ -1218,7 +1109,7 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor // The B-Spline is constrained to be non-rational (equal weights), moving produces a bad effect // because OCCT will normalize the values of the weights. - auto grp = getSolvedSketch().getDependencyGroup(edit->DragCurve, Sketcher::PointPos::none); + auto grp = getSolvedSketch().getDependencyGroup(drag.DragCurve, Sketcher::PointPos::none); int bsplinegeoid = -1; @@ -1227,7 +1118,7 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor for( auto c : getSketchObject()->Constraints.getValues()) { if( c->Type == Sketcher::InternalAlignment && c->AlignmentType == BSplineControlPoint && - c->First == edit->DragCurve ) { + c->First == drag.DragCurve ) { bsplinegeoid = c->Second; break; @@ -1268,46 +1159,38 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId() || geo->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()) { - relative = true; - //xInit = x; - //yInit = y; + drag.relative = true; + // Since the cursor moved from where it was clicked, and this is a relative move, // calculate the click position and use it as initial point. SbLine line2; - getProjectingLine(prvCursorPos, viewer, line2); - getCoordsOnSketchPlane(xInit,yInit,line2.getPosition(),line2.getDirection()); - snapToGrid(xInit, yInit); + getProjectingLine(DoubleClick::prvCursorPos, viewer, line2); + getCoordsOnSketchPlane(line2.getPosition(),line2.getDirection(),drag.xInit,drag.yInit); + snapToGrid(drag.xInit, drag.yInit); } else { - relative = false; - xInit = 0; - yInit = 0; + drag.resetVector(); } - getSketchObject()->initTemporaryMove(edit->DragCurve, Sketcher::PointPos::none, false); + getSketchObject()->initTemporaryMove(drag.DragCurve, Sketcher::PointPos::none, false); } else { Mode = STATUS_NONE; } resetPreselectPoint(); - edit->PreselectCurve = -1; - edit->PreselectCross = -1; - edit->PreselectConstraintSet.clear(); return true; case STATUS_SELECT_Constraint: Mode = STATUS_SKETCH_DragConstraint; - edit->DragConstraintSet = edit->PreselectConstraintSet; + drag.DragConstraintSet = preselection.PreselectConstraintSet; resetPreselectPoint(); - edit->PreselectCurve = -1; - edit->PreselectCross = -1; - edit->PreselectConstraintSet.clear(); return true; case STATUS_SKETCH_DragPoint: - if (edit->DragPoint != -1) { + if (drag.isDragPointValid()) { //Base::Console().Log("Drag Point:%d\n",edit->DragPoint); int GeoId; Sketcher::PointPos PosId; - getSketchObject()->getGeoVertexIndex(edit->DragPoint, GeoId, PosId); + getSketchObject()->getGeoVertexIndex(drag.DragPoint, GeoId, PosId); Base::Vector3d vec(x,y,0); + if (GeoId != Sketcher::GeoEnum::GeoUndef && PosId != Sketcher::PointPos::none) { if (getSketchObject()->moveTemporaryPoint(GeoId, PosId, vec, false) == 0) { setPositionText(Base::Vector2d(x,y)); @@ -1317,11 +1200,11 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor } return true; case STATUS_SKETCH_DragCurve: - if (edit->DragCurve != -1) { - auto geo = getSketchObject()->getGeometry(edit->DragCurve); + if (drag.isDragCurveValid()) { + auto geo = getSketchObject()->getGeometry(drag.DragCurve); auto gf = GeometryFacade::getFacade(geo); - Base::Vector3d vec(x-xInit,y-yInit,0); + Base::Vector3d vec(x-drag.xInit,y-drag.yInit,0); // BSpline weights have a radius corresponding to the weight value // However, in order for them proportional to the B-Spline size, @@ -1346,23 +1229,23 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor vec = center + dir / scalefactor; } - if (getSketchObject()->moveTemporaryPoint(edit->DragCurve, Sketcher::PointPos::none, vec, relative) == 0) { + if (getSketchObject()->moveTemporaryPoint(drag.DragCurve, Sketcher::PointPos::none, vec, drag.relative) == 0) { setPositionText(Base::Vector2d(x,y)); draw(true,false); } } return true; case STATUS_SKETCH_DragConstraint: - if (edit->DragConstraintSet.empty() == false) { - auto idset = edit->DragConstraintSet; + if (drag.DragConstraintSet.empty() == false) { + auto idset = drag.DragConstraintSet; for(int id : idset) moveConstraint(id, Base::Vector2d(x,y)); } return true; case STATUS_SKETCH_UseHandler: - edit->sketchHandler->mouseMove(Base::Vector2d(x,y)); + sketchHandler->mouseMove(Base::Vector2d(x,y)); if (preselectChanged) { - this->drawConstraintIcons(); + editCoinManager->drawConstraintIcons(); this->updateColor(); } return true; @@ -1374,11 +1257,11 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor case STATUS_SKETCH_UseRubberBand: { // Here we must use the device-pixel-ratio to compute the correct y coordinate (#0003130) qreal dpr = viewer->getGLWidget()->devicePixelRatioF(); - newCursorPos = cursorPos; - rubberband->setCoords(prvCursorPos.getValue()[0], - viewer->getGLWidget()->height()*dpr - prvCursorPos.getValue()[1], - newCursorPos.getValue()[0], - viewer->getGLWidget()->height()*dpr - newCursorPos.getValue()[1]); + DoubleClick::newCursorPos = cursorPos; + rubberband->setCoords(DoubleClick::prvCursorPos.getValue()[0], + viewer->getGLWidget()->height()*dpr - DoubleClick::prvCursorPos.getValue()[1], + DoubleClick::newCursorPos.getValue()[0], + viewer->getGLWidget()->height()*dpr - DoubleClick::newCursorPos.getValue()[1]); viewer->redraw(); return true; } @@ -1392,7 +1275,7 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor void ViewProviderSketch::moveConstraint(int constNum, const Base::Vector2d &toPos) { // are we in edit? - if (!edit) + if (!isInEditMode()) return; const std::vector &constrlist = getSketchObject()->Constraints.getValues(); @@ -1421,7 +1304,7 @@ void ViewProviderSketch::moveConstraint(int constNum, const Base::Vector2d &toPo p2 = getSolvedSketch().getPoint(Constr->Second, Constr->SecondPos); } else if (Constr->Second != GeoEnum::GeoUndef) { // point to line distance p1 = getSolvedSketch().getPoint(Constr->First, Constr->FirstPos); - const Part::Geometry *geo = GeoById(geomlist, Constr->Second); + const Part::Geometry *geo = GeoList::getGeometryFromGeoId (geomlist, Constr->Second); if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { const Part::GeomLineSegment *lineSeg = static_cast(geo); Base::Vector3d l2p1 = lineSeg->getStartPoint(); @@ -1434,7 +1317,7 @@ void ViewProviderSketch::moveConstraint(int constNum, const Base::Vector2d &toPo } else if (Constr->FirstPos != Sketcher::PointPos::none) { p2 = getSolvedSketch().getPoint(Constr->First, Constr->FirstPos); } else if (Constr->First != GeoEnum::GeoUndef) { - const Part::Geometry *geo = GeoById(geomlist, Constr->First); + const Part::Geometry *geo = GeoList::getGeometryFromGeoId (geomlist, Constr->First); if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { const Part::GeomLineSegment *lineSeg = static_cast(geo); p1 = lineSeg->getStartPoint(); @@ -1526,9 +1409,11 @@ void ViewProviderSketch::moveConstraint(int constNum, const Base::Vector2d &toPo double factor = 0.5; if (Constr->Second != GeoEnum::GeoUndef) { // line to line angle Base::Vector3d dir1, dir2; + if(Constr->Third == GeoEnum::GeoUndef) { //angle between two lines - const Part::Geometry *geo1 = GeoById(geomlist, Constr->First); - const Part::Geometry *geo2 = GeoById(geomlist, Constr->Second); + const Part::Geometry *geo1 = GeoList::getGeometryFromGeoId (geomlist, Constr->First); + const Part::Geometry *geo2 = GeoList::getGeometryFromGeoId (geomlist, Constr->Second); + if (geo1->getTypeId() != Part::GeomLineSegment::getClassTypeId() || geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId()) return; @@ -1537,6 +1422,7 @@ void ViewProviderSketch::moveConstraint(int constNum, const Base::Vector2d &toPo bool flip1 = (Constr->FirstPos == Sketcher::PointPos::end); bool flip2 = (Constr->SecondPos == Sketcher::PointPos::end); + dir1 = (flip1 ? -1. : 1.) * (lineSeg1->getEndPoint()-lineSeg1->getStartPoint()); dir2 = (flip2 ? -1. : 1.) * (lineSeg2->getEndPoint()-lineSeg2->getStartPoint()); Base::Vector3d pnt1 = flip1 ? lineSeg1->getEndPoint() : lineSeg1->getStartPoint(); @@ -1570,7 +1456,8 @@ void ViewProviderSketch::moveConstraint(int constNum, const Base::Vector2d &toPo } } else if (Constr->First != GeoEnum::GeoUndef) { // line/arc angle - const Part::Geometry *geo = GeoById(geomlist, Constr->First); + const Part::Geometry *geo = GeoList::getGeometryFromGeoId (geomlist, Constr->First); + if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { const Part::GeomLineSegment *lineSeg = static_cast(geo); p0 = (lineSeg->getEndPoint()+lineSeg->getStartPoint())/2; @@ -1596,62 +1483,6 @@ void ViewProviderSketch::moveConstraint(int constNum, const Base::Vector2d &toPo draw(true,false); } -Base::Vector3d ViewProviderSketch::seekConstraintPosition(const Base::Vector3d &origPos, - const Base::Vector3d &norm, - const Base::Vector3d &dir, float step, - const SoNode *constraint) -{ - assert(edit); - Gui::MDIView *mdi = Gui::Application::Instance->editViewOfNode(edit->EditRoot); - if (!(mdi && mdi->isDerivedFrom(Gui::View3DInventor::getClassTypeId()))) - return Base::Vector3d(0, 0, 0); - Gui::View3DInventorViewer *viewer = static_cast(mdi)->getViewer(); - - SoRayPickAction rp(viewer->getSoRenderManager()->getViewportRegion()); - - float scaled_step = step * getScaleFactor(); - - int multiplier = 0; - Base::Vector3d relPos, freePos; - bool isConstraintAtPosition = true; - while (isConstraintAtPosition && multiplier < 10) { - // Calculate new position of constraint - relPos = norm * 0.5f + dir * multiplier; - freePos = origPos + relPos * scaled_step; - - rp.setRadius(0.1f); - rp.setPickAll(true); - rp.setRay(SbVec3f(freePos.x, freePos.y, -1.f), SbVec3f(0, 0, 1) ); - //problem - rp.apply(edit->constrGroup); // We could narrow it down to just the SoGroup containing the constraints - - // returns a copy of the point - SoPickedPoint *pp = rp.getPickedPoint(); - const SoPickedPointList ppl = rp.getPickedPointList(); - - if (ppl.getLength() <= 1 && pp) { - SoPath *path = pp->getPath(); - int length = path->getLength(); - SoNode *tailFather1 = path->getNode(length-2); - SoNode *tailFather2 = path->getNode(length-3); - - // checking if a constraint is the same as the one selected - if (tailFather1 == constraint || tailFather2 == constraint) - isConstraintAtPosition = false; - } - else { - isConstraintAtPosition = false; - } - - multiplier *= -1; // search in both sides - if (multiplier >= 0) - multiplier++; // Increment the multiplier - } - if (multiplier == 10) - relPos = norm * 0.5f; // no free position found - return relPos * step; -} - bool ViewProviderSketch::isSelectable(void) const { if (isEditing()) @@ -1663,7 +1494,7 @@ bool ViewProviderSketch::isSelectable(void) const void ViewProviderSketch::onSelectionChanged(const Gui::SelectionChanges& msg) { // are we in edit? - if (edit) { + if (isInEditMode()) { // ignore external object if(msg.Object.getObjectName().size() && msg.Object.getDocument()!=getObject()->getDocument()) return; @@ -1671,7 +1502,7 @@ void ViewProviderSketch::onSelectionChanged(const Gui::SelectionChanges& msg) bool handled=false; if (Mode == STATUS_SKETCH_UseHandler) { App::AutoTransaction committer; - handled = edit->sketchHandler->onSelectionChanged(msg); + handled = sketchHandler->onSelectionChanged(msg); } if (handled) return; @@ -1679,12 +1510,12 @@ void ViewProviderSketch::onSelectionChanged(const Gui::SelectionChanges& msg) std::string temp; if (msg.Type == Gui::SelectionChanges::ClrSelection) { // if something selected in this object? - if (edit->SelPointSet.size() > 0 || edit->SelCurvSet.size() > 0 || edit->SelConstraintSet.size() > 0) { + if (!selection.SelPointSet.empty() || !selection.SelCurvSet.empty() || !selection.SelConstraintSet.empty()) { // clear our selection and update the color of the viewed edges and points clearSelectPoints(); - edit->SelCurvSet.clear(); - edit->SelConstraintSet.clear(); - this->drawConstraintIcons(); + selection.SelCurvSet.clear(); + selection.SelConstraintSet.clear(); + editCoinManager->drawConstraintIcons(); this->updateColor(); } } @@ -1696,13 +1527,13 @@ void ViewProviderSketch::onSelectionChanged(const Gui::SelectionChanges& msg) std::string shapetype(msg.pSubName); if (shapetype.size() > 4 && shapetype.substr(0,4) == "Edge") { int GeoId = std::atoi(&shapetype[4]) - 1; - edit->SelCurvSet.insert(GeoId); + selection.SelCurvSet.insert(GeoId); this->updateColor(); } else if (shapetype.size() > 12 && shapetype.substr(0,12) == "ExternalEdge") { int GeoId = std::atoi(&shapetype[12]) - 1; GeoId = -GeoId - 3; - edit->SelCurvSet.insert(GeoId); + selection.SelCurvSet.insert(GeoId); this->updateColor(); } else if (shapetype.size() > 6 && shapetype.substr(0,6) == "Vertex") { @@ -1711,21 +1542,21 @@ void ViewProviderSketch::onSelectionChanged(const Gui::SelectionChanges& msg) this->updateColor(); } else if (shapetype == "RootPoint") { - addSelectPoint(Sketcher::GeoEnum::RtPnt); + addSelectPoint(Selection::RootPoint); this->updateColor(); } else if (shapetype == "H_Axis") { - edit->SelCurvSet.insert(Sketcher::GeoEnum::HAxis); + selection.SelCurvSet.insert(Selection::HorizontalAxis); this->updateColor(); } else if (shapetype == "V_Axis") { - edit->SelCurvSet.insert(Sketcher::GeoEnum::VAxis); + selection.SelCurvSet.insert(Selection::VerticalAxis); this->updateColor(); } else if (shapetype.size() > 10 && shapetype.substr(0,10) == "Constraint") { int ConstrId = Sketcher::PropertyConstraintList::getIndexFromConstraintName(shapetype); - edit->SelConstraintSet.insert(ConstrId); - this->drawConstraintIcons(); + selection.SelConstraintSet.insert(ConstrId); + editCoinManager->drawConstraintIcons(); this->updateColor(); } } @@ -1733,7 +1564,7 @@ void ViewProviderSketch::onSelectionChanged(const Gui::SelectionChanges& msg) } else if (msg.Type == Gui::SelectionChanges::RmvSelection) { // Are there any objects selected - if (edit->SelPointSet.size() > 0 || edit->SelCurvSet.size() > 0 || edit->SelConstraintSet.size() > 0) { + if (!selection.SelPointSet.empty() || !selection.SelCurvSet.empty() || !selection.SelConstraintSet.empty()) { // is it this object?? if (strcmp(msg.pDocName,getSketchObject()->getDocument()->getName())==0 && strcmp(msg.pObjectName,getSketchObject()->getNameInDocument())== 0) { @@ -1741,13 +1572,13 @@ void ViewProviderSketch::onSelectionChanged(const Gui::SelectionChanges& msg) std::string shapetype(msg.pSubName); if (shapetype.size() > 4 && shapetype.substr(0,4) == "Edge") { int GeoId = std::atoi(&shapetype[4]) - 1; - edit->SelCurvSet.erase(GeoId); + selection.SelCurvSet.erase(GeoId); this->updateColor(); } else if (shapetype.size() > 12 && shapetype.substr(0,12) == "ExternalEdge") { int GeoId = std::atoi(&shapetype[12]) - 1; GeoId = -GeoId - 3; - edit->SelCurvSet.erase(GeoId); + selection.SelCurvSet.erase(GeoId); this->updateColor(); } else if (shapetype.size() > 6 && shapetype.substr(0,6) == "Vertex") { @@ -1760,17 +1591,17 @@ void ViewProviderSketch::onSelectionChanged(const Gui::SelectionChanges& msg) this->updateColor(); } else if (shapetype == "H_Axis") { - edit->SelCurvSet.erase(Sketcher::GeoEnum::HAxis); + selection.SelCurvSet.erase(Sketcher::GeoEnum::HAxis); this->updateColor(); } else if (shapetype == "V_Axis") { - edit->SelCurvSet.erase(Sketcher::GeoEnum::VAxis); + selection.SelCurvSet.erase(Sketcher::GeoEnum::VAxis); this->updateColor(); } else if (shapetype.size() > 10 && shapetype.substr(0,10) == "Constraint") { int ConstrId = Sketcher::PropertyConstraintList::getIndexFromConstraintName(shapetype); - edit->SelConstraintSet.erase(ConstrId); - this->drawConstraintIcons(); + selection.SelConstraintSet.erase(ConstrId); + editCoinManager->drawConstraintIcons(); this->updateColor(); } } @@ -1801,23 +1632,18 @@ void ViewProviderSketch::onSelectionChanged(const Gui::SelectionChanges& msg) if (shapetype.size() > 4 && shapetype.substr(0,4) == "Edge") { int GeoId = std::atoi(&shapetype[4]) - 1; resetPreselectPoint(); - edit->PreselectCurve = GeoId; - edit->PreselectCross = -1; - edit->PreselectConstraintSet.clear(); + preselection.PreselectCurve = GeoId; - if (edit->sketchHandler) - edit->sketchHandler->applyCursor(); + if (sketchHandler) + sketchHandler->applyCursor(); this->updateColor(); } else if (shapetype.size() > 6 && shapetype.substr(0,6) == "Vertex") { int PtIndex = std::atoi(&shapetype[6]) - 1; setPreselectPoint(PtIndex); - edit->PreselectCurve = -1; - edit->PreselectCross = -1; - edit->PreselectConstraintSet.clear(); - if (edit->sketchHandler) - edit->sketchHandler->applyCursor(); + if (sketchHandler) + sketchHandler->applyCursor(); this->updateColor(); } } @@ -1825,368 +1651,122 @@ void ViewProviderSketch::onSelectionChanged(const Gui::SelectionChanges& msg) } else if (msg.Type == Gui::SelectionChanges::RmvPreselect) { resetPreselectPoint(); - edit->PreselectCurve = -1; - edit->PreselectCross = -1; - edit->PreselectConstraintSet.clear(); - if (edit->sketchHandler) - edit->sketchHandler->applyCursor(); + if (sketchHandler) + sketchHandler->applyCursor(); this->updateColor(); } } } -std::set ViewProviderSketch::detectPreselectionConstr(const SoPickedPoint *Point, - const Gui::View3DInventorViewer *viewer, - const SbVec2s &cursorPos) +bool ViewProviderSketch::detectAndShowPreselection(SoPickedPoint * Point, const SbVec2s &cursorPos) { - std::set constrIndices; - SoPath *path = Point->getPath(); - SoNode *tail = path->getTail(); - SoNode *tailFather = path->getNode(path->getLength()-2); - - for (int i=0; i < edit->constrGroup->getNumChildren(); ++i) { - if (edit->constrGroup->getChild(i) == tailFather) { - SoSeparator *sep = static_cast(tailFather); - if (sep->getNumChildren() > CONSTRAINT_SEPARATOR_INDEX_FIRST_CONSTRAINTID) { - SoInfo *constrIds = NULL; - if (tail == sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_ICON)) { - // First icon was hit - constrIds = static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_CONSTRAINTID)); - } - else { - // Assume second icon was hit - if (CONSTRAINT_SEPARATOR_INDEX_SECOND_CONSTRAINTIDgetNumChildren()) { - constrIds = static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_SECOND_CONSTRAINTID)); - } - } - - if (constrIds) { - QString constrIdsStr = QString::fromLatin1(constrIds->string.getValue().getString()); - if (edit->combinedConstrBoxes.count(constrIdsStr) && dynamic_cast(tail)) { - // If it's a combined constraint icon - - // Screen dimensions of the icon - SbVec3s iconSize = getDisplayedSize(static_cast(tail)); - // Center of the icon - //SbVec2f iconCoords = viewer->screenCoordsOfPath(path); - - // The use of the Path to get the screen coordinates to get the icon center coordinates - // does not work. - // - // This implementation relies on the use of ZoomTranslation to get the absolute and relative - // positions of the icons. - // - // In the case of second icons (the same constraint has two icons at two different positions), - // the translation vectors have to be added, as the second ZoomTranslation operates on top of - // the first. - // - // Coordinates are projected on the sketch plane and then to the screen in the interval [0 1] - // Then this result is converted to pixels using the scale factor. - - SbVec3f absPos; - SbVec3f trans; - - absPos = static_cast(static_cast(tailFather)->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION))->abPos.getValue(); - - trans = static_cast(static_cast(tailFather)->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION))->translation.getValue(); - - if (tail != sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_ICON)) { - - absPos += static_cast(static_cast(tailFather)->getChild(CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION))->abPos.getValue(); - - trans += static_cast(static_cast(tailFather)->getChild(CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION))->translation.getValue(); - } - - Base::Placement sketchPlacement = getEditingPlacement(); - Base::Vector3d sketchPos(sketchPlacement.getPosition()); - Base::Rotation sketchRot(sketchPlacement.getRotation()); - - // get global coordinates from sketcher coordinates - SbVec3f constrPos = absPos + trans*getScaleFactor(); - Base::Vector3d pos(constrPos[0],constrPos[1],0); - sketchRot.multVec(pos,pos); - pos = pos + sketchPos; - - SoCamera* pCam = viewer->getSoRenderManager()->getCamera(); - - if (!pCam) - continue; - - SbViewVolume vol = pCam->getViewVolume(); - Gui::ViewVolumeProjection proj(vol); - - // dimensionless [0 1] (or 1.5 see View3DInventorViewer.cpp ) - Base::Vector3d screencoords = proj(pos); - - int width = viewer->getGLWidget()->width(), - height = viewer->getGLWidget()->height(); - - if (width >= height) { - // "Landscape" orientation, to square - screencoords.x *= height; - screencoords.x += (width-height) / 2.0; - screencoords.y *= height; - } - else { - // "Portrait" orientation - screencoords.x *= width; - screencoords.y *= width; - screencoords.y += (height-width) / 2.0; - } - - SbVec2f iconCoords(screencoords.x,screencoords.y); - - // cursorPos is SbVec2s in screen coordinates coming from SoEvent in mousemove - // - // Coordinates of the mouse cursor on the icon, origin at top-left for Qt - // but bottom-left for OIV. - // The coordinates are needed in Qt format, i.e. from top to bottom. - int iconX = cursorPos[0] - iconCoords[0] + iconSize[0]/2, - iconY = cursorPos[1] - iconCoords[1] + iconSize[1]/2; - iconY = iconSize[1] - iconY; - - for (ConstrIconBBVec::iterator b = edit->combinedConstrBoxes[constrIdsStr].begin(); - b != edit->combinedConstrBoxes[constrIdsStr].end(); ++b) { - -#ifdef FC_DEBUG - // Useful code to debug coordinates and bounding boxes that does not need to be compiled in for - // any debug operations. - - /*Base::Console().Log("Abs(%f,%f),Trans(%f,%f),Coords(%d,%d),iCoords(%f,%f),icon(%d,%d),isize(%d,%d),boundingbox([%d,%d],[%d,%d])\n", absPos[0],absPos[1],trans[0], trans[1], cursorPos[0], cursorPos[1], iconCoords[0], iconCoords[1], iconX, iconY, iconSize[0], iconSize[1], b->first.topLeft().x(),b->first.topLeft().y(),b->first.bottomRight().x(),b->first.bottomRight().y());*/ -#endif - - if (b->first.contains(iconX, iconY)) { - // We've found a bounding box that contains the mouse pointer! - for (std::set::iterator k = b->second.begin(); k != b->second.end(); ++k) - constrIndices.insert(*k); - } - } - } - else { - // It's a constraint icon, not a combined one - QStringList constrIdStrings = constrIdsStr.split(QString::fromLatin1(",")); - while (!constrIdStrings.empty()) - constrIndices.insert(constrIdStrings.takeAt(0).toInt()); - } - } - } - else { - // other constraint icons - eg radius... - constrIndices.clear(); - constrIndices.insert(i); - } - break; - } - } - - return constrIndices; -} - -bool ViewProviderSketch::detectPreselection(const SoPickedPoint *Point, - const Gui::View3DInventorViewer *viewer, - const SbVec2s &cursorPos) -{ - assert(edit); - - int PtIndex = -1; - int GeoIndex = -1; // valid values are 0,1,2,... for normal geometry and -3,-4,-5,... for external geometry - int CrossIndex = -1; - std::set constrIndices; + assert(isInEditMode()); if (Point) { - //Base::Console().Log("Point pick\n"); - SoPath *path = Point->getPath(); - SoNode *tail = path->getTail(); - SoNode *tailFather2 = path->getNode(path->getLength()-3); - // checking for a hit in the points - if (tail == edit->PointSet) { - const SoDetail *point_detail = Point->getDetail(edit->PointSet); - if (point_detail && point_detail->getTypeId() == SoPointDetail::getClassTypeId()) { - // get the index - PtIndex = static_cast(point_detail)->getCoordinateIndex(); - PtIndex -= 1; // shift corresponding to RootPoint - if (PtIndex == Sketcher::GeoEnum::RtPnt) - CrossIndex = 0; // RootPoint was hit - } - } else { - // checking for a hit in the curves - if (tail == edit->CurveSet) { - const SoDetail *curve_detail = Point->getDetail(edit->CurveSet); - if (curve_detail && curve_detail->getTypeId() == SoLineDetail::getClassTypeId()) { - // get the index - int curveIndex = static_cast(curve_detail)->getLineIndex(); - GeoIndex = edit->CurvIdToGeoId[curveIndex]; - } - // checking for a hit in the cross - } else if (tail == edit->RootCrossSet) { - const SoDetail *cross_detail = Point->getDetail(edit->RootCrossSet); - if (cross_detail && cross_detail->getTypeId() == SoLineDetail::getClassTypeId()) { - // get the index (reserve index 0 for root point) - CrossIndex = 1 + static_cast(cross_detail)->getLineIndex(); - } - } else { - // checking if a constraint is hit - if (tailFather2 == edit->constrGroup) - constrIndices = detectPreselectionConstr(Point, viewer, cursorPos); - } - } + EditModeCoinManager::PreselectionResult result = editCoinManager->detectPreselection(Point, cursorPos); - if (PtIndex != -1 && PtIndex != edit->PreselectPoint) { // if a new point is hit + if (result.PointIndex != -1 && result.PointIndex != preselection.PreselectPoint) { // if a new point is hit std::stringstream ss; - ss << "Vertex" << PtIndex + 1; - bool accepted = - Gui::Selection().setPreselect(SEL_PARAMS - ,Point->getPoint()[0] - ,Point->getPoint()[1] - ,Point->getPoint()[2]) != 0; - edit->blockedPreselection = !accepted; + ss << "Vertex" << result.PointIndex + 1; + bool accepted = setPreselect(ss.str(), Point->getPoint()[0], Point->getPoint()[1], Point->getPoint()[2]) != 0; + preselection.blockedPreselection = !accepted; if (accepted) { - setPreselectPoint(PtIndex); - edit->PreselectCurve = -1; - edit->PreselectCross = -1; - edit->PreselectConstraintSet.clear(); - if (edit->sketchHandler) - edit->sketchHandler->applyCursor(); + setPreselectPoint(result.PointIndex ); + + if (sketchHandler) + sketchHandler->applyCursor(); return true; } - } else if (GeoIndex != -1 && GeoIndex != edit->PreselectCurve) { // if a new curve is hit + } else if (result.GeoIndex != -1 && result.GeoIndex != preselection.PreselectCurve) { // if a new curve is hit std::stringstream ss; - if (GeoIndex >= 0) - ss << "Edge" << GeoIndex + 1; + if (result.GeoIndex >= 0) + ss << "Edge" << result.GeoIndex + 1; else // external geometry - ss << "ExternalEdge" << -GeoIndex + Sketcher::GeoEnum::RefExt + 1; // convert index start from -3 to 1 - bool accepted = - Gui::Selection().setPreselect(SEL_PARAMS - ,Point->getPoint()[0] - ,Point->getPoint()[1] - ,Point->getPoint()[2]) != 0; - edit->blockedPreselection = !accepted; + ss << "ExternalEdge" << -result.GeoIndex + Sketcher::GeoEnum::RefExt + 1; // convert index start from -3 to 1 + bool accepted = setPreselect(ss.str(), Point->getPoint()[0], Point->getPoint()[1], Point->getPoint()[2]) != 0; + preselection.blockedPreselection = !accepted; if (accepted) { resetPreselectPoint(); - edit->PreselectCurve = GeoIndex; - edit->PreselectCross = -1; - edit->PreselectConstraintSet.clear(); - if (edit->sketchHandler) - edit->sketchHandler->applyCursor(); + preselection.PreselectCurve = result.GeoIndex; + + if (sketchHandler) + sketchHandler->applyCursor(); return true; } - } else if (CrossIndex != -1 && CrossIndex != edit->PreselectCross) { // if a cross line is hit + } else if (result.Cross != EditModeCoinManager::PreselectionResult::Axes::None && static_cast(result.Cross ) != static_cast(preselection.PreselectCross)) { // if a cross line is hit std::stringstream ss; - switch(CrossIndex){ - case 0: ss << "RootPoint" ; break; - case 1: ss << "H_Axis" ; break; - case 2: ss << "V_Axis" ; break; + switch(result.Cross ){ + case EditModeCoinManager::PreselectionResult::Axes::RootPoint: ss << "RootPoint" ; break; + case EditModeCoinManager::PreselectionResult::Axes::HorizontalAxis: ss << "H_Axis" ; break; + case EditModeCoinManager::PreselectionResult::Axes::VerticalAxis: ss << "V_Axis" ; break; + case EditModeCoinManager::PreselectionResult::Axes::None: break; // silent warning - be explicit } - bool accepted = - Gui::Selection().setPreselect(SEL_PARAMS - ,Point->getPoint()[0] - ,Point->getPoint()[1] - ,Point->getPoint()[2]) != 0; - edit->blockedPreselection = !accepted; + bool accepted = setPreselect(ss.str(), Point->getPoint()[0], Point->getPoint()[1], Point->getPoint()[2]) != 0; + preselection.blockedPreselection = !accepted; if (accepted) { - if (CrossIndex == 0) - setPreselectPoint(-1); + if (result.Cross == EditModeCoinManager::PreselectionResult::Axes::RootPoint) + setPreselectRootPoint(); else resetPreselectPoint(); - edit->PreselectCurve = -1; - edit->PreselectCross = CrossIndex; - edit->PreselectConstraintSet.clear(); - if (edit->sketchHandler) - edit->sketchHandler->applyCursor(); + preselection.PreselectCross = static_cast(static_cast(result.Cross )); + + if (sketchHandler) + sketchHandler->applyCursor(); return true; } - } else if (constrIndices.empty() == false && constrIndices != edit->PreselectConstraintSet) { // if a constraint is hit + } else if (result.ConstrIndices.empty() == false && result.ConstrIndices != preselection.PreselectConstraintSet) { // if a constraint is hit bool accepted = true; - for(std::set::iterator it = constrIndices.begin(); it != constrIndices.end(); ++it) { + for(std::set::iterator it = result.ConstrIndices.begin(); it != result.ConstrIndices.end(); ++it) { std::stringstream ss; ss << Sketcher::PropertyConstraintList::getConstraintName(*it); - accepted &= - Gui::Selection().setPreselect(SEL_PARAMS - ,Point->getPoint()[0] - ,Point->getPoint()[1] - ,Point->getPoint()[2]) != 0; + accepted &= setPreselect(ss.str(), Point->getPoint()[0], Point->getPoint()[1], Point->getPoint()[2]) != 0; - edit->blockedPreselection = !accepted; + preselection.blockedPreselection = !accepted; //TODO: Should we clear preselections that went through, if one fails? } if (accepted) { resetPreselectPoint(); - edit->PreselectCurve = -1; - edit->PreselectCross = -1; - edit->PreselectConstraintSet = constrIndices; - if (edit->sketchHandler) - edit->sketchHandler->applyCursor(); + preselection.PreselectConstraintSet = result.ConstrIndices; + + if (sketchHandler) + sketchHandler->applyCursor(); return true;//Preselection changed } - } else if ((PtIndex == -1 && GeoIndex == -1 && CrossIndex == -1 && constrIndices.empty()) && - (edit->PreselectPoint != -1 || edit->PreselectCurve != -1 || edit->PreselectCross != -1 - || edit->PreselectConstraintSet.empty() != true || edit->blockedPreselection)) { + } else if ((result.PointIndex == -1 && result.GeoIndex == -1 && + result.Cross == EditModeCoinManager::PreselectionResult::Axes::None && result.ConstrIndices.empty()) && + (preselection.isPreselectPointValid() || preselection.isPreselectCurveValid() || preselection.isCrossPreselected() + || preselection.PreselectConstraintSet.empty() != true || preselection.blockedPreselection)) { // we have just left a preselection resetPreselectPoint(); - edit->PreselectCurve = -1; - edit->PreselectCross = -1; - edit->PreselectConstraintSet.clear(); - edit->blockedPreselection = false; - if (edit->sketchHandler) - edit->sketchHandler->applyCursor(); + preselection.blockedPreselection = false; + if (sketchHandler) + sketchHandler->applyCursor(); return true; } - Gui::Selection().setPreselectCoord(Point->getPoint()[0] - ,Point->getPoint()[1] - ,Point->getPoint()[2]); -// if(Point) - } else if (edit->PreselectCurve != -1 || edit->PreselectPoint != -1 || - edit->PreselectConstraintSet.empty() != true || edit->PreselectCross != -1 || edit->blockedPreselection) { + Gui::Selection().setPreselectCoord(Point->getPoint()[0], Point->getPoint()[1], Point->getPoint()[2]); + + } else if (preselection.isPreselectCurveValid() || preselection.isPreselectPointValid() || + preselection.PreselectConstraintSet.empty() != true || preselection.isCrossPreselected() || preselection.blockedPreselection) { resetPreselectPoint(); - edit->PreselectCurve = -1; - edit->PreselectCross = -1; - edit->PreselectConstraintSet.clear(); - edit->blockedPreselection = false; - if (edit->sketchHandler) - edit->sketchHandler->applyCursor(); + preselection.blockedPreselection = false; + if (sketchHandler) + sketchHandler->applyCursor(); return true; } return false; } -SbVec3s ViewProviderSketch::getDisplayedSize(const SoImage *iconPtr) const -{ -#if (COIN_MAJOR_VERSION >= 3) - SbVec3s iconSize = iconPtr->image.getValue().getSize(); -#else - SbVec2s size; - int nc; - const unsigned char * bytes = iconPtr->image.getValue(size, nc); - SbImage img (bytes, size, nc); - SbVec3s iconSize = img.getSize(); -#endif - if (iconPtr->width.getValue() != -1) - iconSize[0] = iconPtr->width.getValue(); - if (iconPtr->height.getValue() != -1) - iconSize[1] = iconPtr->height.getValue(); - return iconSize; -} - void ViewProviderSketch::centerSelection() { Gui::MDIView *mdi = this->getActiveView(); Gui::View3DInventor *view = qobject_cast(mdi); - if (!view || !edit) + if (!view || !isInEditMode()) return; - SoGroup* group = new SoGroup(); - group->ref(); - - for (int i=0; i < edit->constrGroup->getNumChildren(); i++) { - if (edit->SelConstraintSet.find(i) != edit->SelConstraintSet.end()) { - SoSeparator *sep = dynamic_cast(edit->constrGroup->getChild(i)); - if (sep) - group->addChild(sep); - } - } + SoGroup* group = editCoinManager->getSelectedConstraints(); Gui::View3DInventorViewer* viewer = view->getViewer(); SoGetBoundingBoxAction action(viewer->getSoRenderManager()->getViewportRegion()); @@ -2257,7 +1837,7 @@ void ViewProviderSketch::doBoxSelection(const SbVec2s &startPos, const SbVec2s & if (polygon.Contains(Base::Vector2d(pnt0.x, pnt0.y))) { std::stringstream ss; ss << "Vertex" << VertexId + 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } } else if ((*it)->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { @@ -2274,19 +1854,19 @@ void ViewProviderSketch::doBoxSelection(const SbVec2s &startPos, const SbVec2s & if (pnt1Inside) { std::stringstream ss; ss << "Vertex" << VertexId; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } if (pnt2Inside) { std::stringstream ss; ss << "Vertex" << VertexId + 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } if ((pnt1Inside && pnt2Inside) && !touchMode) { std::stringstream ss; ss << "Edge" << GeoId + 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } //check if line intersects with polygon else if (touchMode) { @@ -2298,7 +1878,7 @@ void ViewProviderSketch::doBoxSelection(const SbVec2s &startPos, const SbVec2s & if (!resultList.empty()) { std::stringstream ss; ss << "Edge" << GeoId + 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } } @@ -2316,7 +1896,7 @@ void ViewProviderSketch::doBoxSelection(const SbVec2s &startPos, const SbVec2s & if (polygon.Contains(Base::Vector2d(pnt0.x, pnt0.y))) { std::stringstream ss; ss << "Vertex" << VertexId + 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } int countSegments = 12; if (touchMode) @@ -2350,7 +1930,7 @@ void ViewProviderSketch::doBoxSelection(const SbVec2s &startPos, const SbVec2s & if (bpolyInside) { std::stringstream ss; ss << "Edge" << GeoId + 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } } } else if ((*it)->getTypeId() == Part::GeomEllipse::getClassTypeId()) { @@ -2366,7 +1946,7 @@ void ViewProviderSketch::doBoxSelection(const SbVec2s &startPos, const SbVec2s & if (polygon.Contains(Base::Vector2d(pnt0.x, pnt0.y))) { std::stringstream ss; ss << "Vertex" << VertexId + 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } int countSegments = 12; @@ -2401,7 +1981,7 @@ void ViewProviderSketch::doBoxSelection(const SbVec2s &startPos, const SbVec2s & if (bpolyInside) { std::stringstream ss; ss << "Edge" << GeoId + 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } } @@ -2463,26 +2043,26 @@ void ViewProviderSketch::doBoxSelection(const SbVec2s &startPos, const SbVec2s & if (bpolyInside) { std::stringstream ss; ss << "Edge" << GeoId + 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } } if (pnt0Inside) { std::stringstream ss; ss << "Vertex" << VertexId - 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } if (pnt1Inside) { std::stringstream ss; ss << "Vertex" << VertexId; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } if (polygon.Contains(Base::Vector2d(pnt2.x, pnt2.y))) { std::stringstream ss; ss << "Vertex" << VertexId + 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } } else if ((*it)->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) { // Check if arc lies inside box selection @@ -2545,25 +2125,25 @@ void ViewProviderSketch::doBoxSelection(const SbVec2s &startPos, const SbVec2s & if (bpolyInside) { std::stringstream ss; ss << "Edge" << GeoId + 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } } if (pnt0Inside) { std::stringstream ss; ss << "Vertex" << VertexId - 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } if (pnt1Inside) { std::stringstream ss; ss << "Vertex" << VertexId; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } if (polygon.Contains(Base::Vector2d(pnt2.x, pnt2.y))) { std::stringstream ss; ss << "Vertex" << VertexId + 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } } else if ((*it)->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId()) { @@ -2629,24 +2209,24 @@ void ViewProviderSketch::doBoxSelection(const SbVec2s &startPos, const SbVec2s & if (bpolyInside) { std::stringstream ss; ss << "Edge" << GeoId + 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } if (pnt0Inside) { std::stringstream ss; ss << "Vertex" << VertexId - 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } if (pnt1Inside) { std::stringstream ss; ss << "Vertex" << VertexId; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } if (polygon.Contains(Base::Vector2d(pnt2.x, pnt2.y))) { std::stringstream ss; ss << "Vertex" << VertexId + 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } } @@ -2715,24 +2295,24 @@ void ViewProviderSketch::doBoxSelection(const SbVec2s &startPos, const SbVec2s & if (bpolyInside) { std::stringstream ss; ss << "Edge" << GeoId + 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } if (pnt0Inside) { std::stringstream ss; ss << "Vertex" << VertexId - 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } if (pnt1Inside) { std::stringstream ss; ss << "Vertex" << VertexId; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } if (polygon.Contains(Base::Vector2d(pnt2.x, pnt2.y))) { std::stringstream ss; ss << "Vertex" << VertexId + 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } } @@ -2751,13 +2331,13 @@ void ViewProviderSketch::doBoxSelection(const SbVec2s &startPos, const SbVec2s & if (pnt1Inside || (touchMode && pnt2Inside)) { std::stringstream ss; ss << "Vertex" << VertexId; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } if (pnt2Inside || (touchMode && pnt1Inside)) { std::stringstream ss; ss << "Vertex" << VertexId + 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } // This is a rather approximated approach. No it does not guarantee that the whole curve is boxed, specially @@ -2768,7 +2348,7 @@ void ViewProviderSketch::doBoxSelection(const SbVec2s &startPos, const SbVec2s & if ((pnt1Inside && pnt2Inside) || (touchMode && (pnt1Inside || pnt2Inside))) { std::stringstream ss; ss << "Edge" << GeoId + 1; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } } } @@ -2777,379 +2357,15 @@ void ViewProviderSketch::doBoxSelection(const SbVec2s &startPos, const SbVec2s & if (polygon.Contains(Base::Vector2d(pnt0.x, pnt0.y))) { std::stringstream ss; ss << "RootPoint"; - Gui::Selection().addSelection2(SEL_PARAMS); + addSelection2(ss.str()); } } void ViewProviderSketch::updateColor(void) { - assert(edit); - //Base::Console().Log("Draw preseletion\n"); + assert(isInEditMode()); - int PtNum = edit->PointsMaterials->diffuseColor.getNum(); - SbColor *pcolor = edit->PointsMaterials->diffuseColor.startEditing(); - int CurvNum = edit->CurvesMaterials->diffuseColor.getNum(); - SbColor *color = edit->CurvesMaterials->diffuseColor.startEditing(); - SbColor *crosscolor = edit->RootCrossMaterials->diffuseColor.startEditing(); - - SbVec3f *verts = edit->CurvesCoordinate->point.startEditing(); - //int32_t *index = edit->CurveSet->numVertices.startEditing(); - SbVec3f *pverts = edit->PointsCoordinate->point.startEditing(); - - ParameterGrp::handle hGrpp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General"); - - // 1->Normal Geometry, 2->Construction, 3->External - int topid = hGrpp->GetInt("TopRenderGeometryId",1); - int midid = hGrpp->GetInt("MidRenderGeometryId",2); - - float zNormPoint = (topid==1?zHighPoints:(midid==1 && topid!=2)?zHighPoints:zLowPoints); - float zConstrPoint = (topid==2?zHighPoints:(midid==2 && topid!=1)?zHighPoints:zLowPoints); - - float x,y,z; - - // use a lambda function to only access the geometry when needed - // and properly handle the case where it's null - auto isConstructionGeom = [](Sketcher::SketchObject* obj, int GeoId) -> bool { - const Part::Geometry* geom = obj->getGeometry(GeoId); - if (geom) - return Sketcher::GeometryFacade::getConstruction(geom); - return false; - }; - - auto isDefinedGeomPoint = [](Sketcher::SketchObject* obj, int GeoId) -> bool { - const Part::Geometry* geom = obj->getGeometry(GeoId); - if (geom) - return geom->getTypeId() == Part::GeomPoint::getClassTypeId() && !Sketcher::GeometryFacade::getConstruction(geom); - return false; - }; - - auto isInternalAlignedGeom = [](Sketcher::SketchObject* obj, int GeoId) -> bool { - const Part::Geometry* geom = obj->getGeometry(GeoId); - if (geom) { - auto gf = Sketcher::GeometryFacade::getFacade(geom); - return gf->isInternalAligned(); - } - return false; - }; - - auto isFullyConstraintElement = [](Sketcher::SketchObject* obj, int GeoId) -> bool { - - const Part::Geometry* geom = obj->getGeometry(GeoId); - - if(geom) { - if(geom->hasExtension(Sketcher::SolverGeometryExtension::getClassTypeId())) { - - auto solvext = std::static_pointer_cast( - geom->getExtension(Sketcher::SolverGeometryExtension::getClassTypeId()).lock()); - - return (solvext->getGeometry() == Sketcher::SolverGeometryExtension::FullyConstraint); - } - } - return false; - }; - - bool invalidSketch = getSketchObject()->getLastHasRedundancies() || - getSketchObject()->getLastHasConflicts() || - getSketchObject()->getLastHasMalformedConstraints(); - - // colors of the point set - if( invalidSketch ) { - for (int i=0; i < PtNum; i++) - pcolor[i] = InvalidSketchColor; - } - else if (edit->FullyConstrained) { - for (int i=0; i < PtNum; i++) - pcolor[i] = FullyConstrainedColor; - } - else { - for (int i=0; i < PtNum; i++) { - int GeoId = edit->PointIdToGeoId[i]; - - bool constrainedElement = isFullyConstraintElement(getSketchObject(), GeoId); - - if(isInternalAlignedGeom(getSketchObject(), GeoId)) { - if(constrainedElement) - pcolor[i] = FullyConstraintInternalAlignmentColor; - else - pcolor[i] = InternalAlignedGeoColor; - } - else { - if(!isDefinedGeomPoint(getSketchObject(), GeoId)) { - - if(constrainedElement) - pcolor[i] = FullyConstraintConstructionPointColor; - else - pcolor[i] = VertexColor; - } - else { // this is a defined GeomPoint - if(constrainedElement) - pcolor[i] = FullyConstraintElementColor; - else - pcolor[i] = CurveColor; - } - } - } - } - - for (int i=0; i < PtNum; i++) { // 0 is the origin - pverts[i].getValue(x,y,z); - const Part::Geometry * tmp = getSketchObject()->getGeometry(edit->PointIdToGeoId[i]); - if(tmp && z < zHighlight) { - if(Sketcher::GeometryFacade::getConstruction(tmp)) - pverts[i].setValue(x,y,zConstrPoint); - else - pverts[i].setValue(x,y,zNormPoint); - } - } - - - if (edit->PreselectCross == 0) { - pcolor[0] = PreselectColor; - } - else if (edit->PreselectPoint != -1) { - if (edit->PreselectPoint + 1 < PtNum) - pcolor[edit->PreselectPoint + 1] = PreselectColor; - } - - for (std::set::iterator it = edit->SelPointSet.begin(); it != edit->SelPointSet.end(); ++it) { - if (*it < PtNum) { - pcolor[*it] = (*it==(edit->PreselectPoint + 1) && (edit->PreselectPoint != -1)) - ? PreselectSelectedColor : SelectColor; - } - } - - // colors of the curves - //int intGeoCount = getSketchObject()->getHighestCurveIndex() + 1; - //int extGeoCount = getSketchObject()->getExternalGeometryCount(); - - - - float zNormLine = (topid==1?zHighLines:midid==1?zMidLines:zLowLines); - float zConstrLine = (topid==2?zHighLines:midid==2?zMidLines:zLowLines); - float zExtLine = (topid==3?zHighLines:midid==3?zMidLines:zLowLines); - - int j=0; // vertexindex - - for (int i=0; i < CurvNum; i++) { - int GeoId = edit->CurvIdToGeoId[i]; - // CurvId has several vertices associated to 1 material - //edit->CurveSet->numVertices => [i] indicates number of vertex for line i. - int indexes = (edit->CurveSet->numVertices[i]); - - bool selected = (edit->SelCurvSet.find(GeoId) != edit->SelCurvSet.end()); - bool preselected = (edit->PreselectCurve == GeoId); - - bool constrainedElement = isFullyConstraintElement(getSketchObject(), GeoId); - - if (selected && preselected) { - color[i] = PreselectSelectedColor; - for (int k=j; jFullyConstrained) { - color[i] = FullyConstrainedColor; - for (int k=j; jSelCurvSet.find(-1) != edit->SelCurvSet.end()) - crosscolor[0] = SelectColor; - else if (edit->PreselectCross == 1) - crosscolor[0] = PreselectColor; - else - crosscolor[0] = CrossColorH; - - if (edit->SelCurvSet.find(Sketcher::GeoEnum::VAxis) != edit->SelCurvSet.end()) - crosscolor[1] = SelectColor; - else if (edit->PreselectCross == 2) - crosscolor[1] = PreselectColor; - else - crosscolor[1] = CrossColorV; - - int count = std::min(edit->constrGroup->getNumChildren(), getSketchObject()->Constraints.getSize()); - if(getSketchObject()->Constraints.hasInvalidGeometry()) - count = 0; - - // colors of the constraints - for (int i=0; i < count; i++) { - SoSeparator *s = static_cast(edit->constrGroup->getChild(i)); - - // Check Constraint Type - Sketcher::Constraint* constraint = getSketchObject()->Constraints.getValues()[i]; - ConstraintType type = constraint->Type; - bool hasDatumLabel = (type == Sketcher::Angle || - type == Sketcher::Radius || - type == Sketcher::Diameter || - type == Sketcher::Weight || - type == Sketcher::Symmetric || - type == Sketcher::Distance || - type == Sketcher::DistanceX || - type == Sketcher::DistanceY); - - // Non DatumLabel Nodes will have a material excluding coincident - bool hasMaterial = false; - - SoMaterial *m = 0; - if (!hasDatumLabel && type != Sketcher::Coincident && type != Sketcher::InternalAlignment) { - hasMaterial = true; - m = static_cast(s->getChild(CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL)); - } - - if (edit->SelConstraintSet.find(i) != edit->SelConstraintSet.end()) { - if (hasDatumLabel) { - SoDatumLabel *l = static_cast(s->getChild(CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL)); - l->textColor = SelectColor; - } else if (hasMaterial) { - m->diffuseColor = SelectColor; - } else if (type == Sketcher::Coincident) { - auto selectpoint = [this, pcolor, PtNum](int geoid, Sketcher::PointPos pos){ - if(geoid >= 0) { - int index = getSolvedSketch().getPointId(geoid, pos) + 1; - if (index >= 0 && index < PtNum) - pcolor[index] = SelectColor; - } - }; - - selectpoint(constraint->First, constraint->FirstPos); - selectpoint(constraint->Second, constraint->SecondPos); - } else if (type == Sketcher::InternalAlignment) { - switch(constraint->AlignmentType) { - case EllipseMajorDiameter: - case EllipseMinorDiameter: - { - // color line - int CurvNum = edit->CurvesMaterials->diffuseColor.getNum(); - for (int i=0; i < CurvNum; i++) { - int cGeoId = edit->CurvIdToGeoId[i]; - - if(cGeoId == constraint->First) { - color[i] = SelectColor; - break; - } - } - } - break; - case EllipseFocus1: - case EllipseFocus2: - { - int index = getSolvedSketch().getPointId(constraint->First, constraint->FirstPos) + 1; - if (index >= 0 && index < PtNum) pcolor[index] = SelectColor; - } - break; - default: - break; - } - } - } else if (edit->PreselectConstraintSet.count(i)) { - if (hasDatumLabel) { - SoDatumLabel *l = static_cast(s->getChild(CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL)); - l->textColor = PreselectColor; - } else if (hasMaterial) { - m->diffuseColor = PreselectColor; - } - } - else { - if (hasDatumLabel) { - SoDatumLabel *l = static_cast(s->getChild(CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL)); - - l->textColor = constraint->isActive ? - (getSketchObject()->constraintHasExpression(i) ? - ExprBasedConstrDimColor - :(constraint->isDriving ? - ConstrDimColor - : NonDrivingConstrDimColor)) - :DeactivatedConstrDimColor; - - } else if (hasMaterial) { - m->diffuseColor = constraint->isActive ? - (constraint->isDriving ? - ConstrDimColor - :NonDrivingConstrDimColor) - :DeactivatedConstrDimColor; - } - } - } - - // end editing - edit->CurvesMaterials->diffuseColor.finishEditing(); - edit->PointsMaterials->diffuseColor.finishEditing(); - edit->RootCrossMaterials->diffuseColor.finishEditing(); - edit->CurvesCoordinate->point.finishEditing(); - edit->CurveSet->numVertices.finishEditing(); -} - -bool ViewProviderSketch::isPointOnSketch(const SoPickedPoint *pp) const -{ - // checks if we picked a point on the sketch or any other nodes like the grid - SoPath *path = pp->getPath(); - return path->containsNode(edit->EditRoot); + editCoinManager->updateColor(); } bool ViewProviderSketch::doubleClicked(void) @@ -3158,685 +2374,10 @@ bool ViewProviderSketch::doubleClicked(void) return true; } -QString ViewProviderSketch::getPresentationString(const Constraint *constraint) +float ViewProviderSketch::getScaleFactor() const { - Base::Reference hGrpSketcher; // param group that includes HideUnits and ShowDimensionalName option - bool iHideUnits; // internal HideUnits setting - bool iShowDimName; // internal ShowDimensionalName setting - QString nameStr; // name parameter string - QString valueStr; // dimensional value string - QString presentationStr; // final return string - QString unitStr; // the actual unit string - QString baseUnitStr; // the expected base unit string - QString formatStr; // the user defined format for the representation string - double factor; // unit scaling factor, currently not used - Base::UnitSystem unitSys; // current unit system - - if(!constraint->isActive) - return QString::fromLatin1(" "); - - // get parameter group for Sketcher display settings - hGrpSketcher = App::GetApplication().GetUserParameter().GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/Sketcher"); - // Get value of HideUnits option. Default is false. - iHideUnits = hGrpSketcher->GetBool("HideUnits", 0); - // Get Value of ShowDimensionalName option. Default is true. - iShowDimName = hGrpSketcher->GetBool("ShowDimensionalName", false); - // Get the defined format string - formatStr = QString::fromStdString(hGrpSketcher->GetASCII("DimensionalStringFormat", "%N = %V")); - - // Get the current name parameter string of the constraint - nameStr = QString::fromStdString(constraint->Name); - - // Get the current value string including units - valueStr = constraint->getPresentationValue().getUserString(factor, unitStr); - - // Hide units if user has requested it, is being displayed in the base - // units, and the schema being used has a clear base unit in the first - // place. Otherwise, display units. - if( iHideUnits && constraint->Type != Sketcher::Angle ) - { - // Only hide the default length unit. Right now there is not an easy way - // to get that from the Unit system so we have to manually add it here. - // Hopefully this can be added in the future so this code won't have to - // be updated if a new units schema is added. - unitSys = Base::UnitsApi::getSchema(); - - // If this is a supported unit system then define what the base unit is. - switch (unitSys) - { - case Base::UnitSystem::SI1: - case Base::UnitSystem::MmMin: - baseUnitStr = QString::fromLatin1("mm"); - break; - - case Base::UnitSystem::SI2: - baseUnitStr = QString::fromLatin1("m"); - break; - - case Base::UnitSystem::ImperialDecimal: - baseUnitStr = QString::fromLatin1("in"); - break; - - case Base::UnitSystem::Centimeters: - baseUnitStr = QString::fromLatin1("cm"); - break; - - default: - // Nothing to do - break; - } - - if( !baseUnitStr.isEmpty() ) - { - // expected unit string matches actual unit string. remove. - if( QString::compare(baseUnitStr, unitStr)==0 ) - { - // Example code from: Mod/TechDraw/App/DrawViewDimension.cpp:372 - QRegExp rxUnits(QString::fromUtf8(" \\D*$")); //space + any non digits at end of string - valueStr.remove(rxUnits); //getUserString(defaultDecimals) without units - } - } - } - - if (constraint->Type == Sketcher::Diameter){ - valueStr.prepend(QChar(216)); // Diameter sign - } - else if (constraint->Type == Sketcher::Radius){ - valueStr.prepend(QChar(82)); // Capital letter R - } - - /** - Create the representation string from the user defined format string - Format options are: - %N - the constraint name parameter - %V - the value of the dimensional constraint, including any unit characters - */ - if (iShowDimName && !nameStr.isEmpty()) - { - if (formatStr.contains(QLatin1String("%V")) || formatStr.contains(QLatin1String("%N"))) - { - presentationStr = formatStr; - presentationStr.replace(QLatin1String("%N"), nameStr); - presentationStr.replace(QLatin1String("%V"), valueStr); - } - else - { - // user defined format string does not contain any valid parameter, using default format "%N = %V" - presentationStr = nameStr + QLatin1String(" = ") + valueStr; - FC_WARN("When parsing dimensional format string \"" - << QString(formatStr).toStdString() - << "\", no valid parameter found, using default format."); - } - - return presentationStr; - } - - return valueStr; -} - -QString ViewProviderSketch::iconTypeFromConstraint(Constraint *constraint) -{ - /*! TODO: Consider pushing this functionality up into Constraint */ - switch(constraint->Type) { - case Horizontal: - return QString::fromLatin1("Constraint_Horizontal"); - case Vertical: - return QString::fromLatin1("Constraint_Vertical"); - case PointOnObject: - return QString::fromLatin1("Constraint_PointOnObject"); - case Tangent: - return QString::fromLatin1("Constraint_Tangent"); - case Parallel: - return QString::fromLatin1("Constraint_Parallel"); - case Perpendicular: - return QString::fromLatin1("Constraint_Perpendicular"); - case Equal: - return QString::fromLatin1("Constraint_EqualLength"); - case Symmetric: - return QString::fromLatin1("Constraint_Symmetric"); - case SnellsLaw: - return QString::fromLatin1("Constraint_SnellsLaw"); - case Block: - return QString::fromLatin1("Constraint_Block"); - default: - return QString(); - } -} - -void ViewProviderSketch::sendConstraintIconToCoin(const QImage &icon, SoImage *soImagePtr) -{ - SoSFImage icondata = SoSFImage(); - - Gui::BitmapFactory().convert(icon, icondata); - - SbVec2s iconSize(icon.width(), icon.height()); - - int four = 4; - soImagePtr->image.setValue(iconSize, 4, icondata.getValue(iconSize, four)); - - //Set Image Alignment to Center - soImagePtr->vertAlignment = SoImage::HALF; - soImagePtr->horAlignment = SoImage::CENTER; -} - -void ViewProviderSketch::clearCoinImage(SoImage *soImagePtr) -{ - soImagePtr->setToDefaults(); -} - -QColor ViewProviderSketch::constrColor(int constraintId) -{ - static QColor constrIcoColor((int)(ConstrIcoColor [0] * 255.0f), - (int)(ConstrIcoColor[1] * 255.0f), - (int)(ConstrIcoColor[2] * 255.0f)); - static QColor nonDrivingConstrIcoColor((int)(NonDrivingConstrDimColor[0] * 255.0f), - (int)(NonDrivingConstrDimColor[1] * 255.0f), - (int)(NonDrivingConstrDimColor[2] * 255.0f)); - static QColor constrIconSelColor ((int)(SelectColor[0] * 255.0f), - (int)(SelectColor[1] * 255.0f), - (int)(SelectColor[2] * 255.0f)); - static QColor constrIconPreselColor ((int)(PreselectColor[0] * 255.0f), - (int)(PreselectColor[1] * 255.0f), - (int)(PreselectColor[2] * 255.0f)); - - static QColor constrIconDisabledColor ((int)(DeactivatedConstrDimColor[0] * 255.0f), - (int)(DeactivatedConstrDimColor[1] * 255.0f), - (int)(DeactivatedConstrDimColor[2] * 255.0f)); - - const std::vector &constraints = getSketchObject()->Constraints.getValues(); - - if (edit->PreselectConstraintSet.count(constraintId)) - return constrIconPreselColor; - else if (edit->SelConstraintSet.find(constraintId) != edit->SelConstraintSet.end()) - return constrIconSelColor; - else if(!constraints[constraintId]->isActive) - return constrIconDisabledColor; - else if(!constraints[constraintId]->isDriving) - return nonDrivingConstrIcoColor; - else - return constrIcoColor; - -} - -int ViewProviderSketch::constrColorPriority(int constraintId) -{ - if (edit->PreselectConstraintSet.count(constraintId)) - return 3; - else if (edit->SelConstraintSet.find(constraintId) != edit->SelConstraintSet.end()) - return 2; - else - return 1; -} - -// public function that triggers drawing of most constraint icons -void ViewProviderSketch::drawConstraintIcons() -{ - const std::vector &constraints = getSketchObject()->Constraints.getValues(); - int constrId = 0; - - std::vector iconQueue; - - for (std::vector::const_iterator it=constraints.begin(); - it != constraints.end(); ++it, ++constrId) { - - // Check if Icon Should be created - bool multipleIcons = false; - - QString icoType = iconTypeFromConstraint(*it); - if(icoType.isEmpty()) - continue; - - switch((*it)->Type) { - - case Tangent: - { // second icon is available only for colinear line segments - const Part::Geometry *geo1 = getSketchObject()->getGeometry((*it)->First); - const Part::Geometry *geo2 = getSketchObject()->getGeometry((*it)->Second); - if (geo1 && geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId() && - geo2 && geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { - multipleIcons = true; - } - } - break; - case Horizontal: - case Vertical: - { // second icon is available only for point alignment - if ((*it)->Second != GeoEnum::GeoUndef && - (*it)->FirstPos != Sketcher::PointPos::none && - (*it)->SecondPos != Sketcher::PointPos::none) { - multipleIcons = true; - } - } - break; - case Parallel: - multipleIcons = true; - break; - case Perpendicular: - // second icon is available only when there is no common point - if ((*it)->FirstPos == Sketcher::PointPos::none && (*it)->Third == GeoEnum::GeoUndef) - multipleIcons = true; - break; - case Equal: - multipleIcons = true; - break; - default: - break; - } - - // Double-check that we can safely access the Inventor nodes - if (constrId >= edit->constrGroup->getNumChildren()) { - Base::Console().Warning("Can't update constraint icons because view is not in sync with sketch\n"); - break; - } - - // Find the Constraint Icon SoImage Node - SoSeparator *sep = static_cast(edit->constrGroup->getChild(constrId)); - int numChildren = sep->getNumChildren(); - - SbVec3f absPos; - // Somewhat hacky - we use SoZoomTranslations for most types of icon, - // but symmetry icons use SoTranslations... - SoTranslation *translationPtr = static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION)); - if(dynamic_cast(translationPtr)) - absPos = static_cast(translationPtr)->abPos.getValue(); - else - absPos = translationPtr->translation.getValue(); - - SoImage *coinIconPtr = dynamic_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_ICON)); - SoInfo *infoPtr = static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_CONSTRAINTID)); - - constrIconQueueItem thisIcon; - thisIcon.type = icoType; - thisIcon.constraintId = constrId; - thisIcon.position = absPos; - thisIcon.destination = coinIconPtr; - thisIcon.infoPtr = infoPtr; - thisIcon.visible = (*it)->isInVirtualSpace == getIsShownVirtualSpace(); - - if ((*it)->Type==Symmetric) { - Base::Vector3d startingpoint = getSketchObject()->getPoint((*it)->First,(*it)->FirstPos); - Base::Vector3d endpoint = getSketchObject()->getPoint((*it)->Second,(*it)->SecondPos); - - double x0,y0,x1,y1; - SbVec3f pos0(startingpoint.x,startingpoint.y,startingpoint.z); - SbVec3f pos1(endpoint.x,endpoint.y,endpoint.z); - - Gui::MDIView *mdi = Gui::Application::Instance->editViewOfNode(edit->EditRoot); - if (!(mdi && mdi->isDerivedFrom(Gui::View3DInventor::getClassTypeId()))) - return; - Gui::View3DInventorViewer *viewer = static_cast(mdi)->getViewer(); - SoCamera* pCam = viewer->getSoRenderManager()->getCamera(); - if (!pCam) - return; - - try { - SbViewVolume vol = pCam->getViewVolume(); - - getCoordsOnSketchPlane(x0,y0,pos0,vol.getProjectionDirection()); - getCoordsOnSketchPlane(x1,y1,pos1,vol.getProjectionDirection()); - - thisIcon.iconRotation = -atan2((y1-y0),(x1-x0))*180/M_PI; - } - catch (const Base::DivisionByZeroError&) { - thisIcon.iconRotation = 0; - } - } - else { - thisIcon.iconRotation = 0; - } - - if (multipleIcons) { - if((*it)->Name.empty()) - thisIcon.label = QString::number(constrId + 1); - else - thisIcon.label = QString::fromUtf8((*it)->Name.c_str()); - iconQueue.push_back(thisIcon); - - // Note that the second translation is meant to be applied after the first. - // So, to get the position of the second icon, we add the two translations together - // - // See note ~30 lines up. - if (numChildren > CONSTRAINT_SEPARATOR_INDEX_SECOND_CONSTRAINTID) { - translationPtr = static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION)); - if(dynamic_cast(translationPtr)) - thisIcon.position += static_cast(translationPtr)->abPos.getValue(); - else - thisIcon.position += translationPtr->translation.getValue(); - - thisIcon.destination = dynamic_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_SECOND_ICON)); - thisIcon.infoPtr = static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_SECOND_CONSTRAINTID)); - } - } - else { - if ((*it)->Name.empty()) - thisIcon.label = QString(); - else - thisIcon.label = QString::fromUtf8((*it)->Name.c_str()); - } - - iconQueue.push_back(thisIcon); - } - - combineConstraintIcons(iconQueue); -} - -void ViewProviderSketch::combineConstraintIcons(IconQueue iconQueue) -{ - // getScaleFactor gives us a ratio of pixels per some kind of real units - float maxDistSquared = pow(getScaleFactor(), 2); - - // There's room for optimisation here; we could reuse the combined icons... - edit->combinedConstrBoxes.clear(); - - while(!iconQueue.empty()) { - // A group starts with an item popped off the back of our initial queue - IconQueue thisGroup; - thisGroup.push_back(iconQueue.back()); - ViewProviderSketch::constrIconQueueItem init = iconQueue.back(); - iconQueue.pop_back(); - - // we group only icons not being Symmetry icons, because we want those on the line - // and only icons that are visible - if(init.type != QString::fromLatin1("Constraint_Symmetric") && init.visible){ - - IconQueue::iterator i = iconQueue.begin(); - - - while(i != iconQueue.end()) { - if((*i).visible) { - bool addedToGroup = false; - - for(IconQueue::iterator j = thisGroup.begin(); - j != thisGroup.end(); ++j) { - float distSquared = pow(i->position[0]-j->position[0],2) + pow(i->position[1]-j->position[1],2); - if(distSquared <= maxDistSquared && (*i).type != QString::fromLatin1("Constraint_Symmetric")) { - // Found an icon in iconQueue that's close enough to - // a member of thisGroup, so move it into thisGroup - thisGroup.push_back(*i); - i = iconQueue.erase(i); - addedToGroup = true; - break; - } - } - - if(addedToGroup) { - if(i == iconQueue.end()) - // We just got the last icon out of iconQueue - break; - else - // Start looking through the iconQueue again, in case - // we have an icon that's now close enough to thisGroup - i = iconQueue.begin(); - } else - ++i; - } - else // if !visible we skip it - i++; - } - - } - - if(thisGroup.size() == 1) { - drawTypicalConstraintIcon(thisGroup[0]); - } - else { - drawMergedConstraintIcons(thisGroup); - } - } -} - -void ViewProviderSketch::drawMergedConstraintIcons(IconQueue iconQueue) -{ - for(IconQueue::iterator i = iconQueue.begin(); i != iconQueue.end(); ++i) { - clearCoinImage(i->destination); - } - - QImage compositeIcon; - SoImage *thisDest = iconQueue[0].destination; - SoInfo *thisInfo = iconQueue[0].infoPtr; - - // Tracks all constraint IDs that are combined into this icon - QString idString; - int lastVPad = 0; - - QStringList labels; - std::vector ids; - QString thisType; - QColor iconColor; - QList labelColors; - int maxColorPriority; - double iconRotation; - - ConstrIconBBVec boundingBoxes; - while(!iconQueue.empty()) { - IconQueue::iterator i = iconQueue.begin(); - - labels.clear(); - labels.append(i->label); - - ids.clear(); - ids.push_back(i->constraintId); - - thisType = i->type; - iconColor = constrColor(i->constraintId); - labelColors.clear(); - labelColors.append(iconColor); - iconRotation= i->iconRotation; - - maxColorPriority = constrColorPriority(i->constraintId); - - if(idString.length()) - idString.append(QString::fromLatin1(",")); - idString.append(QString::number(i->constraintId)); - - i = iconQueue.erase(i); - while(i != iconQueue.end()) { - if(i->type != thisType) { - ++i; - continue; - } - - labels.append(i->label); - ids.push_back(i->constraintId); - labelColors.append(constrColor(i->constraintId)); - - if(constrColorPriority(i->constraintId) > maxColorPriority) { - maxColorPriority = constrColorPriority(i->constraintId); - iconColor= constrColor(i->constraintId); - } - - idString.append(QString::fromLatin1(",") + - QString::number(i->constraintId)); - - i = iconQueue.erase(i); - } - - // To be inserted into edit->combinedConstBoxes - std::vector boundingBoxesVec; - int oldHeight = 0; - - // Render the icon here. - if(compositeIcon.isNull()) { - compositeIcon = renderConstrIcon(thisType, - iconColor, - labels, - labelColors, - iconRotation, - &boundingBoxesVec, - &lastVPad); - } else { - int thisVPad; - QImage partialIcon = renderConstrIcon(thisType, - iconColor, - labels, - labelColors, - iconRotation, - &boundingBoxesVec, - &thisVPad); - - // Stack vertically for now. Down the road, it might make sense - // to figure out the best orientation automatically. - oldHeight = compositeIcon.height(); - - // This is overkill for the currently used (20 July 2014) font, - // since it always seems to have the same vertical pad, but this - // might not always be the case. The 3 pixel buffer might need - // to vary depending on font size too... - oldHeight -= std::max(lastVPad - 3, 0); - - compositeIcon = compositeIcon.copy(0, 0, - std::max(partialIcon.width(), - compositeIcon.width()), - partialIcon.height() + - compositeIcon.height()); - - QPainter qp(&compositeIcon); - qp.drawImage(0, oldHeight, partialIcon); - - lastVPad = thisVPad; - } - - // Add bounding boxes for the icon we just rendered to boundingBoxes - std::vector::iterator id = ids.begin(); - std::set nextIds; - for(std::vector::iterator bb = boundingBoxesVec.begin(); - bb != boundingBoxesVec.end(); ++bb) { - nextIds.clear(); - - if(bb == boundingBoxesVec.begin()) { - // The first bounding box is for the icon at left, so assign - // all IDs for that type of constraint to the icon. - for(std::vector::iterator j = ids.begin(); j != ids.end(); ++j) - nextIds.insert(*j); - } - else { - nextIds.insert(*(id++)); - } - - ConstrIconBB newBB(bb->adjusted(0, oldHeight, 0, oldHeight), - nextIds); - - boundingBoxes.push_back(newBB); - } - } - - edit->combinedConstrBoxes[idString] = boundingBoxes; - thisInfo->string.setValue(idString.toLatin1().data()); - sendConstraintIconToCoin(compositeIcon, thisDest); -} - - -/// Note: labels, labelColors, and boundingBoxes are all -/// assumed to be the same length. -QImage ViewProviderSketch::renderConstrIcon(const QString &type, - const QColor &iconColor, - const QStringList &labels, - const QList &labelColors, - double iconRotation, - std::vector *boundingBoxes, - int *vPad) -{ - // Constants to help create constraint icons - QString joinStr = QString::fromLatin1(", "); - - QPixmap pxMap; - std::stringstream constraintName; - constraintName << type.toLatin1().data() << edit->constraintIconSize; // allow resizing by embedding size - if (! Gui::BitmapFactory().findPixmapInCache(constraintName.str().c_str(), pxMap)) { - pxMap = Gui::BitmapFactory().pixmapFromSvg(type.toLatin1().data(),QSizeF(edit->constraintIconSize,edit->constraintIconSize)); - Gui::BitmapFactory().addPixmapToCache(constraintName.str().c_str(), pxMap); // Cache for speed, avoiding pixmapFromSvg - } - QImage icon = pxMap.toImage(); - - QFont font = QApplication::font(); - font.setPixelSize(static_cast(1.0 * edit->constraintIconSize)); - font.setBold(true); - QFontMetrics qfm = QFontMetrics(font); - - int labelWidth = qfm.boundingRect(labels.join(joinStr)).width(); - // See Qt docs on qRect::bottom() for explanation of the +1 - int pxBelowBase = qfm.boundingRect(labels.join(joinStr)).bottom() + 1; - - if(vPad) - *vPad = pxBelowBase; - - QTransform rotation; - rotation.rotate(iconRotation); - - QImage roticon = icon.transformed(rotation); - QImage image = roticon.copy(0, 0, roticon.width() + labelWidth, - roticon.height() + pxBelowBase); - - // Make a bounding box for the icon - if(boundingBoxes) - boundingBoxes->push_back(QRect(0, 0, roticon.width(), roticon.height())); - - // Render the Icons - QPainter qp(&image); - qp.setCompositionMode(QPainter::CompositionMode_SourceIn); - qp.fillRect(roticon.rect(), iconColor); - - // Render constraint label if necessary - if (!labels.join(QString()).isEmpty()) { - qp.setCompositionMode(QPainter::CompositionMode_SourceOver); - qp.setFont(font); - - int cursorOffset = 0; - - //In Python: "for label, color in zip(labels, labelColors):" - QStringList::const_iterator labelItr; - QString labelStr; - QList::const_iterator colorItr; - QRect labelBB; - for(labelItr = labels.begin(), colorItr = labelColors.begin(); - labelItr != labels.end() && colorItr != labelColors.end(); - ++labelItr, ++colorItr) { - - qp.setPen(*colorItr); - - if(labelItr + 1 == labels.end()) // if this is the last label - labelStr = *labelItr; - else - labelStr = *labelItr + joinStr; - - // Note: text can sometimes draw to the left of the starting - // position, eg italic fonts. Check QFontMetrics - // documentation for more info, but be mindful if the - // icon.width() is ever very small (or removed). - qp.drawText(icon.width() + cursorOffset, icon.height(), labelStr); - - if(boundingBoxes) { - labelBB = qfm.boundingRect(labelStr); - labelBB.moveTo(icon.width() + cursorOffset, - icon.height() - qfm.height() + pxBelowBase); - boundingBoxes->push_back(labelBB); - } - - cursorOffset += Gui::QtTools::horizontalAdvance(qfm, labelStr); - } - } - - return image; -} - -void ViewProviderSketch::drawTypicalConstraintIcon(const constrIconQueueItem &i) -{ - QColor color = constrColor(i.constraintId); - - QImage image = renderConstrIcon(i.type, - color, - QStringList(i.label), - QList() << color, - i.iconRotation); - - i.infoPtr->string.setValue(QString::number(i.constraintId).toLatin1().data()); - sendConstraintIconToCoin(image, i.destination); -} - -float ViewProviderSketch::getScaleFactor() -{ - assert(edit); - Gui::MDIView *mdi = Gui::Application::Instance->editViewOfNode(edit->EditRoot); + assert(isInEditMode()); + Gui::MDIView *mdi = Gui::Application::Instance->editViewOfNode(editCoinManager->getRootEditNode()); if (mdi && mdi->isDerivedFrom(Gui::View3DInventor::getClassTypeId())) { Gui::View3DInventorViewer *viewer = static_cast(mdi)->getViewer(); SoCamera* camera = viewer->getSoRenderManager()->getCamera(); @@ -3848,179 +2389,36 @@ float ViewProviderSketch::getScaleFactor() } } -void ViewProviderSketch::OnChange(Base::Subject &rCaller, const char * sReason) +// This function ensures that the geometry used for drawing takes into account: +// 1. the OCC mandated weight, which is normalised for non-rational BSplines, but not normalised for rational BSplines. +// That includes properly sizing for drawing any weight constraint. +// This function ensures that both the geometry of the SketchObject and solver are updated with the new value of the scaling factor (via the extension) +// 2. the scaling factor, including inserting the scaling factor into the ViewProviderSketchGeometryExtension so as to enable +// That ensures that dragging operations on the circles of the poles of the B-Splines are properly rendered. +// +// This function takes a reference to a vector of deep copies to delete. These deep copies are necessary to transparently perform (1) while doing (2). +void ViewProviderSketch::scaleBSplinePoleCirclesAndUpdateSolverAndSketchObjectGeometry( + GeoList & geolist, + bool geometrywithmemoryallocation, + std::vector> &deepCopiesToDelete ) { - (void) rCaller; - //ParameterGrp& rclGrp = ((ParameterGrp&)rCaller); - if (strcmp(sReason, "ViewScalingFactor") == 0 || - strcmp(sReason, "MarkerSize") == 0 || - strcmp(sReason, "EditSketcherFontSize") == 0 ) { - if(edit) { // only if in edit mode, if not it gets updated when entering edit mode - initItemsSizes(); - updateInventorNodeSizes(); - rebuildConstraintsVisual(); - draw(); - } - } -} - -void ViewProviderSketch::subscribeToParameters() -{ - ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); - hGrp->Attach(this); -} - -void ViewProviderSketch::unsubscribeToParameters() -{ - ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); - hGrp->Detach(this); -} - -void ViewProviderSketch::updateInventorNodeSizes() -{ - assert(edit); - edit->PointsDrawStyle->pointSize = 8 * edit->pixelScalingFactor; - edit->PointSet->markerIndex = Gui::Inventor::MarkerBitmaps::getMarkerIndex("CIRCLE_FILLED", edit->MarkerSize); - edit->CurvesDrawStyle->lineWidth = 3 * edit->pixelScalingFactor; - edit->RootCrossDrawStyle->lineWidth = 2 * edit->pixelScalingFactor; - edit->EditCurvesDrawStyle->lineWidth = 3 * edit->pixelScalingFactor; - edit->EditMarkersDrawStyle->pointSize = 8 * edit->pixelScalingFactor; - edit->EditMarkerSet->markerIndex = Gui::Inventor::MarkerBitmaps::getMarkerIndex("CIRCLE_LINE", edit->MarkerSize); - edit->ConstraintDrawStyle->lineWidth = 1 * edit->pixelScalingFactor; - edit->InformationDrawStyle->lineWidth = 1 * edit->pixelScalingFactor; -} - -void ViewProviderSketch::initItemsSizes() -{ - //Add scaling to Constraint icons - ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); - double viewScalingFactor = hGrp->GetFloat("ViewScalingFactor", 1.0); - viewScalingFactor = Base::clamp(viewScalingFactor, 0.5, 5.0); - int markersize = hGrp->GetInt("MarkerSize", 7); - - int defaultFontSizePixels = QApplication::fontMetrics().height(); // returns height in pixels, not points - int sketcherfontSize = hGrp->GetInt("EditSketcherFontSize", defaultFontSizePixels); - - int dpi = QApplication::desktop()->logicalDpiX(); - - if(edit) { - // simple scaling factor for hardcoded pixel values in the Sketcher - edit->pixelScalingFactor = viewScalingFactor * dpi / 96; // 96 ppi is the standard pixel density for which pixel quantities were calculated - - // Coin documentation indicates the size of a font is: - // SoSFFloat SoFont::size Size of font. Defaults to 10.0. - // - // For 2D rendered bitmap fonts (like for SoText2), this value is the height of a character in screen pixels. For 3D text, this value is the world-space coordinates height of a character in the current units setting (see documentation for SoUnits node). - // - // However, with hdpi monitors, the coin font labels do not respect the size passed in pixels: - // https://forum.freecadweb.org/viewtopic.php?f=3&t=54347&p=467610#p467610 - // https://forum.freecadweb.org/viewtopic.php?f=10&t=49972&start=40#p467471 - // - // Because I (abdullah) have 96 dpi logical, 82 dpi physical, and I see a 35px font setting for a "1" in a datum label as 34px, - // and I see kilsore and Elyas screenshots showing 41px and 61px in higher resolution monitors for the same configuration, I think - // that coin pixel size has to be corrected by the logical dpi of the monitor. The rationale is that: a) it obviously needs dpi - // correction, b) with physical dpi, the ratio of representation between kilsore and me is too far away. - // - // This means that the following correction does not have a documented basis, but appears necessary so that the Sketcher is usable in - // HDPI monitors. - - edit->coinFontSize = std::lround(sketcherfontSize * 96.0f / dpi); - edit->constraintIconSize = std::lround(0.8 * sketcherfontSize); - - // For marker size the global default is used. - // - // Rationale: - // -> Other WBs use the default value as is - // -> If a user has a HDPI, they will eventually change the value for the other WBs - // -> If we correct the value here in addition, we would get two times a resize - edit->MarkerSize = markersize; - } -} - -void ViewProviderSketch::draw(bool temp /*=false*/, bool rebuildinformationlayer /*=true*/) -{ - assert(edit); - - // Render Geometry =================================================== - std::vector Coords; - std::vector Points; - std::vector Index; - - int intGeoCount = getSketchObject()->getHighestCurveIndex() + 1; - int extGeoCount = getSketchObject()->getExternalGeometryCount(); - - const std::vector *geomlist; - std::vector tempGeo; - if (temp) - tempGeo = getSolvedSketch().extractGeometry(true, true); // with memory allocation - else - tempGeo = getSketchObject()->getCompleteGeometry(); // without memory allocation - geomlist = &tempGeo; - - - assert(int(geomlist->size()) == extGeoCount + intGeoCount); - assert(int(geomlist->size()) >= 2); - - edit->CurvIdToGeoId.clear(); - edit->PointIdToGeoId.clear(); - - edit->PointIdToGeoId.push_back(-1); // root point - - // information layer - if(rebuildinformationlayer) { - // every time we start with empty information layer - Gui::coinRemoveAllChildren(edit->infoGroup); - } - - int currentInfoNode = 0; - - ParameterGrp::handle hGrpsk = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General"); - - std::vector bsplineGeoIds; - - double combrepscale = 0; // the repscale that would correspond to this comb based only on this calculation. - - // end information layer + // In order to allow to tweak geometry and insert scaling factors, this function needs to + // change the geometry vector. This is highly exceptional for a drawing function and special + // care needs to be taken. This is valid because: + // 1. The treatment is exceptional and no other appropriate place is available to perform this tweak + // 2. The original object needs to remain const for the benefit of all other class hierarchy of drawing functions + // 3. When referring to actual geometry, the modified pointers are short lived, as they are destroyed after drawing + auto tempGeo = const_cast< std::vector< Part::Geometry *> &>(geolist.geomlist); int GeoId = 0; + for (std::vector::const_iterator it = tempGeo.begin(); it != tempGeo.end()-2; ++it, GeoId++) { + if (GeoId >= geolist.getInternalCount()) + GeoId = -geolist.getExternalCount(); - ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); - int stdcountsegments = hGrp->GetInt("SegmentsPerGeometry", 50); - // value cannot be smaller than 3 - if (stdcountsegments < 3) - stdcountsegments = 3; - - // RootPoint - Points.emplace_back(0.,0.,0.); - - for (std::vector::const_iterator it = geomlist->begin(); it != geomlist->end()-2; ++it, GeoId++) { - if (GeoId >= intGeoCount) - GeoId = -extGeoCount; - if ((*it)->getTypeId() == Part::GeomPoint::getClassTypeId()) { // add a point - const Part::GeomPoint *point = static_cast(*it); - Points.push_back(point->getPoint()); - edit->PointIdToGeoId.push_back(GeoId); - } - else if ((*it)->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { // add a line - const Part::GeomLineSegment *lineSeg = static_cast(*it); - // create the definition struct for that geom - Coords.push_back(lineSeg->getStartPoint()); - Coords.push_back(lineSeg->getEndPoint()); - Points.push_back(lineSeg->getStartPoint()); - Points.push_back(lineSeg->getEndPoint()); - Index.push_back(2); - edit->CurvIdToGeoId.push_back(GeoId); - edit->PointIdToGeoId.push_back(GeoId); - edit->PointIdToGeoId.push_back(GeoId); - } - else if ((*it)->getTypeId() == Part::GeomCircle::getClassTypeId()) { // add a circle + if ((*it)->getTypeId() == Part::GeomCircle::getClassTypeId()) { // circle const Part::GeomCircle *circle = static_cast(*it); - Handle(Geom_Circle) curve = Handle(Geom_Circle)::DownCast(circle->handle()); auto gf = GeometryFacade::getFacade(circle); - int countSegments = stdcountsegments; - Base::Vector3d center = circle->getCenter(); - // BSpline weights have a radius corresponding to the weight value // However, in order for them proportional to the B-Spline size, // the scenograph has a size scalefactor times the weight @@ -4029,7 +2427,7 @@ void ViewProviderSketch::draw(bool temp /*=false*/, bool rebuildinformationlayer if(gf->getInternalType() == InternalType::BSplineControlPoint) { for( auto c : getSketchObject()->Constraints.getValues()) { if( c->Type == InternalAlignment && c->AlignmentType == BSplineControlPoint && c->First == GeoId) { - auto bspline = dynamic_cast((*geomlist)[c->Second]); + auto bspline = dynamic_cast(tempGeo[c->Second]); if(bspline){ auto weights = bspline->getWeights(); @@ -4069,22 +2467,18 @@ void ViewProviderSketch::draw(bool temp /*=false*/, bool rebuildinformationlayer } } - // virtual circle or radius vradius - auto mcurve = [¢er, vradius](double param, double &x, double &y) { - x = center.x + vradius*cos(param); - y = center.y + vradius*sin(param); - }; + Part::GeomCircle * tmpcircle; - double x; - double y; - for (int i=0; i < countSegments; i++) { - double param = 2*M_PI*i/countSegments; - mcurve(param,x,y); - Coords.emplace_back(x, y, 0); + if(geometrywithmemoryallocation) { // with memory allocation + tmpcircle = static_cast(*it); + tmpcircle->setRadius(vradius); + } + else { // without memory allocation + tmpcircle = static_cast((*it)->clone()); + tmpcircle->setRadius(vradius); + deepCopiesToDelete.push_back(std::unique_ptr(tmpcircle)); + tempGeo[GeoId] = tmpcircle; // this is the circle that will be drawn, with the updated vradius. } - - mcurve(0,x,y); - Coords.emplace_back(x, y, 0); // save scale factor for any prospective dragging operation // 1. Solver must be updated, in case a dragging operation starts @@ -4115,1838 +2509,61 @@ void ViewProviderSketch::draw(bool temp /*=false*/, bool rebuildinformationlayer } } } - else { - - double segment = (2 * M_PI) / countSegments; - - for (int i=0; i < countSegments; i++) { - gp_Pnt pnt = curve->Value(i*segment); - Coords.emplace_back(pnt.X(), pnt.Y(), pnt.Z()); - } - - gp_Pnt pnt = curve->Value(0); - Coords.emplace_back(pnt.X(), pnt.Y(), pnt.Z()); - } - - Index.push_back(countSegments+1); - edit->CurvIdToGeoId.push_back(GeoId); - Points.push_back(center); - edit->PointIdToGeoId.push_back(GeoId); - } - else if ((*it)->getTypeId() == Part::GeomEllipse::getClassTypeId()) { // add an ellipse - const Part::GeomEllipse *ellipse = static_cast(*it); - Handle(Geom_Ellipse) curve = Handle(Geom_Ellipse)::DownCast(ellipse->handle()); - - int countSegments = stdcountsegments; - Base::Vector3d center = ellipse->getCenter(); - double segment = (2 * M_PI) / countSegments; - for (int i=0; i < countSegments; i++) { - gp_Pnt pnt = curve->Value(i*segment); - Coords.emplace_back(pnt.X(), pnt.Y(), pnt.Z()); - } - - gp_Pnt pnt = curve->Value(0); - Coords.emplace_back(pnt.X(), pnt.Y(), pnt.Z()); - - Index.push_back(countSegments+1); - edit->CurvIdToGeoId.push_back(GeoId); - Points.push_back(center); - edit->PointIdToGeoId.push_back(GeoId); - } - else if ((*it)->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { // add an arc - const Part::GeomArcOfCircle *arc = static_cast(*it); - Handle(Geom_TrimmedCurve) curve = Handle(Geom_TrimmedCurve)::DownCast(arc->handle()); - - double startangle, endangle; - arc->getRange(startangle, endangle, /*emulateCCW=*/false); - if (startangle > endangle) // if arc is reversed - std::swap(startangle, endangle); - - double range = endangle-startangle; - int countSegments = std::max(6, int(stdcountsegments * range / (2 * M_PI))); - double segment = range / countSegments; - - Base::Vector3d center = arc->getCenter(); - Base::Vector3d start = arc->getStartPoint(/*emulateCCW=*/true); - Base::Vector3d end = arc->getEndPoint(/*emulateCCW=*/true); - - for (int i=0; i < countSegments; i++) { - gp_Pnt pnt = curve->Value(startangle); - Coords.emplace_back(pnt.X(), pnt.Y(), pnt.Z()); - startangle += segment; - } - - // end point - gp_Pnt pnt = curve->Value(endangle); - Coords.emplace_back(pnt.X(), pnt.Y(), pnt.Z()); - - Index.push_back(countSegments+1); - edit->CurvIdToGeoId.push_back(GeoId); - Points.push_back(start); - Points.push_back(end); - Points.push_back(center); - edit->PointIdToGeoId.push_back(GeoId); - edit->PointIdToGeoId.push_back(GeoId); - edit->PointIdToGeoId.push_back(GeoId); - } - else if ((*it)->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) { // add an arc - const Part::GeomArcOfEllipse *arc = static_cast(*it); - Handle(Geom_TrimmedCurve) curve = Handle(Geom_TrimmedCurve)::DownCast(arc->handle()); - - double startangle, endangle; - arc->getRange(startangle, endangle, /*emulateCCW=*/false); - if (startangle > endangle) // if arc is reversed - std::swap(startangle, endangle); - - double range = endangle-startangle; - int countSegments = std::max(6, int(stdcountsegments * range / (2 * M_PI))); - double segment = range / countSegments; - - Base::Vector3d center = arc->getCenter(); - Base::Vector3d start = arc->getStartPoint(/*emulateCCW=*/true); - Base::Vector3d end = arc->getEndPoint(/*emulateCCW=*/true); - - for (int i=0; i < countSegments; i++) { - gp_Pnt pnt = curve->Value(startangle); - Coords.emplace_back(pnt.X(), pnt.Y(), pnt.Z()); - startangle += segment; - } - - // end point - gp_Pnt pnt = curve->Value(endangle); - Coords.emplace_back(pnt.X(), pnt.Y(), pnt.Z()); - - Index.push_back(countSegments+1); - edit->CurvIdToGeoId.push_back(GeoId); - Points.push_back(start); - Points.push_back(end); - Points.push_back(center); - edit->PointIdToGeoId.push_back(GeoId); - edit->PointIdToGeoId.push_back(GeoId); - edit->PointIdToGeoId.push_back(GeoId); - } - else if ((*it)->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId()) { - const Part::GeomArcOfHyperbola *aoh = static_cast(*it); - Handle(Geom_TrimmedCurve) curve = Handle(Geom_TrimmedCurve)::DownCast(aoh->handle()); - - double startangle, endangle; - aoh->getRange(startangle, endangle, /*emulateCCW=*/true); - if (startangle > endangle) // if arc is reversed - std::swap(startangle, endangle); - - double range = endangle-startangle; - int countSegments = std::max(6, int(stdcountsegments * range / (2 * M_PI))); - double segment = range / countSegments; - - Base::Vector3d center = aoh->getCenter(); - Base::Vector3d start = aoh->getStartPoint(); - Base::Vector3d end = aoh->getEndPoint(); - - for (int i=0; i < countSegments; i++) { - gp_Pnt pnt = curve->Value(startangle); - Coords.emplace_back(pnt.X(), pnt.Y(), pnt.Z()); - startangle += segment; - } - - // end point - gp_Pnt pnt = curve->Value(endangle); - Coords.emplace_back(pnt.X(), pnt.Y(), pnt.Z()); - - Index.push_back(countSegments+1); - edit->CurvIdToGeoId.push_back(GeoId); - Points.push_back(start); - Points.push_back(end); - Points.push_back(center); - edit->PointIdToGeoId.push_back(GeoId); - edit->PointIdToGeoId.push_back(GeoId); - edit->PointIdToGeoId.push_back(GeoId); - } - else if ((*it)->getTypeId() == Part::GeomArcOfParabola::getClassTypeId()) { - const Part::GeomArcOfParabola *aop = static_cast(*it); - Handle(Geom_TrimmedCurve) curve = Handle(Geom_TrimmedCurve)::DownCast(aop->handle()); - - double startangle, endangle; - aop->getRange(startangle, endangle, /*emulateCCW=*/true); - if (startangle > endangle) // if arc is reversed - std::swap(startangle, endangle); - - double range = endangle-startangle; - int countSegments = std::max(6, int(stdcountsegments * range / (2 * M_PI))); - double segment = range / countSegments; - - Base::Vector3d center = aop->getCenter(); - Base::Vector3d start = aop->getStartPoint(); - Base::Vector3d end = aop->getEndPoint(); - - for (int i=0; i < countSegments; i++) { - gp_Pnt pnt = curve->Value(startangle); - Coords.emplace_back(pnt.X(), pnt.Y(), pnt.Z()); - startangle += segment; - } - - // end point - gp_Pnt pnt = curve->Value(endangle); - Coords.emplace_back(pnt.X(), pnt.Y(), pnt.Z()); - - Index.push_back(countSegments+1); - edit->CurvIdToGeoId.push_back(GeoId); - Points.push_back(start); - Points.push_back(end); - Points.push_back(center); - edit->PointIdToGeoId.push_back(GeoId); - edit->PointIdToGeoId.push_back(GeoId); - edit->PointIdToGeoId.push_back(GeoId); - } - else if ((*it)->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()) { // add a bspline - bsplineGeoIds.push_back(GeoId); - const Part::GeomBSplineCurve *spline = static_cast(*it); - Handle(Geom_BSplineCurve) curve = Handle(Geom_BSplineCurve)::DownCast(spline->handle()); - - Base::Vector3d startp = spline->getStartPoint(); - Base::Vector3d endp = spline->getEndPoint(); - - double first = curve->FirstParameter(); - double last = curve->LastParameter(); - if (first > last) // if arc is reversed - std::swap(first, last); - - double range = last-first; - int countSegments = stdcountsegments; - double segment = range / countSegments; - - for (int i=0; i < countSegments; i++) { - gp_Pnt pnt = curve->Value(first); - Coords.emplace_back(pnt.X(), pnt.Y(), pnt.Z()); - first += segment; - } - - // end point - gp_Pnt end = curve->Value(last); - Coords.emplace_back(end.X(), end.Y(), end.Z()); - - Index.push_back(countSegments+1); - edit->CurvIdToGeoId.push_back(GeoId); - Points.push_back(startp); - Points.push_back(endp); - edit->PointIdToGeoId.push_back(GeoId); - edit->PointIdToGeoId.push_back(GeoId); - - //*************************************************************************************************************** - // global information gathering for geometry information layer - - std::vector poles = spline->getPoles(); - - Base::Vector3d midp = Base::Vector3d(0,0,0); - - for (std::vector::iterator it = poles.begin(); it != poles.end(); ++it) { - midp += (*it); - } - - midp /= poles.size(); - - double firstparam = spline->getFirstParameter(); - double lastparam = spline->getLastParameter(); - - const int ndiv = poles.size()>4?poles.size()*16:64; - double step = (lastparam - firstparam ) / (ndiv -1); - - std::vector paramlist(ndiv); - std::vector pointatcurvelist(ndiv); - std::vector curvaturelist(ndiv); - std::vector normallist(ndiv); - - double maxcurv = 0; - double maxdisttocenterofmass = 0; - - for (int i = 0; i < ndiv; i++) { - paramlist[i] = firstparam + i * step; - pointatcurvelist[i] = spline->pointAtParameter(paramlist[i]); - - try { - curvaturelist[i] = spline->curvatureAt(paramlist[i]); - } - catch(Base::CADKernelError &e) { - // it is "just" a visualisation matter OCC could not calculate the curvature - // terminating here would mean that the other shapes would not be drawn. - // Solution: Report the issue and set dummy curvature to 0 - e.ReportException(); - Base::Console().Error("Curvature graph for B-Spline with GeoId=%d could not be calculated.\n", GeoId); - curvaturelist[i] = 0; - } - - if (curvaturelist[i] > maxcurv) - maxcurv = curvaturelist[i]; - - double tempf = ( pointatcurvelist[i] - midp ).Length(); - - if (tempf > maxdisttocenterofmass) - maxdisttocenterofmass = tempf; - - } - - double temprepscale = 0; - if (maxcurv > 0) - temprepscale = (0.5 * maxdisttocenterofmass) / maxcurv; // just a factor to make a comb reasonably visible - - if (temprepscale > combrepscale) - combrepscale = temprepscale; } } +} - if ( (combrepscale > (2 * combrepscalehyst)) || (combrepscale < (combrepscalehyst/2))) - combrepscalehyst = combrepscale ; +void ViewProviderSketch::draw(bool temp /*=false*/, bool rebuildinformationoverlay /*=true*/) +{ + assert(isInEditMode()); + // ============== Retrieve geometry to be represented ================================= - // geometry information layer for bsplines, as they need a second round now that max curvature is known - for (std::vector::const_iterator it = bsplineGeoIds.begin(); it != bsplineGeoIds.end(); ++it) { + std::vector tempGeo; - const Part::Geometry *geo = GeoById(*geomlist, *it); + if (temp) + tempGeo = getSolvedSketch().extractGeometry(true, true); // with memory allocation + else + tempGeo = getSketchObject()->getCompleteGeometry(); // without memory allocation - const Part::GeomBSplineCurve *spline = static_cast(geo); + int intGeoCount = getSketchObject()->getHighestCurveIndex() + 1; - //---------------------------------------------------------- - // geometry information layer + auto geolist = GeoList::getGeoListModel(tempGeo, intGeoCount); - // polynom degree -------------------------------------------------------- - std::vector poles = spline->getPoles(); + assert(int(tempGeo.size()) == geolist.getExternalCount() + intGeoCount); + assert(int(tempGeo.size()) >= 2); - Base::Vector3d midp = Base::Vector3d(0,0,0); + // ============== Prepare geometry for representation ================================== - for (std::vector::iterator it = poles.begin(); it != poles.end(); ++it) { - midp += (*it); - } + // ************ Manage BSpline pole circle scaling **************************** - midp /= poles.size(); + // memory management of deep copies necessary for drawing which are destroyed when the vector gets out of scope (i.e. at the end of this function). + std::vector> deepCopiesToDelete; - if (rebuildinformationlayer) { - SoSwitch *sw = new SoSwitch(); + // This function ensures that the geometry used for drawing takes into account: + // 1. the OCC mandated weight, which is normalised for non-rational BSplines, but not normalised for rational BSplines. + // That includes properly sizing for drawing any weight constraint. + // This function ensures that both the geometry of the SketchObject and solver are updated with the new value of the scaling factor (via the extension) + // 2. the scaling factor, including inserting the scaling factor into the ViewProviderSketchGeometryExtension so as to enable + // That ensures that dragging operations on the circles of the poles of the B-Splines are properly rendered. + // + // This function takes a reference to a vector of deep copies to delete. These deep copies are necessary to transparently perform (1) while doing (2). - sw->whichChild = hGrpsk->GetBool("BSplineDegreeVisible", true)?SO_SWITCH_ALL:SO_SWITCH_NONE; + scaleBSplinePoleCirclesAndUpdateSolverAndSketchObjectGeometry( + geolist, + temp, + deepCopiesToDelete); - SoSeparator *sep = new SoSeparator(); - sep->ref(); - // no caching for frequently-changing data structures - sep->renderCaching = SoSeparator::OFF; + // ============== Render geometry, constraints and geometry information overlays ================================== - // every information visual node gets its own material for to-be-implemented preselection and selection - SoMaterial *mat = new SoMaterial; - mat->ref(); - mat->diffuseColor = InformationColor; + auto geolistfacade = Sketcher::getGeoListFacade(geolist); - SoTranslation *translate = new SoTranslation; - - translate->translation.setValue(midp.x,midp.y,zInfo); - - SoFont *font = new SoFont; - font->name.setValue("Helvetica"); - font->size.setValue(edit->coinFontSize); - - SoText2 *degreetext = new SoText2; - degreetext->string = SbString(spline->getDegree()); - - sep->addChild(translate); - sep->addChild(mat); - sep->addChild(font); - sep->addChild(degreetext); - - sw->addChild(sep); - - edit->infoGroup->addChild(sw); - sep->unref(); - mat->unref(); - } - else { - SoSwitch *sw = static_cast(edit->infoGroup->getChild(currentInfoNode)); - - if (visibleInformationChanged) - sw->whichChild = hGrpsk->GetBool("BSplineDegreeVisible", true)?SO_SWITCH_ALL:SO_SWITCH_NONE; - - SoSeparator *sep = static_cast(sw->getChild(0)); - - static_cast(sep->getChild(GEOINFO_BSPLINE_DEGREE_POS))->translation.setValue(midp.x,midp.y,zInfo); - - static_cast(sep->getChild(GEOINFO_BSPLINE_DEGREE_TEXT))->string = SbString(spline->getDegree()); - } - - currentInfoNode++; // switch to next node - - // control polygon -------------------------------------------------------- - if (rebuildinformationlayer) { - SoSwitch *sw = new SoSwitch(); - - sw->whichChild = hGrpsk->GetBool("BSplineControlPolygonVisible", true)?SO_SWITCH_ALL:SO_SWITCH_NONE; - - SoSeparator *sep = new SoSeparator(); - sep->ref(); - // no caching for frequently-changing data structures - sep->renderCaching = SoSeparator::OFF; - - // every information visual node gets its own material for to-be-implemented preselection and selection - SoMaterial *mat = new SoMaterial; - mat->ref(); - mat->diffuseColor = InformationColor; - - SoLineSet *polygon = new SoLineSet; - - SoCoordinate3 *polygoncoords = new SoCoordinate3; - - if (spline->isPeriodic()) { - polygoncoords->point.setNum(poles.size()+1); - } - else { - polygoncoords->point.setNum(poles.size()); - } - - SbVec3f *vts = polygoncoords->point.startEditing(); - - int i=0; - for (std::vector::iterator it = poles.begin(); it != poles.end(); ++it, i++) { - vts[i].setValue((*it).x,(*it).y,zInfo); - } - - if (spline->isPeriodic()) { - vts[poles.size()].setValue(poles[0].x,poles[0].y,zInfo); - } - - polygoncoords->point.finishEditing(); - - sep->addChild(mat); - sep->addChild(polygoncoords); - sep->addChild(polygon); - - sw->addChild(sep); - - edit->infoGroup->addChild(sw); - sep->unref(); - mat->unref(); - } - else { - SoSwitch *sw = static_cast(edit->infoGroup->getChild(currentInfoNode)); - - if(visibleInformationChanged) - sw->whichChild = hGrpsk->GetBool("BSplineControlPolygonVisible", true)?SO_SWITCH_ALL:SO_SWITCH_NONE; - - SoSeparator *sep = static_cast(sw->getChild(0)); - - SoCoordinate3 *polygoncoords = static_cast(sep->getChild(GEOINFO_BSPLINE_POLYGON)); - - if(spline->isPeriodic()) { - polygoncoords->point.setNum(poles.size()+1); - } - else { - polygoncoords->point.setNum(poles.size()); - } - - SbVec3f *vts = polygoncoords->point.startEditing(); - - int i=0; - for (std::vector::iterator it = poles.begin(); it != poles.end(); ++it, i++) { - vts[i].setValue((*it).x,(*it).y,zInfo); - } - - if(spline->isPeriodic()) { - vts[poles.size()].setValue(poles[0].x,poles[0].y,zInfo); - } - - polygoncoords->point.finishEditing(); - - } - currentInfoNode++; // switch to next node - - // curvature graph -------------------------------------------------------- - - // reimplementation of python source: - // https://github.com/tomate44/CurvesWB/blob/master/ParametricComb.py - // by FreeCAD user Chris_G - - double firstparam = spline->getFirstParameter(); - double lastparam = spline->getLastParameter(); - - const int ndiv = poles.size()>4?poles.size()*16:64; - double step = (lastparam - firstparam ) / (ndiv -1); - - std::vector paramlist(ndiv); - std::vector pointatcurvelist(ndiv); - std::vector curvaturelist(ndiv); - std::vector normallist(ndiv); - - for(int i = 0; i < ndiv; i++) { - paramlist[i] = firstparam + i * step; - pointatcurvelist[i] = spline->pointAtParameter(paramlist[i]); - - try { - curvaturelist[i] = spline->curvatureAt(paramlist[i]); - } - catch(Base::CADKernelError &e) { - // it is "just" a visualisation matter OCC could not calculate the curvature - // terminating here would mean that the other shapes would not be drawn. - // Solution: Report the issue and set dummy curvature to 0 - e.ReportException(); - Base::Console().Error("Curvature graph for B-Spline with GeoId=%d could not be calculated.\n", GeoId); - curvaturelist[i] = 0; - } - - try { - spline->normalAt(paramlist[i],normallist[i]); - } - catch(Base::Exception&) { - normallist[i] = Base::Vector3d(0,0,0); - } - - } - - std::vector pointatcomblist(ndiv); - - for(int i = 0; i < ndiv; i++) { - pointatcomblist[i] = pointatcurvelist[i] - combrepscalehyst * curvaturelist[i] * normallist[i]; - } - - if (rebuildinformationlayer) { - SoSwitch *sw = new SoSwitch(); - - sw->whichChild = hGrpsk->GetBool("BSplineCombVisible", true)?SO_SWITCH_ALL:SO_SWITCH_NONE; - - SoSeparator *sep = new SoSeparator(); - sep->ref(); - // no caching for frequently-changing data structures - sep->renderCaching = SoSeparator::OFF; - - // every information visual node gets its own material for to-be-implemented preselection and selection - SoMaterial *mat = new SoMaterial; - mat->ref(); - mat->diffuseColor = InformationColor; - - SoLineSet *comblineset = new SoLineSet; - - SoCoordinate3 *combcoords = new SoCoordinate3; - - combcoords->point.setNum(3*ndiv); // 2*ndiv +1 points of ndiv separate segments + ndiv points for last segment - comblineset->numVertices.setNum(ndiv+1); // ndiv separate segments of radials + 1 segment connecting at comb end - - int32_t *index = comblineset->numVertices.startEditing(); - SbVec3f *vts = combcoords->point.startEditing(); - - for(int i = 0; i < ndiv; i++) { - vts[2*i].setValue(pointatcurvelist[i].x, pointatcurvelist[i].y, zInfo); // radials - vts[2*i+1].setValue(pointatcomblist[i].x, pointatcomblist[i].y, zInfo); - index[i] = 2; - - vts[2*ndiv+i].setValue(pointatcomblist[i].x, pointatcomblist[i].y, zInfo); // comb endpoint closing segment - } - - index[ndiv] = ndiv; // comb endpoint closing segment - - combcoords->point.finishEditing(); - comblineset->numVertices.finishEditing(); - - sep->addChild(mat); - sep->addChild(combcoords); - sep->addChild(comblineset); - - sw->addChild(sep); - - edit->infoGroup->addChild(sw); - sep->unref(); - mat->unref(); - } - else { - SoSwitch *sw = static_cast(edit->infoGroup->getChild(currentInfoNode)); - - if(visibleInformationChanged) - sw->whichChild = hGrpsk->GetBool("BSplineCombVisible", true)?SO_SWITCH_ALL:SO_SWITCH_NONE; - - SoSeparator *sep = static_cast(sw->getChild(0)); - - SoCoordinate3 *combcoords = static_cast(sep->getChild(GEOINFO_BSPLINE_POLYGON)); - - SoLineSet *comblineset = static_cast(sep->getChild(GEOINFO_BSPLINE_POLYGON+1)); - - combcoords->point.setNum(3*ndiv); // 2*ndiv +1 points of ndiv separate segments + ndiv points for last segment - comblineset->numVertices.setNum(ndiv+1); // ndiv separate segments of radials + 1 segment connecting at comb end - - int32_t *index = comblineset->numVertices.startEditing(); - SbVec3f *vts = combcoords->point.startEditing(); - - for(int i = 0; i < ndiv; i++) { - vts[2*i].setValue(pointatcurvelist[i].x, pointatcurvelist[i].y, zInfo); // radials - vts[2*i+1].setValue(pointatcomblist[i].x, pointatcomblist[i].y, zInfo); - index[i] = 2; - - vts[2*ndiv+i].setValue(pointatcomblist[i].x, pointatcomblist[i].y, zInfo); // comb endpoint closing segment - } - - index[ndiv] = ndiv; // comb endpoint closing segment - - combcoords->point.finishEditing(); - comblineset->numVertices.finishEditing(); - - } - - currentInfoNode++; // switch to next node - - // knot multiplicity -------------------------------------------------------- - std::vector knots = spline->getKnots(); - std::vector mult = spline->getMultiplicities(); - - std::vector::const_iterator itk; - std::vector::const_iterator itm; - - - if (rebuildinformationlayer) { - - for( itk = knots.begin(), itm = mult.begin(); itk != knots.end() && itm != mult.end(); ++itk, ++itm) { - - SoSwitch *sw = new SoSwitch(); - - sw->whichChild = hGrpsk->GetBool("BSplineKnotMultiplicityVisible", true)?SO_SWITCH_ALL:SO_SWITCH_NONE; - - SoSeparator *sep = new SoSeparator(); - sep->ref(); - // no caching for frequently-changing data structures - sep->renderCaching = SoSeparator::OFF; - - // every information visual node gets its own material for to-be-implemented preselection and selection - SoMaterial *mat = new SoMaterial; - mat->ref(); - mat->diffuseColor = InformationColor; - - SoTranslation *translate = new SoTranslation; - - Base::Vector3d knotposition = spline->pointAtParameter(*itk); - - translate->translation.setValue(knotposition.x, knotposition.y, zInfo); - - SoFont *font = new SoFont; - font->name.setValue("Helvetica"); - font->size.setValue(edit->coinFontSize); - - SoText2 *degreetext = new SoText2; - degreetext->string = SbString("(") + SbString(*itm) + SbString(")"); - - sep->addChild(translate); - sep->addChild(mat); - sep->addChild(font); - sep->addChild(degreetext); - - sw->addChild(sep); - - edit->infoGroup->addChild(sw); - sep->unref(); - mat->unref(); - - currentInfoNode++; // switch to next node - } - } - else { - for( itk = knots.begin(), itm = mult.begin(); itk != knots.end() && itm != mult.end(); ++itk, ++itm) { - SoSwitch *sw = static_cast(edit->infoGroup->getChild(currentInfoNode)); - - if(visibleInformationChanged) - sw->whichChild = hGrpsk->GetBool("BSplineKnotMultiplicityVisible", true)?SO_SWITCH_ALL:SO_SWITCH_NONE; - - SoSeparator *sep = static_cast(sw->getChild(0)); - - Base::Vector3d knotposition = spline->pointAtParameter(*itk); - - static_cast(sep->getChild(GEOINFO_BSPLINE_DEGREE_POS))->translation.setValue(knotposition.x,knotposition.y,zInfo); - - static_cast(sep->getChild(GEOINFO_BSPLINE_DEGREE_TEXT))->string = SbString("(") + SbString(*itm) + SbString(")"); - - currentInfoNode++; // switch to next node - } - } - - // End of knot multiplicity - - // pole weights -------------------------------------------------------- - std::vector weights = spline->getWeights(); - - if (rebuildinformationlayer) { - - for (size_t index = 0; index < weights.size(); ++index) { - - SoSwitch* sw = new SoSwitch(); - - sw->whichChild = hGrpsk->GetBool("BSplinePoleWeightVisible", true) ? SO_SWITCH_ALL : SO_SWITCH_NONE; - - SoSeparator* sep = new SoSeparator(); - sep->ref(); - // no caching for frequently-changing data structures - sep->renderCaching = SoSeparator::OFF; - - // every information visual node gets its own material for to-be-implemented preselection and selection - SoMaterial* mat = new SoMaterial; - mat->ref(); - mat->diffuseColor = InformationColor; - - SoTranslation* translate = new SoTranslation; - - Base::Vector3d poleposition = poles[index]; - - SoFont* font = new SoFont; - font->name.setValue("Helvetica"); - font->size.setValue(edit->coinFontSize); - - translate->translation.setValue(poleposition.x, poleposition.y, zInfo); - - // set up string with weight value and the user-defined number of decimals - QString WeightString = QString::fromLatin1("%1").arg(weights[index], 0, 'f', Base::UnitsApi::getDecimals()); - - SoText2* WeightText = new SoText2; - // since the first and last control point of a spline is also treated as knot and thus - // can also have a displayed multiplicity, we must assure the multiplicity is not visibly overwritten - // therefore be output the weight in a second line - SoMFString label; - label.set1Value(0, SbString("")); - label.set1Value(1, SbString("[") + SbString(WeightString.toStdString().c_str()) + SbString("]")); - WeightText->string = label; - - sep->addChild(translate); - sep->addChild(mat); - sep->addChild(font); - sep->addChild(WeightText); - - sw->addChild(sep); - - edit->infoGroup->addChild(sw); - sep->unref(); - mat->unref(); - - currentInfoNode++; // switch to next node - } - } - else { - for (size_t index = 0; index < weights.size(); ++index) { - SoSwitch* sw = static_cast(edit->infoGroup->getChild(currentInfoNode)); - - if (visibleInformationChanged) - sw->whichChild = hGrpsk->GetBool("BSplinePoleWeightVisible", true) ? SO_SWITCH_ALL : SO_SWITCH_NONE; - - SoSeparator* sep = static_cast(sw->getChild(0)); - - Base::Vector3d poleposition = poles[index]; - - static_cast(sep->getChild(GEOINFO_BSPLINE_DEGREE_POS)) - ->translation.setValue(poleposition.x, poleposition.y, zInfo); - - // set up string with weight value and the user-defined number of decimals - QString WeightString = QString::fromLatin1("%1").arg(weights[index], 0, 'f', Base::UnitsApi::getDecimals()); - - // since the first and last control point of a spline is also treated as knot and thus - // can also have a displayed multiplicity, we must assure the multiplicity is not visibly overwritten - // therefore be output the weight in a second line - SoMFString label; - label.set1Value(0, SbString("")); - label.set1Value(1, SbString("[") + SbString(WeightString.toStdString().c_str()) + SbString("]")); - - static_cast(sep->getChild(GEOINFO_BSPLINE_DEGREE_TEXT)) - ->string = label; - - currentInfoNode++; // switch to next node - } - } - - // End of pole weights - } - - - - visibleInformationChanged=false; // whatever that changed in Information layer is already updated - - edit->CurvesCoordinate->point.setNum(Coords.size()); - edit->CurveSet->numVertices.setNum(Index.size()); - edit->CurvesMaterials->diffuseColor.setNum(Index.size()); - edit->PointsCoordinate->point.setNum(Points.size()); - edit->PointsMaterials->diffuseColor.setNum(Points.size()); - - SbVec3f *verts = edit->CurvesCoordinate->point.startEditing(); - int32_t *index = edit->CurveSet->numVertices.startEditing(); - SbVec3f *pverts = edit->PointsCoordinate->point.startEditing(); - - float dMg = 100; - - int i=0; // setting up the line set - for (std::vector::const_iterator it = Coords.begin(); it != Coords.end(); ++it,i++) { - dMg = dMg>std::abs(it->x)?dMg:std::abs(it->x); - dMg = dMg>std::abs(it->y)?dMg:std::abs(it->y); - verts[i].setValue(it->x,it->y,zLowLines); - } - - i=0; // setting up the indexes of the line set - for (std::vector::const_iterator it = Index.begin(); it != Index.end(); ++it,i++) - index[i] = *it; - - i=0; // setting up the point set - for (std::vector::const_iterator it = Points.begin(); it != Points.end(); ++it,i++){ - dMg = dMg>std::abs(it->x)?dMg:std::abs(it->x); - dMg = dMg>std::abs(it->y)?dMg:std::abs(it->y); - pverts[i].setValue(it->x,it->y,zLowPoints); - } - - edit->CurvesCoordinate->point.finishEditing(); - edit->CurveSet->numVertices.finishEditing(); - edit->PointsCoordinate->point.finishEditing(); - - // set cross coordinates - edit->RootCrossSet->numVertices.set1Value(0,2); - edit->RootCrossSet->numVertices.set1Value(1,2); - - // This code relies on Part2D, which is generally not updated in no update mode. - // Additionally it does not relate to the actual sketcher geometry. - - /* - Base::Console().Log("MinX:%d,MaxX:%d,MinY:%d,MaxY:%d\n",MinX,MaxX,MinY,MaxY); - // make sure that nine of the numbers are exactly zero because log(0) - // is not defined - float xMin = std::abs(MinX) < FLT_EPSILON ? 0.01f : MinX; - float xMax = std::abs(MaxX) < FLT_EPSILON ? 0.01f : MaxX; - float yMin = std::abs(MinY) < FLT_EPSILON ? 0.01f : MinY; - float yMax = std::abs(MaxY) < FLT_EPSILON ? 0.01f : MaxY; - */ - - float dMagF = exp(ceil(log(std::abs(dMg)))); - - updateGridExtent(-dMagF, dMagF, -dMagF, dMagF); - - edit->RootCrossCoordinate->point.set1Value(0,SbVec3f(-dMagF, 0.0f, zCross)); - edit->RootCrossCoordinate->point.set1Value(1,SbVec3f(dMagF, 0.0f, zCross)); - edit->RootCrossCoordinate->point.set1Value(2,SbVec3f(0.0f, -dMagF, zCross)); - edit->RootCrossCoordinate->point.set1Value(3,SbVec3f(0.0f, dMagF, zCross)); - - // Render Constraints =================================================== - const std::vector &constrlist = getSketchObject()->Constraints.getValues(); - // After an undo/redo it can happen that we have an empty geometry list but a non-empty constraint list - // In this case just ignore the constraints. (See bug #0000421) - if (geomlist->size() <= 2 && !constrlist.empty()) { - rebuildConstraintsVisual(); - return; - } - // reset point if the constraint type has changed -Restart: - // check if a new constraint arrived - if (constrlist.size() != edit->vConstrType.size()) - rebuildConstraintsVisual(); - assert(int(constrlist.size()) == edit->constrGroup->getNumChildren()); - assert(int(edit->vConstrType.size()) == edit->constrGroup->getNumChildren()); - // update the virtual space - updateVirtualSpace(); - // go through the constraints and update the position - i = 0; - for (std::vector::const_iterator it=constrlist.begin(); - it != constrlist.end(); ++it, i++) { - // check if the type has changed - if ((*it)->Type != edit->vConstrType[i]) { - // clearing the type vector will force a rebuild of the visual nodes - edit->vConstrType.clear(); - //TODO: The 'goto' here is unsafe as it can happen that we cause an endless loop (see bug #0001956). - goto Restart; - } - try{//because calculateNormalAtPoint, used in there, can throw - // root separator for this constraint - SoSeparator *sep = static_cast(edit->constrGroup->getChild(i)); - const Constraint *Constr = *it; - - if(Constr->First < -extGeoCount || Constr->First >= intGeoCount - || (Constr->Second!=GeoEnum::GeoUndef - && (Constr->Second < -extGeoCount || Constr->Second >= intGeoCount)) - || (Constr->Third!=GeoEnum::GeoUndef - && (Constr->Third < -extGeoCount || Constr->Third >= intGeoCount))) - { - // Constraint can refer to non-existent geometry during undo/redo - continue; - } - - // distinguish different constraint types to build up - switch (Constr->Type) { - case Block: - case Horizontal: // write the new position of the Horizontal constraint Same as vertical position. - case Vertical: // write the new position of the Vertical constraint - { - assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); - bool alignment = Constr->Type!=Block && Constr->Second != GeoEnum::GeoUndef; - - // get the geometry - const Part::Geometry *geo = GeoById(*geomlist, Constr->First); - - if (!alignment) { - // Vertical & Horiz can only be a GeomLineSegment, but Blocked can be anything. - Base::Vector3d midpos; - Base::Vector3d dir; - Base::Vector3d norm; - - if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { - const Part::GeomLineSegment *lineSeg = static_cast(geo); - - // calculate the half distance between the start and endpoint - midpos = ((lineSeg->getEndPoint()+lineSeg->getStartPoint())/2); - - //Get a set of vectors perpendicular and tangential to these - dir = (lineSeg->getEndPoint()-lineSeg->getStartPoint()).Normalize(); - - norm = Base::Vector3d(-dir.y,dir.x,0); - } - else if (geo->getTypeId() == Part::GeomBSplineCurve::getClassTypeId()) { - const Part::GeomBSplineCurve *bsp = static_cast(geo); - midpos = Base::Vector3d(0,0,0); - - std::vector poles = bsp->getPoles(); - - // Move center of gravity towards start not to collide with bspline degree information. - double ws = 1.0 / poles.size(); - double w = 1.0; - - for (std::vector::iterator it = poles.begin(); it != poles.end(); ++it) { - midpos += w*(*it); - w -= ws; - } - - midpos /= poles.size(); - - dir = (bsp->getEndPoint() - bsp->getStartPoint()).Normalize(); - norm = Base::Vector3d(-dir.y,dir.x,0); - } - else { - double ra=0,rb=0; - double angle,angleplus=0.;//angle = rotation of object as a whole; angleplus = arc angle (t parameter for ellipses). - if (geo->getTypeId() == Part::GeomCircle::getClassTypeId()) { - const Part::GeomCircle *circle = static_cast(geo); - ra = circle->getRadius(); - angle = M_PI/4; - midpos = circle->getCenter(); - } else if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { - const Part::GeomArcOfCircle *arc = static_cast(geo); - ra = arc->getRadius(); - double startangle, endangle; - arc->getRange(startangle, endangle, /*emulateCCW=*/true); - angle = (startangle + endangle)/2; - midpos = arc->getCenter(); - } else if (geo->getTypeId() == Part::GeomEllipse::getClassTypeId()) { - const Part::GeomEllipse *ellipse = static_cast(geo); - ra = ellipse->getMajorRadius(); - rb = ellipse->getMinorRadius(); - Base::Vector3d majdir = ellipse->getMajorAxisDir(); - angle = atan2(majdir.y, majdir.x); - angleplus = M_PI/4; - midpos = ellipse->getCenter(); - } else if (geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) { - const Part::GeomArcOfEllipse *aoe = static_cast(geo); - ra = aoe->getMajorRadius(); - rb = aoe->getMinorRadius(); - double startangle, endangle; - aoe->getRange(startangle, endangle, /*emulateCCW=*/true); - Base::Vector3d majdir = aoe->getMajorAxisDir(); - angle = atan2(majdir.y, majdir.x); - angleplus = (startangle + endangle)/2; - midpos = aoe->getCenter(); - } else if (geo->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId()) { - const Part::GeomArcOfHyperbola *aoh = static_cast(geo); - ra = aoh->getMajorRadius(); - rb = aoh->getMinorRadius(); - double startangle, endangle; - aoh->getRange(startangle, endangle, /*emulateCCW=*/true); - Base::Vector3d majdir = aoh->getMajorAxisDir(); - angle = atan2(majdir.y, majdir.x); - angleplus = (startangle + endangle)/2; - midpos = aoh->getCenter(); - } else if (geo->getTypeId() == Part::GeomArcOfParabola::getClassTypeId()) { - const Part::GeomArcOfParabola *aop = static_cast(geo); - ra = aop->getFocal(); - double startangle, endangle; - aop->getRange(startangle, endangle, /*emulateCCW=*/true); - Base::Vector3d majdir = - aop->getXAxisDir(); - angle = atan2(majdir.y, majdir.x); - angleplus = (startangle + endangle)/2; - midpos = aop->getFocus(); - } else - break; - - if( geo->getTypeId() == Part::GeomEllipse::getClassTypeId() || - geo->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() || - geo->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ){ - - Base::Vector3d majDir, minDir, rvec; - majDir = Base::Vector3d(cos(angle),sin(angle),0);//direction of major axis of ellipse - minDir = Base::Vector3d(-majDir.y,majDir.x,0);//direction of minor axis of ellipse - rvec = (ra*cos(angleplus)) * majDir + (rb*sin(angleplus)) * minDir; - midpos += rvec; - rvec.Normalize(); - norm = rvec; - dir = Base::Vector3d(-rvec.y,rvec.x,0);//DeepSOIC: I'm not sure what dir is supposed to mean. - } - else { - norm = Base::Vector3d(cos(angle),sin(angle),0); - dir = Base::Vector3d(-norm.y,norm.x,0); - midpos += ra*norm; - } - } - - Base::Vector3d relpos = seekConstraintPosition(midpos, norm, dir, 2.5, edit->constrGroup->getChild(i)); - - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION))->abPos = SbVec3f(midpos.x, midpos.y, zConstr); //Absolute Reference - - //Reference Position that is scaled according to zoom - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION))->translation = SbVec3f(relpos.x, relpos.y, 0); - } - else { - assert(Constr->Second >= -extGeoCount && Constr->Second < intGeoCount); - assert(Constr->FirstPos != Sketcher::PointPos::none && Constr->SecondPos != Sketcher::PointPos::none); - - Base::Vector3d midpos1, dir1, norm1; - Base::Vector3d midpos2, dir2, norm2; - - if (temp) - midpos1 = getSolvedSketch().getPoint(Constr->First, Constr->FirstPos); - else - midpos1 = getSketchObject()->getPoint(Constr->First, Constr->FirstPos); - - if (temp) - midpos2 = getSolvedSketch().getPoint(Constr->Second, Constr->SecondPos); - else - midpos2 = getSketchObject()->getPoint(Constr->Second, Constr->SecondPos); - - dir1 = (midpos2-midpos1).Normalize(); - dir2 = -dir1; - norm1 = Base::Vector3d(-dir1.y,dir1.x,0.); - norm2 = norm1; - - Base::Vector3d relpos1 = seekConstraintPosition(midpos1, norm1, dir1, 4.0, edit->constrGroup->getChild(i)); - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION))->abPos = SbVec3f(midpos1.x, midpos1.y, zConstr); - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION))->translation = SbVec3f(relpos1.x, relpos1.y, 0); - - Base::Vector3d relpos2 = seekConstraintPosition(midpos2, norm2, dir2, 4.0, edit->constrGroup->getChild(i)); - - Base::Vector3d secondPos = midpos2 - midpos1; - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION))->abPos = SbVec3f(secondPos.x, secondPos.y, zConstr); - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION))->translation = SbVec3f(relpos2.x -relpos1.x, relpos2.y -relpos1.y, 0); - } - } - break; - case Perpendicular: - { - assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); - assert(Constr->Second >= -extGeoCount && Constr->Second < intGeoCount); - // get the geometry - const Part::Geometry *geo1 = GeoById(*geomlist, Constr->First); - const Part::Geometry *geo2 = GeoById(*geomlist, Constr->Second); - - Base::Vector3d midpos1, dir1, norm1; - Base::Vector3d midpos2, dir2, norm2; - bool twoIcons = false;//a very local flag. It's set to true to indicate that the second dir+norm are valid and should be used - - - if (Constr->Third != GeoEnum::GeoUndef || //perpty via point - Constr->FirstPos != Sketcher::PointPos::none) { //endpoint-to-curve or endpoint-to-endpoint perpty - - int ptGeoId; - Sketcher::PointPos ptPosId; - do {//dummy loop to use break =) Maybe goto? - ptGeoId = Constr->First; - ptPosId = Constr->FirstPos; - if (ptPosId != Sketcher::PointPos::none) break; - ptGeoId = Constr->Second; - ptPosId = Constr->SecondPos; - if (ptPosId != Sketcher::PointPos::none) break; - ptGeoId = Constr->Third; - ptPosId = Constr->ThirdPos; - if (ptPosId != Sketcher::PointPos::none) break; - assert(0);//no point found! - } while (false); - if (temp) - midpos1 = getSolvedSketch().getPoint(ptGeoId, ptPosId); - else - midpos1 = getSketchObject()->getPoint(ptGeoId, ptPosId); - - norm1 = getSolvedSketch().calculateNormalAtPoint(Constr->Second, midpos1.x, midpos1.y); - norm1.Normalize(); - dir1 = norm1; dir1.RotateZ(-M_PI/2.0); - - } else if (Constr->FirstPos == Sketcher::PointPos::none) { - - if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { - const Part::GeomLineSegment *lineSeg1 = static_cast(geo1); - midpos1 = ((lineSeg1->getEndPoint()+lineSeg1->getStartPoint())/2); - dir1 = (lineSeg1->getEndPoint()-lineSeg1->getStartPoint()).Normalize(); - norm1 = Base::Vector3d(-dir1.y,dir1.x,0.); - } else if (geo1->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { - const Part::GeomArcOfCircle *arc = static_cast(geo1); - double startangle, endangle, midangle; - arc->getRange(startangle, endangle, /*emulateCCW=*/true); - midangle = (startangle + endangle)/2; - norm1 = Base::Vector3d(cos(midangle),sin(midangle),0); - dir1 = Base::Vector3d(-norm1.y,norm1.x,0); - midpos1 = arc->getCenter() + arc->getRadius() * norm1; - } else if (geo1->getTypeId() == Part::GeomCircle::getClassTypeId()) { - const Part::GeomCircle *circle = static_cast(geo1); - norm1 = Base::Vector3d(cos(M_PI/4),sin(M_PI/4),0); - dir1 = Base::Vector3d(-norm1.y,norm1.x,0); - midpos1 = circle->getCenter() + circle->getRadius() * norm1; - } else - break; - - if (geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { - const Part::GeomLineSegment *lineSeg2 = static_cast(geo2); - midpos2 = ((lineSeg2->getEndPoint()+lineSeg2->getStartPoint())/2); - dir2 = (lineSeg2->getEndPoint()-lineSeg2->getStartPoint()).Normalize(); - norm2 = Base::Vector3d(-dir2.y,dir2.x,0.); - } else if (geo2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { - const Part::GeomArcOfCircle *arc = static_cast(geo2); - double startangle, endangle, midangle; - arc->getRange(startangle, endangle, /*emulateCCW=*/true); - midangle = (startangle + endangle)/2; - norm2 = Base::Vector3d(cos(midangle),sin(midangle),0); - dir2 = Base::Vector3d(-norm2.y,norm2.x,0); - midpos2 = arc->getCenter() + arc->getRadius() * norm2; - } else if (geo2->getTypeId() == Part::GeomCircle::getClassTypeId()) { - const Part::GeomCircle *circle = static_cast(geo2); - norm2 = Base::Vector3d(cos(M_PI/4),sin(M_PI/4),0); - dir2 = Base::Vector3d(-norm2.y,norm2.x,0); - midpos2 = circle->getCenter() + circle->getRadius() * norm2; - } else - break; - twoIcons = true; - } - - Base::Vector3d relpos1 = seekConstraintPosition(midpos1, norm1, dir1, 4.0, edit->constrGroup->getChild(i)); - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION))->abPos = SbVec3f(midpos1.x, midpos1.y, zConstr); - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION))->translation = SbVec3f(relpos1.x, relpos1.y, 0); - - if (twoIcons) { - Base::Vector3d relpos2 = seekConstraintPosition(midpos2, norm2, dir2, 4.0, edit->constrGroup->getChild(i)); - - Base::Vector3d secondPos = midpos2 - midpos1; - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION))->abPos = SbVec3f(secondPos.x, secondPos.y, zConstr); - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION))->translation = SbVec3f(relpos2.x -relpos1.x, relpos2.y -relpos1.y, 0); - } - - } - break; - case Parallel: - case Equal: - { - assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); - assert(Constr->Second >= -extGeoCount && Constr->Second < intGeoCount); - // get the geometry - const Part::Geometry *geo1 = GeoById(*geomlist, Constr->First); - const Part::Geometry *geo2 = GeoById(*geomlist, Constr->Second); - - Base::Vector3d midpos1, dir1, norm1; - Base::Vector3d midpos2, dir2, norm2; - if (geo1->getTypeId() != Part::GeomLineSegment::getClassTypeId() || - geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId()) { - if (Constr->Type == Equal) { - double r1a=0,r1b=0,r2a=0,r2b=0; - double angle1,angle1plus=0., angle2, angle2plus=0.;//angle1 = rotation of object as a whole; angle1plus = arc angle (t parameter for ellipses). - if (geo1->getTypeId() == Part::GeomCircle::getClassTypeId()) { - const Part::GeomCircle *circle = static_cast(geo1); - r1a = circle->getRadius(); - angle1 = M_PI/4; - midpos1 = circle->getCenter(); - } else if (geo1->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { - const Part::GeomArcOfCircle *arc = static_cast(geo1); - r1a = arc->getRadius(); - double startangle, endangle; - arc->getRange(startangle, endangle, /*emulateCCW=*/true); - angle1 = (startangle + endangle)/2; - midpos1 = arc->getCenter(); - } else if (geo1->getTypeId() == Part::GeomEllipse::getClassTypeId()) { - const Part::GeomEllipse *ellipse = static_cast(geo1); - r1a = ellipse->getMajorRadius(); - r1b = ellipse->getMinorRadius(); - Base::Vector3d majdir = ellipse->getMajorAxisDir(); - angle1 = atan2(majdir.y, majdir.x); - angle1plus = M_PI/4; - midpos1 = ellipse->getCenter(); - } else if (geo1->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) { - const Part::GeomArcOfEllipse *aoe = static_cast(geo1); - r1a = aoe->getMajorRadius(); - r1b = aoe->getMinorRadius(); - double startangle, endangle; - aoe->getRange(startangle, endangle, /*emulateCCW=*/true); - Base::Vector3d majdir = aoe->getMajorAxisDir(); - angle1 = atan2(majdir.y, majdir.x); - angle1plus = (startangle + endangle)/2; - midpos1 = aoe->getCenter(); - } else if (geo1->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId()) { - const Part::GeomArcOfHyperbola *aoh = static_cast(geo1); - r1a = aoh->getMajorRadius(); - r1b = aoh->getMinorRadius(); - double startangle, endangle; - aoh->getRange(startangle, endangle, /*emulateCCW=*/true); - Base::Vector3d majdir = aoh->getMajorAxisDir(); - angle1 = atan2(majdir.y, majdir.x); - angle1plus = (startangle + endangle)/2; - midpos1 = aoh->getCenter(); - } else if (geo1->getTypeId() == Part::GeomArcOfParabola::getClassTypeId()) { - const Part::GeomArcOfParabola *aop = static_cast(geo1); - r1a = aop->getFocal(); - double startangle, endangle; - aop->getRange(startangle, endangle, /*emulateCCW=*/true); - Base::Vector3d majdir = - aop->getXAxisDir(); - angle1 = atan2(majdir.y, majdir.x); - angle1plus = (startangle + endangle)/2; - midpos1 = aop->getFocus(); - } else - break; - - if (geo2->getTypeId() == Part::GeomCircle::getClassTypeId()) { - const Part::GeomCircle *circle = static_cast(geo2); - r2a = circle->getRadius(); - angle2 = M_PI/4; - midpos2 = circle->getCenter(); - } else if (geo2->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { - const Part::GeomArcOfCircle *arc = static_cast(geo2); - r2a = arc->getRadius(); - double startangle, endangle; - arc->getRange(startangle, endangle, /*emulateCCW=*/true); - angle2 = (startangle + endangle)/2; - midpos2 = arc->getCenter(); - } else if (geo2->getTypeId() == Part::GeomEllipse::getClassTypeId()) { - const Part::GeomEllipse *ellipse = static_cast(geo2); - r2a = ellipse->getMajorRadius(); - r2b = ellipse->getMinorRadius(); - Base::Vector3d majdir = ellipse->getMajorAxisDir(); - angle2 = atan2(majdir.y, majdir.x); - angle2plus = M_PI/4; - midpos2 = ellipse->getCenter(); - } else if (geo2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId()) { - const Part::GeomArcOfEllipse *aoe = static_cast(geo2); - r2a = aoe->getMajorRadius(); - r2b = aoe->getMinorRadius(); - double startangle, endangle; - aoe->getRange(startangle, endangle, /*emulateCCW=*/true); - Base::Vector3d majdir = aoe->getMajorAxisDir(); - angle2 = atan2(majdir.y, majdir.x); - angle2plus = (startangle + endangle)/2; - midpos2 = aoe->getCenter(); - } else if (geo2->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId()) { - const Part::GeomArcOfHyperbola *aoh = static_cast(geo2); - r2a = aoh->getMajorRadius(); - r2b = aoh->getMinorRadius(); - double startangle, endangle; - aoh->getRange(startangle, endangle, /*emulateCCW=*/true); - Base::Vector3d majdir = aoh->getMajorAxisDir(); - angle2 = atan2(majdir.y, majdir.x); - angle2plus = (startangle + endangle)/2; - midpos2 = aoh->getCenter(); - } else if (geo2->getTypeId() == Part::GeomArcOfParabola::getClassTypeId()) { - const Part::GeomArcOfParabola *aop = static_cast(geo2); - r2a = aop->getFocal(); - double startangle, endangle; - aop->getRange(startangle, endangle, /*emulateCCW=*/true); - Base::Vector3d majdir = -aop->getXAxisDir(); - angle2 = atan2(majdir.y, majdir.x); - angle2plus = (startangle + endangle)/2; - midpos2 = aop->getFocus(); - } else - break; - - if( geo1->getTypeId() == Part::GeomEllipse::getClassTypeId() || - geo1->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() || - geo1->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId() ){ - - Base::Vector3d majDir, minDir, rvec; - majDir = Base::Vector3d(cos(angle1),sin(angle1),0);//direction of major axis of ellipse - minDir = Base::Vector3d(-majDir.y,majDir.x,0);//direction of minor axis of ellipse - rvec = (r1a*cos(angle1plus)) * majDir + (r1b*sin(angle1plus)) * minDir; - midpos1 += rvec; - rvec.Normalize(); - norm1 = rvec; - dir1 = Base::Vector3d(-rvec.y,rvec.x,0);//DeepSOIC: I'm not sure what dir is supposed to mean. - } - else { - norm1 = Base::Vector3d(cos(angle1),sin(angle1),0); - dir1 = Base::Vector3d(-norm1.y,norm1.x,0); - midpos1 += r1a*norm1; - } - - - if( geo2->getTypeId() == Part::GeomEllipse::getClassTypeId() || - geo2->getTypeId() == Part::GeomArcOfEllipse::getClassTypeId() || - geo2->getTypeId() == Part::GeomArcOfHyperbola::getClassTypeId()) { - - Base::Vector3d majDir, minDir, rvec; - majDir = Base::Vector3d(cos(angle2),sin(angle2),0);//direction of major axis of ellipse - minDir = Base::Vector3d(-majDir.y,majDir.x,0);//direction of minor axis of ellipse - rvec = (r2a*cos(angle2plus)) * majDir + (r2b*sin(angle2plus)) * minDir; - midpos2 += rvec; - rvec.Normalize(); - norm2 = rvec; - dir2 = Base::Vector3d(-rvec.y,rvec.x,0); - } - else { - norm2 = Base::Vector3d(cos(angle2),sin(angle2),0); - dir2 = Base::Vector3d(-norm2.y,norm2.x,0); - midpos2 += r2a*norm2; - } - - } else // Parallel can only apply to a GeomLineSegment - break; - } else { - const Part::GeomLineSegment *lineSeg1 = static_cast(geo1); - const Part::GeomLineSegment *lineSeg2 = static_cast(geo2); - - // calculate the half distance between the start and endpoint - midpos1 = ((lineSeg1->getEndPoint()+lineSeg1->getStartPoint())/2); - midpos2 = ((lineSeg2->getEndPoint()+lineSeg2->getStartPoint())/2); - //Get a set of vectors perpendicular and tangential to these - dir1 = (lineSeg1->getEndPoint()-lineSeg1->getStartPoint()).Normalize(); - dir2 = (lineSeg2->getEndPoint()-lineSeg2->getStartPoint()).Normalize(); - norm1 = Base::Vector3d(-dir1.y,dir1.x,0.); - norm2 = Base::Vector3d(-dir2.y,dir2.x,0.); - } - - Base::Vector3d relpos1 = seekConstraintPosition(midpos1, norm1, dir1, 4.0, edit->constrGroup->getChild(i)); - Base::Vector3d relpos2 = seekConstraintPosition(midpos2, norm2, dir2, 4.0, edit->constrGroup->getChild(i)); - - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION))->abPos = SbVec3f(midpos1.x, midpos1.y, zConstr); //Absolute Reference - - //Reference Position that is scaled according to zoom - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION))->translation = SbVec3f(relpos1.x, relpos1.y, 0); - - Base::Vector3d secondPos = midpos2 - midpos1; - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION))->abPos = SbVec3f(secondPos.x, secondPos.y, zConstr); //Absolute Reference - - //Reference Position that is scaled according to zoom - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION))->translation = SbVec3f(relpos2.x - relpos1.x, relpos2.y -relpos1.y, 0); - - } - break; - case Distance: - case DistanceX: - case DistanceY: - { - assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); - - Base::Vector3d pnt1(0.,0.,0.), pnt2(0.,0.,0.); - if (Constr->SecondPos != Sketcher::PointPos::none) { // point to point distance - if (temp) { - pnt1 = getSolvedSketch().getPoint(Constr->First, Constr->FirstPos); - pnt2 = getSolvedSketch().getPoint(Constr->Second, Constr->SecondPos); - } else { - pnt1 = getSketchObject()->getPoint(Constr->First, Constr->FirstPos); - pnt2 = getSketchObject()->getPoint(Constr->Second, Constr->SecondPos); - } - } else if (Constr->Second != GeoEnum::GeoUndef) { // point to line distance - if (temp) { - pnt1 = getSolvedSketch().getPoint(Constr->First, Constr->FirstPos); - } else { - pnt1 = getSketchObject()->getPoint(Constr->First, Constr->FirstPos); - } - const Part::Geometry *geo = GeoById(*geomlist, Constr->Second); - if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { - const Part::GeomLineSegment *lineSeg = static_cast(geo); - Base::Vector3d l2p1 = lineSeg->getStartPoint(); - Base::Vector3d l2p2 = lineSeg->getEndPoint(); - // calculate the projection of p1 onto line2 - pnt2.ProjectToLine(pnt1-l2p1, l2p2-l2p1); - pnt2 += pnt1; - } else - break; - } else if (Constr->FirstPos != Sketcher::PointPos::none) { - if (temp) { - pnt2 = getSolvedSketch().getPoint(Constr->First, Constr->FirstPos); - } else { - pnt2 = getSketchObject()->getPoint(Constr->First, Constr->FirstPos); - } - } else if (Constr->First != GeoEnum::GeoUndef) { - const Part::Geometry *geo = GeoById(*geomlist, Constr->First); - if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { - const Part::GeomLineSegment *lineSeg = static_cast(geo); - pnt1 = lineSeg->getStartPoint(); - pnt2 = lineSeg->getEndPoint(); - } else - break; - } else - break; - - SoDatumLabel *asciiText = static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL)); - - // Get presentation string (w/o units if option is set) - asciiText->string = SbString( getPresentationString(Constr).toUtf8().constData() ); - - if (Constr->Type == Distance) - asciiText->datumtype = SoDatumLabel::DISTANCE; - else if (Constr->Type == DistanceX) - asciiText->datumtype = SoDatumLabel::DISTANCEX; - else if (Constr->Type == DistanceY) - asciiText->datumtype = SoDatumLabel::DISTANCEY; - - // Assign the Datum Points - asciiText->pnts.setNum(2); - SbVec3f *verts = asciiText->pnts.startEditing(); - - verts[0] = SbVec3f (pnt1.x,pnt1.y,zConstr); - verts[1] = SbVec3f (pnt2.x,pnt2.y,zConstr); - - asciiText->pnts.finishEditing(); - - //Assign the Label Distance - asciiText->param1 = Constr->LabelDistance; - asciiText->param2 = Constr->LabelPosition; - } - break; - case PointOnObject: - case Tangent: - case SnellsLaw: - { - assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); - assert(Constr->Second >= -extGeoCount && Constr->Second < intGeoCount); - - Base::Vector3d pos, relPos; - if ( Constr->Type == PointOnObject || - Constr->Type == SnellsLaw || - (Constr->Type == Tangent && Constr->Third != GeoEnum::GeoUndef) || //Tangency via point - (Constr->Type == Tangent && Constr->FirstPos != Sketcher::PointPos::none) //endpoint-to-curve or endpoint-to-endpoint tangency - ) { - - //find the point of tangency/point that is on object - //just any point among first/second/third should be OK - int ptGeoId; - Sketcher::PointPos ptPosId; - do {//dummy loop to use break =) Maybe goto? - ptGeoId = Constr->First; - ptPosId = Constr->FirstPos; - if (ptPosId != Sketcher::PointPos::none) break; - ptGeoId = Constr->Second; - ptPosId = Constr->SecondPos; - if (ptPosId != Sketcher::PointPos::none) break; - ptGeoId = Constr->Third; - ptPosId = Constr->ThirdPos; - if (ptPosId != Sketcher::PointPos::none) break; - assert(0);//no point found! - } while (false); - pos = getSolvedSketch().getPoint(ptGeoId, ptPosId); - - Base::Vector3d norm = getSolvedSketch().calculateNormalAtPoint(Constr->Second, pos.x, pos.y); - norm.Normalize(); - Base::Vector3d dir = norm; dir.RotateZ(-M_PI/2.0); - - relPos = seekConstraintPosition(pos, norm, dir, 2.5, edit->constrGroup->getChild(i)); - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION))->abPos = SbVec3f(pos.x, pos.y, zConstr); //Absolute Reference - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION))->translation = SbVec3f(relPos.x, relPos.y, 0); - } - else if (Constr->Type == Tangent) { - // get the geometry - const Part::Geometry *geo1 = GeoById(*geomlist, Constr->First); - const Part::Geometry *geo2 = GeoById(*geomlist, Constr->Second); - - if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId() && - geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { - const Part::GeomLineSegment *lineSeg1 = static_cast(geo1); - const Part::GeomLineSegment *lineSeg2 = static_cast(geo2); - // tangency between two lines - Base::Vector3d midpos1 = ((lineSeg1->getEndPoint()+lineSeg1->getStartPoint())/2); - Base::Vector3d midpos2 = ((lineSeg2->getEndPoint()+lineSeg2->getStartPoint())/2); - Base::Vector3d dir1 = (lineSeg1->getEndPoint()-lineSeg1->getStartPoint()).Normalize(); - Base::Vector3d dir2 = (lineSeg2->getEndPoint()-lineSeg2->getStartPoint()).Normalize(); - Base::Vector3d norm1 = Base::Vector3d(-dir1.y,dir1.x,0.f); - Base::Vector3d norm2 = Base::Vector3d(-dir2.y,dir2.x,0.f); - - Base::Vector3d relpos1 = seekConstraintPosition(midpos1, norm1, dir1, 4.0, edit->constrGroup->getChild(i)); - Base::Vector3d relpos2 = seekConstraintPosition(midpos2, norm2, dir2, 4.0, edit->constrGroup->getChild(i)); - - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION))->abPos = SbVec3f(midpos1.x, midpos1.y, zConstr); //Absolute Reference - - //Reference Position that is scaled according to zoom - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION))->translation = SbVec3f(relpos1.x, relpos1.y, 0); - - Base::Vector3d secondPos = midpos2 - midpos1; - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION))->abPos = SbVec3f(secondPos.x, secondPos.y, zConstr); //Absolute Reference - - //Reference Position that is scaled according to zoom - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION))->translation = SbVec3f(relpos2.x -relpos1.x, relpos2.y -relpos1.y, 0); - - break; - } - else if (geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { - std::swap(geo1,geo2); - } - - if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { - const Part::GeomLineSegment *lineSeg = static_cast(geo1); - Base::Vector3d dir = (lineSeg->getEndPoint() - lineSeg->getStartPoint()).Normalize(); - Base::Vector3d norm(-dir.y, dir.x, 0); - if (geo2->getTypeId()== Part::GeomCircle::getClassTypeId()) { - const Part::GeomCircle *circle = static_cast(geo2); - // tangency between a line and a circle - float length = (circle->getCenter() - lineSeg->getStartPoint())*dir; - - pos = lineSeg->getStartPoint() + dir * length; - relPos = norm * 1; //TODO Huh? - } - else if (geo2->getTypeId()== Part::GeomEllipse::getClassTypeId() || - geo2->getTypeId()== Part::GeomArcOfEllipse::getClassTypeId()) { - - Base::Vector3d center; - if(geo2->getTypeId()== Part::GeomEllipse::getClassTypeId()){ - const Part::GeomEllipse *ellipse = static_cast(geo2); - center=ellipse->getCenter(); - } else { - const Part::GeomArcOfEllipse *aoc = static_cast(geo2); - center=aoc->getCenter(); - } - - // tangency between a line and an ellipse - float length = (center - lineSeg->getStartPoint())*dir; - - pos = lineSeg->getStartPoint() + dir * length; - relPos = norm * 1; - } - else if (geo2->getTypeId()== Part::GeomArcOfCircle::getClassTypeId()) { - const Part::GeomArcOfCircle *arc = static_cast(geo2); - // tangency between a line and an arc - float length = (arc->getCenter() - lineSeg->getStartPoint())*dir; - - pos = lineSeg->getStartPoint() + dir * length; - relPos = norm * 1; //TODO Huh? - } - } - - if (geo1->getTypeId()== Part::GeomCircle::getClassTypeId() && - geo2->getTypeId()== Part::GeomCircle::getClassTypeId()) { - const Part::GeomCircle *circle1 = static_cast(geo1); - const Part::GeomCircle *circle2 = static_cast(geo2); - // tangency between two cicles - Base::Vector3d dir = (circle2->getCenter() - circle1->getCenter()).Normalize(); - pos = circle1->getCenter() + dir * circle1->getRadius(); - relPos = dir * 1; - } - else if (geo2->getTypeId()== Part::GeomCircle::getClassTypeId()) { - std::swap(geo1,geo2); - } - - if (geo1->getTypeId()== Part::GeomCircle::getClassTypeId() && - geo2->getTypeId()== Part::GeomArcOfCircle::getClassTypeId()) { - const Part::GeomCircle *circle = static_cast(geo1); - const Part::GeomArcOfCircle *arc = static_cast(geo2); - // tangency between a circle and an arc - Base::Vector3d dir = (arc->getCenter() - circle->getCenter()).Normalize(); - pos = circle->getCenter() + dir * circle->getRadius(); - relPos = dir * 1; - } - else if (geo1->getTypeId()== Part::GeomArcOfCircle::getClassTypeId() && - geo2->getTypeId()== Part::GeomArcOfCircle::getClassTypeId()) { - const Part::GeomArcOfCircle *arc1 = static_cast(geo1); - const Part::GeomArcOfCircle *arc2 = static_cast(geo2); - // tangency between two arcs - Base::Vector3d dir = (arc2->getCenter() - arc1->getCenter()).Normalize(); - pos = arc1->getCenter() + dir * arc1->getRadius(); - relPos = dir * 1; - } - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION))->abPos = SbVec3f(pos.x, pos.y, zConstr); //Absolute Reference - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION))->translation = SbVec3f(relPos.x, relPos.y, 0); - } - } - break; - case Symmetric: - { - assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); - assert(Constr->Second >= -extGeoCount && Constr->Second < intGeoCount); - - Base::Vector3d pnt1 = getSolvedSketch().getPoint(Constr->First, Constr->FirstPos); - Base::Vector3d pnt2 = getSolvedSketch().getPoint(Constr->Second, Constr->SecondPos); - - SbVec3f p1(pnt1.x,pnt1.y,zConstr); - SbVec3f p2(pnt2.x,pnt2.y,zConstr); - SbVec3f dir = (p2-p1); - dir.normalize(); - SbVec3f norm (-dir[1],dir[0],0); - - SoDatumLabel *asciiText = static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL)); - asciiText->datumtype = SoDatumLabel::SYMMETRIC; - - asciiText->pnts.setNum(2); - SbVec3f *verts = asciiText->pnts.startEditing(); - - verts[0] = p1; - verts[1] = p2; - - asciiText->pnts.finishEditing(); - - static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION))->translation = (p1 + p2)/2; - } - break; - case Angle: - { - assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); - assert((Constr->Second >= -extGeoCount && Constr->Second < intGeoCount) || - Constr->Second == GeoEnum::GeoUndef); - - SbVec3f p0; - double startangle,range,endangle; - if (Constr->Second != GeoEnum::GeoUndef) { - Base::Vector3d dir1, dir2; - if(Constr->Third == GeoEnum::GeoUndef) { //angle between two lines - const Part::Geometry *geo1 = GeoById(*geomlist, Constr->First); - const Part::Geometry *geo2 = GeoById(*geomlist, Constr->Second); - if (geo1->getTypeId() != Part::GeomLineSegment::getClassTypeId() || - geo2->getTypeId() != Part::GeomLineSegment::getClassTypeId()) - break; - const Part::GeomLineSegment *lineSeg1 = static_cast(geo1); - const Part::GeomLineSegment *lineSeg2 = static_cast(geo2); - - bool flip1 = (Constr->FirstPos == Sketcher::PointPos::end); - bool flip2 = (Constr->SecondPos == Sketcher::PointPos::end); - dir1 = (flip1 ? -1. : 1.) * (lineSeg1->getEndPoint()-lineSeg1->getStartPoint()); - dir2 = (flip2 ? -1. : 1.) * (lineSeg2->getEndPoint()-lineSeg2->getStartPoint()); - Base::Vector3d pnt1 = flip1 ? lineSeg1->getEndPoint() : lineSeg1->getStartPoint(); - Base::Vector3d pnt2 = flip2 ? lineSeg2->getEndPoint() : lineSeg2->getStartPoint(); - - // line-line intersection - { - double det = dir1.x*dir2.y - dir1.y*dir2.x; - if ((det > 0 ? det : -det) < 1e-10) { - // lines are coincident (or parallel) and in this case the center - // of the point pairs with the shortest distance is used - Base::Vector3d p1[2], p2[2]; - p1[0] = lineSeg1->getStartPoint(); - p1[1] = lineSeg1->getEndPoint(); - p2[0] = lineSeg2->getStartPoint(); - p2[1] = lineSeg2->getEndPoint(); - double length = DBL_MAX; - for (int i=0; i <= 1; i++) { - for (int j=0; j <= 1; j++) { - double tmp = (p2[j]-p1[i]).Length(); - if (tmp < length) { - length = tmp; - p0.setValue((p2[j].x+p1[i].x)/2,(p2[j].y+p1[i].y)/2,0); - } - } - } - } - else { - double c1 = dir1.y*pnt1.x - dir1.x*pnt1.y; - double c2 = dir2.y*pnt2.x - dir2.x*pnt2.y; - double x = (dir1.x*c2 - dir2.x*c1)/det; - double y = (dir1.y*c2 - dir2.y*c1)/det; - p0 = SbVec3f(x,y,0); - } - } - - range = Constr->getValue(); // WYSIWYG - startangle = atan2(dir1.y,dir1.x); - } - else {//angle-via-point - Base::Vector3d p = getSolvedSketch().getPoint(Constr->Third, Constr->ThirdPos); - p0 = SbVec3f(p.x, p.y, 0); - dir1 = getSolvedSketch().calculateNormalAtPoint(Constr->First, p.x, p.y); - dir1.RotateZ(-M_PI/2);//convert to vector of tangency by rotating - dir2 = getSolvedSketch().calculateNormalAtPoint(Constr->Second, p.x, p.y); - dir2.RotateZ(-M_PI/2); - - startangle = atan2(dir1.y,dir1.x); - range = atan2(dir1.x*dir2.y-dir1.y*dir2.x, - dir1.x*dir2.x+dir1.y*dir2.y); - } - - endangle = startangle + range; - - } else if (Constr->First != GeoEnum::GeoUndef) { - const Part::Geometry *geo = GeoById(*geomlist, Constr->First); - if (geo->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { - const Part::GeomLineSegment *lineSeg = static_cast(geo); - p0 = Base::convertTo((lineSeg->getEndPoint()+lineSeg->getStartPoint())/2); - - Base::Vector3d dir = lineSeg->getEndPoint()-lineSeg->getStartPoint(); - startangle = 0.; - range = atan2(dir.y,dir.x); - endangle = startangle + range; - } - else if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { - const Part::GeomArcOfCircle *arc = static_cast(geo); - p0 = Base::convertTo(arc->getCenter()); - - arc->getRange(startangle, endangle,/*emulateCCWXY=*/true); - range = endangle - startangle; - } - else { - break; - } - } else - break; - - SoDatumLabel *asciiText = static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL)); - asciiText->string = SbString( getPresentationString(Constr).toUtf8().constData() ); - asciiText->datumtype = SoDatumLabel::ANGLE; - asciiText->param1 = Constr->LabelDistance; - asciiText->param2 = startangle; - asciiText->param3 = range; - - asciiText->pnts.setNum(2); - SbVec3f *verts = asciiText->pnts.startEditing(); - - verts[0] = p0; - - asciiText->pnts.finishEditing(); - - } - break; - case Diameter: - { - assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); - - Base::Vector3d pnt1(0.,0.,0.), pnt2(0.,0.,0.); - if (Constr->First != GeoEnum::GeoUndef) { - const Part::Geometry *geo = GeoById(*geomlist, Constr->First); - - if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { - const Part::GeomArcOfCircle *arc = static_cast(geo); - double radius = arc->getRadius(); - double angle = (double) Constr->LabelPosition; - if (angle == 10) { - double startangle, endangle; - arc->getRange(startangle, endangle, /*emulateCCW=*/true); - angle = (startangle + endangle)/2; - } - Base::Vector3d center = arc->getCenter(); - pnt1 = center - radius * Base::Vector3d(cos(angle),sin(angle),0.); - pnt2 = center + radius * Base::Vector3d(cos(angle),sin(angle),0.); - } - else if (geo->getTypeId() == Part::GeomCircle::getClassTypeId()) { - const Part::GeomCircle *circle = static_cast(geo); - double radius = circle->getRadius(); - double angle = (double) Constr->LabelPosition; - if (angle == 10) { - angle = 0; - } - Base::Vector3d center = circle->getCenter(); - pnt1 = center - radius * Base::Vector3d(cos(angle),sin(angle),0.); - pnt2 = center + radius * Base::Vector3d(cos(angle),sin(angle),0.); - } - else - break; - } else - break; - - SbVec3f p1(pnt1.x,pnt1.y,zConstr); - SbVec3f p2(pnt2.x,pnt2.y,zConstr); - - SoDatumLabel *asciiText = static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL)); - - // Get display string with units hidden if so requested - asciiText->string = SbString( getPresentationString(Constr).toUtf8().constData() ); - - asciiText->datumtype = SoDatumLabel::DIAMETER; - asciiText->param1 = Constr->LabelDistance; - asciiText->param2 = Constr->LabelPosition; - - asciiText->pnts.setNum(2); - SbVec3f *verts = asciiText->pnts.startEditing(); - - verts[0] = p1; - verts[1] = p2; - - asciiText->pnts.finishEditing(); - } - break; - case Weight: - case Radius: - { - assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); - - Base::Vector3d pnt1(0.,0.,0.), pnt2(0.,0.,0.); - - if (Constr->First != GeoEnum::GeoUndef) { - const Part::Geometry *geo = GeoById(*geomlist, Constr->First); - - if (geo->getTypeId() == Part::GeomArcOfCircle::getClassTypeId()) { - const Part::GeomArcOfCircle *arc = static_cast(geo); - double radius = arc->getRadius(); - double angle = (double) Constr->LabelPosition; - if (angle == 10) { - double startangle, endangle; - arc->getRange(startangle, endangle, /*emulateCCW=*/true); - angle = (startangle + endangle)/2; - } - pnt1 = arc->getCenter(); - pnt2 = pnt1 + radius * Base::Vector3d(cos(angle),sin(angle),0.); - } - else if (geo->getTypeId() == Part::GeomCircle::getClassTypeId()) { - const Part::GeomCircle *circle = static_cast(geo); - auto gf = GeometryFacade::getFacade(geo); - - double radius; - - if(Constr->Type == Weight) { - double scalefactor = 1.0; - - if(circle->hasExtension(SketcherGui::ViewProviderSketchGeometryExtension::getClassTypeId())) - { - auto vpext = std::static_pointer_cast( - circle->getExtension(SketcherGui::ViewProviderSketchGeometryExtension::getClassTypeId()).lock()); - - scalefactor = vpext->getRepresentationFactor(); - } - - radius = circle->getRadius()*scalefactor; - } - else { - radius = circle->getRadius(); - } - - double angle = (double) Constr->LabelPosition; - if (angle == 10) { - angle = 0; - } - pnt1 = circle->getCenter(); - pnt2 = pnt1 + radius * Base::Vector3d(cos(angle),sin(angle),0.); - } - else - break; - } else - break; - - SbVec3f p1(pnt1.x,pnt1.y,zConstr); - SbVec3f p2(pnt2.x,pnt2.y,zConstr); - - SoDatumLabel *asciiText = static_cast(sep->getChild(CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL)); - - // Get display string with units hidden if so requested - if(Constr->Type == Weight) - asciiText->string = SbString( QString::number(Constr->getValue()).toStdString().c_str()); - else - asciiText->string = SbString( getPresentationString(Constr).toUtf8().constData() ); - - asciiText->datumtype = SoDatumLabel::RADIUS; - asciiText->param1 = Constr->LabelDistance; - asciiText->param2 = Constr->LabelPosition; - - asciiText->pnts.setNum(2); - SbVec3f *verts = asciiText->pnts.startEditing(); - - verts[0] = p1; - verts[1] = p2; - - asciiText->pnts.finishEditing(); - } - break; - case Coincident: // nothing to do for coincident - case None: - case InternalAlignment: - case NumConstraintTypes: - break; - } - - } catch (Base::Exception &e) { - Base::Console().Error("Exception during draw: %s\n", e.what()); - } catch (...){ - Base::Console().Error("Exception during draw: unknown\n"); - } - - } + editCoinManager->processGeometryConstraintsInformationOverlay(geolistfacade, rebuildinformationoverlay); // Avoids unneeded calls to pixmapFromSvg if(Mode==STATUS_NONE || Mode==STATUS_SKETCH_UseHandler) { - this->drawConstraintIcons(); - this->updateColor(); - } - - // delete the cloned objects - if (temp) { - for (std::vector::iterator it=tempGeo.begin(); it != tempGeo.end(); ++it) { - if (*it) - delete *it; - } + editCoinManager->drawConstraintIcons(geolist); + editCoinManager->updateColor(geolistfacade); } Gui::MDIView *mdi = this->getActiveView(); @@ -5955,285 +2572,29 @@ Restart: } } -void ViewProviderSketch::rebuildConstraintsVisual(void) -{ - const std::vector &constrlist = getSketchObject()->Constraints.getValues(); - // clean up - Gui::coinRemoveAllChildren(edit->constrGroup); - edit->vConstrType.clear(); - - for (std::vector::const_iterator it=constrlist.begin(); it != constrlist.end(); ++it) { - // root separator for one constraint - SoSeparator *sep = new SoSeparator(); - sep->ref(); - // no caching for frequently-changing data structures - sep->renderCaching = SoSeparator::OFF; - - // every constrained visual node gets its own material for preselection and selection - SoMaterial *mat = new SoMaterial; - mat->ref(); - mat->diffuseColor = (*it)->isActive ? - ((*it)->isDriving ? - ConstrDimColor - :NonDrivingConstrDimColor) - :DeactivatedConstrDimColor; - // Get sketch normal - Base::Vector3d RN(0,0,1); - - // move to position of Sketch - Base::Placement Plz = getEditingPlacement(); - Base::Rotation tmp(Plz.getRotation()); - tmp.multVec(RN,RN); - Plz.setRotation(tmp); - - SbVec3f norm(RN.x, RN.y, RN.z); - - // distinguish different constraint types to build up - switch ((*it)->Type) { - case Distance: - case DistanceX: - case DistanceY: - case Radius: - case Diameter: - case Weight: - case Angle: - { - SoDatumLabel *text = new SoDatumLabel(); - text->norm.setValue(norm); - text->string = ""; - text->textColor = (*it)->isActive ? - ((*it)->isDriving ? - ConstrDimColor - :NonDrivingConstrDimColor) - :DeactivatedConstrDimColor; - text->size.setValue(edit->coinFontSize); - text->lineWidth = 2 * edit->pixelScalingFactor; - text->useAntialiasing = false; - SoAnnotation *anno = new SoAnnotation(); - anno->renderCaching = SoSeparator::OFF; - anno->addChild(text); - // #define CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL 0 - sep->addChild(text); - edit->constrGroup->addChild(anno); - edit->vConstrType.push_back((*it)->Type); - // nodes not needed - sep->unref(); - mat->unref(); - continue; // jump to next constraint - } - break; - case Horizontal: - case Vertical: - case Block: - { - // #define CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL 0 - sep->addChild(mat); - // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION 1 - sep->addChild(new SoZoomTranslation()); - // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_ICON 2 - sep->addChild(new SoImage()); - // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_CONSTRAINTID 3 - sep->addChild(new SoInfo()); - // #define CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION 4 - sep->addChild(new SoZoomTranslation()); - // #define CONSTRAINT_SEPARATOR_INDEX_SECOND_ICON 5 - sep->addChild(new SoImage()); - // #define CONSTRAINT_SEPARATOR_INDEX_SECOND_CONSTRAINTID 6 - sep->addChild(new SoInfo()); - - // remember the type of this constraint node - edit->vConstrType.push_back((*it)->Type); - } - break; - case Coincident: // no visual for coincident so far - edit->vConstrType.push_back(Coincident); - break; - case Parallel: - case Perpendicular: - case Equal: - { - // #define CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL 0 - sep->addChild(mat); - // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION 1 - sep->addChild(new SoZoomTranslation()); - // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_ICON 2 - sep->addChild(new SoImage()); - // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_CONSTRAINTID 3 - sep->addChild(new SoInfo()); - // #define CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION 4 - sep->addChild(new SoZoomTranslation()); - // #define CONSTRAINT_SEPARATOR_INDEX_SECOND_ICON 5 - sep->addChild(new SoImage()); - // #define CONSTRAINT_SEPARATOR_INDEX_SECOND_CONSTRAINTID 6 - sep->addChild(new SoInfo()); - - // remember the type of this constraint node - edit->vConstrType.push_back((*it)->Type); - } - break; - case PointOnObject: - case Tangent: - case SnellsLaw: - { - // #define CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL 0 - sep->addChild(mat); - // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION 1 - sep->addChild(new SoZoomTranslation()); - // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_ICON 2 - sep->addChild(new SoImage()); - // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_CONSTRAINTID 3 - sep->addChild(new SoInfo()); - - if ((*it)->Type == Tangent) { - const Part::Geometry *geo1 = getSketchObject()->getGeometry((*it)->First); - const Part::Geometry *geo2 = getSketchObject()->getGeometry((*it)->Second); - if (!geo1 || !geo2) { - Base::Console().Warning("Tangent constraint references non-existing geometry\n"); - } - else if (geo1->getTypeId() == Part::GeomLineSegment::getClassTypeId() && - geo2->getTypeId() == Part::GeomLineSegment::getClassTypeId()) { - // #define CONSTRAINT_SEPARATOR_INDEX_SECOND_TRANSLATION 4 - sep->addChild(new SoZoomTranslation()); - // #define CONSTRAINT_SEPARATOR_INDEX_SECOND_ICON 5 - sep->addChild(new SoImage()); - // #define CONSTRAINT_SEPARATOR_INDEX_SECOND_CONSTRAINTID 6 - sep->addChild(new SoInfo()); - } - } - - edit->vConstrType.push_back((*it)->Type); - } - break; - case Symmetric: - { - SoDatumLabel *arrows = new SoDatumLabel(); - arrows->norm.setValue(norm); - arrows->string = ""; - arrows->textColor = ConstrDimColor; - arrows->lineWidth = 2 * edit->pixelScalingFactor; - - // #define CONSTRAINT_SEPARATOR_INDEX_MATERIAL_OR_DATUMLABEL 0 - sep->addChild(arrows); - // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_TRANSLATION 1 - sep->addChild(new SoTranslation()); - // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_ICON 2 - sep->addChild(new SoImage()); - // #define CONSTRAINT_SEPARATOR_INDEX_FIRST_CONSTRAINTID 3 - sep->addChild(new SoInfo()); - - edit->vConstrType.push_back((*it)->Type); - } - break; - case InternalAlignment: - { - edit->vConstrType.push_back((*it)->Type); - } - break; - default: - edit->vConstrType.push_back((*it)->Type); - } - - edit->constrGroup->addChild(sep); - // decrement ref counter again - sep->unref(); - mat->unref(); - } -} - -void ViewProviderSketch::updateVirtualSpace(void) -{ - const std::vector &constrlist = getSketchObject()->Constraints.getValues(); - - if(constrlist.size() == edit->vConstrType.size()) { - - edit->constrGroup->enable.setNum(constrlist.size()); - - SbBool *sws = edit->constrGroup->enable.startEditing(); - - for (size_t i = 0; i < constrlist.size(); i++) - sws[i] = !(constrlist[i]->isInVirtualSpace != isShownVirtualSpace); // XOR of constraint mode and VP mode - - - edit->constrGroup->enable.finishEditing(); - } -} - void ViewProviderSketch::setIsShownVirtualSpace(bool isshownvirtualspace) { - this->isShownVirtualSpace = isshownvirtualspace; + viewProviderParameters.isShownVirtualSpace = isshownvirtualspace; - updateVirtualSpace(); + editCoinManager->updateVirtualSpace(); signalConstraintsChanged(); } bool ViewProviderSketch::getIsShownVirtualSpace() const { - return this->isShownVirtualSpace; + return viewProviderParameters.isShownVirtualSpace; } void ViewProviderSketch::drawEdit(const std::vector &EditCurve) { - assert(edit); - - edit->EditCurveSet->numVertices.setNum(1); - edit->EditCurvesCoordinate->point.setNum(EditCurve.size()); - edit->EditCurvesMaterials->diffuseColor.setNum(EditCurve.size()); - SbVec3f *verts = edit->EditCurvesCoordinate->point.startEditing(); - int32_t *index = edit->EditCurveSet->numVertices.startEditing(); - SbColor *color = edit->EditCurvesMaterials->diffuseColor.startEditing(); - - int i=0; // setting up the line set - for (std::vector::const_iterator it = EditCurve.begin(); it != EditCurve.end(); ++it,i++) { - verts[i].setValue(it->x,it->y,zEdit); - color[i] = CreateCurveColor; - } - - index[0] = EditCurve.size(); - edit->EditCurvesCoordinate->point.finishEditing(); - edit->EditCurveSet->numVertices.finishEditing(); - edit->EditCurvesMaterials->diffuseColor.finishEditing(); + editCoinManager->drawEdit(EditCurve); } void ViewProviderSketch::drawEditMarkers(const std::vector &EditMarkers, unsigned int augmentationlevel) { - assert(edit); - - // determine marker size - int augmentedmarkersize = edit->MarkerSize; - - auto supportedsizes = Gui::Inventor::MarkerBitmaps::getSupportedSizes("CIRCLE_LINE"); - - auto defaultmarker = std::find(supportedsizes.begin(), supportedsizes.end(), edit->MarkerSize); - - if(defaultmarker != supportedsizes.end()) { - auto validAugmentationLevels = std::distance(defaultmarker,supportedsizes.end()); - - if(augmentationlevel >= validAugmentationLevels) - augmentationlevel = validAugmentationLevels - 1; - - augmentedmarkersize = *std::next(defaultmarker, augmentationlevel); - } - - edit->EditMarkerSet->markerIndex.startEditing(); - edit->EditMarkerSet->markerIndex = Gui::Inventor::MarkerBitmaps::getMarkerIndex("CIRCLE_LINE", augmentedmarkersize); - - // add the points to set - edit->EditMarkersCoordinate->point.setNum(EditMarkers.size()); - edit->EditMarkersMaterials->diffuseColor.setNum(EditMarkers.size()); - SbVec3f *verts = edit->EditMarkersCoordinate->point.startEditing(); - SbColor *color = edit->EditMarkersMaterials->diffuseColor.startEditing(); - - int i=0; // setting up the line set - for (std::vector::const_iterator it = EditMarkers.begin(); it != EditMarkers.end(); ++it,i++) { - verts[i].setValue(it->x,it->y,zEdit); - color[i] = InformationColor; - } - - edit->EditMarkersCoordinate->point.finishEditing(); - edit->EditMarkersMaterials->diffuseColor.finishEditing(); - edit->EditMarkerSet->markerIndex.finishEditing(); + editCoinManager->drawEditMarkers(EditMarkers, augmentationlevel); } void ViewProviderSketch::updateData(const App::Property *prop) @@ -6242,11 +2603,10 @@ void ViewProviderSketch::updateData(const App::Property *prop) // In the case of an undo/redo transaction, updateData is triggered by SketchObject::onUndoRedoFinished() in the solve() // In the case of an internal transaction, touching the geometry results in a call to updateData. - if ( edit && !getSketchObject()->getDocument()->isPerformingTransaction() && + if ( isInEditMode() && !getSketchObject()->getDocument()->isPerformingTransaction() && !getSketchObject()->isPerformingInternalTransaction() && (prop == &(getSketchObject()->Geometry) || prop == &(getSketchObject()->Constraints))) { - edit->FullyConstrained = false; // At this point, we do not need to solve the Sketch // If we are adding geometry an update can be triggered before the sketch is actually solved. // Because a solve is mandatory to any addition (at least to update the DoF of the solver), @@ -6336,16 +2696,10 @@ bool ViewProviderSketch::setEdit(int ModNum) this->attachSelection(); // create the container for the additional edit data - assert(!edit); - edit = new EditData(); - - // Init icon, font and marker sizes - initItemsSizes(); - - ParameterGrp::handle hSketch = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); - edit->handleEscapeButton = !hSketch->GetBool("LeaveSketchWithEscape", true); - - createEditInventorNodes(); + assert(!isInEditMode()); + preselection.reset(); + selection.reset(); + editCoinManager = std::make_unique(*this); auto editDoc = Gui::Application::Instance->editDocument(); App::DocumentObject *editObj = getSketchObject(); @@ -6395,95 +2749,6 @@ bool ViewProviderSketch::setEdit(int ModNum) ViewProvider2DObjectGrid::setEdit(ModNum); // notify to handle grid according to edit mode property - float transparency; - - ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); - // set the point color - unsigned long color = (unsigned long)(VertexColor.getPackedValue()); - color = hGrp->GetUnsigned("EditedVertexColor", color); - VertexColor.setPackedValue((uint32_t)color, transparency); - // set the curve color - color = (unsigned long)(CurveColor.getPackedValue()); - color = hGrp->GetUnsigned("EditedEdgeColor", color); - CurveColor.setPackedValue((uint32_t)color, transparency); - // set the create line (curve) color - color = (unsigned long)(CreateCurveColor.getPackedValue()); - color = hGrp->GetUnsigned("CreateLineColor", color); - CreateCurveColor.setPackedValue((uint32_t)color, transparency); - // set the construction curve color - color = (unsigned long)(CurveDraftColor.getPackedValue()); - color = hGrp->GetUnsigned("ConstructionColor", color); - CurveDraftColor.setPackedValue((uint32_t)color, transparency); - // set the internal alignment geometry color - color = (unsigned long)(InternalAlignedGeoColor.getPackedValue()); - color = hGrp->GetUnsigned("InternalAlignedGeoColor", color); - InternalAlignedGeoColor.setPackedValue((uint32_t)color, transparency); - // set the color for a fully constrained element - color = (unsigned long)(FullyConstraintElementColor.getPackedValue()); - color = hGrp->GetUnsigned("FullyConstraintElementColor", color); - FullyConstraintElementColor.setPackedValue((uint32_t)color, transparency); - // set the color for fully constrained construction element - color = (unsigned long)(FullyConstraintConstructionElementColor.getPackedValue()); - color = hGrp->GetUnsigned("FullyConstraintConstructionElementColor", color); - FullyConstraintConstructionElementColor.setPackedValue((uint32_t)color, transparency); - // set the color for fully constrained internal alignment element - color = (unsigned long)(FullyConstraintInternalAlignmentColor.getPackedValue()); - color = hGrp->GetUnsigned("FullyConstraintInternalAlignmentColor", color); - FullyConstraintInternalAlignmentColor.setPackedValue((uint32_t)color, transparency); - // set the color for fully constrained construction points - color = (unsigned long)(FullyConstraintConstructionPointColor.getPackedValue()); - color = hGrp->GetUnsigned("FullyConstraintConstructionPointColor", color); - FullyConstraintConstructionPointColor.setPackedValue((uint32_t)color, transparency); - // set fullyconstraint element color - color = (unsigned long)(FullyConstraintElementColor.getPackedValue()); - color = hGrp->GetUnsigned("FullyConstraintElementColor", color); - FullyConstraintElementColor.setPackedValue((uint32_t)color, transparency); - // set the cross lines color - //CrossColorV.setPackedValue((uint32_t)color, transparency); - //CrossColorH.setPackedValue((uint32_t)color, transparency); - // set invalid sketch color - color = (unsigned long)(InvalidSketchColor.getPackedValue()); - color = hGrp->GetUnsigned("InvalidSketchColor", color); - InvalidSketchColor.setPackedValue((uint32_t)color, transparency); - // set the fully constrained color - color = (unsigned long)(FullyConstrainedColor.getPackedValue()); - color = hGrp->GetUnsigned("FullyConstrainedColor", color); - FullyConstrainedColor.setPackedValue((uint32_t)color, transparency); - // set the constraint dimension color - color = (unsigned long)(ConstrDimColor.getPackedValue()); - color = hGrp->GetUnsigned("ConstrainedDimColor", color); - ConstrDimColor.setPackedValue((uint32_t)color, transparency); - // set the constraint color - color = (unsigned long)(ConstrIcoColor.getPackedValue()); - color = hGrp->GetUnsigned("ConstrainedIcoColor", color); - ConstrIcoColor.setPackedValue((uint32_t)color, transparency); - // set non-driving constraint color - color = (unsigned long)(NonDrivingConstrDimColor.getPackedValue()); - color = hGrp->GetUnsigned("NonDrivingConstrDimColor", color); - NonDrivingConstrDimColor.setPackedValue((uint32_t)color, transparency); - // set expression based constraint color - color = (unsigned long)(ExprBasedConstrDimColor.getPackedValue()); - color = hGrp->GetUnsigned("ExprBasedConstrDimColor", color); - ExprBasedConstrDimColor.setPackedValue((uint32_t)color, transparency); - // set expression based constraint color - color = (unsigned long)(DeactivatedConstrDimColor.getPackedValue()); - color = hGrp->GetUnsigned("DeactivatedConstrDimColor", color); - DeactivatedConstrDimColor.setPackedValue((uint32_t)color, transparency); - - // set the external geometry color - color = (unsigned long)(CurveExternalColor.getPackedValue()); - color = hGrp->GetUnsigned("ExternalColor", color); - CurveExternalColor.setPackedValue((uint32_t)color, transparency); - - // set the highlight color - unsigned long highlight = (unsigned long)(PreselectColor.getPackedValue()); - highlight = hGrp->GetUnsigned("HighlightColor", highlight); - PreselectColor.setPackedValue((uint32_t)highlight, transparency); - // set the selection color - highlight = (unsigned long)(SelectColor.getPackedValue()); - highlight = hGrp->GetUnsigned("SelectionColor", highlight); - SelectColor.setPackedValue((uint32_t)highlight, transparency); - // start the edit dialog if (sketchDlg) Gui::Control().showDialog(sketchDlg); @@ -6517,9 +2782,7 @@ bool ViewProviderSketch::setEdit(int ModNum) ->signalRedoDocument.connect(boost::bind(&ViewProviderSketch::slotRedoDocument, this, bp::_1)); // Enable solver initial solution update while dragging. - ParameterGrp::handle hGrp2 = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); - - getSketchObject()->setRecalculateInitialSolutionWhileMovingPoint(hGrp2->GetBool("RecalculateInitialSolutionWhileDragging",true)); + getSketchObject()->setRecalculateInitialSolutionWhileMovingPoint(viewProviderParameters.recalculateInitialSolutionWhileDragging); // intercept del key press from main app listener = new ShortcutListener(this); @@ -6650,213 +2913,9 @@ void ViewProviderSketch::UpdateSolverInformation() } else { signalSetUp(QString::fromUtf8("fully_constrained"), tr("Fully constrained"), QString(), QString()); - // color the sketch as fully constrained if it has geometry (other than the axes) - if(getSolvedSketch().getGeometrySize()>2) - edit->FullyConstrained = true; } } - -void ViewProviderSketch::createEditInventorNodes(void) -{ - assert(edit); - - edit->EditRoot = new SoSeparator; - edit->EditRoot->ref(); - edit->EditRoot->setName("Sketch_EditRoot"); - pcRoot->addChild(edit->EditRoot); - edit->EditRoot->renderCaching = SoSeparator::OFF ; - - // stuff for the points ++++++++++++++++++++++++++++++++++++++ - SoSeparator* pointsRoot = new SoSeparator; - edit->EditRoot->addChild(pointsRoot); - edit->PointsMaterials = new SoMaterial; - edit->PointsMaterials->setName("PointsMaterials"); - pointsRoot->addChild(edit->PointsMaterials); - - SoMaterialBinding *MtlBind = new SoMaterialBinding; - MtlBind->setName("PointsMaterialBinding"); - MtlBind->value = SoMaterialBinding::PER_VERTEX; - pointsRoot->addChild(MtlBind); - - edit->PointsCoordinate = new SoCoordinate3; - edit->PointsCoordinate->setName("PointsCoordinate"); - pointsRoot->addChild(edit->PointsCoordinate); - - edit->PointsDrawStyle = new SoDrawStyle; - edit->PointsDrawStyle->setName("PointsDrawStyle"); - edit->PointsDrawStyle->pointSize = 8 * edit->pixelScalingFactor; - pointsRoot->addChild(edit->PointsDrawStyle); - - edit->PointSet = new SoMarkerSet; - edit->PointSet->setName("PointSet"); - edit->PointSet->markerIndex = Gui::Inventor::MarkerBitmaps::getMarkerIndex("CIRCLE_FILLED", edit->MarkerSize); - pointsRoot->addChild(edit->PointSet); - - // stuff for the Curves +++++++++++++++++++++++++++++++++++++++ - SoSeparator* curvesRoot = new SoSeparator; - edit->EditRoot->addChild(curvesRoot); - edit->CurvesMaterials = new SoMaterial; - edit->CurvesMaterials->setName("CurvesMaterials"); - curvesRoot->addChild(edit->CurvesMaterials); - - MtlBind = new SoMaterialBinding; - MtlBind->setName("CurvesMaterialsBinding"); - MtlBind->value = SoMaterialBinding::PER_FACE; - curvesRoot->addChild(MtlBind); - - edit->CurvesCoordinate = new SoCoordinate3; - edit->CurvesCoordinate->setName("CurvesCoordinate"); - curvesRoot->addChild(edit->CurvesCoordinate); - - edit->CurvesDrawStyle = new SoDrawStyle; - edit->CurvesDrawStyle->setName("CurvesDrawStyle"); - edit->CurvesDrawStyle->lineWidth = 3 * edit->pixelScalingFactor; - curvesRoot->addChild(edit->CurvesDrawStyle); - - edit->CurveSet = new SoLineSet; - edit->CurveSet->setName("CurvesLineSet"); - curvesRoot->addChild(edit->CurveSet); - - // stuff for the RootCross lines +++++++++++++++++++++++++++++++++++++++ - SoGroup* crossRoot = new Gui::SoSkipBoundingGroup; - edit->pickStyleAxes = new SoPickStyle(); - edit->pickStyleAxes->style = SoPickStyle::SHAPE; - crossRoot->addChild(edit->pickStyleAxes); - edit->EditRoot->addChild(crossRoot); - MtlBind = new SoMaterialBinding; - MtlBind->setName("RootCrossMaterialBinding"); - MtlBind->value = SoMaterialBinding::PER_FACE; - crossRoot->addChild(MtlBind); - - edit->RootCrossDrawStyle = new SoDrawStyle; - edit->RootCrossDrawStyle->setName("RootCrossDrawStyle"); - edit->RootCrossDrawStyle->lineWidth = 2 * edit->pixelScalingFactor; - crossRoot->addChild(edit->RootCrossDrawStyle); - - edit->RootCrossMaterials = new SoMaterial; - edit->RootCrossMaterials->setName("RootCrossMaterials"); - edit->RootCrossMaterials->diffuseColor.set1Value(0,CrossColorH); - edit->RootCrossMaterials->diffuseColor.set1Value(1,CrossColorV); - crossRoot->addChild(edit->RootCrossMaterials); - - edit->RootCrossCoordinate = new SoCoordinate3; - edit->RootCrossCoordinate->setName("RootCrossCoordinate"); - crossRoot->addChild(edit->RootCrossCoordinate); - - edit->RootCrossSet = new SoLineSet; - edit->RootCrossSet->setName("RootCrossLineSet"); - crossRoot->addChild(edit->RootCrossSet); - - // stuff for the EditCurves +++++++++++++++++++++++++++++++++++++++ - SoSeparator* editCurvesRoot = new SoSeparator; - edit->EditRoot->addChild(editCurvesRoot); - edit->EditCurvesMaterials = new SoMaterial; - edit->EditCurvesMaterials->setName("EditCurvesMaterials"); - editCurvesRoot->addChild(edit->EditCurvesMaterials); - - edit->EditCurvesCoordinate = new SoCoordinate3; - edit->EditCurvesCoordinate->setName("EditCurvesCoordinate"); - editCurvesRoot->addChild(edit->EditCurvesCoordinate); - - edit->EditCurvesDrawStyle = new SoDrawStyle; - edit->EditCurvesDrawStyle->setName("EditCurvesDrawStyle"); - edit->EditCurvesDrawStyle->lineWidth = 3 * edit->pixelScalingFactor; - editCurvesRoot->addChild(edit->EditCurvesDrawStyle); - - edit->EditCurveSet = new SoLineSet; - edit->EditCurveSet->setName("EditCurveLineSet"); - editCurvesRoot->addChild(edit->EditCurveSet); - - ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); - float transparency; - SbColor cursorTextColor(0,0,1); - cursorTextColor.setPackedValue((uint32_t)hGrp->GetUnsigned("CursorTextColor", cursorTextColor.getPackedValue()), transparency); - - // stuff for the EditMarkers +++++++++++++++++++++++++++++++++++++++ - SoSeparator* editMarkersRoot = new SoSeparator; - edit->EditRoot->addChild(editMarkersRoot); - edit->EditMarkersMaterials = new SoMaterial; - edit->EditMarkersMaterials->setName("EditMarkersMaterials"); - editMarkersRoot->addChild(edit->EditMarkersMaterials); - - edit->EditMarkersCoordinate = new SoCoordinate3; - edit->EditMarkersCoordinate->setName("EditMarkersCoordinate"); - editMarkersRoot->addChild(edit->EditMarkersCoordinate); - - edit->EditMarkersDrawStyle = new SoDrawStyle; - edit->EditMarkersDrawStyle->setName("EditMarkersDrawStyle"); - edit->EditMarkersDrawStyle->pointSize = 8 * edit->pixelScalingFactor; - editMarkersRoot->addChild(edit->EditMarkersDrawStyle); - - edit->EditMarkerSet = new SoMarkerSet; - edit->EditMarkerSet->setName("EditMarkerSet"); - edit->EditMarkerSet->markerIndex = Gui::Inventor::MarkerBitmaps::getMarkerIndex("CIRCLE_LINE", edit->MarkerSize); - editMarkersRoot->addChild(edit->EditMarkerSet); - - // stuff for the edit coordinates ++++++++++++++++++++++++++++++++++++++ - SoSeparator *Coordsep = new SoSeparator(); - SoPickStyle* ps = new SoPickStyle(); - ps->style.setValue(SoPickStyle::UNPICKABLE); - Coordsep->addChild(ps); - Coordsep->setName("CoordSeparator"); - // no caching for frequently-changing data structures - Coordsep->renderCaching = SoSeparator::OFF; - - SoMaterial *CoordTextMaterials = new SoMaterial; - CoordTextMaterials->setName("CoordTextMaterials"); - CoordTextMaterials->diffuseColor = cursorTextColor; - Coordsep->addChild(CoordTextMaterials); - - SoFont *font = new SoFont(); - font->size.setValue(edit->coinFontSize); - - Coordsep->addChild(font); - - edit->textPos = new SoTranslation(); - Coordsep->addChild(edit->textPos); - - edit->textX = new SoText2(); - edit->textX->justification = SoText2::LEFT; - edit->textX->string = ""; - Coordsep->addChild(edit->textX); - edit->EditRoot->addChild(Coordsep); - - // group node for the Constraint visual +++++++++++++++++++++++++++++++++++ - MtlBind = new SoMaterialBinding; - MtlBind->setName("ConstraintMaterialBinding"); - MtlBind->value = SoMaterialBinding::OVERALL ; - edit->EditRoot->addChild(MtlBind); - - // use small line width for the Constraints - edit->ConstraintDrawStyle = new SoDrawStyle; - edit->ConstraintDrawStyle->setName("ConstraintDrawStyle"); - edit->ConstraintDrawStyle->lineWidth = 1 * edit->pixelScalingFactor; - edit->EditRoot->addChild(edit->ConstraintDrawStyle); - - // add the group where all the constraints has its SoSeparator - edit->constrGroup = new SmSwitchboard(); - edit->constrGroup->setName("ConstraintGroup"); - edit->EditRoot->addChild(edit->constrGroup); - - // group node for the Geometry information visual +++++++++++++++++++++++++++++++++++ - MtlBind = new SoMaterialBinding; - MtlBind->setName("InformationMaterialBinding"); - MtlBind->value = SoMaterialBinding::OVERALL ; - edit->EditRoot->addChild(MtlBind); - - // use small line width for the information visual - edit->InformationDrawStyle = new SoDrawStyle; - edit->InformationDrawStyle->setName("InformationDrawStyle"); - edit->InformationDrawStyle->lineWidth = 1 * edit->pixelScalingFactor; - edit->EditRoot->addChild(edit->InformationDrawStyle); - - // add the group where all the information entity has its SoSeparator - edit->infoGroup = new SoGroup(); - edit->infoGroup->setName("InformationGroup"); - edit->EditRoot->addChild(edit->infoGroup); -} - void ViewProviderSketch::unsetEdit(int ModNum) { Q_UNUSED(ModNum); @@ -6867,16 +2926,13 @@ void ViewProviderSketch::unsetEdit(int ModNum) delete listener; } - if (edit) { - if (edit->sketchHandler) + if (isInEditMode()) { + if (sketchHandler) deactivateHandler(); - Gui::coinRemoveAllChildren(edit->EditRoot); - pcRoot->removeChild(edit->EditRoot); - edit->EditRoot->unref(); - - delete edit; - edit = 0; + editCoinManager = nullptr; + preselection.reset(); + selection.reset(); this->detachSelection(); App::AutoTransaction trans("Sketch recompute"); @@ -6995,7 +3051,7 @@ void ViewProviderSketch::setEditViewer(Gui::View3DInventorViewer* viewer, int Mo SoNode* root = viewer->getSceneGraph(); static_cast(root)->selectionRole.setValue(false); - viewer->addGraphicsItem(rubberband); + viewer->addGraphicsItem(rubberband.get()); rubberband->setViewer(viewer); viewer->setupEditingRoot(); @@ -7003,138 +3059,31 @@ void ViewProviderSketch::setEditViewer(Gui::View3DInventorViewer* viewer, int Mo void ViewProviderSketch::unsetEditViewer(Gui::View3DInventorViewer* viewer) { - viewer->removeGraphicsItem(rubberband); + viewer->removeGraphicsItem(rubberband.get()); viewer->setEditing(false); SoNode* root = viewer->getSceneGraph(); static_cast(root)->selectionRole.setValue(true); } -void ViewProviderSketch::setPositionText(const Base::Vector2d &Pos, const SbString &text) -{ - edit->textX->string = text; - edit->textPos->translation = SbVec3f(Pos.x,Pos.y,zText); -} - -void ViewProviderSketch::setPositionText(const Base::Vector2d &Pos) -{ - SbString text; - text.sprintf(" (%.1f,%.1f)", Pos.x, Pos.y); - setPositionText(Pos,text); -} - -void ViewProviderSketch::resetPositionText(void) -{ - edit->textX->string = ""; -} - -void ViewProviderSketch::setPreselectPoint(int PreselectPoint) -{ - if (edit) { - int oldPtId = -1; - if (edit->PreselectPoint != -1) - oldPtId = edit->PreselectPoint + 1; - else if (edit->PreselectCross == 0) - oldPtId = 0; - int newPtId = PreselectPoint + 1; - SbVec3f *pverts = edit->PointsCoordinate->point.startEditing(); - float x,y,z; - if (oldPtId != -1 && - edit->SelPointSet.find(oldPtId) == edit->SelPointSet.end()) { - // send to background - pverts[oldPtId].getValue(x,y,z); - pverts[oldPtId].setValue(x,y,zLowPoints); - } - // bring to foreground - pverts[newPtId].getValue(x,y,z); - pverts[newPtId].setValue(x,y,zHighlight); - edit->PreselectPoint = PreselectPoint; - edit->PointsCoordinate->point.finishEditing(); - } -} - -void ViewProviderSketch::resetPreselectPoint(void) -{ - if (edit) { - int oldPtId = -1; - if (edit->PreselectPoint != -1) - oldPtId = edit->PreselectPoint + 1; - else if (edit->PreselectCross == 0) - oldPtId = 0; - if (oldPtId != -1 && - edit->SelPointSet.find(oldPtId) == edit->SelPointSet.end()) { - // send to background - SbVec3f *pverts = edit->PointsCoordinate->point.startEditing(); - float x,y,z; - pverts[oldPtId].getValue(x,y,z); - pverts[oldPtId].setValue(x,y,zLowPoints); - edit->PointsCoordinate->point.finishEditing(); - } - edit->PreselectPoint = -1; - } -} - -void ViewProviderSketch::addSelectPoint(int SelectPoint) -{ - if (edit) { - int PtId = SelectPoint + 1; - SbVec3f *pverts = edit->PointsCoordinate->point.startEditing(); - // bring to foreground - float x,y,z; - pverts[PtId].getValue(x,y,z); - pverts[PtId].setValue(x,y,zHighlight); - edit->SelPointSet.insert(PtId); - edit->PointsCoordinate->point.finishEditing(); - } -} - -void ViewProviderSketch::removeSelectPoint(int SelectPoint) -{ - if (edit) { - int PtId = SelectPoint + 1; - SbVec3f *pverts = edit->PointsCoordinate->point.startEditing(); - // send to background - float x,y,z; - pverts[PtId].getValue(x,y,z); - pverts[PtId].setValue(x,y,zLowPoints); - edit->SelPointSet.erase(PtId); - edit->PointsCoordinate->point.finishEditing(); - } -} - -void ViewProviderSketch::clearSelectPoints(void) -{ - if (edit) { - SbVec3f *pverts = edit->PointsCoordinate->point.startEditing(); - // send to background - float x,y,z; - for (std::set::const_iterator it=edit->SelPointSet.begin(); - it != edit->SelPointSet.end(); ++it) { - pverts[*it].getValue(x,y,z); - pverts[*it].setValue(x,y,zLowPoints); - } - edit->PointsCoordinate->point.finishEditing(); - edit->SelPointSet.clear(); - } -} - int ViewProviderSketch::getPreselectPoint(void) const { - if (edit) - return edit->PreselectPoint; + if (isInEditMode()) + return preselection.PreselectPoint; return -1; } int ViewProviderSketch::getPreselectCurve(void) const { - if (edit) - return edit->PreselectCurve; + if (isInEditMode()) + return preselection.PreselectCurve; return -1; } int ViewProviderSketch::getPreselectCross(void) const { - if (edit) - return edit->PreselectCross; + // TODO: This function spreads over several files. It should be refactored into something less "numeric" at a second stage. + if (isInEditMode()) + return static_cast(preselection.PreselectCross); return -1; } @@ -7175,14 +3124,11 @@ void ViewProviderSketch::deleteSelected() bool ViewProviderSketch::onDelete(const std::vector &subList) { - if (edit) { + if (isInEditMode()) { std::vector SubNames = subList; Gui::Selection().clearSelection(); resetPreselectPoint(); - edit->PreselectCurve = -1; - edit->PreselectCross = -1; - edit->PreselectConstraintSet.clear(); std::set delInternalGeometries, delExternalGeometries, delCoincidents, delConstraints; // go through the selected subelements @@ -7240,6 +3186,7 @@ bool ViewProviderSketch::onDelete(const std::vector &subList) if (*rit == GeoEnum::RtPnt) { // RootPoint GeoId = Sketcher::GeoEnum::RtPnt; PosId = Sketcher::PointPos::start; + } else { getSketchObject()->getGeoVertexIndex(*rit, GeoId, PosId); } @@ -7312,14 +3259,11 @@ bool ViewProviderSketch::onDelete(const std::vector &subList) // as there is an event filter installed that intercepts the del key event. So now we do // need to tidy up after ourselves again. - ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher"); - bool autoRecompute = hGrp->GetBool("AutoRecompute",false); - - if (autoRecompute) { + if (viewProviderParameters.autoRecompute) { Gui::Command::updateActive(); } else { - this->drawConstraintIcons(); + editCoinManager->drawConstraintIcons(); this->updateColor(); } @@ -7330,12 +3274,6 @@ bool ViewProviderSketch::onDelete(const std::vector &subList) return PartGui::ViewProviderPart::onDelete(subList); } -void ViewProviderSketch::showRestoreInformationLayer() { - - visibleInformationChanged = true ; - draw(false,false); -} - QIcon ViewProviderSketch::mergeColorfulOverlayIcons (const QIcon & orig) const { QIcon mergedicon = orig; @@ -7365,3 +3303,285 @@ QIcon ViewProviderSketch::mergeColorfulOverlayIcons (const QIcon & orig) const return Gui::ViewProvider::mergeColorfulOverlayIcons (mergedicon); } + + +/*************************** functions ViewProviderSketch offers to friends such as DrawHandlerSketch ************************/ + +void ViewProviderSketch::setPositionText(const Base::Vector2d &Pos, const SbString &text) +{ + editCoinManager->setPositionText(Pos,text); +} + +void ViewProviderSketch::setPositionText(const Base::Vector2d &Pos) +{ + editCoinManager->setPositionText(Pos); +} + +void ViewProviderSketch::resetPositionText(void) +{ + editCoinManager->resetPositionText(); +} + +void ViewProviderSketch::setPreselectPoint(int PreselectPoint) +{ + preselection.PreselectPoint = PreselectPoint; + preselection.PreselectCurve = Preselection::InvalidCurve; + preselection.PreselectCross = Preselection::Axes::None;; + preselection.PreselectConstraintSet.clear(); +} + +void ViewProviderSketch::setPreselectRootPoint() +{ + preselection.PreselectPoint = Preselection::InvalidPoint; + preselection.PreselectCurve = Preselection::InvalidCurve; + preselection.PreselectCross = Preselection::Axes::RootPoint; + preselection.PreselectConstraintSet.clear(); +} + + +void ViewProviderSketch::resetPreselectPoint(void) +{ + preselection.PreselectPoint = Preselection::InvalidPoint; + preselection.PreselectCurve = Preselection::InvalidCurve; + preselection.PreselectCross = Preselection::Axes::None;; + preselection.PreselectConstraintSet.clear(); +} + +void ViewProviderSketch::addSelectPoint(int SelectPoint) +{ + selection.SelPointSet.insert(SelectPoint); +} + +void ViewProviderSketch::removeSelectPoint(int SelectPoint) +{ + selection.SelPointSet.erase(SelectPoint); +} + +void ViewProviderSketch::clearSelectPoints(void) +{ + selection.SelPointSet.clear(); +} + +bool ViewProviderSketch::isSelected(const std::string &subNameSuffix) const +{ + return Gui::Selection().isSelected(editDocName.c_str(), editObjName.c_str(), (editSubName+subNameSuffix).c_str()); +} + +void ViewProviderSketch::rmvSelection(const std::string &subNameSuffix) +{ + Gui::Selection().rmvSelection(editDocName.c_str(), editObjName.c_str(), (editSubName+subNameSuffix).c_str()); +} + +bool ViewProviderSketch::addSelection(const std::string &subNameSuffix, float x, float y, float z) +{ + return Gui::Selection().addSelection(editDocName.c_str(), editObjName.c_str(), (editSubName+subNameSuffix).c_str(), x , y, z); +} + +bool ViewProviderSketch::addSelection2(const std::string &subNameSuffix, float x, float y, float z) +{ + return Gui::Selection().addSelection2(editDocName.c_str(), editObjName.c_str(), (editSubName+subNameSuffix).c_str(), x , y, z); +} + +bool ViewProviderSketch::setPreselect(const std::string &subNameSuffix, float x, float y, float z) +{ + return Gui::Selection().setPreselect(editDocName.c_str(), editObjName.c_str(), (editSubName+subNameSuffix).c_str(), x , y, z); +} + +/*************************** private functions to decouple Attorneys and Clients ********************************************/ + +// Establishes a private collaboration interface with EditModeCoinManager to perform EditModeCoinManager tasks, while abstracting EditModeCoinManager +// from the specific ViewProviderSketch implementation, while allowing ViewProviderSketch to fully delegate coin management. + +const std::vector ViewProviderSketch::getConstraints() const +{ + return getSketchObject()->Constraints.getValues(); +} + +const GeoList ViewProviderSketch::getGeoList() const +{ + const std::vector tempGeo = getSketchObject()->getCompleteGeometry(); // without memory allocation + + int intGeoCount = getSketchObject()->getHighestCurveIndex() + 1; + + auto geolist = GeoList::getGeoListModel(std::move(tempGeo), intGeoCount); + + return geolist; +} + +bool ViewProviderSketch::constraintHasExpression(int constrid) const +{ + return getSketchObject()->constraintHasExpression(constrid); +} + +std::unique_ptr ViewProviderSketch::getRayPickAction() const +{ + assert(isInEditMode()); + Gui::MDIView *mdi = Gui::Application::Instance->editViewOfNode(editCoinManager->getRootEditNode()); + if (!(mdi && mdi->isDerivedFrom(Gui::View3DInventor::getClassTypeId()))) + return nullptr; + Gui::View3DInventorViewer *viewer = static_cast(mdi)->getViewer(); + + return std::make_unique(viewer->getSoRenderManager()->getViewportRegion()); +} + +SbVec2f ViewProviderSketch::getScreenCoordinates(SbVec2f sketchcoordinates) const +{ + + Base::Placement sketchPlacement = getEditingPlacement(); + Base::Vector3d sketchPos(sketchPlacement.getPosition()); + Base::Rotation sketchRot(sketchPlacement.getRotation()); + + // get global coordinates from sketcher coordinates + Base::Vector3d pos(sketchcoordinates[0], sketchcoordinates[1],0); + sketchRot.multVec(pos,pos); + pos = pos + sketchPos; + + Gui::MDIView *mdi = this->getActiveView(); + Gui::View3DInventor *view = qobject_cast(mdi); + if (!view || !isInEditMode()) + return SbVec2f(0,0); + + Gui::View3DInventorViewer* viewer = view->getViewer(); + + SoCamera* pCam = viewer->getSoRenderManager()->getCamera(); + + if (!pCam) + return SbVec2f(0,0); + + SbViewVolume vol = pCam->getViewVolume(); + Gui::ViewVolumeProjection proj(vol); + + // dimensionless [0 1] (or 1.5 see View3DInventorViewer.cpp ) + Base::Vector3d screencoords = proj(pos); + + int width = viewer->getGLWidget()->width(), + height = viewer->getGLWidget()->height(); + + if (width >= height) { + // "Landscape" orientation, to square + screencoords.x *= height; + screencoords.x += (width-height) / 2.0; + screencoords.y *= height; + } + else { + // "Portrait" orientation + screencoords.x *= width; + screencoords.y *= width; + screencoords.y += (height-width) / 2.0; + } + + SbVec2f iconCoords(screencoords.x,screencoords.y); + + return iconCoords; +} + +QFont ViewProviderSketch::getApplicationFont() const +{ + return QApplication::font(); +} + +int ViewProviderSketch::defaultFontSizePixels() const +{ + return QApplication::fontMetrics().height(); +} + +int ViewProviderSketch::getApplicationLogicalDPIX() const { + return QApplication::desktop()->logicalDpiX(); +} + +double ViewProviderSketch::getRotation(SbVec3f pos0, SbVec3f pos1) const +{ + double x0,y0,x1,y1; + + Gui::MDIView *mdi = Gui::Application::Instance->editViewOfNode(editCoinManager->getRootEditNode()); + if (!(mdi && mdi->isDerivedFrom(Gui::View3DInventor::getClassTypeId()))) + return 0; + Gui::View3DInventorViewer *viewer = static_cast(mdi)->getViewer(); + SoCamera* pCam = viewer->getSoRenderManager()->getCamera(); + if (!pCam) + return 0; + + try { + SbViewVolume vol = pCam->getViewVolume(); + + getCoordsOnSketchPlane(pos0,vol.getProjectionDirection(),x0,y0); + getCoordsOnSketchPlane(pos1,vol.getProjectionDirection(),x1,y1); + + return -atan2((y1-y0),(x1-x0))*180/M_PI; + } + catch (const Base::DivisionByZeroError&) { + return 0; + } +} + + +GeoListFacade ViewProviderSketch::getGeoListFacade() const +{ + auto tempGeoFacade = getSketchObject()->getCompleteGeometryFacade(); + + int intGeoCount = getSketchObject()->getHighestCurveIndex() + 1; + + auto geolistfacade = GeoListFacade::getGeoListModel(std::move(tempGeoFacade), intGeoCount); + + return geolistfacade; +} + +bool ViewProviderSketch::isSketchInvalid() const +{ + + bool sketchinvalid = getSketchObject()->getLastHasRedundancies() || + getSketchObject()->getLastHasConflicts() || + getSketchObject()->getLastHasMalformedConstraints(); + return sketchinvalid; +} + +bool ViewProviderSketch::isSketchFullyConstrained() const +{ + return getSketchObject()->FullyConstrained.getValue(); +} + +bool ViewProviderSketch::haveConstraintsInvalidGeometry() const +{ + return getSketchObject()->Constraints.hasInvalidGeometry(); +} + +void ViewProviderSketch::addNodeToRoot(SoSeparator * node) +{ + pcRoot->addChild(node); +} + +void ViewProviderSketch::removeNodeFromRoot(SoSeparator * node) +{ + pcRoot->removeChild(node); +} + +bool ViewProviderSketch::isConstraintPreselected(int constraintId) const +{ + return preselection.PreselectConstraintSet.count(constraintId); +} + +bool ViewProviderSketch::isPointSelected(int pointId) const +{ + return selection.SelPointSet.find(pointId) != selection.SelPointSet.end(); +} + +bool ViewProviderSketch::isCurveSelected(int curveId) const +{ + return selection.SelCurvSet.find(curveId) != selection.SelCurvSet.end(); +} + +bool ViewProviderSketch::isConstraintSelected(int constraintId) const +{ + return selection.SelConstraintSet.find(constraintId) != selection.SelConstraintSet.end(); +} + +void ViewProviderSketch::executeOnSelectionPointSet(std::function && operation) const +{ + for(const auto v : selection.SelPointSet) + operation(v); +} + +bool ViewProviderSketch::isInEditMode() const +{ + return editCoinManager != nullptr; +} diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.h b/src/Mod/Sketcher/Gui/ViewProviderSketch.h index fabb68c1ea..bf3f10b41d 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.h +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.h @@ -24,11 +24,11 @@ #ifndef SKETCHERGUI_VIEWPROVIDERSKETCH_H #define SKETCHERGUI_VIEWPROVIDERSKETCH_H +#include + #include #include #include -#include -#include #include #include #include @@ -39,11 +39,13 @@ #include #include "ShortcutListener.h" +#include class TopoDS_Shape; class TopoDS_Face; class SoSeparator; class SbLine; +class SbVec2f; class SbVec3f; class SoCoordinate3; class SoInfo; @@ -52,6 +54,7 @@ class SoTransform; class SoLineSet; class SoMarkerSet; class SoPickedPoint; +class SoRayPickAction; class SoImage; class QImage; @@ -62,7 +65,9 @@ class SoTranslation; class SbString; class SbTime; -struct EditData; +namespace Part { + class Geometry; +} namespace Gui { class View3DInventorViewer; @@ -76,37 +81,320 @@ namespace Sketcher { namespace SketcherGui { +class EditModeCoinManager; class DrawSketchHandler; -/** The Sketch ViewProvider - * This class handles mainly the drawing and editing of the sketch. - * It draws the geometry and the constraints applied to the sketch. - * It uses the class DrawSketchHandler to facilitate the creation - * of new geometry while editing. - */ +using GeoList = Sketcher::GeoList; +using GeoListFacade = Sketcher::GeoListFacade; + +/** @brief The Sketch ViewProvider + * + * @details + * + * As any ViewProvider, this class is responsible for the view representation + * of Sketches. + * + * Functionality inherited from parent classes deal with the majority of the + * representation of a sketch when it is ** not ** in edit mode. + * + * This class handles mainly the drawing and editing of the sketch. + * + * The class delegates a substantial part of this functionality on two main + * classes, DrawSketchHandler and EditModeCoinManager. + * + * In order to enforce a certain degree of encapsulation and promote a not + * too tight coupling, while still allowing well defined collaboration, + * DrawSketchHandler and EditModeCoinManager access ViewProviderSketch via + * two Attorney classes (Attorney-Client pattern), ViewProviderSketchDrawSketchHandlerAttorney + * and ViewProviderSketchCoinAttorney. + * + * Given the substantial amount of code involved in coin node management, EditModeCoinManager + * further delegates on other specialised helper classes. Some of them share the + * ViewProviderSketchCoinAttorney, which defines the maximum coupling and minimum encapsulation. + * + * DrawSketchHandler aids temporary edit mode drawing and is extensively used for the creation + * of geometry. + * + * EditModeCoinManager takes over the responsibility of creating the Coin (Inventor) scenograph + * and modifying it, including all the drawing of geometry, constraints and overlay layer. This + * is an exclusive responsibility under the Single Responsibility Principle. + * + * EditModeCoinManager exposes a public interface to be used by ViewProviderSketch. Where, + * EditModeCoinManager needs special access to facilities of ViewProviderSketch in order to fulfil + * its responsibility, this access is defined by ViewProviderSketchCoinAttorney. + * + * Similarly, DrawSketchHandler takes over the responsibility of drawing edit temporal curves and + * markers necessary to enable visual feedback to the user, as well as the UI interaction during + * such edits. This is its exclusive responsibility under the Single Responsibility Principle. + * + * A plethora of speciliased handlers derive from DrawSketchHandler for each specialised editing (see + * for example all the handlers for creation of new geometry). These derived classes do * not * have + * direct access to the ViewProviderSketchDrawSketchHandlerAttorney. This is intended to keep coupling + * under control. However, generic functionality requiring access to the Attorney can be implemented + * in DrawSketchHandler and used from its derived classes by virtue of the inheritance. This promotes a + * concentrating the coupling in a single point (and code reuse). + * + */ class SketcherGuiExport ViewProviderSketch : public PartGui::ViewProvider2DObjectGrid , public PartGui::ViewProviderAttachExtension , public Gui::SelectionObserver - , public ParameterGrp::ObserverType { Q_DECLARE_TR_FUNCTIONS(SketcherGui::ViewProviderSketch) - /// generates a warning message about constraint conflicts and appends it to the given message - static QString appendConflictMsg(const std::vector &conflicting); - /// generates a warning message about redundant constraints and appends it to the given message - static QString appendRedundantMsg(const std::vector &redundant); - /// generates a warning message about partially redundant constraints and appends it to the given message - static QString appendPartiallyRedundantMsg(const std::vector &partiallyredundant); - /// generates a warning message about redundant constraints and appends it to the given message - static QString appendMalformedMsg(const std::vector &redundant); PROPERTY_HEADER_WITH_OVERRIDE(SketcherGui::ViewProviderSketch); +private: + /** + * @brief + * This nested class is responsible for attaching to the parameters relevant for + * ViewProviderSketch, initialising the ViewProviderSketch to the current configuration + * and handle in real time any change to their values. + */ + class ParameterObserver : public ParameterGrp::ObserverType + { + public: + ParameterObserver(ViewProviderSketch & client); + ~ParameterObserver(); + + void initParameters(); + + void subscribeToParameters(); + + void unsubscribeToParameters(); + + /** Observer for parameter group. */ + void OnChange(Base::Subject &rCaller, const char * sReason) override; + + private: + + void updateBoolProperty(const std::string & string, App::Property * property, bool defaultvalue); + void updateGridSize(const std::string & string, App::Property * property); + + // Only for colors outside of edit mode, edit mode colors are handled by EditModeCoinManager. + void updateColorProperty(const std::string & string, App::Property * property, float r, float g, float b); + + void updateEscapeKeyBehaviour(const std::string & string, App::Property * property); + + void updateAutoRecompute(const std::string & string, App::Property * property); + + void updateRecalculateInitialSolutionWhileDragging(const std::string & string, App::Property * property); + + private: + ViewProviderSketch &Client; + std::map, App::Property * >> parameterMap; + }; + + /** @name Classes storing the state of Dragging, Selection and Preselection + * All these classes enable the identification of a Vertex, a Curve, the root + * Point, axes, and constraints. + * + * A word on indices and ways to identify elements and parts of them: + * + * The Sketcher has a general main way to identify geometry, {GeoId, PointPos}, + * these can be provided as separate data, or using Sketcher::GeoElementId. The + * latter defines comparison and equality operators enabling, for example to use + * it as key in a std::map. + * + * While it is indeed possible to refer to any point using that nomenclature, creating + * maps in certain circumnstances leads to a performance drawback. Additionally, the + * legacy selection mechanism refers to positive indexed Vertices (for both normal and + * external vertices). Both reasons discourage moving to a single identification. This + * situation has been identified at different levels: + * + * (1) In sketch.cpp, the solver facade defining the interface with GCS, both + * {GeoId, PointPos} and PointId are used. + * + * (2) In SketchObject.cpp, the actual document object, both {GeoId, PointPos} and VertexId + * are used (see VertexId2GeoId and VertexId2GeoPosId). + * + * (3) In ViewProviderSketch, both {GeoId, PointPos} an Point indices are used (see these structures) + * + * (4) At CoinManager level, {GeoId, PointPos}, Point indices (for selection) and MultiFieldIds (specific + * structure defining a coin multifield index and layer) are used. + * + * Using a single index instead of a multi-index field, allows mappings to be implemented via std::vectors + * instead of std::maps. Direct mappings using std::vectors are accessed in constant time. Multi-index mappings + * relying on std::maps involve a search for the key. This leads to a drop in performance. + * + * What are these indices and how do they relate each other? + * 1. PointId, VertexId depend on the order of the geometries in the sketch. GeoList and GeoListFacade enable to + * convert between indices. + * 2. CurveId is basically the GeoId assuming PointPos to be PointPos::none (edge) + * 3. Sometimes, Axes and root point are separated into a third index or enum. Legacy reasons aside, the root point + * is has GeoId=-1, which is sometimes used as invalid value in positive only indices. Additionally, root point and + * the Horizontal Axes both have GeoId=-1 (differing in PointPos only). Following the decision not to rely on PointPos, + * creating a separate index, best when enum-ed, appears justified. + */ + //@{ + + /** @brief Class to store vector and item Id for dragging. + * + * @details + * Ids are zero-indexed points and curves. + * + * The DragPoint indexing matches PreselectPoint indexing. + * + */ + class Drag { + public: + enum SpecialValues { + InvalidPoint = -1, + InvalidCurve = -1 + }; + + Drag() { + resetVector(); + resetIds(); + } + + void resetVector() { + xInit = 0; + yInit = 0; + relative = false; + } + + void resetIds() { + DragPoint = InvalidPoint; + DragCurve = InvalidCurve; + DragConstraintSet.clear(); + } + + bool isDragPointValid() { return DragPoint > InvalidPoint;} + bool isDragCurveValid() { return DragCurve > InvalidCurve;} + + double xInit, yInit; // starting point of the dragging operation + bool relative; // whether the dragging move vector is relative or absolute + + + int DragPoint; // dragged point id (only positive integers) + int DragCurve; // dragged curve id (only positive integers), negative external curves cannot be dragged. + std::set DragConstraintSet; // dragged constraints ids + }; + + // TODO: Selection and Preselection should use a same structure. Probably Drag should use the same structure too. To be refactored separately. + + /** @brief Class to store preselected element ids. + * + * @details + * + * PreselectPoint is the positive VertexId. + * + * PreselectCurve is the GeoID, but without the Axes (indices -1 and -2). + * + * VertexN, with N = PreselectPoint + 1, same as DragPoint indexing (NOTE -1 is NOT the root point) + * + * EdgeN, with N = PreselectCurve + 1 for positive values ; ExternalEdgeN, with N = -PreselectCurve - 2 + * + * The PreselectPoint indexing matches DragPoint indexing (it further includes negative edges, which are + * not meaningful for Dragging). + * + */ + class Preselection { + public: + enum SpecialValues { + InvalidPoint = -1, + InvalidCurve = -1, + ExternalCurve = -3 + }; + + enum class Axes { + None = -1, + RootPoint = 0, + HorizontalAxis = 1, + VerticalAxis = 2 + }; + + Preselection() { + reset(); + } + + void reset(){ + PreselectPoint = InvalidPoint; + PreselectCurve = InvalidCurve; + PreselectCross = Axes::None; + PreselectConstraintSet.clear(); + blockedPreselection = false; + } + + bool isPreselectPointValid() const { return PreselectPoint > InvalidPoint;} + bool isPreselectCurveValid() const { return PreselectCurve > InvalidCurve || PreselectCurve <= ExternalCurve;} + bool isCrossPreselected() const { return PreselectCross != Axes::None;} + bool isEdge() const { return PreselectCurve > InvalidCurve;} + bool isExternalEdge() const { return PreselectCurve <= ExternalCurve;} + + int getPreselectionVertexIndex() const { return PreselectPoint + 1;} + int getPreselectionEdgeIndex() const { return PreselectCurve + 1;} + int getPreselectionExternalEdgeIndex() const { return -PreselectCurve - 2;} + + int PreselectPoint; // VertexN, with N = PreselectPoint + 1, same as DragPoint indexing (NOTE -1 is NOT the root point) + int PreselectCurve; // EdgeN, with N = PreselectCurve + 1 for positive values ; ExternalEdgeN, with N = -PreselectCurve - 2 + Axes PreselectCross; // 0 => rootPoint, 1 => HAxis, 2 => VAxis + std::set PreselectConstraintSet; // ConstraintN, N = index + 1 + bool blockedPreselection; + }; + + /** @brief Class to store selected element ids. + * + * @details + * Selection follows yet a different mechanism than preselection. + * + * SelPointSet indices as PreselectPoint, with the addition that -1 is indeed the rootpoint. + * + * SelCurvSet indices as PreselectCurve, with the addition that -1 is the HAxis and -2 is the VAxis + * + */ + class Selection { + public: + enum SpecialValues { + RootPoint = -1, + HorizontalAxis = -1, + VerticalAxis = -2 + }; + + Selection() { + reset(); + } + + void reset() { + SelPointSet.clear(); + SelCurvSet.clear(); + SelConstraintSet.clear(); + } + + std::set SelPointSet; // Indices as PreselectPoint (and -1 for rootpoint) + std::set SelCurvSet; // also holds cross axes at -1 and -2 + std::set SelConstraintSet; // ConstraintN, N = index + 1. + }; + //@} + + /** @brief Private struct maintaining information necessary for detecting double click. + */ + struct DoubleClick { + static SbTime prvClickTime; + static SbVec2s prvClickPos; //used by double-click-detector + static SbVec2s prvCursorPos; + static SbVec2s newCursorPos; + }; + + /** @brief Private struct grouping ViewProvider parameters and internal variables + */ + struct ViewProviderParameters { + bool handleEscapeButton = false; + bool autoRecompute = false; + bool recalculateInitialSolutionWhileDragging = false; + + bool isShownVirtualSpace = false; // indicates whether the present virtual space view is the Real Space or the Virtual Space (virtual space 1 or 2) + bool buttonPress = false; + }; + public: /// constructor ViewProviderSketch(); /// destructor virtual ~ViewProviderSketch(); + /** @name Properties */ + //@{ App::PropertyBool Autoconstraints; App::PropertyBool AvoidRedundant; App::PropertyPythonObject TempoVis; @@ -117,40 +405,23 @@ public: App::PropertyBool ForceOrtho; App::PropertyBool SectionView; App::PropertyString EditingWorkbench; + //@} - /// Draw all constraint icons - /*! Except maybe the radius and lock ones? */ - void drawConstraintIcons(); - - /// draw the sketch in the inventor nodes - /// temp => use temporary solver solution in SketchObject - /// recreateinformationscenography => forces a rebuild of the information layer scenography - void draw(bool temp=false, bool rebuildinformationlayer=true); - - /// draw the edit curve - void drawEdit(const std::vector &EditCurve); - - /// draw the edit markers - void drawEditMarkers(const std::vector &EditMarkers, unsigned int augmentationlevel = 0); - - /// Is the view provider selectable - bool isSelectable(void) const override; - /// Observer message from the Selection - virtual void onSelectionChanged(const Gui::SelectionChanges& msg) override; - - /// Show/Hide nodes from information layer - void showRestoreInformationLayer(); - + // TODO: It is difficult to imagine that these functions are necessary in the public interface. This requires review at a second stage and possibly + // refactor it. /** @name handler control */ //@{ /// sets an DrawSketchHandler in control void activateHandler(DrawSketchHandler *newHandler); /// removes the active handler void purgeHandler(void); - /// set the pick style of the sketch coordinate axes - void setAxisPickStyle(bool on); //@} + + // TODO: SketchMode should be refactored. DrawSketchHandler, its inheritance and free functions should access this mode via the DrawSketchHandler + // Attorney. I will not refactor this at this moment, as the refactor will be even more extensive and difficult to review. But this should be done + // in a second stage. + /** @name modus handling */ //@{ /// mode table @@ -167,49 +438,42 @@ public: STATUS_SKETCH_StartRubberBand, /**< enum value for initiating a rubber band selection */ STATUS_SKETCH_UseRubberBand /**< enum value when making a rubber band selection *//**< enum value a DrawSketchHandler is in control. */ }; + /// is called by GuiCommands to set the drawing mode void setSketchMode(SketchMode mode) {Mode = mode;} /// get the sketch mode SketchMode getSketchMode(void) const {return Mode;} //@} - /** @name helper functions */ + /** @name Drawing functions */ //@{ - /// give the coordinates of a line on the sketch plane in sketcher (2D) coordinates - void getCoordsOnSketchPlane(double &u, double &v, const SbVec3f &point, - const SbVec3f &normal); - - /// give projecting line of position - void getProjectingLine(const SbVec2s&, - const Gui::View3DInventorViewer *viewer, - SbLine&) const; - - /// helper to detect preselection - bool detectPreselection(const SoPickedPoint *Point, - const Gui::View3DInventorViewer *viewer, - const SbVec2s &cursorPos); - - /// Helper for detectPreselection(), for constraints only. - std::set detectPreselectionConstr(const SoPickedPoint *Point, - const Gui::View3DInventorViewer *viewer, - const SbVec2s &cursorPos); - - /*! Look at the center of the bounding of all selected items */ - void centerSelection(); - - /// box selection method - void doBoxSelection(const SbVec2s &startPos, const SbVec2s &endPos, - const Gui::View3DInventorViewer *viewer); + /// draw the sketch in the inventor nodes + /// temp => use temporary solver solution in SketchObject + /// recreateinformationscenography => forces a rebuild of the information overlay scenography + void draw(bool temp=false, bool rebuildinformationoverlay=true); /// helper change the color of the sketch according to selection and solver status void updateColor(void); + //@} + + /** @name Selection functions */ + //@{ + /// Is the view provider selectable + bool isSelectable(void) const override; + + /// Observer message from the Selection + virtual void onSelectionChanged(const Gui::SelectionChanges& msg) override; + //@} + + /** @name Access to Sketch and Solver objects */ + //@{ /// get the pointer to the sketch document object Sketcher::SketchObject *getSketchObject(void) const; /** returns a const reference to the last solved sketch object. It guarantees that * the solver object does not lose synchronisation with the SketchObject properties. * - * NOTE: Operations requiring write access to the solver must be done via SketchObject + * NOTE: Operations requiring * write * access to the solver must be done via SketchObject * interface. See for example functions: * -> inline void setRecalculateInitialSolutionWhileMovingPoint(bool recalculateInitialSolutionWhileMovingPoint) * -> inline int initTemporaryMove(int geoId, PointPos pos, bool fine=true) @@ -217,22 +481,24 @@ public: * -> inline void updateSolverExtension(int geoId, std::unique_ptr && ext) */ const Sketcher::Sketch &getSolvedSketch(void) const; + //@} - /// snap points x,y (mouse coordinates) onto grid if enabled - void snapToGrid(double &x, double &y); + /** @name miscelanea utilities */ + //@{ + /*! Look at the center of the bounding of all selected items */ + void centerSelection(); + /// returns the scale factor + float getScaleFactor() const; + //@} - /// moves a selected constraint - void moveConstraint(int constNum, const Base::Vector2d &toPos); - /// finds a free position for placing a constraint icon - Base::Vector3d seekConstraintPosition(const Base::Vector3d &origPos, - const Base::Vector3d &norm, - const Base::Vector3d &dir, float step, - const SoNode *constraint); - - float getScaleFactor(); - int getPreselectPoint(void) const; - int getPreselectCurve(void) const; - int getPreselectCross(void) const; + /** @name constraint Virtual Space visibility management */ + //@{ + /// updates the visibility of the virtual space of constraints + void updateVirtualSpace(void); + /// determines whether the constraints in the normal space or the ones in the virtual are to be shown + void setIsShownVirtualSpace(bool isshownvirtualspace); + /// returns whether the virtual space is being shown + bool getIsShownVirtualSpace(void) const; //@} /** @name base class implementer */ @@ -257,238 +523,239 @@ public: virtual bool mouseButtonPressed(int Button, bool pressed, const SbVec2s& cursorPos, const Gui::View3DInventorViewer* viewer) override; //@} - void deleteSelected(); - - /// updates the visibility of the virtual space - void updateVirtualSpace(void); - void setIsShownVirtualSpace(bool isshownvirtualspace); - bool getIsShownVirtualSpace(void) const; - - /// Icons and Icon overlays + /// Control the overlays appearing on the Tree and reflecting different sketcher states virtual QIcon mergeColorfulOverlayIcons (const QIcon & orig) const override; - friend class DrawSketchHandler; - friend struct ::EditData; - + /** @name Signals for controlling information in Task dialogs */ + //@{ /// signals if the constraints list has changed boost::signals2::signal signalConstraintsChanged; /// signals if the sketch has been set up boost::signals2::signal signalSetUp; /// signals if the elements list has changed boost::signals2::signal signalElementsChanged; + //@} - /** Observer for parameter group. */ - void OnChange(Base::Subject &rCaller, const char * sReason) override; - + /** @name Attorneys for collaboration with helper classes */ + //@{ + friend class ViewProviderSketchDrawSketchHandlerAttorney; + friend class ViewProviderSketchCoinAttorney; + friend class ViewProviderSketchShortcutListenerAttorney; + //@} protected: - Base::Placement getEditingPlacement() const; - + /** @name enter/exit edit mode */ + //@{ virtual bool setEdit(int ModNum) override; virtual void unsetEdit(int ModNum) override; virtual void setEditViewer(Gui::View3DInventorViewer*, int ModNum) override; virtual void unsetEditViewer(Gui::View3DInventorViewer*) override; - void deactivateHandler(); - /// update solver information based on last solving at SketchObject - void UpdateSolverInformation(void); - /// helper to detect whether the picked point lies on the sketch - bool isPointOnSketch(const SoPickedPoint *pp) const; - /// get called by the container whenever a property has been changed - virtual void onChanged(const App::Property *prop) override; + //@} + /** @name miscelanea editing functions */ + //@{ + /// purges the DrawHandler if existing and tidies up + void deactivateHandler(); /// get called if a subelement is double clicked while editing void editDoubleClicked(void); + //@} - /// set up the edition data structure EditData - void createEditInventorNodes(void); - /// pointer to the edit data structure if the ViewProvider is in edit. - EditData *edit; - /// build up the visual of the constraints - void rebuildConstraintsVisual(void); - - void slotUndoDocument(const Gui::Document&); - void slotRedoDocument(const Gui::Document&); - -protected: - boost::signals2::connection connectUndoDocument; - boost::signals2::connection connectRedoDocument; - - /// set icon & font sizes - void initItemsSizes(); - /// subscribe to parameter groups as an observer - void subscribeToParameters(); - /// unsubscribe to parameter groups as an observer - void unsubscribeToParameters(); - /// updates the sizes of the edit mode inventor node - void updateInventorNodeSizes(); - - void forceUpdateData(); + /** @name Solver Information */ + //@{ + /// update solver information based on last solving at SketchObject + void UpdateSolverInformation(void); /// Auxiliary function to generate messages about conflicting, redundant and malformed constraints static QString appendConstraintMsg( const QString & singularmsg, const QString & pluralmsg, const std::vector &vector); - - /// Return display string for constraint including hiding units if - //requested. - QString getPresentationString(const Sketcher::Constraint *constraint); - - /** @name Protected helpers for drawing constraint icons*/ - //@{ - QString iconTypeFromConstraint(Sketcher::Constraint *constraint); - - /// Returns a QColor object appropriate for constraint with given id - /*! In the case of combined icons, the icon color is chosen based on - * the constraint with the highest priority from constrColorPriority() - */ - QColor constrColor(int constraintId); - /// Used by drawMergedConstraintIcons to decide what color to make icons - /*! See constrColor() */ - int constrColorPriority(int constraintId); - - /// Internal type used for drawing constraint icons - struct constrIconQueueItem { - /// Type of constraint the icon represents. Eg: "small/Constraint_PointOnObject_sm" - QString type; - - /// Internal constraint ID number - /// These map to results of getSketchObject()->Constraints.getValues() - int constraintId; - - /// Label to be rendered with this icon, if any - QString label; - - /// Absolute coordinates of the constraint icon - SbVec3f position; - - /// Pointer to the SoImage object where the icon should be written - SoImage *destination; - - /// Pointer to SoInfo object where we store the constraint IDs that the icon refers to - SoInfo *infoPtr; - - /// Angle to rotate an icon - double iconRotation; - - bool visible; - }; - - /// Internal type used for drawing constraint icons - typedef std::vector IconQueue; - /// For constraint icon bounding boxes - typedef std::pair > ConstrIconBB; - /// For constraint icon bounding boxes - typedef std::vector ConstrIconBBVec; - - void combineConstraintIcons(IconQueue iconQueue); - - /// Renders an icon for a single constraint and sends it to Coin - void drawTypicalConstraintIcon(const constrIconQueueItem &i); - - /// Combines multiple constraint icons and sends them to Coin - void drawMergedConstraintIcons(IconQueue iconQueue); - - /// Helper for drawMergedConstraintIcons and drawTypicalConstraintIcon - QImage renderConstrIcon(const QString &type, - const QColor &iconColor, - const QStringList &labels, - const QList &labelColors, - double iconRotation, - //! Gets populated with bounding boxes (in icon - //! image coordinates) for the icon at left, then - //! labels for different constraints. - std::vector *boundingBoxes = NULL, - //! If not NULL, gets set to the number of pixels - //! that the text extends below the icon base. - int *vPad = NULL); - - /// Copies a QImage constraint icon into a SoImage* - /*! Used by drawTypicalConstraintIcon() and drawMergedConstraintIcons() */ - void sendConstraintIconToCoin(const QImage &icon, SoImage *soImagePtr); - - /// Essentially a version of sendConstraintIconToCoin, with a blank icon - void clearCoinImage(SoImage *soImagePtr); - - /// Returns the size that Coin should display the indicated image at - SbVec3s getDisplayedSize(const SoImage *) const; //@} - void setPositionText(const Base::Vector2d &Pos, const SbString &txt); - void setPositionText(const Base::Vector2d &Pos); - void resetPositionText(void); + /** @name manage updates during undo/redo operations */ + //@{ + void slotUndoDocument(const Gui::Document&); + void slotRedoDocument(const Gui::Document&); + void forceUpdateData(); + //@} - // handle preselection and selection of points + /** @name base class implementer */ + //@{ + /// get called by the container whenever a property has been changed + virtual void onChanged(const App::Property *prop) override; + //@} + +private: + /// function to handle OCCT BSpline weight calculation singularities and representation + void scaleBSplinePoleCirclesAndUpdateSolverAndSketchObjectGeometry( + GeoList & geolist, + bool geometrywithmemoryallocation, + std::vector> &deepCopiesToDelete); + + /** @name geometry and coordinates auxiliary functions */ + //@{ + /// give the coordinates of a line on the sketch plane in sketcher (2D) coordinates + void getCoordsOnSketchPlane(const SbVec3f &point, const SbVec3f &normal, double &u, double &v) const; + + /// give projecting line of position + void getProjectingLine(const SbVec2s&, + const Gui::View3DInventorViewer *viewer, + SbLine&) const; + //@} + + /** @name preselection functions */ + //@{ + /// helper to detect preselection + bool detectAndShowPreselection (SoPickedPoint * Point, const SbVec2s &cursorPos); + int getPreselectPoint(void) const; + int getPreselectCurve(void) const; + int getPreselectCross(void) const; void setPreselectPoint(int PreselectPoint); + void setPreselectRootPoint(); void resetPreselectPoint(void); + + bool setPreselect(const std::string &subNameSuffix, float x = 0, float y = 0, float z = 0); + //@} + + /** @name Selection functions */ + //@{ + /// box selection method + void doBoxSelection(const SbVec2s &startPos, const SbVec2s &endPos, + const Gui::View3DInventorViewer *viewer); + void addSelectPoint(int SelectPoint); void removeSelectPoint(int SelectPoint); void clearSelectPoints(void); + bool isSelected(const std::string & ss) const; + void rmvSelection(const std::string &subNameSuffix); + bool addSelection(const std::string &subNameSuffix, float x = 0, float y = 0, float z = 0); + bool addSelection2(const std::string &subNameSuffix, float x = 0, float y = 0, float z = 0); + //@} + + /** @name miscelanea utilities */ + //@{ + /// snap points x,y (mouse coordinates) onto grid if enabled + void snapToGrid(double &x, double &y); + + /// moves a selected constraint + void moveConstraint(int constNum, const Base::Vector2d &toPos); + + /// returns whether the sketch is in edit mode. + bool isInEditMode() const; + //@} + + /** @name Attorney functions*/ + //@{ + /* private functions to decouple Attorneys and Clients from the internal implementation of + the ViewProvider and its members, such as sketchObject (see friend attorney classes) and + improve encapsulation. + */ + + //********* ViewProviderSketchCoinAttorney *********************** + + bool constraintHasExpression(int constrid) const; + + const std::vector getConstraints() const; + + // gets the list of geometry of the sketchobject or of the solver instance + const GeoList getGeoList() const; + + GeoListFacade getGeoListFacade() const; + + Base::Placement getEditingPlacement() const; + + std::unique_ptr getRayPickAction() const; + + SbVec2f getScreenCoordinates(SbVec2f sketchcoordinates) const; + + QFont getApplicationFont() const; + + int defaultFontSizePixels() const; + + int getApplicationLogicalDPIX() const; + + double getRotation(SbVec3f pos0, SbVec3f pos1) const; + + bool isSketchInvalid() const; + + bool isSketchFullyConstrained() const; + + bool haveConstraintsInvalidGeometry() const; + + void addNodeToRoot(SoSeparator * node); + + void removeNodeFromRoot(SoSeparator * node); + + bool isConstraintPreselected(int constraintId) const; + + bool isPointSelected(int pointId) const; + + void executeOnSelectionPointSet(std::function && operation) const; + + bool isCurveSelected(int curveId) const; + + bool isConstraintSelected(int constraintId) const; + + //********* ViewProviderSketchShortcutListenerAttorney ***********// + void deleteSelected(); + + //********* ViewProviderSketchDrawSketchHandlerAttorney **********// + void setPositionText(const Base::Vector2d &Pos, const SbString &txt); + void setPositionText(const Base::Vector2d &Pos); + void resetPositionText(void); + + /// draw the edit curve + void drawEdit(const std::vector &EditCurve); + /// draw the edit markers + void drawEditMarkers(const std::vector &EditMarkers, unsigned int augmentationlevel = 0); + /// set the pick style of the sketch coordinate axes + void setAxisPickStyle(bool on); + //@} + +private: + /** @name Solver message creation*/ + //@{ + /* private functions to decouple Attorneys and Clients from the internal implementation of + the ViewProvider and its members, such as sketchObject (see friend attorney classes) and + improve encapsulation. + */ + /// generates a warning message about constraint conflicts and appends it to the given message + static QString appendConflictMsg(const std::vector &conflicting); + /// generates a warning message about redundant constraints and appends it to the given message + static QString appendRedundantMsg(const std::vector &redundant); + /// generates a warning message about partially redundant constraints and appends it to the given message + static QString appendPartiallyRedundantMsg(const std::vector &partiallyredundant); + /// generates a warning message about redundant constraints and appends it to the given message + static QString appendMalformedMsg(const std::vector &redundant); + //@} + +private: + boost::signals2::connection connectUndoDocument; + boost::signals2::connection connectRedoDocument; + // modes while sketching SketchMode Mode; - // colors - static SbColor VertexColor; - static SbColor CurveColor; - static SbColor CreateCurveColor; - static SbColor CurveDraftColor; - static SbColor CurveExternalColor; - static SbColor CrossColorV; - static SbColor CrossColorH; - static SbColor FullyConstrainedColor; - static SbColor ConstrDimColor; - static SbColor ConstrIcoColor; - static SbColor NonDrivingConstrDimColor; - static SbColor ExprBasedConstrDimColor; - static SbColor PreselectColor; - static SbColor SelectColor; - static SbColor PreselectSelectedColor; - static SbColor InformationColor; - static SbColor DeactivatedConstrDimColor; - static SbColor InternalAlignedGeoColor; - static SbColor FullyConstraintElementColor; - static SbColor FullyConstraintConstructionElementColor; - static SbColor FullyConstraintInternalAlignmentColor; - static SbColor FullyConstraintConstructionPointColor; - static SbColor InvalidSketchColor; - - static SbTime prvClickTime; - static SbVec2s prvClickPos; //used by double-click-detector - static SbVec2s prvCursorPos; - static SbVec2s newCursorPos; - - float zCross; - //float zLines; - //float zPoints; - float zLowPoints; - float zHighPoints; - float zConstr; - float zHighlight; - float zText; - float zEdit; - float zHighLine; - float zInfo; - float zLowLines; - float zMidLines; - float zHighLines; - // reference coordinates for relative operations - double xInit,yInit; - bool relative; + Drag drag; - Gui::Rubberband* rubberband; + Preselection preselection; + Selection selection; - // information layer variables - bool visibleInformationChanged; - double combrepscalehyst; + std::unique_ptr rubberband; std::string editDocName; std::string editObjName; std::string editSubName; - // Virtual space variables - bool isShownVirtualSpace; // indicates whether the present virtual space view is the Real Space or the Virtual Space (virtual space 1 or 2) - ShortcutListener* listener; + + std::unique_ptr editCoinManager; + + std::unique_ptr pObserver; + + std::unique_ptr sketchHandler; + + ViewProviderParameters viewProviderParameters; }; } // namespace PartGui diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketchCoinAttorney.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketchCoinAttorney.cpp new file mode 100644 index 0000000000..99bc2cca1e --- /dev/null +++ b/src/Mod/Sketcher/Gui/ViewProviderSketchCoinAttorney.cpp @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (c) 2021 Abdullah Tahiri * + * * + * 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_ + +#endif // #ifndef _PreComp_ + +#include "ViewProviderSketch.h" + +#include "ViewProviderSketchCoinAttorney.h" + +using namespace SketcherGui; +using namespace Sketcher; diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketchCoinAttorney.h b/src/Mod/Sketcher/Gui/ViewProviderSketchCoinAttorney.h new file mode 100644 index 0000000000..a4f1fbc1db --- /dev/null +++ b/src/Mod/Sketcher/Gui/ViewProviderSketchCoinAttorney.h @@ -0,0 +1,257 @@ +/*************************************************************************** + * Copyright (c) 2021 Abdullah Tahiri * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef SKETCHERGUI_ViewProviderSketchCoinAttorney_H +#define SKETCHERGUI_ViewProviderSketchCoinAttorney_H + +#include + +#include +#include +#include + + +namespace Base { + template< typename T > + class Vector3; + + class Vector2d; + + class Placement; +} + +namespace Part { + class Geometry; +} + +namespace Sketcher { + class Constraint; + class PropertyConstraintList; +}; + +namespace SketcherGui { + +class ViewProviderSketch; + +using GeoList = Sketcher::GeoList; +using GeoListFacade = Sketcher::GeoListFacade; + +/** @brief Attorney class for limiting access to viewprovider + * @details + * ViewProviderSketch delegates a substantial part of coin related visualisation to + * ViewProviderSketchCoinAttorney during edit mode. + * + * Sometimes friend classes of ViewProviderSketchCoinAttorney need to access selected functionalities only available to ViewProviderSketch. + * + * This attorney class regulates which specific functionalities ViewProviderSketchCoinAttorney is able to access in + * ViewProviderSketch. + * + * The objective is: + * - to preserve as much as possible ViewProviderSketch encapsulation + * - to promote as much as reasonably possible loose coupling between tightly related classes. + * - to keep control over the interactions between these classes and easily identify the cooperation interface. + */ +class ViewProviderSketchCoinAttorney { +private: + static inline bool constraintHasExpression(const ViewProviderSketch &vp, int constrid); + static inline const std::vector getConstraints(const ViewProviderSketch & vp); + static inline const GeoList getGeoList(const ViewProviderSketch & vp); + static inline const GeoListFacade getGeoListFacade(const ViewProviderSketch & vp); + static inline Base::Placement getEditingPlacement(const ViewProviderSketch & vp); + static inline void updateGridExtent(ViewProviderSketch & vp, float minx, float maxx, float miny, float maxy); + static inline bool isShownVirtualSpace(const ViewProviderSketch & vp); + static inline std::unique_ptr getRayPickAction(const ViewProviderSketch & vp); + + static inline float getScaleFactor(const ViewProviderSketch & vp); + static inline SbVec2f getScreenCoordinates(const ViewProviderSketch & vp, SbVec2f sketchcoordinates); + static inline QFont getApplicationFont(const ViewProviderSketch & vp); + static inline double getRotation(const ViewProviderSketch & vp, SbVec3f pos0, SbVec3f pos1); + static inline int defaultApplicationFontSizePixels(const ViewProviderSketch & vp); + static inline int getApplicationLogicalDPIX(const ViewProviderSketch & vp); + + static inline bool isSketchInvalid(const ViewProviderSketch & vp); + static inline bool isSketchFullyConstrained(const ViewProviderSketch & vp); + static inline bool haveConstraintsInvalidGeometry(const ViewProviderSketch & vp); + + static inline void addNodeToRoot(ViewProviderSketch & vp, SoSeparator * node); + + static inline void removeNodeFromRoot(ViewProviderSketch & vp, SoSeparator * node); + + static inline int getPreselectPoint(const ViewProviderSketch &vp); + static inline int getPreselectCurve(const ViewProviderSketch &vp); + static inline int getPreselectCross(const ViewProviderSketch &vp); + + static inline bool isConstraintPreselected(const ViewProviderSketch &vp, int constraintId); + static inline bool isPointSelected(const ViewProviderSketch &vp, int pointId); + static inline bool isCurveSelected(const ViewProviderSketch &vp, int curveId); + static inline bool isConstraintSelected(const ViewProviderSketch &vp, int constraintId); + + static inline void executeOnSelectionPointSet(const ViewProviderSketch &vp, std::function && operation); + + friend class EditModeCoinManager; + friend class EditModeConstraintCoinManager; + friend class EditModeGeometryCoinManager; +}; + +inline bool ViewProviderSketchCoinAttorney::constraintHasExpression(const ViewProviderSketch & vp, int constrid) +{ + return vp.constraintHasExpression(constrid); +}; + +inline const std::vector ViewProviderSketchCoinAttorney::getConstraints(const ViewProviderSketch & vp) +{ + return vp.getConstraints(); +} + +inline const GeoList ViewProviderSketchCoinAttorney::getGeoList(const ViewProviderSketch & vp) +{ + return vp.getGeoList(); +} + +const GeoListFacade ViewProviderSketchCoinAttorney::getGeoListFacade(const ViewProviderSketch & vp) +{ + return vp.getGeoListFacade(); +} + +inline Base::Placement ViewProviderSketchCoinAttorney::getEditingPlacement(const ViewProviderSketch & vp) +{ + return vp.getEditingPlacement(); +} + +inline void ViewProviderSketchCoinAttorney::updateGridExtent(ViewProviderSketch & vp, float minx, float maxx, float miny, float maxy) +{ + vp.updateGridExtent(minx, maxx, miny, maxy); +} + +inline bool ViewProviderSketchCoinAttorney::isShownVirtualSpace(const ViewProviderSketch & vp) +{ + return vp.viewProviderParameters.isShownVirtualSpace; +} + +inline std::unique_ptr ViewProviderSketchCoinAttorney::getRayPickAction(const ViewProviderSketch & vp) +{ + return vp.getRayPickAction(); +} + +inline float ViewProviderSketchCoinAttorney::getScaleFactor(const ViewProviderSketch & vp) +{ + return vp.getScaleFactor(); +} + +inline SbVec2f ViewProviderSketchCoinAttorney::getScreenCoordinates(const ViewProviderSketch & vp, SbVec2f sketchcoordinates) +{ + return vp.getScreenCoordinates(sketchcoordinates); +} + +inline QFont ViewProviderSketchCoinAttorney::getApplicationFont(const ViewProviderSketch & vp) +{ + return vp.getApplicationFont(); +} + +inline double ViewProviderSketchCoinAttorney::getRotation(const ViewProviderSketch & vp, SbVec3f pos0, SbVec3f pos1) +{ + return vp.getRotation(pos0,pos1); +} + +inline int ViewProviderSketchCoinAttorney::defaultApplicationFontSizePixels(const ViewProviderSketch & vp) +{ + return vp.defaultFontSizePixels(); +} + +inline int ViewProviderSketchCoinAttorney::getApplicationLogicalDPIX(const ViewProviderSketch & vp) +{ + return vp.getApplicationLogicalDPIX(); +} + +inline bool ViewProviderSketchCoinAttorney::isSketchInvalid(const ViewProviderSketch & vp) +{ + return vp.isSketchInvalid(); +} + +inline bool ViewProviderSketchCoinAttorney::isSketchFullyConstrained(const ViewProviderSketch & vp) +{ + return vp.isSketchFullyConstrained(); +} + +inline bool ViewProviderSketchCoinAttorney::haveConstraintsInvalidGeometry(const ViewProviderSketch & vp) +{ + return vp.haveConstraintsInvalidGeometry(); +} + +inline void ViewProviderSketchCoinAttorney::addNodeToRoot(ViewProviderSketch & vp, SoSeparator * node) +{ + vp.addNodeToRoot(node); +} + +inline void ViewProviderSketchCoinAttorney::removeNodeFromRoot(ViewProviderSketch & vp, SoSeparator * node) +{ + vp.removeNodeFromRoot(node); +} + +inline int ViewProviderSketchCoinAttorney::getPreselectPoint(const ViewProviderSketch &vp) +{ + return vp.getPreselectPoint(); +} + +inline int ViewProviderSketchCoinAttorney::getPreselectCurve(const ViewProviderSketch &vp) +{ + return vp.getPreselectCurve(); +} + +inline int ViewProviderSketchCoinAttorney::getPreselectCross(const ViewProviderSketch &vp) +{ + return vp.getPreselectCross(); +} + +inline bool ViewProviderSketchCoinAttorney::isConstraintPreselected(const ViewProviderSketch &vp, int constraintId) +{ + return vp.isConstraintPreselected(constraintId); +} + +inline bool ViewProviderSketchCoinAttorney::isPointSelected(const ViewProviderSketch &vp, int pointId) +{ + return vp.isPointSelected(pointId); +} + +inline bool ViewProviderSketchCoinAttorney::isCurveSelected(const ViewProviderSketch &vp, int curveId) +{ + return vp.isCurveSelected(curveId); +} + +inline bool ViewProviderSketchCoinAttorney::isConstraintSelected(const ViewProviderSketch &vp, int constraintId) +{ + return vp.isConstraintSelected(constraintId); +} + +inline void ViewProviderSketchCoinAttorney::executeOnSelectionPointSet(const ViewProviderSketch &vp, std::function && operation) +{ + vp.executeOnSelectionPointSet(std::move(operation)); +} + +} // namespace SketcherGui + + +#endif // SKETCHERGUI_ViewProviderSketchCoinAttorney_H + + +