From f12999724bacd5aa01b88f0fc334338b86550490 Mon Sep 17 00:00:00 2001 From: WandererFan Date: Fri, 13 Dec 2024 12:07:54 -0500 Subject: [PATCH 1/9] [TD]respect locale for date autofill (#18132) * [TD]respect locale for date autofill * [TD]fix merge conflict * Apply suggestions from code review --------- Co-authored-by: Chris Hennes --- src/Mod/TechDraw/App/DrawTemplate.cpp | 10 +- src/Mod/TechDraw/App/Preferences.cpp | 5 + src/Mod/TechDraw/App/Preferences.h | 1 + .../Gui/DlgPrefsTechDrawAnnotation.ui | 624 +++++++++--------- .../Gui/DlgPrefsTechDrawAnnotationImp.cpp | 2 + src/Mod/TechDraw/Gui/TemplateTextField.cpp | 9 +- 6 files changed, 344 insertions(+), 307 deletions(-) diff --git a/src/Mod/TechDraw/App/DrawTemplate.cpp b/src/Mod/TechDraw/App/DrawTemplate.cpp index 8964f10042..e36cba4373 100644 --- a/src/Mod/TechDraw/App/DrawTemplate.cpp +++ b/src/Mod/TechDraw/App/DrawTemplate.cpp @@ -37,6 +37,7 @@ #include "DrawTemplatePy.h" #include "DrawPage.h" #include "DrawUtil.h" +#include "Preferences.h" using namespace TechDraw; @@ -129,6 +130,7 @@ std::pair DrawTemplate::getPageNumbers() const //! get replacement values from document QString DrawTemplate::getAutofillValue(const QString &id) const { + constexpr int ISODATELENGTH {10}; auto doc = getDocument(); if (!doc) { return QString(); @@ -142,8 +144,14 @@ QString DrawTemplate::getAutofillValue(const QString &id) const } // date else if (id.compare(QString::fromUtf8(Autofill::Date)) == 0) { + auto timeLocale = std::setlocale(LC_TIME, nullptr); QDateTime date = QDateTime::currentDateTime(); - return date.toString(QLocale().dateFormat(QLocale::ShortFormat)); + if (Preferences::enforceISODate()) { + auto rawDate = date.toString(Qt::ISODate); + return rawDate.left(ISODATELENGTH); + } + auto qTimeLocale = QString::fromUtf8(timeLocale); + return date.toString(QLocale(qTimeLocale).dateFormat(QLocale::ShortFormat)); } // organization ( also organisation/owner/company ) else if (id.compare(QString::fromUtf8(Autofill::Organization)) == 0 || diff --git a/src/Mod/TechDraw/App/Preferences.cpp b/src/Mod/TechDraw/App/Preferences.cpp index 7962467cd0..9515e7b2d6 100644 --- a/src/Mod/TechDraw/App/Preferences.cpp +++ b/src/Mod/TechDraw/App/Preferences.cpp @@ -650,6 +650,11 @@ void Preferences::setBalloonDragModifiers(Qt::KeyboardModifiers newModifiers) getPreferenceGroup("General")->SetUnsigned("BalloonDragModifier", (uint)newModifiers); } +bool Preferences::enforceISODate() +{ + return getPreferenceGroup("Standards")->GetBool("EnforceISODate", false); +} + //! if true, shapes are validated before use and problematic ones are skipped. //! validating shape takes time, but can prevent crashes/bad results in occt. //! this would normally be set to false and set to true to aid in debugging/support. diff --git a/src/Mod/TechDraw/App/Preferences.h b/src/Mod/TechDraw/App/Preferences.h index b93889f1bd..2d1a99476f 100644 --- a/src/Mod/TechDraw/App/Preferences.h +++ b/src/Mod/TechDraw/App/Preferences.h @@ -151,6 +151,7 @@ public: static Qt::KeyboardModifiers balloonDragModifiers(); static void setBalloonDragModifiers(Qt::KeyboardModifiers newModifiers); + static bool enforceISODate(); static bool switchOnClick(); static bool checkShapesBeforeUse(); diff --git a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawAnnotation.ui b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawAnnotation.ui index 5f49c6a912..eb5d494323 100644 --- a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawAnnotation.ui +++ b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawAnnotation.ui @@ -6,8 +6,8 @@ 0 0 - 580 - 795 + 835 + 956 @@ -28,231 +28,6 @@ - - - - - 0 - 0 - - - - Length of balloon leader line kink - - - 5.000000000000000 - - - BalloonKink - - - Mod/TechDraw/Dimensions - - - - - - - This checkbox controls whether or not to display a highlight around the detail area in the detail's source view. - - - Detail Source Show Highlight - - - true - - - ShowDetailHighlight - - - /Mod/TechDraw/General - - - - - - - - true - - - - Section Cut Surface - - - - - - - - 0 - 0 - - - - Style for balloon leader line ends - - - BalloonArrow - - - Mod/TechDraw/Decorations - - - - - - - - true - - - - Balloon Leader End - - - - - - - - true - - - - If checked, the cut line will be drawn on the Source view. If unchecked, only the change marks, arrows and symbols will be displayed. - - - Include Cut Line in Section Annotation - - - true - - - IncludeCutLine - - - Mod/TechDraw/Decorations - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - true - - - - Show or hide marks at direction changes on ComplexSection lines. - - - Complex Section Line Marks - - - true - - - SectionLineMarks - - - Mod/TechDraw/Decorations - - - - - - - This checkbox controls whether or not to display the outline around a detail view. - - - Detail View Show Matting - - - true - - - ShowDetailMatting - - - /Mod/TechDraw/General - - - - - - - - true - - - - If checked, the section annotation will be drawn on the Source view. If unchecked, no section line, arrows or symbol will be shown in the Source view. - - - Show Section Line in Source View - - - true - - - ShowSectionLine - - - Mod/TechDraw/Decorations - - - - - - - - false - - - - Detail View Outline Shape - - - - - - - - 0 - 0 - - - - Restrict Filled Triangle line end to vertical or horizontal directions - - - Balloon Orthogonal Triangle - - - true - - - PyramidOrtho - - - Mod/TechDraw/Decorations - - - @@ -275,36 +50,6 @@ - - - - - 0 - 0 - - - - - true - - - - Forces last leader line segment to be horizontal - - - Leader Line Auto Horizontal - - - true - - - AutoHorizontal - - - Mod/TechDraw/LeaderLine - - - @@ -335,15 +80,141 @@ - - + + + + + true + + + + If checked, the section annotation will be drawn on the Source view. If unchecked, no section line, arrows or symbol will be shown in the Source view. + + + Show Section Line in Source View + + + true + + + ShowSectionLine + + + Mod/TechDraw/Decorations + + + + + + + + 0 + 0 + + + + Shape of balloon annotations + + + BalloonShape + + + Mod/TechDraw/Decorations + + + + + + + + true + + + + If checked, the cut line will be drawn on the Source view. If unchecked, only the change marks, arrows and symbols will be displayed. + + + Include Cut Line in Section Annotation + + + true + + + IncludeCutLine + + + Mod/TechDraw/Decorations + + + + + + + + 0 + 0 + + + + Style for balloon leader line ends + + + BalloonArrow + + + Mod/TechDraw/Decorations + + + + + + + + true + + + + Length of horizontal portion of Balloon leader + + + Balloon Leader Kink Length + + + + + true - Balloon Shape + Broken View Break Type + + + + + + + + 0 + 0 + + + + Restrict Filled Triangle line end to vertical or horizontal directions + + + Balloon Orthogonal Triangle + + + true + + + PyramidOrtho + + + Mod/TechDraw/Decorations @@ -383,68 +254,108 @@ - - + + + + This checkbox controls whether or not to display the outline around a detail view. + + + Detail View Show Matting + + + true + + + ShowDetailMatting + + + /Mod/TechDraw/General + + + + + + + This checkbox controls whether or not to display a highlight around the detail area in the detail's source view. + + + Detail Source Show Highlight + + + true + + + ShowDetailHighlight + + + /Mod/TechDraw/General + + + + + + + + false + + + + Detail View Outline Shape + + + + + 0 0 - - Outline shape for detail views - - - MattingStyle - - - /Mod/TechDraw/Decorations - - - - - - - - 0 - 0 - - - - Shape of balloon annotations - - - BalloonShape - - - Mod/TechDraw/Decorations - - - - - true - Length of horizontal portion of Balloon leader + Forces last leader line segment to be horizontal - Balloon Leader Kink Length + Leader Line Auto Horizontal + + + true + + + AutoHorizontal + + + Mod/TechDraw/LeaderLine - - + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + true - Broken View Break Type + Balloon Leader End @@ -476,6 +387,111 @@ + + + + + true + + + + Balloon Shape + + + + + + + + true + + + + Section Cut Surface + + + + + + + + 0 + 0 + + + + Outline shape for detail views + + + MattingStyle + + + /Mod/TechDraw/Decorations + + + + + + + + true + + + + Show or hide marks at direction changes on ComplexSection lines. + + + Complex Section Line Marks + + + true + + + SectionLineMarks + + + Mod/TechDraw/Decorations + + + + + + + + 0 + 0 + + + + Length of balloon leader line kink + + + 5.000000000000000 + + + BalloonKink + + + Mod/TechDraw/Dimensions + + + + + + + If this box is checked, templates will auto fill date fields using ccyy-mm-dd format even if that is not the standard format for the current locale. + + + Enforce ISO 8601 Date Format + + + EnforceISODate + + + Mod/TechDraw/Standards + + + @@ -507,7 +523,7 @@ 6 - QComboBox::AdjustToContents + QComboBox::SizeAdjustPolicy::AdjustToContents @@ -775,7 +791,7 @@ if you are planning to use a drawing as a 1:1 cutting guide. - Qt::Horizontal + Qt::Orientation::Horizontal @@ -837,7 +853,7 @@ if you are planning to use a drawing as a 1:1 cutting guide. - Qt::Vertical + Qt::Orientation::Vertical @@ -871,8 +887,6 @@ if you are planning to use a drawing as a 1:1 cutting guide.
Gui/PrefWidgets.h
- - - + diff --git a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawAnnotationImp.cpp b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawAnnotationImp.cpp index 551d20d45a..1affe1666a 100644 --- a/src/Mod/TechDraw/Gui/DlgPrefsTechDrawAnnotationImp.cpp +++ b/src/Mod/TechDraw/Gui/DlgPrefsTechDrawAnnotationImp.cpp @@ -127,6 +127,7 @@ void DlgPrefsTechDrawAnnotationImp::saveSettings() ui->pcbBreakType->onSave(); ui->pcbBreakStyle->onSave(); + ui->cbISODates->onSave(); } void DlgPrefsTechDrawAnnotationImp::loadSettings() @@ -186,6 +187,7 @@ void DlgPrefsTechDrawAnnotationImp::loadSettings() loadLineStyleBoxes(); ui->pcbBreakType->onRestore(); + ui->cbISODates->onRestore(); } /** diff --git a/src/Mod/TechDraw/Gui/TemplateTextField.cpp b/src/Mod/TechDraw/Gui/TemplateTextField.cpp index b0327184a2..dd22a49863 100644 --- a/src/Mod/TechDraw/Gui/TemplateTextField.cpp +++ b/src/Mod/TechDraw/Gui/TemplateTextField.cpp @@ -77,14 +77,21 @@ void TemplateTextField::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) ui.setFieldName(fieldNameStr); ui.setFieldContent(tmplte->EditableTexts[fieldNameStr]); + + auto qName = QString::fromStdString(fieldNameStr); + auto svgTemplate = dynamic_cast(tmplte); + if (svgTemplate) { + // preset the autofill with the current value - something might have changed since this field was created + m_autofillString = svgTemplate->getAutofillByEditableName(qName); + } ui.setAutofillContent(m_autofillString.toStdString()); if (ui.exec() == QDialog::Accepted) { QString qsClean = ui.getFieldContent(); std::string utf8Content = qsClean.toUtf8().constData(); if (ui.getAutofillState()) { - auto svgTemplate = dynamic_cast(tmplte); if (svgTemplate) { + // unlikely, but something could have changed since we grabbed the autofill value QString fieldName = QString::fromStdString(fieldNameStr); QString autofillValue = svgTemplate->getAutofillByEditableName(fieldName); if (!autofillValue.isEmpty()) { From e7e410323eeb6f9291299f4aa53345c84d59da3c Mon Sep 17 00:00:00 2001 From: wmayer Date: Mon, 9 Dec 2024 11:05:14 +0100 Subject: [PATCH 2/9] Sketch: Fix possible crash in BSpline::splineValue There is an underflow of an unsigned int in the calling instance that sets the parameter 'p' to 2**32-1. But the size of the passed vector is 0. To fix the crash first check if p is less then the size of the vector. See: https://forum.freecad.org/viewtopic.php?t=92815 --- src/Mod/Sketcher/App/planegcs/Geo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Sketcher/App/planegcs/Geo.cpp b/src/Mod/Sketcher/App/planegcs/Geo.cpp index b46ce7c0f7..cbb4e30983 100644 --- a/src/Mod/Sketcher/App/planegcs/Geo.cpp +++ b/src/Mod/Sketcher/App/planegcs/Geo.cpp @@ -1141,7 +1141,7 @@ double BSpline::splineValue(double x, size_t k, unsigned int p, VEC_D& d, const } } - return d[p]; + return p < d.size() ? d[p] : 0.0; } void BSpline::setupFlattenedKnots() From 00f6fbeaa3a717d61b5391a98e855377108679e8 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 11 Dec 2024 19:12:49 +0100 Subject: [PATCH 3/9] PD: Fix possible crash in up to shape --- src/Mod/PartDesign/App/FeatureExtrude.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureExtrude.cpp b/src/Mod/PartDesign/App/FeatureExtrude.cpp index 6d0e4938e0..e2deb3f82b 100644 --- a/src/Mod/PartDesign/App/FeatureExtrude.cpp +++ b/src/Mod/PartDesign/App/FeatureExtrude.cpp @@ -141,8 +141,13 @@ TopoShape FeatureExtrude::makeShellFromUpToShape(TopoShape shape, TopoShape sket dir = -dir; cfaces = Part::findAllFacesCutBy(shape, sketchshape, dir); } - struct Part::cutTopoShapeFaces *nearFace; - struct Part::cutTopoShapeFaces *farFace; + + if (cfaces.empty()) { + return shape; + } + + struct Part::cutTopoShapeFaces *nearFace {}; + struct Part::cutTopoShapeFaces *farFace {}; nearFace = farFace = &cfaces.front(); for (auto &face : cfaces) { if (face.distsq > farFace->distsq) { From 1955f280536488ae08ccb25080086d490f5ad5c3 Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Fri, 13 Dec 2024 12:20:32 -0500 Subject: [PATCH 4/9] [CAM] implement multipass profile operations (#17326) * implement multipass profile operations * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- src/Mod/CAM/App/Area.cpp | 4 -- .../Resources/panels/PageOpProfileFullEdit.ui | 37 ++++++++++ src/Mod/CAM/Path/Op/Gui/Profile.py | 11 +++ src/Mod/CAM/Path/Op/Profile.py | 68 ++++++++++++++++++- 4 files changed, 115 insertions(+), 5 deletions(-) diff --git a/src/Mod/CAM/App/Area.cpp b/src/Mod/CAM/App/Area.cpp index 6d94ea27c4..bb1969a63f 100644 --- a/src/Mod/CAM/App/Area.cpp +++ b/src/Mod/CAM/App/Area.cpp @@ -2420,14 +2420,10 @@ void Area::makeOffset(list>& areas, #endif if (offset < 0) { - stepover = -fabs(stepover); if (count < 0) { if (!last_stepover) { last_stepover = offset * 0.5; } - else { - last_stepover = -fabs(last_stepover); - } } else { last_stepover = 0; diff --git a/src/Mod/CAM/Gui/Resources/panels/PageOpProfileFullEdit.ui b/src/Mod/CAM/Gui/Resources/panels/PageOpProfileFullEdit.ui index 4b6ced799d..8c539a4a51 100644 --- a/src/Mod/CAM/Gui/Resources/panels/PageOpProfileFullEdit.ui +++ b/src/Mod/CAM/Gui/Resources/panels/PageOpProfileFullEdit.ui @@ -115,6 +115,43 @@
+ + + + Number of Passes + + + + + + + 1 + + + The number of passes to do. If more than one, requires a non-zero value for Pass Stepover. + + + + + + + Pass Stepover + + + + + + + + 0 + 0 + + + + If doing multiple passes, the extra offset of each additional pass. + + +
diff --git a/src/Mod/CAM/Path/Op/Gui/Profile.py b/src/Mod/CAM/Path/Op/Gui/Profile.py index a9ce036787..f7aa0aff22 100644 --- a/src/Mod/CAM/Path/Op/Gui/Profile.py +++ b/src/Mod/CAM/Path/Op/Gui/Profile.py @@ -77,6 +77,8 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): if obj.Direction != str(self.form.direction.currentData()): obj.Direction = str(self.form.direction.currentData()) PathGuiUtil.updateInputField(obj, "OffsetExtra", self.form.extraOffset) + obj.NumPasses = self.form.numPasses.value() + PathGuiUtil.updateInputField(obj, "Stepover", self.form.stepover) if obj.UseComp != self.form.useCompensation.isChecked(): obj.UseComp = self.form.useCompensation.isChecked() @@ -100,6 +102,10 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): self.form.extraOffset.setText( FreeCAD.Units.Quantity(obj.OffsetExtra.Value, FreeCAD.Units.Length).UserString ) + self.form.numPasses.setValue(obj.NumPasses) + self.form.stepover.setText( + FreeCAD.Units.Quantity(obj.Stepover.Value, FreeCAD.Units.Length).UserString + ) self.form.useCompensation.setChecked(obj.UseComp) self.form.useStartPoint.setChecked(obj.UseStartPoint) @@ -117,6 +123,8 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): signals.append(self.form.cutSide.currentIndexChanged) signals.append(self.form.direction.currentIndexChanged) signals.append(self.form.extraOffset.editingFinished) + signals.append(self.form.numPasses.editingFinished) + signals.append(self.form.stepover.editingFinished) signals.append(self.form.useCompensation.stateChanged) signals.append(self.form.useStartPoint.stateChanged) signals.append(self.form.processHoles.stateChanged) @@ -148,8 +156,11 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): self.form.processHoles.hide() self.form.processPerimeter.hide() + self.form.stepover.setEnabled(self.obj.NumPasses > 1) + def registerSignalHandlers(self, obj): self.form.useCompensation.stateChanged.connect(self.updateVisibility) + self.form.numPasses.editingFinished.connect(self.updateVisibility) # Eclass diff --git a/src/Mod/CAM/Path/Op/Profile.py b/src/Mod/CAM/Path/Op/Profile.py index b2037e5494..e19bdac9c4 100644 --- a/src/Mod/CAM/Path/Op/Profile.py +++ b/src/Mod/CAM/Path/Op/Profile.py @@ -172,6 +172,24 @@ class ObjectProfile(PathAreaOp.ObjectOp): "App::Property", "Make True, if using Cutter Radius Compensation" ), ), + ( + "App::PropertyInteger", + "NumPasses", + "Profile", + QT_TRANSLATE_NOOP( + "App::Property", + "The number of passes to do. If more than one, requires a non-zero value for Stepover", + ), + ), + ( + "App::PropertyDistance", + "Stepover", + "Profile", + QT_TRANSLATE_NOOP( + "App::Property", + "If doing multiple passes, the extra offset of each additional pass", + ), + ), ] @classmethod @@ -235,6 +253,8 @@ class ObjectProfile(PathAreaOp.ObjectOp): "processCircles": False, "processHoles": False, "processPerimeter": True, + "Stepover": 0, + "NumPasses": 1, } def areaOpApplyPropertyDefaults(self, obj, job, propList): @@ -295,6 +315,26 @@ class ObjectProfile(PathAreaOp.ObjectOp): self.initAreaOpProperties(obj, warn=True) self.areaOpSetDefaultValues(obj, PathUtils.findParentJob(obj)) self.setOpEditorProperties(obj) + if not hasattr(obj, "NumPasses"): + obj.addProperty( + "App::PropertyInteger", + "NumPasses", + "Profile", + QT_TRANSLATE_NOOP( + "App::Property", + "The number of passes to do. Requires a non-zero value for Stepover", + ), + ) + if not hasattr(obj, "Stepover"): + obj.addProperty( + "App::PropertyDistance", + "Stepover", + "Profile", + QT_TRANSLATE_NOOP( + "App::Property", + "If doing multiple passes, the extra offset of each additional pass", + ), + ) def areaOpOnChanged(self, obj, prop): """areaOpOnChanged(obj, prop) ... updates certain property visibilities depending on changed properties.""" @@ -311,13 +351,31 @@ class ObjectProfile(PathAreaOp.ObjectOp): params["SectionCount"] = -1 offset = obj.OffsetExtra.Value # 0.0 + num_passes = max(1, obj.NumPasses) + stepover = obj.Stepover.Value + if num_passes > 1 and stepover == 0: + # This check is important because C++ code has a default value for stepover if it's 0 and extra passes are requested + num_passes = 1 + Path.Log.warning( + "Multipass profile requires a non-zero stepover. Reducing to a single pass." + ) + if obj.UseComp: offset = self.radius + obj.OffsetExtra.Value if obj.Side == "Inside": offset = 0 - offset + stepover = -stepover if isHole: offset = 0 - offset + stepover = -stepover + + # Modify offset and stepover to do passes from most-offset to least + offset += stepover * (num_passes - 1) + stepover = -stepover + params["Offset"] = offset + params["ExtraPass"] = num_passes - 1 + params["Stepover"] = stepover jointype = ["Round", "Square", "Miter"] params["JoinType"] = jointype.index(obj.JoinType) @@ -356,6 +414,10 @@ class ObjectProfile(PathAreaOp.ObjectOp): else: params["orientation"] = 0 + if obj.NumPasses > 1: + # Disable path sorting to ensure that offsets appear in order, from farthest offset to closest, on all layers + params["sort_mode"] = 0 + return params def areaOpUseProjection(self, obj): @@ -590,7 +652,11 @@ class ObjectProfile(PathAreaOp.ObjectOp): if flattened and zDiff >= self.JOB.GeometryTolerance.Value: cutWireObjs = False openEdges = [] - passOffsets = [self.ofstRadius] + params = self.areaOpAreaParams(obj, False) + passOffsets = [ + self.ofstRadius + i * abs(params["Stepover"]) + for i in range(params["ExtraPass"] + 1) + ][::-1] (origWire, flatWire) = flattened self._addDebugObject("FlatWire", flatWire) From 9efa615fb4eafe679efcf16df43a424b5fc72352 Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Fri, 13 Dec 2024 15:07:32 +0100 Subject: [PATCH 5/9] BIM: Fixed shape loading - fixes #18391 --- src/Mod/BIM/nativeifc/ifc_viewproviders.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/BIM/nativeifc/ifc_viewproviders.py b/src/Mod/BIM/nativeifc/ifc_viewproviders.py index 3d7558ec31..9880e1dc3a 100644 --- a/src/Mod/BIM/nativeifc/ifc_viewproviders.py +++ b/src/Mod/BIM/nativeifc/ifc_viewproviders.py @@ -245,7 +245,7 @@ class ifc_vp_object: import Part # lazy loading self.Object.Shape = Part.Shape() - elif self.Object.ShapeMode == "Coin": + else: self.Object.ShapeMode = "Shape" self.Object.Document.recompute() self.Object.ViewObject.DiffuseColor = self.Object.ViewObject.DiffuseColor From 29d685b452749cbe2827030ccdea89c45b62eecf Mon Sep 17 00:00:00 2001 From: mosfet80 Date: Tue, 10 Dec 2024 08:03:32 +0100 Subject: [PATCH 6/9] Clean ViewProviderAssembly.cpp --- src/Mod/Assembly/Gui/ViewProviderAssembly.cpp | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp b/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp index 1f3016927d..40a24c4237 100644 --- a/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp +++ b/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp @@ -136,8 +136,6 @@ void ViewProviderAssembly::setupContextMenu(QMenu* menu, QObject* receiver, cons bool ViewProviderAssembly::doubleClicked() { if (isInEditMode()) { - // Part is already 'Active' so we exit edit mode. - // Gui::Command::doCommand(Gui::Command::Gui, "Gui.activeDocument().resetEdit()"); getDocument()->resetEdit(); } else { @@ -165,7 +163,6 @@ bool ViewProviderAssembly::canDragObject(App::DocumentObject* obj) const if (!obj || obj->getTypeId() == Assembly::JointGroup::getClassTypeId()) { return false; } - return true; } @@ -214,7 +211,6 @@ bool ViewProviderAssembly::canDragObjectToTarget(App::DocumentObject* obj, joint->getNameInDocument()); } } - return true; } @@ -245,7 +241,6 @@ bool ViewProviderAssembly::setEdit(int mode) return true; } - return ViewProviderPart::setEdit(mode); } @@ -257,7 +252,6 @@ void ViewProviderAssembly::unsetEdit(int mode) docsToMove.clear(); unsetDragger(); - detachSelection(); // Check if the view is still active before trying to deactivate the assembly. @@ -274,7 +268,6 @@ void ViewProviderAssembly::unsetEdit(int mode) PARTKEY); return; } - ViewProviderPart::unsetEdit(mode); } @@ -325,7 +318,6 @@ App::DocumentObject* ViewProviderAssembly::getActivePart() const if (!activeView) { return nullptr; } - return activeView->getActiveObject(PARTKEY); } @@ -347,7 +339,6 @@ bool ViewProviderAssembly::keyPressed(bool pressed, int key) if (key == SoKeyboardEvent::LEFT_CONTROL || key == SoKeyboardEvent::RIGHT_CONTROL) { ctrlPressed = pressed; } - return false; // handle all other key events } @@ -412,14 +403,12 @@ bool ViewProviderAssembly::tryMouseMove(const SbVec2s& cursorPos, Gui::View3DInv newPos = Base::Vector3d(vec[0], vec[1], vec[2]); } - for (auto& objToMove : docsToMove) { App::DocumentObject* obj = objToMove.obj; auto* propPlacement = dynamic_cast(obj->getPropertyByName("Placement")); if (propPlacement) { Base::Placement plc = objToMove.plc; - // Base::Console().Warning("newPos %f %f %f\n", newPos.x, newPos.y, newPos.z); if (dragMode == DragMode::RotationOnPlane) { Base::Vector3d center = jcsGlobalPlc.getPosition(); @@ -427,7 +416,6 @@ bool ViewProviderAssembly::tryMouseMove(const SbVec2s& cursorPos, Gui::View3DInv jcsGlobalPlc.getRotation().multVec(Base::Vector3d(0., 0., -1.)); double angle = (newPosRot - center).GetAngleOriented(initialPositionRot - center, norm); - // Base::Console().Warning("angle %f\n", angle); Base::Rotation zRotation = Base::Rotation(Base::Vector3d(0., 0., 1.), angle); Base::Placement rotatedGlovalJcsPlc = jcsGlobalPlc * Base::Placement(Base::Vector3d(), zRotation); @@ -455,7 +443,6 @@ bool ViewProviderAssembly::tryMouseMove(const SbVec2s& cursorPos, Gui::View3DInv boost::ignore_unused(projInitialPositionRot); double angle = (newPosRot - center).GetAngleOriented(initialPositionRot - center, norm); - // Base::Console().Warning("angle %f\n", angle); Base::Rotation zRotation = Base::Rotation(Base::Vector3d(0., 0., 1.), angle); Base::Placement rotatedGlovalJcsPlc = newJcsGlobalPlc * Base::Placement(Base::Vector3d(), zRotation); @@ -470,7 +457,6 @@ bool ViewProviderAssembly::tryMouseMove(const SbVec2s& cursorPos, Gui::View3DInv Base::Vector3d delta = newPos - prevPosition; Base::Vector3d pos = propPlacement->getValue().getPosition() + delta; - // Base::Vector3d pos = newPos + (plc.getPosition() - initialPosition); plc.setPosition(pos); } propPlacement->setValue(plc); @@ -484,7 +470,6 @@ bool ViewProviderAssembly::tryMouseMove(const SbVec2s& cursorPos, Gui::View3DInv "User parameter:BaseApp/Preferences/Mod/Assembly"); bool solveOnMove = hGrp->GetBool("SolveOnMove", true); if (solveOnMove && dragMode != DragMode::TranslationNoSolve) { - // assemblyPart->solve(/*enableRedo = */ false, /*updateJCS = */ false); assemblyPart->doDragStep(); } else { @@ -546,7 +531,6 @@ bool ViewProviderAssembly::mouseButtonPressed(int Button, } } } - return false; } @@ -589,7 +573,6 @@ bool ViewProviderAssembly::canDragObjectIn3d(App::DocumentObject* obj) const return true; } } - return false; } @@ -688,9 +671,6 @@ bool ViewProviderAssembly::getSelectedObjectsWithinAssembly(bool addPreselection // it is not selected at that point. So we need to get the preselection too. if (addPreselection && Gui::Selection().hasPreselection()) { - // Base::Console().Warning("Gui::Selection().getPreselection().pSubName %s\n", - // Gui::Selection().getPreselection().pSubName); - App::DocumentObject* selRoot = Gui::Selection().getPreselection().Object.getObject(); std::string sub = Gui::Selection().getPreselection().pSubName; @@ -1140,7 +1120,6 @@ bool ViewProviderAssembly::canDelete(App::DocumentObject* objBeingDeleted) const joint->getNameInDocument()); } } - return res; } From d4b7d074ee201357c86589a164454d9716a87be1 Mon Sep 17 00:00:00 2001 From: Vincent Belpois Date: Mon, 2 Dec 2024 21:28:41 -0600 Subject: [PATCH 7/9] Sketcher: Fix double RMB to quite polyline after closed shape --- src/Mod/Sketcher/Gui/DrawSketchHandlerLineSet.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerLineSet.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerLineSet.h index f8673d0285..b8600b5435 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerLineSet.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerLineSet.h @@ -554,6 +554,7 @@ public: static_cast(lastEndPosId), firstCurve, static_cast(firstPosId)); + firstsegment = true; } Gui::Command::commitCommand(); From a4c85232c92fc5b36327e5be2fe9bb551f6d0907 Mon Sep 17 00:00:00 2001 From: kwahoo2 Date: Fri, 13 Dec 2024 18:32:37 +0100 Subject: [PATCH 8/9] Gui: add method to select objects with a 3D ray (#16789) * Implementation of ray picking method for 3d picking * Ray picking logic moved to C++ (cherry picked from commit ed23214c0bce7b70fd1003a7c4612e2d0d7da4cb) * formatting, do not return unecessary dict keys, near plane clipping --- src/Gui/View3DInventor.cpp | 95 ++++++++++++++++++++++++++++++++++++++ src/Gui/View3DInventor.h | 13 ++++++ src/Gui/View3DPy.cpp | 56 ++++++++++++++++++++++ src/Gui/View3DPy.h | 1 + 4 files changed, 165 insertions(+) diff --git a/src/Gui/View3DInventor.cpp b/src/Gui/View3DInventor.cpp index 0b13430634..1c542629c5 100644 --- a/src/Gui/View3DInventor.cpp +++ b/src/Gui/View3DInventor.cpp @@ -47,9 +47,11 @@ # include # include # include +# include #endif #include +#include #include #include #include @@ -70,8 +72,10 @@ #include "View3DInventorViewer.h" #include "View3DPy.h" #include "ViewProvider.h" +#include "ViewProviderDocumentObject.h" #include "WaitCursor.h" +#include "Utilities.h" using namespace Gui; @@ -773,6 +777,97 @@ void View3DInventor::setCurrentViewMode(ViewMode newmode) } } +RayPickInfo View3DInventor::getObjInfoRay(Base::Vector3d* startvec, Base::Vector3d* dirvec) +{ + double vsx, vsy, vsz; + double vdx, vdy, vdz; + vsx = startvec->x; + vsy = startvec->y; + vsz = startvec->z; + vdx = dirvec->x; + vdy = dirvec->y; + vdz = dirvec->z; + // near plane clipping is required to avoid false intersections + float near = 0.1; + + RayPickInfo ret = {.isValid = false, + .point = Base::Vector3d(), + .document = "", + .object = "", + .parentObject = std::nullopt, + .component = std::nullopt, + .subName = std::nullopt}; + SoRayPickAction action(getViewer()->getSoRenderManager()->getViewportRegion()); + action.setRay(SbVec3f(vsx, vsy, vsz), SbVec3f(vdx, vdy, vdz), near); + action.apply(getViewer()->getSoRenderManager()->getSceneGraph()); + SoPickedPoint* Point = action.getPickedPoint(); + + if (!Point) { + return ret; + } + + ret.point = Base::convertTo(Point->getPoint()); + ViewProvider* vp = getViewer()->getViewProviderByPath(Point->getPath()); + if (vp && vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) { + if (!vp->isSelectable()) { + return ret; + } + auto vpd = static_cast(vp); + if (vp->useNewSelectionModel()) { + std::string subname; + if (!vp->getElementPicked(Point, subname)) { + return ret; + } + auto obj = vpd->getObject(); + if (!obj) { + return ret; + } + if (!subname.empty()) { + App::ElementNamePair elementName; + auto sobj = App::GeoFeature::resolveElement(obj, subname.c_str(), elementName); + if (!sobj) { + return ret; + } + if (sobj != obj) { + ret.parentObject = obj->getExportName(); + ret.subName = subname; + obj = sobj; + } + subname = !elementName.oldName.empty() ? elementName.oldName : elementName.newName; + } + ret.document = obj->getDocument()->getName(); + ret.object = obj->getNameInDocument(); + ret.component = subname; + ret.isValid = true; + } + else { + ret.document = vpd->getObject()->getDocument()->getName(); + ret.object = vpd->getObject()->getNameInDocument(); + // search for a SoFCSelection node + SoFCDocumentObjectAction objaction; + objaction.apply(Point->getPath()); + if (objaction.isHandled()) { + ret.component = objaction.componentName.getString(); + } + } + // ok, found the node of interest + ret.isValid = true; + } + else { + // custom nodes not in a VP: search for a SoFCSelection node + SoFCDocumentObjectAction objaction; + objaction.apply(Point->getPath()); + if (objaction.isHandled()) { + ret.document = objaction.documentName.getString(); + ret.object = objaction.objectName.getString(); + ret.component = objaction.componentName.getString(); + // ok, found the node of interest + ret.isValid = true; + } + } + return ret; +} + bool View3DInventor::eventFilter(QObject* watched, QEvent* e) { // As long as this widget is a top-level window (either in 'TopLevel' or 'FullScreen' mode) we diff --git a/src/Gui/View3DInventor.h b/src/Gui/View3DInventor.h index 3bfe2b237a..882265183a 100644 --- a/src/Gui/View3DInventor.h +++ b/src/Gui/View3DInventor.h @@ -31,6 +31,7 @@ #include "MDIView.h" +#include "Base/Vector3D.h" class QPrinter; class QStackedWidget; @@ -43,6 +44,16 @@ class View3DPy; class View3DSettings; class NaviCubeSettings; +struct RayPickInfo +{ + bool isValid; + Base::Vector3d point; + std::string document; + std::string object; + std::optional parentObject; + std::optional component; + std::optional subName; +}; class GuiExport GLOverlayWidget : public QWidget { Q_OBJECT @@ -98,6 +109,8 @@ public: * GL widget to get all key events in \a TopLevel or \a Fullscreen mode. */ void setCurrentViewMode(ViewMode b) override; + RayPickInfo getObjInfoRay(Base::Vector3d* startvec, + Base::Vector3d* dirvec); bool setCamera(const char* pCamera); void toggleClippingPlane(); bool hasClippingPlane() const; diff --git a/src/Gui/View3DPy.cpp b/src/Gui/View3DPy.cpp index e2e73772a5..41eae0c32e 100644 --- a/src/Gui/View3DPy.cpp +++ b/src/Gui/View3DPy.cpp @@ -158,6 +158,14 @@ void View3DInventorPy::init_type() "\n" "Does the same as getObjectInfo() but returns a list of dictionaries or None.\n"); add_noargs_method("getSize",&View3DInventorPy::getSize,"getSize()"); + add_varargs_method("getObjectInfoRay",&View3DInventorPy::getObjectInfoRay, + "getObjectInfoRay(tuple(3D vector,3D vector) or tuple of 6 floats) -> dictionary or None\n" + "\n" + "Vectors represent start point and direction of intersection ray\n" + "Return a dictionary with the name of document, object and component. The\n" + "dictionary also contains the coordinates of the appropriate 3d point of\n" + "the underlying geometry in the scenegraph.\n" + "If no geometry was found 'None' is returned, instead.\n"); add_varargs_method("getPoint",&View3DInventorPy::getPointOnFocalPlane, "Same as getPointOnFocalPlane"); add_varargs_method("getPointOnFocalPlane",&View3DInventorPy::getPointOnFocalPlane, @@ -1501,6 +1509,54 @@ Py::Object View3DInventorPy::getObjectsInfo(const Py::Tuple& args) } } +Py::Object View3DInventorPy::getObjectInfoRay(const Py::Tuple& args) +{ + PyObject* vs; + PyObject* vd; + double vsx, vsy, vsz; + double vdx, vdy, vdz; + Py::Object ret = Py::None(); + if (PyArg_ParseTuple(args.ptr(), + "O!O!", + &Base::VectorPy::Type, + &vs, + &Base::VectorPy::Type, + &vd)) { + Base::Vector3d* startvec = static_cast(vs)->getVectorPtr(); + Base::Vector3d* dirvec = static_cast(vd)->getVectorPtr(); + try { + RayPickInfo pinfo = getView3DIventorPtr()->getObjInfoRay(startvec, dirvec); + if (!pinfo.isValid) { + return ret; + } + Py::Dict dict; + dict.setItem("PickedPoint", Py::asObject(new Base::VectorPy(pinfo.point))); + dict.setItem("Document", Py::String(pinfo.document)); + dict.setItem("Object", Py::String(pinfo.object)); + if (pinfo.parentObject) { + dict.setItem("ParentObject", Py::String(pinfo.parentObject.value())); + } + if (pinfo.component) { + dict.setItem("Component", Py::String(pinfo.component.value())); + } + if (pinfo.subName) { + dict.setItem("SubName", Py::String(pinfo.subName.value())); + } + ret = dict; + } + catch (const Py::Exception&) { + throw; + } + } + else { + PyErr_Clear(); + if (!PyArg_ParseTuple(args.ptr(), "dddddd", &vsx, &vsy, &vsz, &vdx, &vdy, &vdz)) { + throw Py::TypeError("Wrong arguments, two Vectors or six floats expected"); + } + } + return ret; +} + Py::Object View3DInventorPy::getSize() { try { diff --git a/src/Gui/View3DPy.h b/src/Gui/View3DPy.h index 2a63dc7d04..08ad8aa07e 100644 --- a/src/Gui/View3DPy.h +++ b/src/Gui/View3DPy.h @@ -95,6 +95,7 @@ public: Py::Object getObjectInfo(const Py::Tuple&); Py::Object getObjectsInfo(const Py::Tuple&); Py::Object getSize(); + Py::Object getObjectInfoRay(const Py::Tuple&); Py::Object getPointOnFocalPlane(const Py::Tuple&); Py::Object projectPointToLine(const Py::Tuple&); Py::Object getPointOnViewport(const Py::Tuple&); From f5a4ec01f8b095051355226be735104ce8eb8b44 Mon Sep 17 00:00:00 2001 From: Andrea Date: Sun, 1 Dec 2024 11:29:03 +0100 Subject: [PATCH 9/9] [Mod] Clean AssemblyObject.cpp Removed dead code [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci . --- src/Mod/Assembly/App/AssemblyObject.cpp | 28 +------------------------ 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/src/Mod/Assembly/App/AssemblyObject.cpp b/src/Mod/Assembly/App/AssemblyObject.cpp index ee82a21e4e..6dad79caa0 100644 --- a/src/Mod/Assembly/App/AssemblyObject.cpp +++ b/src/Mod/Assembly/App/AssemblyObject.cpp @@ -92,25 +92,6 @@ FC_LOG_LEVEL_INIT("Assembly", true, true, true) using namespace Assembly; using namespace MbD; -/* -static void printPlacement(Base::Placement plc, const char* name) -{ - Base::Vector3d pos = plc.getPosition(); - Base::Vector3d axis; - double angle; - Base::Rotation rot = plc.getRotation(); - rot.getRawValue(axis, angle); - Base::Console().Warning( - "placement %s : position (%.1f, %.1f, %.1f) - axis (%.1f, %.1f, %.1f) angle %.1f\n", - name, - pos.x, - pos.y, - pos.z, - axis.x, - axis.y, - axis.z, - angle); -}*/ // ================================ Assembly Object ============================ @@ -1595,8 +1576,6 @@ std::string AssemblyObject::handleOneSideOfJoint(App::DocumentObject* joint, // containing Part. if (obj->getNameInDocument() != part->getNameInDocument()) { - // Make plc relative to the containing part - // plc = objPlc * plc; // this would not work for nested parts. auto* ref = dynamic_cast(joint->getPropertyByName(propRefName)); if (!ref) { @@ -1839,9 +1818,6 @@ AssemblyObject::makeMbdPart(std::string& name, Base::Placement plc, double mass) Base::Vector3d r1 = mat.getRow(1); Base::Vector3d r2 = mat.getRow(2); mbdPart->setRotationMatrix(r0.x, r0.y, r0.z, r1.x, r1.y, r1.z, r2.x, r2.y, r2.z); - /*double q0, q1, q2, q3; - rot.getValue(q0, q1, q2, q3); - mbdPart->setQuarternions(q0, q1, q2, q3);*/ return mbdPart; } @@ -1862,9 +1838,7 @@ std::shared_ptr AssemblyObject::makeMbdMarker(std::string& name, Bas Base::Vector3d r1 = mat.getRow(1); Base::Vector3d r2 = mat.getRow(2); mbdMarker->setRotationMatrix(r0.x, r0.y, r0.z, r1.x, r1.y, r1.z, r2.x, r2.y, r2.z); - /*double q0, q1, q2, q3; - rot.getValue(q0, q1, q2, q3); - mbdMarker->setQuarternions(q0, q1, q2, q3);*/ + return mbdMarker; }