From 91440e6103ff0d9cb28ea2b92829ab52499a5d81 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Tue, 2 Jan 2024 17:35:17 +0100 Subject: [PATCH 001/135] Mesh: Fix build with pch Add missing for FLT_MAX --- src/Mod/Mesh/Gui/PreCompiled.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mod/Mesh/Gui/PreCompiled.h b/src/Mod/Mesh/Gui/PreCompiled.h index e492cbd9aa..8e9b804a4e 100644 --- a/src/Mod/Mesh/Gui/PreCompiled.h +++ b/src/Mod/Mesh/Gui/PreCompiled.h @@ -44,6 +44,7 @@ // standard #include +#include // STL #include From 755bad25da9728554477ca3443cdde67461fc168 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Tue, 2 Jan 2024 17:35:43 +0100 Subject: [PATCH 002/135] Part: Fix build with pch Move QtAll inclusion (which includes GL itself) after manually including to correctly get ARB extensions. --- src/Mod/Part/Gui/PreCompiled.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Mod/Part/Gui/PreCompiled.h b/src/Mod/Part/Gui/PreCompiled.h index f88b6d206d..4758d16e94 100644 --- a/src/Mod/Part/Gui/PreCompiled.h +++ b/src/Mod/Part/Gui/PreCompiled.h @@ -62,13 +62,8 @@ #include #include -// Qt Toolkit -#ifndef __QtAll__ -# include -#endif - // GL -// Include glext before InventorAll +// Include glext before QtAll/InventorAll #ifdef FC_OS_WIN32 # include # include @@ -87,6 +82,11 @@ // Should come after glext.h to avoid warnings #include +// Qt Toolkit +#ifndef __QtAll__ +# include +#endif + // Inventor includes OpenGL #ifndef __InventorAll__ # include From f6e4c6829f8d11fe0b741bffa374741bf3114a98 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Tue, 2 Jan 2024 19:28:09 +0100 Subject: [PATCH 003/135] App: Fix build with pch Include for DBL_MAX --- src/App/PreCompiled.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/App/PreCompiled.h b/src/App/PreCompiled.h index 046783405f..86389849a5 100644 --- a/src/App/PreCompiled.h +++ b/src/App/PreCompiled.h @@ -49,6 +49,7 @@ #include #include #include +#include #ifdef FC_OS_WIN32 # include From c017b000fc5225f54531a3e216b04bbb7bb16a81 Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Thu, 4 Jan 2024 15:05:07 -0500 Subject: [PATCH 004/135] proof of concept rest machining from prior path gcode --- src/Mod/Path/App/Area.cpp | 178 +++++++++++++++++++++++++++++++++ src/Mod/Path/App/Area.h | 1 + src/Mod/Path/App/AreaPy.xml | 5 + src/Mod/Path/App/AreaPyImp.cpp | 24 +++++ src/Mod/Path/Path/Op/Area.py | 18 +++- 5 files changed, 225 insertions(+), 1 deletion(-) diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index b57317bd4a..d26bd7fff0 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -75,6 +75,7 @@ #include #include #include +#include #include #include "Area.h" @@ -527,6 +528,181 @@ std::shared_ptr Area::getClearedArea(double tipDiameter, double diameter) return clearedArea; } +class ClearedAreaSegmentVisitor : public PathSegmentVisitor +{ +private: + CArea pathSegments; + double maxZ; + double diameter; + + void line(const Base::Vector3d &last, const Base::Vector3d &next) + { + if (last.z <= maxZ && next.z <= maxZ) { + CCurve curve; + curve.append(CVertex{{last.x, last.y}}); + curve.append(CVertex{{next.x, next.y}}); + pathSegments.append(curve); + count++; + } else { + // printf("SKIP!"); + } + } +public: + int count = 0; + + ClearedAreaSegmentVisitor(double maxZ, double diameter) : maxZ(maxZ), diameter(diameter) + { + } + + CArea getClearedArea() + { + CArea result{pathSegments}; + result.Thicken(diameter / 2); + return result; + } + + void g0(int id, const Base::Vector3d &last, const Base::Vector3d &next, const std::deque &pts) override + { + (void)id; + // printf("g0 [ "); + processPt(last); + // printf("] "); + processPts(pts); + // printf("_ "); + processPt(next); + + line(last, next); + // printf("\n"); + } + + void g1(int id, const Base::Vector3d &last, const Base::Vector3d &next, const std::deque &pts) override + { + (void)id; + // printf("g1 [ "); + processPt(last); + // printf("] "); + processPts(pts); + // printf("_ "); + processPt(next); + + line(last, next); + // printf("\n"); + } + + void g23(int id, const Base::Vector3d &last, const Base::Vector3d &next, const std::deque &pts, const Base::Vector3d ¢er) override + { + (void)id; + (void)center; + // printf("g23 [ "); + processPt(last); + // printf("] "); + processPts(pts); + // printf("_ "); + processPt(next); + + // TODO do I need to add last and next to the list??? + // TODO rounding resolution of the function that calls this is too big + Base::Vector3d prev = last; + for (Base::Vector3d p : pts) { + line(prev, p); + prev = p; + } + line(prev, next); + // printf("\n"); + } + + void g8x(int id, const Base::Vector3d &last, const Base::Vector3d &next, const std::deque &pts, + const std::deque &p, const std::deque &q) override + { + (void)id; + (void)q; // always within the bounds of p + printf("g8x UNHANDLED\n"); + // processPt(last); + // processPts(pts); + // processPts(p); + // processPt(next); + (void)last; + (void)pts; + (void)p; + (void)next; + } + void g38(int id, const Base::Vector3d &last, const Base::Vector3d &next) override + { + (void)id; + printf("g38 UNHANDLED\n"); + // processPt(last); + // processPt(next); + (void)last; + (void)next; + } + +private: + void processPts(const std::deque &pts) { + for (std::deque::const_iterator it=pts.begin(); pts.end() != it; ++it) { + processPt(*it); + } + } + + void processPt(const Base::Vector3d &pt) { + // printf("(%7.3f, %7.3f, %7.3f) ", pt.x, pt.y, pt.z); + } +}; + +std::shared_ptr Area::getClearedAreaFromPath(const Toolpath *path, double diameter, double zmax) { + build(); +#define AREA_MY(_param) myParams.PARAM_FNAME(_param) + PARAM_ENUM_CONVERT(AREA_MY, PARAM_FNAME, PARAM_ENUM_EXCEPT, AREA_PARAMS_OFFSET_CONF); + PARAM_ENUM_CONVERT(AREA_MY, PARAM_FNAME, PARAM_ENUM_EXCEPT, AREA_PARAMS_CLIPPER_FILL); + (void)SubjectFill; + (void)ClipFill; + + // Do not fit arcs after these offsets; it introduces unnecessary approximation error, and all off + // those arcs will be converted back to segments again for clipper differencing in getRestArea anyway + CAreaConfig conf(myParams, /*no_fit_arcs*/ true); + + const double roundPrecision = myParams.Accuracy; + const double buffer = 2 * roundPrecision; + (void)buffer; + (void)JoinType; + (void)EndType; + + Base::Vector3d pos = Base::Vector3d(0, 0, zmax + 1); + printf("getClearedAreaFromPath(path, diameter=%g, zmax=%g:\n", diameter, zmax); + // printf("Gcode:\n"); + for (auto c : path->getCommands()) { + // printf("\t%s ", c->Name.c_str()); + for (std::map::iterator i = c->Parameters.begin(); i != c->Parameters.end(); ++i) { + // printf("%s%g ", i->first.c_str(), i->second); + } + // printf("\n"); + } + + printf("\n"); + printf("GCode walker:\n"); + ClearedAreaSegmentVisitor visitor(zmax, diameter); + PathSegmentWalker walker(*path); + walker.walk(visitor, Base::Vector3d(0, 0, zmax + 1)); + printf("Count: %d\n", visitor.count); + printf("\n"); + printf("\n"); + + + std::shared_ptr clearedArea = make_shared(&myParams); + //clearedArea->myTrsf = myTrsf; + clearedArea->myTrsf = {}; + if (visitor.count > 0) { + //gp_Trsf trsf(myTrsf.Inverted()); + TopoDS_Shape clearedAreaShape = Area::toShape(visitor.getClearedArea(), false/*, &trsf*/); + clearedArea->add(clearedAreaShape, OperationCompound); + clearedArea->build(); + } else { + clearedArea->myArea = std::make_unique(); + clearedArea->myAreaOpen = std::make_unique(); + } + + return clearedArea; +} + std::shared_ptr Area::getRestArea(std::vector> clearedAreas, double diameter) { build(); PARAM_ENUM_CONVERT(AREA_MY, PARAM_FNAME, PARAM_ENUM_EXCEPT, AREA_PARAMS_OFFSET_CONF); @@ -538,6 +714,8 @@ std::shared_ptr Area::getRestArea(std::vector> clear // transform all clearedAreas into our workplane Area clearedAreasInPlane(&myParams); clearedAreasInPlane.myArea.reset(new CArea()); + printf("getRestArea\n"); + printf("\n"); for (std::shared_ptr clearedArea : clearedAreas) { gp_Trsf trsf = clearedArea->myTrsf; trsf.Invert(); diff --git a/src/Mod/Path/App/Area.h b/src/Mod/Path/App/Area.h index 5b192ae08a..3dc769dc9a 100644 --- a/src/Mod/Path/App/Area.h +++ b/src/Mod/Path/App/Area.h @@ -243,6 +243,7 @@ public: const TopoDS_Shape& plane = TopoDS_Shape()); std::shared_ptr getClearedArea(double tipDiameter, double diameter); + std::shared_ptr getClearedAreaFromPath(const Toolpath *path, double diameter, double zmax); std::shared_ptr getRestArea(std::vector> clearedAreas, double diameter); TopoDS_Shape toTopoShape(); diff --git a/src/Mod/Path/App/AreaPy.xml b/src/Mod/Path/App/AreaPy.xml index a38a84aec1..029b245c21 100644 --- a/src/Mod/Path/App/AreaPy.xml +++ b/src/Mod/Path/App/AreaPy.xml @@ -67,6 +67,11 @@ same algorithm + + + + + diff --git a/src/Mod/Path/App/AreaPyImp.cpp b/src/Mod/Path/App/AreaPyImp.cpp index 817aed0e37..ec6c8d66e8 100644 --- a/src/Mod/Path/App/AreaPyImp.cpp +++ b/src/Mod/Path/App/AreaPyImp.cpp @@ -27,6 +27,7 @@ #include // inclusion of the generated files (generated out of AreaPy.xml) +#include "PathPy.h" #include "AreaPy.h" #include "AreaPy.cpp" @@ -152,6 +153,11 @@ static const PyMethodDef areaOverrides[] = { "getClearedArea(tipDiameter, diameter):\n" "Gets the area cleared when a tool maximally clears this area. This method assumes a tool tip diameter 'tipDiameter' traces the full area, and that (perhaps at a different height on the tool) this clears a different region with tool diameter 'diameter'.\n", }, + { + "getClearedAreaFromPath",nullptr,0, + "getClearedAreaFromPath(path, diameter, zmax):\n" + "Gets the area cleared when a tool of the specified diameter follows the gcode represented in the path, ignoring cleared space above zmax.\n", + }, { "getRestArea",nullptr,0, "getRestArea(clearedAreas, diameter):\n" @@ -415,6 +421,24 @@ PyObject* AreaPy::getClearedArea(PyObject *args) } PY_CATCH_OCC } +PyObject* AreaPy::getClearedAreaFromPath(PyObject *args) +{ + PY_TRY { + PyObject *pyPath; + double diameter, zmax; + if (!PyArg_ParseTuple(args, "Odd", &pyPath, &diameter, &zmax)) + return nullptr; + if (!PyObject_TypeCheck(pyPath, &(PathPy::Type))) { + PyErr_SetString(PyExc_TypeError, "path must be of type PathPy"); + return nullptr; + } + const PathPy *path = static_cast(pyPath); + std::shared_ptr clearedArea = getAreaPtr()->getClearedAreaFromPath(path->getToolpathPtr(), diameter, zmax); + auto pyClearedArea = Py::asObject(new AreaPy(new Area(*clearedArea, true))); + return Py::new_reference_to(pyClearedArea); + } PY_CATCH_OCC +} + PyObject* AreaPy::getRestArea(PyObject *args) { PY_TRY { diff --git a/src/Mod/Path/Path/Op/Area.py b/src/Mod/Path/Path/Op/Area.py index 8e329ae4b5..77862274c2 100644 --- a/src/Mod/Path/Path/Op/Area.py +++ b/src/Mod/Path/Path/Op/Area.py @@ -272,7 +272,23 @@ class ObjectOp(PathOp.ObjectOp): restSections = [] for section in sections: z = section.getShape().BoundBox.ZMin - sectionClearedAreas = [a for a in clearedAreas if a.getShape().BoundBox.ZMax <= z] + # sectionClearedAreas = [a for a in clearedAreas if a.getShape().BoundBox.ZMax <= z] + sectionClearedAreas = [] + for op in self.job.Operations.Group: + print(op.Name) + if self in [x.Proxy for x in [op] + op.OutListRecursive if hasattr(x, "Proxy")]: + print("found self") + break + if hasattr(op, "Active") and op.Active and op.Path: + tool = op.Proxy.tool if hasattr(op.Proxy, "tool") else op.ToolController.Proxy.getTool(op.ToolController) + diameter = tool.Diameter.getValueAs("mm") + sectionClearedAreas.append(area.getClearedAreaFromPath(op.Path, diameter, z+0.001)) + debugZ = -1.5 + if debugZ -.1 < z and z < debugZ + .1: + debugObj = obj.Document.addObject("Part::Feature", "Debug_{}_{}".format(debugZ, op.Name)) + debugObj.Label = "Debug_{}_{}".format(debugZ, op.Label) + debugObj.Shape = sectionClearedAreas[-1].getShape() + pass restSection = section.getRestArea(sectionClearedAreas, self.tool.Diameter.getValueAs("mm")) restSections.append(restSection) sections = restSections From f68066a219da799164963076082e191c227f90fd Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Fri, 5 Jan 2024 11:46:39 -0500 Subject: [PATCH 005/135] expand cleared area by a small buffer to cover numeric errors --- src/Mod/Path/App/Area.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index d26bd7fff0..de9b33e85d 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -662,7 +662,6 @@ std::shared_ptr Area::getClearedAreaFromPath(const Toolpath *path, double const double roundPrecision = myParams.Accuracy; const double buffer = 2 * roundPrecision; - (void)buffer; (void)JoinType; (void)EndType; @@ -679,7 +678,7 @@ std::shared_ptr Area::getClearedAreaFromPath(const Toolpath *path, double printf("\n"); printf("GCode walker:\n"); - ClearedAreaSegmentVisitor visitor(zmax, diameter); + ClearedAreaSegmentVisitor visitor(zmax, diameter + buffer); PathSegmentWalker walker(*path); walker.walk(visitor, Base::Vector3d(0, 0, zmax + 1)); printf("Count: %d\n", visitor.count); From 73e8b5ec877b91e6659a90979e65f65f9eaec76f Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Sun, 7 Jan 2024 16:53:45 -0500 Subject: [PATCH 006/135] fix precision computation --- src/Mod/Path/App/Area.cpp | 108 +++++++++++++++++++++------------ src/Mod/Path/App/AreaPyImp.cpp | 3 + src/Mod/Path/Path/Op/Area.py | 14 ++--- 3 files changed, 79 insertions(+), 46 deletions(-) diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index de9b33e85d..73b8200327 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -533,7 +533,7 @@ class ClearedAreaSegmentVisitor : public PathSegmentVisitor private: CArea pathSegments; double maxZ; - double diameter; + double radius; void line(const Base::Vector3d &last, const Base::Vector3d &next) { @@ -550,26 +550,27 @@ private: public: int count = 0; - ClearedAreaSegmentVisitor(double maxZ, double diameter) : maxZ(maxZ), diameter(diameter) + ClearedAreaSegmentVisitor(double maxZ, double radius) : maxZ(maxZ), radius(radius) { } CArea getClearedArea() { CArea result{pathSegments}; - result.Thicken(diameter / 2); + result.Thicken(radius); return result; } void g0(int id, const Base::Vector3d &last, const Base::Vector3d &next, const std::deque &pts) override { (void)id; + (void)pts; // printf("g0 [ "); - processPt(last); + //processPt(last); // printf("] "); - processPts(pts); + //processPts(pts); // printf("_ "); - processPt(next); + //processPt(next); line(last, next); // printf("\n"); @@ -578,12 +579,13 @@ public: void g1(int id, const Base::Vector3d &last, const Base::Vector3d &next, const std::deque &pts) override { (void)id; + (void)pts; // printf("g1 [ "); - processPt(last); + //processPt(last); // printf("] "); - processPts(pts); + //processPts(pts); // printf("_ "); - processPt(next); + //processPt(next); line(last, next); // printf("\n"); @@ -594,20 +596,29 @@ public: (void)id; (void)center; // printf("g23 [ "); - processPt(last); + // processPt(last); // printf("] "); - processPts(pts); + // processPts(pts); // printf("_ "); - processPt(next); + // processPt(next); - // TODO do I need to add last and next to the list??? - // TODO rounding resolution of the function that calls this is too big - Base::Vector3d prev = last; - for (Base::Vector3d p : pts) { - line(prev, p); - prev = p; - } - line(prev, next); + // Compute cw vs ccw + const Base::Vector3d vdirect = next - last; + const Base::Vector3d vstep = pts[0] - last; + const bool ccw = vstep.x * vdirect.y - vstep.y * vdirect.x > 0; + + // Add an arc + CCurve curve; + curve.append(CVertex{{last.x, last.y}}); + curve.append(CVertex{ccw ? 1 : -1, {next.x, next.y}, {center.x, center.y}}); + pathSegments.append(curve); + count++; + // Base::Vector3d prev = last; + // for (Base::Vector3d p : pts) { + // line(prev, p); + // prev = p; + // } + // line(prev, next); // printf("\n"); } @@ -644,27 +655,25 @@ private: } void processPt(const Base::Vector3d &pt) { - // printf("(%7.3f, %7.3f, %7.3f) ", pt.x, pt.y, pt.z); + printf("(%7.3f, %7.3f, %7.3f) ", pt.x, pt.y, pt.z); } }; std::shared_ptr Area::getClearedAreaFromPath(const Toolpath *path, double diameter, double zmax) { build(); -#define AREA_MY(_param) myParams.PARAM_FNAME(_param) - PARAM_ENUM_CONVERT(AREA_MY, PARAM_FNAME, PARAM_ENUM_EXCEPT, AREA_PARAMS_OFFSET_CONF); - PARAM_ENUM_CONVERT(AREA_MY, PARAM_FNAME, PARAM_ENUM_EXCEPT, AREA_PARAMS_CLIPPER_FILL); - (void)SubjectFill; - (void)ClipFill; + + // Precision losses in arc/segment conversions (multiples of Accuracy): + // 2.3 in generation of gcode (see documentation in the implementation of CCurve::CheckForArc (libarea/Curve.cpp) + // 1 in gcode arc to segment + // 1 in Thicken() cleared area + // 2 in getRestArea target area offset in and back out + // Oversize cleared areas by buffer to smooth out imprecision in arc/segment conversion. getRestArea() will compensate for this + const double buffer = myParams.Accuracy * 6.3; // Do not fit arcs after these offsets; it introduces unnecessary approximation error, and all off // those arcs will be converted back to segments again for clipper differencing in getRestArea anyway CAreaConfig conf(myParams, /*no_fit_arcs*/ true); - const double roundPrecision = myParams.Accuracy; - const double buffer = 2 * roundPrecision; - (void)JoinType; - (void)EndType; - Base::Vector3d pos = Base::Vector3d(0, 0, zmax + 1); printf("getClearedAreaFromPath(path, diameter=%g, zmax=%g:\n", diameter, zmax); // printf("Gcode:\n"); @@ -678,7 +687,7 @@ std::shared_ptr Area::getClearedAreaFromPath(const Toolpath *path, double printf("\n"); printf("GCode walker:\n"); - ClearedAreaSegmentVisitor visitor(zmax, diameter + buffer); + ClearedAreaSegmentVisitor visitor(zmax, diameter/2 + buffer); PathSegmentWalker walker(*path); walker.walk(visitor, Base::Vector3d(0, 0, zmax + 1)); printf("Count: %d\n", visitor.count); @@ -707,8 +716,14 @@ std::shared_ptr Area::getRestArea(std::vector> clear PARAM_ENUM_CONVERT(AREA_MY, PARAM_FNAME, PARAM_ENUM_EXCEPT, AREA_PARAMS_OFFSET_CONF); PARAM_ENUM_CONVERT(AREA_MY, PARAM_FNAME, PARAM_ENUM_EXCEPT, AREA_PARAMS_CLIPPER_FILL); - const double roundPrecision = myParams.Accuracy; - const double buffer = 2 * roundPrecision; + // Precision losses in arc/segment conversions (multiples of Accuracy): + // 2.3 in generation of gcode (see documentation in the implementation of CCurve::CheckForArc (libarea/Curve.cpp) + // 1 in gcode arc to segment + // 1 in Thicken() cleared area + // 2 in getRestArea target area offset in and back out + // Cleared area representations are oversized by buffer to smooth out imprecision in arc/segment conversion. getRestArea() will compensate for this + const double buffer = myParams.Accuracy * 6.3; + const double roundPrecision = buffer; // transform all clearedAreas into our workplane Area clearedAreasInPlane(&myParams); @@ -724,20 +739,35 @@ std::shared_ptr Area::getRestArea(std::vector> clear &myWorkPlane); } - // remaining = A - prevCleared - CArea remaining(*myArea); + // clearable = offset(offset(A, -dTool/2), dTool/2) + printf("Compute clearable\n"); + CArea clearable(*myArea); + clearable.OffsetWithClipper(-diameter/2, JoinType, EndType, myParams.MiterLimit, roundPrecision); + clearable.OffsetWithClipper(diameter/2, JoinType, EndType, myParams.MiterLimit, roundPrecision); + + // remaining = clearable - prevCleared + printf("Compute remaining\n"); + CArea remaining(clearable); remaining.Clip(toClipperOp(Area::OperationDifference), &*(clearedAreasInPlane.myArea), SubjectFill, ClipFill); - // rest = intersect(A, offset(remaining, dTool)) + // rest = intersect(clearable, offset(remaining, dTool)) + // add buffer to dTool to compensate for oversizing in getClearedAreaFromPath + printf("Compute rest\n"); CArea restCArea(remaining); restCArea.OffsetWithClipper(diameter + buffer, JoinType, EndType, myParams.MiterLimit, roundPrecision); - restCArea.Clip(toClipperOp(Area::OperationIntersection), &*myArea, SubjectFill, ClipFill); + restCArea.Clip(toClipperOp(Area::OperationIntersection), &clearable, SubjectFill, ClipFill); + printf("Convert CArea to Area (num curves: %d)\n", (int)restCArea.m_curves.size()); + if(restCArea.m_curves.size() == 0) { + return {}; + } + + std::shared_ptr restArea = make_shared(&myParams); gp_Trsf trsf(myTrsf.Inverted()); TopoDS_Shape restShape = Area::toShape(restCArea, false, &trsf); - std::shared_ptr restArea = make_shared(&myParams); restArea->add(restShape, OperationCompound); + printf("return\n"); return restArea; } diff --git a/src/Mod/Path/App/AreaPyImp.cpp b/src/Mod/Path/App/AreaPyImp.cpp index ec6c8d66e8..4041d7c734 100644 --- a/src/Mod/Path/App/AreaPyImp.cpp +++ b/src/Mod/Path/App/AreaPyImp.cpp @@ -464,6 +464,9 @@ PyObject* AreaPy::getRestArea(PyObject *args) } std::shared_ptr restArea = getAreaPtr()->getRestArea(clearedAreas, diameter); + if (!restArea) { + return Py_None; + } auto pyRestArea = Py::asObject(new AreaPy(new Area(*restArea, true))); return Py::new_reference_to(pyRestArea); } PY_CATCH_OCC diff --git a/src/Mod/Path/Path/Op/Area.py b/src/Mod/Path/Path/Op/Area.py index 77862274c2..e98794b59b 100644 --- a/src/Mod/Path/Path/Op/Area.py +++ b/src/Mod/Path/Path/Op/Area.py @@ -283,14 +283,14 @@ class ObjectOp(PathOp.ObjectOp): tool = op.Proxy.tool if hasattr(op.Proxy, "tool") else op.ToolController.Proxy.getTool(op.ToolController) diameter = tool.Diameter.getValueAs("mm") sectionClearedAreas.append(area.getClearedAreaFromPath(op.Path, diameter, z+0.001)) - debugZ = -1.5 - if debugZ -.1 < z and z < debugZ + .1: - debugObj = obj.Document.addObject("Part::Feature", "Debug_{}_{}".format(debugZ, op.Name)) - debugObj.Label = "Debug_{}_{}".format(debugZ, op.Label) - debugObj.Shape = sectionClearedAreas[-1].getShape() - pass + # debugZ = -1.5 + # if debugZ -.1 < z and z < debugZ + .1: + # debugObj = obj.Document.addObject("Part::Feature", "Debug_{}_{}".format(debugZ, op.Name)) + # debugObj.Label = "Debug_{}_{}".format(debugZ, op.Label) + # debugObj.Shape = sectionClearedAreas[-1].getShape() restSection = section.getRestArea(sectionClearedAreas, self.tool.Diameter.getValueAs("mm")) - restSections.append(restSection) + if (restSection is not None): + restSections.append(restSection) sections = restSections shapelist = [sec.getShape() for sec in sections] From 58a8d32a164ca89772a4e7ce87934f0584966d3f Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Sun, 7 Jan 2024 17:00:34 -0500 Subject: [PATCH 007/135] do computations at higher precision to mitigate error stackup --- src/Mod/Path/App/Area.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index 73b8200327..cb96f3cf71 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -668,11 +668,13 @@ std::shared_ptr Area::getClearedAreaFromPath(const Toolpath *path, double // 1 in Thicken() cleared area // 2 in getRestArea target area offset in and back out // Oversize cleared areas by buffer to smooth out imprecision in arc/segment conversion. getRestArea() will compensate for this - const double buffer = myParams.Accuracy * 6.3; + AreaParams params = myParams; + params.Accuracy = myParams.Accuracy * .7/4; // 2.3 already encoded in gcode; 4 * .7/4 = 3 total + const double buffer = myParams.Accuracy * 3; // Do not fit arcs after these offsets; it introduces unnecessary approximation error, and all off // those arcs will be converted back to segments again for clipper differencing in getRestArea anyway - CAreaConfig conf(myParams, /*no_fit_arcs*/ true); + CAreaConfig conf(params, /*no_fit_arcs*/ true); Base::Vector3d pos = Base::Vector3d(0, 0, zmax + 1); printf("getClearedAreaFromPath(path, diameter=%g, zmax=%g:\n", diameter, zmax); @@ -695,7 +697,7 @@ std::shared_ptr Area::getClearedAreaFromPath(const Toolpath *path, double printf("\n"); - std::shared_ptr clearedArea = make_shared(&myParams); + std::shared_ptr clearedArea = make_shared(¶ms); //clearedArea->myTrsf = myTrsf; clearedArea->myTrsf = {}; if (visitor.count > 0) { @@ -722,11 +724,13 @@ std::shared_ptr Area::getRestArea(std::vector> clear // 1 in Thicken() cleared area // 2 in getRestArea target area offset in and back out // Cleared area representations are oversized by buffer to smooth out imprecision in arc/segment conversion. getRestArea() will compensate for this - const double buffer = myParams.Accuracy * 6.3; - const double roundPrecision = buffer; + AreaParams params = myParams; + params.Accuracy = myParams.Accuracy * .7/4; // 2.3 already encoded in gcode; 4 * .7/4 = 3 total + const double buffer = myParams.Accuracy * 3; + const double roundPrecision = params.Accuracy; // transform all clearedAreas into our workplane - Area clearedAreasInPlane(&myParams); + Area clearedAreasInPlane(¶ms); clearedAreasInPlane.myArea.reset(new CArea()); printf("getRestArea\n"); printf("\n"); @@ -742,8 +746,8 @@ std::shared_ptr Area::getRestArea(std::vector> clear // clearable = offset(offset(A, -dTool/2), dTool/2) printf("Compute clearable\n"); CArea clearable(*myArea); - clearable.OffsetWithClipper(-diameter/2, JoinType, EndType, myParams.MiterLimit, roundPrecision); - clearable.OffsetWithClipper(diameter/2, JoinType, EndType, myParams.MiterLimit, roundPrecision); + clearable.OffsetWithClipper(-diameter/2, JoinType, EndType, params.MiterLimit, roundPrecision); + clearable.OffsetWithClipper(diameter/2, JoinType, EndType, params.MiterLimit, roundPrecision); // remaining = clearable - prevCleared printf("Compute remaining\n"); @@ -754,7 +758,7 @@ std::shared_ptr Area::getRestArea(std::vector> clear // add buffer to dTool to compensate for oversizing in getClearedAreaFromPath printf("Compute rest\n"); CArea restCArea(remaining); - restCArea.OffsetWithClipper(diameter + buffer, JoinType, EndType, myParams.MiterLimit, roundPrecision); + restCArea.OffsetWithClipper(diameter + buffer, JoinType, EndType, params.MiterLimit, roundPrecision); restCArea.Clip(toClipperOp(Area::OperationIntersection), &clearable, SubjectFill, ClipFill); printf("Convert CArea to Area (num curves: %d)\n", (int)restCArea.m_curves.size()); @@ -762,7 +766,7 @@ std::shared_ptr Area::getRestArea(std::vector> clear return {}; } - std::shared_ptr restArea = make_shared(&myParams); + std::shared_ptr restArea = make_shared(¶ms); gp_Trsf trsf(myTrsf.Inverted()); TopoDS_Shape restShape = Area::toShape(restCArea, false, &trsf); restArea->add(restShape, OperationCompound); From 833796c297ef531bb39f530526d0a5ee5a1feb67 Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Sun, 7 Jan 2024 17:18:02 -0500 Subject: [PATCH 008/135] remove old/deprecated rest machining code --- src/Mod/Path/App/Area.cpp | 32 +------------------------- src/Mod/Path/App/Area.h | 1 - src/Mod/Path/App/AreaPy.xml | 5 ---- src/Mod/Path/App/AreaPyImp.cpp | 17 -------------- src/Mod/Path/Path/Op/Area.py | 34 --------------------------- src/Mod/Path/Path/Op/PocketBase.py | 37 ++++-------------------------- 6 files changed, 5 insertions(+), 121 deletions(-) diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index cb96f3cf71..b979a6d155 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -497,37 +497,6 @@ void Area::add(const TopoDS_Shape& shape, short op) { myShapes.emplace_back(op, shape); } -std::shared_ptr Area::getClearedArea(double tipDiameter, double diameter) { - build(); -#define AREA_MY(_param) myParams.PARAM_FNAME(_param) - PARAM_ENUM_CONVERT(AREA_MY, PARAM_FNAME, PARAM_ENUM_EXCEPT, AREA_PARAMS_OFFSET_CONF); - PARAM_ENUM_CONVERT(AREA_MY, PARAM_FNAME, PARAM_ENUM_EXCEPT, AREA_PARAMS_CLIPPER_FILL); - (void)SubjectFill; - (void)ClipFill; - - // Do not fit arcs after these offsets; it introduces unnecessary approximation error, and all off - // those arcs will be converted back to segments again for clipper differencing in getRestArea anyway - CAreaConfig conf(myParams, /*no_fit_arcs*/ true); - - const double roundPrecision = myParams.Accuracy; - const double buffer = 2 * roundPrecision; - - // A = myArea - // prevCenters = offset(A, -rTip) - const double rTip = tipDiameter / 2.; - CArea prevCenter(*myArea); - prevCenter.OffsetWithClipper(-rTip, JoinType, EndType, myParams.MiterLimit, roundPrecision); - - // prevCleared = offset(prevCenter, r). - CArea prevCleared(prevCenter); - prevCleared.OffsetWithClipper(diameter / 2. + buffer, JoinType, EndType, myParams.MiterLimit, roundPrecision); - - std::shared_ptr clearedArea = make_shared(*this); - clearedArea->myArea.reset(new CArea(prevCleared)); - - return clearedArea; -} - class ClearedAreaSegmentVisitor : public PathSegmentVisitor { private: @@ -715,6 +684,7 @@ std::shared_ptr Area::getClearedAreaFromPath(const Toolpath *path, double std::shared_ptr Area::getRestArea(std::vector> clearedAreas, double diameter) { build(); +#define AREA_MY(_param) myParams.PARAM_FNAME(_param) PARAM_ENUM_CONVERT(AREA_MY, PARAM_FNAME, PARAM_ENUM_EXCEPT, AREA_PARAMS_OFFSET_CONF); PARAM_ENUM_CONVERT(AREA_MY, PARAM_FNAME, PARAM_ENUM_EXCEPT, AREA_PARAMS_CLIPPER_FILL); diff --git a/src/Mod/Path/App/Area.h b/src/Mod/Path/App/Area.h index 3dc769dc9a..af21097705 100644 --- a/src/Mod/Path/App/Area.h +++ b/src/Mod/Path/App/Area.h @@ -242,7 +242,6 @@ public: const std::vector& heights = std::vector(), const TopoDS_Shape& plane = TopoDS_Shape()); - std::shared_ptr getClearedArea(double tipDiameter, double diameter); std::shared_ptr getClearedAreaFromPath(const Toolpath *path, double diameter, double zmax); std::shared_ptr getRestArea(std::vector> clearedAreas, double diameter); TopoDS_Shape toTopoShape(); diff --git a/src/Mod/Path/App/AreaPy.xml b/src/Mod/Path/App/AreaPy.xml index 029b245c21..209765c3e0 100644 --- a/src/Mod/Path/App/AreaPy.xml +++ b/src/Mod/Path/App/AreaPy.xml @@ -62,11 +62,6 @@ same algorithm - - - - - diff --git a/src/Mod/Path/App/AreaPyImp.cpp b/src/Mod/Path/App/AreaPyImp.cpp index 4041d7c734..5ee87a3a9c 100644 --- a/src/Mod/Path/App/AreaPyImp.cpp +++ b/src/Mod/Path/App/AreaPyImp.cpp @@ -148,11 +148,6 @@ static const PyMethodDef areaOverrides[] = { "\n* plane (None): optional shape to specify a section plane. If not give, the current workplane\n" "of this Area is used if section mode is 'Workplane'.", }, - { - "getClearedArea",nullptr,0, - "getClearedArea(tipDiameter, diameter):\n" - "Gets the area cleared when a tool maximally clears this area. This method assumes a tool tip diameter 'tipDiameter' traces the full area, and that (perhaps at a different height on the tool) this clears a different region with tool diameter 'diameter'.\n", - }, { "getClearedAreaFromPath",nullptr,0, "getClearedAreaFromPath(path, diameter, zmax):\n" @@ -409,18 +404,6 @@ PyObject* AreaPy::makeSections(PyObject *args, PyObject *keywds) } PY_CATCH_OCC } -PyObject* AreaPy::getClearedArea(PyObject *args) -{ - PY_TRY { - double tipDiameter, diameter; - if (!PyArg_ParseTuple(args, "dd", &tipDiameter, &diameter)) - return nullptr; - std::shared_ptr clearedArea = getAreaPtr()->getClearedArea(tipDiameter, diameter); - auto pyClearedArea = Py::asObject(new AreaPy(new Area(*clearedArea, true))); - return Py::new_reference_to(pyClearedArea); - } PY_CATCH_OCC -} - PyObject* AreaPy::getClearedAreaFromPath(PyObject *args) { PY_TRY { diff --git a/src/Mod/Path/Path/Op/Area.py b/src/Mod/Path/Path/Op/Area.py index e98794b59b..36d0c923eb 100644 --- a/src/Mod/Path/Path/Op/Area.py +++ b/src/Mod/Path/Path/Op/Area.py @@ -240,39 +240,9 @@ class ObjectOp(PathOp.ObjectOp): # Rest machining self.sectionShapes = self.sectionShapes + [section.toTopoShape() for section in sections] if hasattr(obj, "UseRestMachining") and obj.UseRestMachining: - # Loop through prior operations - clearedAreas = [] - foundSelf = False - for op in self.job.Operations.Group: - if foundSelf: - break - oplist = [op] + op.OutListRecursive - oplist = list(filter(lambda op: hasattr(op, "Active"), oplist)) - for op in oplist: - if op.Proxy == self: - # Ignore self, and all later operations - foundSelf = True - break - if hasattr(op, "RestMachiningRegions") and op.Active: - if hasattr(op, "RestMachiningRegionsNeedRecompute") and op.RestMachiningRegionsNeedRecompute: - Path.Log.warning( - translate("PathAreaOp", "Previous operation %s is required for rest machining, but it has no stored rest machining metadata. Recomputing to generate this metadata...") % op.Label - ) - op.recompute() - - tool = op.Proxy.tool if hasattr(op.Proxy, "tool") else op.ToolController.Proxy.getTool(op.ToolController) - diameter = tool.Diameter.getValueAs("mm") - def shapeToArea(shape): - area = Path.Area() - area.setPlane(PathUtils.makeWorkplane(shape)) - area.add(shape) - return area - opClearedAreas = [shapeToArea(pa).getClearedArea(diameter, diameter) for pa in op.RestMachiningRegions.SubShapes] - clearedAreas.extend(opClearedAreas) restSections = [] for section in sections: z = section.getShape().BoundBox.ZMin - # sectionClearedAreas = [a for a in clearedAreas if a.getShape().BoundBox.ZMax <= z] sectionClearedAreas = [] for op in self.job.Operations.Group: print(op.Name) @@ -497,10 +467,6 @@ class ObjectOp(PathOp.ObjectOp): ) ) - if hasattr(obj, "RestMachiningRegions"): - obj.RestMachiningRegions = Part.makeCompound(self.sectionShapes) - if hasattr(obj, "RestMachiningRegionsNeedRecompute"): - obj.RestMachiningRegionsNeedRecompute = False Path.Log.debug("obj.Name: " + str(obj.Name) + "\n\n") return sims diff --git a/src/Mod/Path/Path/Op/PocketBase.py b/src/Mod/Path/Path/Op/PocketBase.py index 829dd865c3..9dc30836de 100644 --- a/src/Mod/Path/Path/Op/PocketBase.py +++ b/src/Mod/Path/Path/Op/PocketBase.py @@ -194,16 +194,6 @@ class ObjectPocket(PathAreaOp.ObjectOp): "Skips machining regions that have already been cleared by previous operations.", ), ) - obj.addProperty( - "Part::PropertyPartShape", - "RestMachiningRegions", - "Pocket", - QT_TRANSLATE_NOOP( - "App::Property", - "The areas cleared by this operation, one area per height, stored as a compound part. Used internally for rest machining.", - ), - ) - obj.setEditorMode("RestMachiningRegions", 2) # hide for n in self.pocketPropertyEnumerations(): setattr(obj, n[0], n[1]) @@ -277,29 +267,10 @@ class ObjectPocket(PathAreaOp.ObjectOp): ), ) - if not hasattr(obj, "RestMachiningRegions"): - obj.addProperty( - "Part::PropertyPartShape", - "RestMachiningRegions", - "Pocket", - QT_TRANSLATE_NOOP( - "App::Property", - "The areas cleared by this operation, one area per height, stored as a compound part. Used internally for rest machining.", - ), - ) - obj.setEditorMode("RestMachiningRegions", 2) # hide - - obj.addProperty( - "App::PropertyBool", - "RestMachiningRegionsNeedRecompute", - "Pocket", - QT_TRANSLATE_NOOP( - "App::Property", - "Flag to indicate that the rest machining regions have never been computed, and must be recomputed before being used.", - ), - ) - obj.setEditorMode("RestMachiningRegionsNeedRecompute", 2) # hide - obj.RestMachiningRegionsNeedRecompute = True + if hasattr(obj, "RestMachiningRegions"): + obj.removeProperty("RestMachiningRegions") + if hasattr(obj, "RestMachiningRegionsNeedRecompute"): + obj.removeProperty("RestMachiningRegionsNeedRecompute") Path.Log.track() From dddd13f7c008667a0157157424b475ac9e28e4b2 Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Sun, 7 Jan 2024 17:23:03 -0500 Subject: [PATCH 009/135] rename getClearedAreaFromPath -> getClearedArea --- src/Mod/Path/App/Area.cpp | 6 +++--- src/Mod/Path/App/Area.h | 2 +- src/Mod/Path/App/AreaPy.xml | 2 +- src/Mod/Path/App/AreaPyImp.cpp | 8 ++++---- src/Mod/Path/Path/Op/Area.py | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index b979a6d155..30d680ff12 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -628,7 +628,7 @@ private: } }; -std::shared_ptr Area::getClearedAreaFromPath(const Toolpath *path, double diameter, double zmax) { +std::shared_ptr Area::getClearedArea(const Toolpath *path, double diameter, double zmax) { build(); // Precision losses in arc/segment conversions (multiples of Accuracy): @@ -646,7 +646,7 @@ std::shared_ptr Area::getClearedAreaFromPath(const Toolpath *path, double CAreaConfig conf(params, /*no_fit_arcs*/ true); Base::Vector3d pos = Base::Vector3d(0, 0, zmax + 1); - printf("getClearedAreaFromPath(path, diameter=%g, zmax=%g:\n", diameter, zmax); + printf("getClearedArea(path, diameter=%g, zmax=%g:\n", diameter, zmax); // printf("Gcode:\n"); for (auto c : path->getCommands()) { // printf("\t%s ", c->Name.c_str()); @@ -725,7 +725,7 @@ std::shared_ptr Area::getRestArea(std::vector> clear remaining.Clip(toClipperOp(Area::OperationDifference), &*(clearedAreasInPlane.myArea), SubjectFill, ClipFill); // rest = intersect(clearable, offset(remaining, dTool)) - // add buffer to dTool to compensate for oversizing in getClearedAreaFromPath + // add buffer to dTool to compensate for oversizing in getClearedArea printf("Compute rest\n"); CArea restCArea(remaining); restCArea.OffsetWithClipper(diameter + buffer, JoinType, EndType, params.MiterLimit, roundPrecision); diff --git a/src/Mod/Path/App/Area.h b/src/Mod/Path/App/Area.h index af21097705..8056f4a3da 100644 --- a/src/Mod/Path/App/Area.h +++ b/src/Mod/Path/App/Area.h @@ -242,7 +242,7 @@ public: const std::vector& heights = std::vector(), const TopoDS_Shape& plane = TopoDS_Shape()); - std::shared_ptr getClearedAreaFromPath(const Toolpath *path, double diameter, double zmax); + std::shared_ptr getClearedArea(const Toolpath *path, double diameter, double zmax); std::shared_ptr getRestArea(std::vector> clearedAreas, double diameter); TopoDS_Shape toTopoShape(); diff --git a/src/Mod/Path/App/AreaPy.xml b/src/Mod/Path/App/AreaPy.xml index 209765c3e0..a38a84aec1 100644 --- a/src/Mod/Path/App/AreaPy.xml +++ b/src/Mod/Path/App/AreaPy.xml @@ -62,7 +62,7 @@ same algorithm - + diff --git a/src/Mod/Path/App/AreaPyImp.cpp b/src/Mod/Path/App/AreaPyImp.cpp index 5ee87a3a9c..a0a85b930b 100644 --- a/src/Mod/Path/App/AreaPyImp.cpp +++ b/src/Mod/Path/App/AreaPyImp.cpp @@ -149,8 +149,8 @@ static const PyMethodDef areaOverrides[] = { "of this Area is used if section mode is 'Workplane'.", }, { - "getClearedAreaFromPath",nullptr,0, - "getClearedAreaFromPath(path, diameter, zmax):\n" + "getClearedArea",nullptr,0, + "getClearedArea(path, diameter, zmax):\n" "Gets the area cleared when a tool of the specified diameter follows the gcode represented in the path, ignoring cleared space above zmax.\n", }, { @@ -404,7 +404,7 @@ PyObject* AreaPy::makeSections(PyObject *args, PyObject *keywds) } PY_CATCH_OCC } -PyObject* AreaPy::getClearedAreaFromPath(PyObject *args) +PyObject* AreaPy::getClearedArea(PyObject *args) { PY_TRY { PyObject *pyPath; @@ -416,7 +416,7 @@ PyObject* AreaPy::getClearedAreaFromPath(PyObject *args) return nullptr; } const PathPy *path = static_cast(pyPath); - std::shared_ptr clearedArea = getAreaPtr()->getClearedAreaFromPath(path->getToolpathPtr(), diameter, zmax); + std::shared_ptr clearedArea = getAreaPtr()->getClearedArea(path->getToolpathPtr(), diameter, zmax); auto pyClearedArea = Py::asObject(new AreaPy(new Area(*clearedArea, true))); return Py::new_reference_to(pyClearedArea); } PY_CATCH_OCC diff --git a/src/Mod/Path/Path/Op/Area.py b/src/Mod/Path/Path/Op/Area.py index 36d0c923eb..5f9e3a759f 100644 --- a/src/Mod/Path/Path/Op/Area.py +++ b/src/Mod/Path/Path/Op/Area.py @@ -252,7 +252,7 @@ class ObjectOp(PathOp.ObjectOp): if hasattr(op, "Active") and op.Active and op.Path: tool = op.Proxy.tool if hasattr(op.Proxy, "tool") else op.ToolController.Proxy.getTool(op.ToolController) diameter = tool.Diameter.getValueAs("mm") - sectionClearedAreas.append(area.getClearedAreaFromPath(op.Path, diameter, z+0.001)) + sectionClearedAreas.append(area.getClearedArea(op.Path, diameter, z+0.001)) # debugZ = -1.5 # if debugZ -.1 < z and z < debugZ + .1: # debugObj = obj.Document.addObject("Part::Feature", "Debug_{}_{}".format(debugZ, op.Name)) From 41c1206d803500ef58227c05ba6465d8fc34f292 Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Mon, 8 Jan 2024 00:52:19 -0500 Subject: [PATCH 010/135] Important performance optimization: union Thicken() polygons individually, not en masse --- src/Mod/Path/libarea/AreaClipper.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Mod/Path/libarea/AreaClipper.cpp b/src/Mod/Path/libarea/AreaClipper.cpp index d3e9ff8397..48f3dc93af 100644 --- a/src/Mod/Path/libarea/AreaClipper.cpp +++ b/src/Mod/Path/libarea/AreaClipper.cpp @@ -253,11 +253,15 @@ static void OffsetSpansWithObrounds(const CArea& area, TPolyPolygon &pp_new, dou { Clipper c; c.StrictlySimple(CArea::m_clipper_simple); - + pp_new.clear(); for(std::list::const_iterator It = area.m_curves.begin(); It != area.m_curves.end(); It++) { + c.Clear(); + c.AddPaths(pp_new, ptSubject, true); + pp_new.clear(); pts_for_AddVertex.clear(); + const CCurve& curve = *It; const CVertex* prev_vertex = NULL; for(std::list::const_iterator It2 = curve.m_vertices.begin(); It2 != curve.m_vertices.end(); It2++) @@ -278,10 +282,9 @@ static void OffsetSpansWithObrounds(const CArea& area, TPolyPolygon &pp_new, dou } prev_vertex = &vertex; } + c.Execute(ctUnion, pp_new, pftNonZero, pftNonZero); } - pp_new.clear(); - c.Execute(ctUnion, pp_new, pftNonZero, pftNonZero); // reverse all the resulting polygons TPolyPolygon copy = pp_new; From 0b219307516a5ca873ae25b5d7ac07c202d69487 Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Mon, 8 Jan 2024 14:33:19 -0500 Subject: [PATCH 011/135] add bbox check to getClearedArea to filter out irrelevant gcode --- src/Mod/Path/App/Area.cpp | 47 ++++++++++++++++------------------ src/Mod/Path/App/Area.h | 2 +- src/Mod/Path/App/AreaPyImp.cpp | 16 ++++++++---- src/Mod/Path/Path/Op/Area.py | 5 ++-- 4 files changed, 37 insertions(+), 33 deletions(-) diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index 30d680ff12..7c9ed4ce00 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -503,24 +503,26 @@ private: CArea pathSegments; double maxZ; double radius; + Base::BoundBox3d bbox; void line(const Base::Vector3d &last, const Base::Vector3d &next) { if (last.z <= maxZ && next.z <= maxZ) { - CCurve curve; - curve.append(CVertex{{last.x, last.y}}); - curve.append(CVertex{{next.x, next.y}}); - pathSegments.append(curve); - count++; - } else { - // printf("SKIP!"); + Base::BoundBox2d segBox = {}; + segBox.Add({last.x, last.y}); + segBox.Add({next.x, next.y}); + if (bbox.Intersect(segBox)) { + CCurve curve; + curve.append(CVertex{{last.x, last.y}}); + curve.append(CVertex{{next.x, next.y}}); + pathSegments.append(curve); + } } } public: - int count = 0; - - ClearedAreaSegmentVisitor(double maxZ, double radius) : maxZ(maxZ), radius(radius) + ClearedAreaSegmentVisitor(double maxZ, double radius, Base::BoundBox3d bbox) : maxZ(maxZ), radius(radius), bbox(bbox) { + bbox.Enlarge(radius); } CArea getClearedArea() @@ -581,7 +583,6 @@ public: curve.append(CVertex{{last.x, last.y}}); curve.append(CVertex{ccw ? 1 : -1, {next.x, next.y}, {center.x, center.y}}); pathSegments.append(curve); - count++; // Base::Vector3d prev = last; // for (Base::Vector3d p : pts) { // line(prev, p); @@ -597,10 +598,10 @@ public: (void)id; (void)q; // always within the bounds of p printf("g8x UNHANDLED\n"); - // processPt(last); - // processPts(pts); - // processPts(p); - // processPt(next); + processPt(last); + processPts(pts); + processPts(p); + processPt(next); (void)last; (void)pts; (void)p; @@ -608,10 +609,8 @@ public: } void g38(int id, const Base::Vector3d &last, const Base::Vector3d &next) override { + // probe operation; clears nothing (void)id; - printf("g38 UNHANDLED\n"); - // processPt(last); - // processPt(next); (void)last; (void)next; } @@ -628,7 +627,7 @@ private: } }; -std::shared_ptr Area::getClearedArea(const Toolpath *path, double diameter, double zmax) { +std::shared_ptr Area::getClearedArea(const Toolpath *path, double diameter, double zmax, Base::BoundBox3d bbox) { build(); // Precision losses in arc/segment conversions (multiples of Accuracy): @@ -658,20 +657,18 @@ std::shared_ptr Area::getClearedArea(const Toolpath *path, double diameter printf("\n"); printf("GCode walker:\n"); - ClearedAreaSegmentVisitor visitor(zmax, diameter/2 + buffer); + ClearedAreaSegmentVisitor visitor(zmax, diameter/2 + buffer, bbox); PathSegmentWalker walker(*path); walker.walk(visitor, Base::Vector3d(0, 0, zmax + 1)); - printf("Count: %d\n", visitor.count); printf("\n"); printf("\n"); std::shared_ptr clearedArea = make_shared(¶ms); - //clearedArea->myTrsf = myTrsf; clearedArea->myTrsf = {}; - if (visitor.count > 0) { - //gp_Trsf trsf(myTrsf.Inverted()); - TopoDS_Shape clearedAreaShape = Area::toShape(visitor.getClearedArea(), false/*, &trsf*/); + const CArea ca = visitor.getClearedArea(); + if (ca.m_curves.size() > 0) { + TopoDS_Shape clearedAreaShape = Area::toShape(ca, false); clearedArea->add(clearedAreaShape, OperationCompound); clearedArea->build(); } else { diff --git a/src/Mod/Path/App/Area.h b/src/Mod/Path/App/Area.h index 8056f4a3da..e79a62577d 100644 --- a/src/Mod/Path/App/Area.h +++ b/src/Mod/Path/App/Area.h @@ -242,7 +242,7 @@ public: const std::vector& heights = std::vector(), const TopoDS_Shape& plane = TopoDS_Shape()); - std::shared_ptr getClearedArea(const Toolpath *path, double diameter, double zmax); + std::shared_ptr getClearedArea(const Toolpath *path, double diameter, double zmax, Base::BoundBox3d bbox); std::shared_ptr getRestArea(std::vector> clearedAreas, double diameter); TopoDS_Shape toTopoShape(); diff --git a/src/Mod/Path/App/AreaPyImp.cpp b/src/Mod/Path/App/AreaPyImp.cpp index a0a85b930b..bbe4182c23 100644 --- a/src/Mod/Path/App/AreaPyImp.cpp +++ b/src/Mod/Path/App/AreaPyImp.cpp @@ -22,6 +22,7 @@ #include "PreCompiled.h" +#include #include #include #include @@ -150,8 +151,8 @@ static const PyMethodDef areaOverrides[] = { }, { "getClearedArea",nullptr,0, - "getClearedArea(path, diameter, zmax):\n" - "Gets the area cleared when a tool of the specified diameter follows the gcode represented in the path, ignoring cleared space above zmax.\n", + "getClearedArea(path, diameter, zmax, bbox):\n" + "Gets the area cleared when a tool of the specified diameter follows the gcode represented in the path, ignoring cleared space above zmax and path segments that don't affect space within bbox.\n", }, { "getRestArea",nullptr,0, @@ -407,16 +408,21 @@ PyObject* AreaPy::makeSections(PyObject *args, PyObject *keywds) PyObject* AreaPy::getClearedArea(PyObject *args) { PY_TRY { - PyObject *pyPath; + PyObject *pyPath, *pyBbox; double diameter, zmax; - if (!PyArg_ParseTuple(args, "Odd", &pyPath, &diameter, &zmax)) + if (!PyArg_ParseTuple(args, "OddO", &pyPath, &diameter, &zmax, &pyBbox)) return nullptr; if (!PyObject_TypeCheck(pyPath, &(PathPy::Type))) { PyErr_SetString(PyExc_TypeError, "path must be of type PathPy"); return nullptr; } + if (!PyObject_TypeCheck(pyBbox, &(Base::BoundBoxPy::Type))) { + PyErr_SetString(PyExc_TypeError, "bbox must be of type BoundBoxPy"); + return nullptr; + } const PathPy *path = static_cast(pyPath); - std::shared_ptr clearedArea = getAreaPtr()->getClearedArea(path->getToolpathPtr(), diameter, zmax); + const Py::BoundingBox bbox(pyBbox, false); + std::shared_ptr clearedArea = getAreaPtr()->getClearedArea(path->getToolpathPtr(), diameter, zmax, bbox.getValue()); auto pyClearedArea = Py::asObject(new AreaPy(new Area(*clearedArea, true))); return Py::new_reference_to(pyClearedArea); } PY_CATCH_OCC diff --git a/src/Mod/Path/Path/Op/Area.py b/src/Mod/Path/Path/Op/Area.py index 5f9e3a759f..e88440f55f 100644 --- a/src/Mod/Path/Path/Op/Area.py +++ b/src/Mod/Path/Path/Op/Area.py @@ -242,7 +242,8 @@ class ObjectOp(PathOp.ObjectOp): if hasattr(obj, "UseRestMachining") and obj.UseRestMachining: restSections = [] for section in sections: - z = section.getShape().BoundBox.ZMin + bbox = section.getShape().BoundBox + z = bbox.ZMin sectionClearedAreas = [] for op in self.job.Operations.Group: print(op.Name) @@ -252,7 +253,7 @@ class ObjectOp(PathOp.ObjectOp): if hasattr(op, "Active") and op.Active and op.Path: tool = op.Proxy.tool if hasattr(op.Proxy, "tool") else op.ToolController.Proxy.getTool(op.ToolController) diameter = tool.Diameter.getValueAs("mm") - sectionClearedAreas.append(area.getClearedArea(op.Path, diameter, z+0.001)) + sectionClearedAreas.append(section.getClearedArea(op.Path, diameter, z+0.001, bbox)) # debugZ = -1.5 # if debugZ -.1 < z and z < debugZ + .1: # debugObj = obj.Document.addObject("Part::Feature", "Debug_{}_{}".format(debugZ, op.Name)) From 4a35e788c24c9a324e514c91411f6e9002ca90cd Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Mon, 8 Jan 2024 19:31:00 -0500 Subject: [PATCH 012/135] add support for drilling gcode --- src/Mod/Path/App/Area.cpp | 44 +++++++++++++++++++++++++++--------- src/Mod/Path/Path/Op/Area.py | 3 ++- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index 7c9ed4ce00..5b13bd75cc 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -501,10 +501,27 @@ class ClearedAreaSegmentVisitor : public PathSegmentVisitor { private: CArea pathSegments; + CArea holes; double maxZ; double radius; Base::BoundBox3d bbox; + void point(const Base::Vector3d &p) + { + printf("Point? (%.0f, %.0f, %.0f) ", p.x, p.y, p.z); + if (p.z <= maxZ) { + if (bbox.MinX <= p.x && p.x <= bbox.MaxX && bbox.MinY <= p.y && p.y <= bbox.MaxY) { + CCurve curve; + curve.append(CVertex{{p.x + radius, p.y}}); + curve.append(CVertex{1, {p.x - radius, p.y}, {p.x, p.y}}); + curve.append(CVertex{1, {p.x + radius, p.y}, {p.x, p.y}}); + holes.append(curve); + printf("accepted"); + } + } + printf("\n"); + } + void line(const Base::Vector3d &last, const Base::Vector3d &next) { if (last.z <= maxZ && next.z <= maxZ) { @@ -529,6 +546,7 @@ public: { CArea result{pathSegments}; result.Thicken(radius); + result.Union(holes); return result; } @@ -593,19 +611,20 @@ public: } void g8x(int id, const Base::Vector3d &last, const Base::Vector3d &next, const std::deque &pts, - const std::deque &p, const std::deque &q) override + const std::deque &plist, const std::deque &qlist) override { + // (peck) drilling (void)id; - (void)q; // always within the bounds of p - printf("g8x UNHANDLED\n"); - processPt(last); - processPts(pts); - processPts(p); - processPt(next); - (void)last; - (void)pts; - (void)p; - (void)next; + (void)qlist; // pecks are always within the bounds of plist + + point(last); + for (const auto p : pts) { + point(p); + } + for (const auto p : plist) { + point(p); + } + point(next); } void g38(int id, const Base::Vector3d &last, const Base::Vector3d &next) override { @@ -638,6 +657,8 @@ std::shared_ptr Area::getClearedArea(const Toolpath *path, double diameter // Oversize cleared areas by buffer to smooth out imprecision in arc/segment conversion. getRestArea() will compensate for this AreaParams params = myParams; params.Accuracy = myParams.Accuracy * .7/4; // 2.3 already encoded in gcode; 4 * .7/4 = 3 total + params.SubjectFill = ClipperLib::pftNonZero; + params.ClipFill = ClipperLib::pftNonZero; const double buffer = myParams.Accuracy * 3; // Do not fit arcs after these offsets; it introduces unnecessary approximation error, and all off @@ -667,6 +688,7 @@ std::shared_ptr Area::getClearedArea(const Toolpath *path, double diameter std::shared_ptr clearedArea = make_shared(¶ms); clearedArea->myTrsf = {}; const CArea ca = visitor.getClearedArea(); + printf("Cleared area segments: %ld\n", ca.m_curves.size()); if (ca.m_curves.size() > 0) { TopoDS_Shape clearedAreaShape = Area::toShape(ca, false); clearedArea->add(clearedAreaShape, OperationCompound); diff --git a/src/Mod/Path/Path/Op/Area.py b/src/Mod/Path/Path/Op/Area.py index e88440f55f..5ec88b640f 100644 --- a/src/Mod/Path/Path/Op/Area.py +++ b/src/Mod/Path/Path/Op/Area.py @@ -253,7 +253,8 @@ class ObjectOp(PathOp.ObjectOp): if hasattr(op, "Active") and op.Active and op.Path: tool = op.Proxy.tool if hasattr(op.Proxy, "tool") else op.ToolController.Proxy.getTool(op.ToolController) diameter = tool.Diameter.getValueAs("mm") - sectionClearedAreas.append(section.getClearedArea(op.Path, diameter, z+0.001, bbox)) + dz = 0 if not hasattr(tool, "TipAngle") else -PathUtils.drillTipLength(tool) # for drills, dz moves to the full width part of the tool + sectionClearedAreas.append(section.getClearedArea(op.Path, diameter, z+dz+0.001, bbox)) # debugZ = -1.5 # if debugZ -.1 < z and z < debugZ + .1: # debugObj = obj.Document.addObject("Part::Feature", "Debug_{}_{}".format(debugZ, op.Name)) From 257b500779ca03fd1c2a456bfdd02c814fe62b16 Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Mon, 8 Jan 2024 19:44:22 -0500 Subject: [PATCH 013/135] cleanup --- src/Mod/Path/App/Area.cpp | 67 ---------------------------- src/Mod/Path/App/AreaPyImp.cpp | 2 +- src/Mod/Path/Path/Op/Area.py | 9 +--- src/Mod/Path/libarea/AreaClipper.cpp | 10 ++--- 4 files changed, 7 insertions(+), 81 deletions(-) diff --git a/src/Mod/Path/App/Area.cpp b/src/Mod/Path/App/Area.cpp index 5b13bd75cc..39fbc884af 100644 --- a/src/Mod/Path/App/Area.cpp +++ b/src/Mod/Path/App/Area.cpp @@ -508,7 +508,6 @@ private: void point(const Base::Vector3d &p) { - printf("Point? (%.0f, %.0f, %.0f) ", p.x, p.y, p.z); if (p.z <= maxZ) { if (bbox.MinX <= p.x && p.x <= bbox.MaxX && bbox.MinY <= p.y && p.y <= bbox.MaxY) { CCurve curve; @@ -516,10 +515,8 @@ private: curve.append(CVertex{1, {p.x - radius, p.y}, {p.x, p.y}}); curve.append(CVertex{1, {p.x + radius, p.y}, {p.x, p.y}}); holes.append(curve); - printf("accepted"); } } - printf("\n"); } void line(const Base::Vector3d &last, const Base::Vector3d &next) @@ -554,42 +551,20 @@ public: { (void)id; (void)pts; - // printf("g0 [ "); - //processPt(last); - // printf("] "); - //processPts(pts); - // printf("_ "); - //processPt(next); - line(last, next); - // printf("\n"); } void g1(int id, const Base::Vector3d &last, const Base::Vector3d &next, const std::deque &pts) override { (void)id; (void)pts; - // printf("g1 [ "); - //processPt(last); - // printf("] "); - //processPts(pts); - // printf("_ "); - //processPt(next); - line(last, next); - // printf("\n"); } void g23(int id, const Base::Vector3d &last, const Base::Vector3d &next, const std::deque &pts, const Base::Vector3d ¢er) override { (void)id; (void)center; - // printf("g23 [ "); - // processPt(last); - // printf("] "); - // processPts(pts); - // printf("_ "); - // processPt(next); // Compute cw vs ccw const Base::Vector3d vdirect = next - last; @@ -601,13 +576,6 @@ public: curve.append(CVertex{{last.x, last.y}}); curve.append(CVertex{ccw ? 1 : -1, {next.x, next.y}, {center.x, center.y}}); pathSegments.append(curve); - // Base::Vector3d prev = last; - // for (Base::Vector3d p : pts) { - // line(prev, p); - // prev = p; - // } - // line(prev, next); - // printf("\n"); } void g8x(int id, const Base::Vector3d &last, const Base::Vector3d &next, const std::deque &pts, @@ -633,17 +601,6 @@ public: (void)last; (void)next; } - -private: - void processPts(const std::deque &pts) { - for (std::deque::const_iterator it=pts.begin(); pts.end() != it; ++it) { - processPt(*it); - } - } - - void processPt(const Base::Vector3d &pt) { - printf("(%7.3f, %7.3f, %7.3f) ", pt.x, pt.y, pt.z); - } }; std::shared_ptr Area::getClearedArea(const Toolpath *path, double diameter, double zmax, Base::BoundBox3d bbox) { @@ -665,30 +622,13 @@ std::shared_ptr Area::getClearedArea(const Toolpath *path, double diameter // those arcs will be converted back to segments again for clipper differencing in getRestArea anyway CAreaConfig conf(params, /*no_fit_arcs*/ true); - Base::Vector3d pos = Base::Vector3d(0, 0, zmax + 1); - printf("getClearedArea(path, diameter=%g, zmax=%g:\n", diameter, zmax); - // printf("Gcode:\n"); - for (auto c : path->getCommands()) { - // printf("\t%s ", c->Name.c_str()); - for (std::map::iterator i = c->Parameters.begin(); i != c->Parameters.end(); ++i) { - // printf("%s%g ", i->first.c_str(), i->second); - } - // printf("\n"); - } - - printf("\n"); - printf("GCode walker:\n"); ClearedAreaSegmentVisitor visitor(zmax, diameter/2 + buffer, bbox); PathSegmentWalker walker(*path); walker.walk(visitor, Base::Vector3d(0, 0, zmax + 1)); - printf("\n"); - printf("\n"); - std::shared_ptr clearedArea = make_shared(¶ms); clearedArea->myTrsf = {}; const CArea ca = visitor.getClearedArea(); - printf("Cleared area segments: %ld\n", ca.m_curves.size()); if (ca.m_curves.size() > 0) { TopoDS_Shape clearedAreaShape = Area::toShape(ca, false); clearedArea->add(clearedAreaShape, OperationCompound); @@ -721,8 +661,6 @@ std::shared_ptr Area::getRestArea(std::vector> clear // transform all clearedAreas into our workplane Area clearedAreasInPlane(¶ms); clearedAreasInPlane.myArea.reset(new CArea()); - printf("getRestArea\n"); - printf("\n"); for (std::shared_ptr clearedArea : clearedAreas) { gp_Trsf trsf = clearedArea->myTrsf; trsf.Invert(); @@ -733,24 +671,20 @@ std::shared_ptr Area::getRestArea(std::vector> clear } // clearable = offset(offset(A, -dTool/2), dTool/2) - printf("Compute clearable\n"); CArea clearable(*myArea); clearable.OffsetWithClipper(-diameter/2, JoinType, EndType, params.MiterLimit, roundPrecision); clearable.OffsetWithClipper(diameter/2, JoinType, EndType, params.MiterLimit, roundPrecision); // remaining = clearable - prevCleared - printf("Compute remaining\n"); CArea remaining(clearable); remaining.Clip(toClipperOp(Area::OperationDifference), &*(clearedAreasInPlane.myArea), SubjectFill, ClipFill); // rest = intersect(clearable, offset(remaining, dTool)) // add buffer to dTool to compensate for oversizing in getClearedArea - printf("Compute rest\n"); CArea restCArea(remaining); restCArea.OffsetWithClipper(diameter + buffer, JoinType, EndType, params.MiterLimit, roundPrecision); restCArea.Clip(toClipperOp(Area::OperationIntersection), &clearable, SubjectFill, ClipFill); - printf("Convert CArea to Area (num curves: %d)\n", (int)restCArea.m_curves.size()); if(restCArea.m_curves.size() == 0) { return {}; } @@ -760,7 +694,6 @@ std::shared_ptr Area::getRestArea(std::vector> clear TopoDS_Shape restShape = Area::toShape(restCArea, false, &trsf); restArea->add(restShape, OperationCompound); - printf("return\n"); return restArea; } diff --git a/src/Mod/Path/App/AreaPyImp.cpp b/src/Mod/Path/App/AreaPyImp.cpp index bbe4182c23..bbfb1b5088 100644 --- a/src/Mod/Path/App/AreaPyImp.cpp +++ b/src/Mod/Path/App/AreaPyImp.cpp @@ -152,7 +152,7 @@ static const PyMethodDef areaOverrides[] = { { "getClearedArea",nullptr,0, "getClearedArea(path, diameter, zmax, bbox):\n" - "Gets the area cleared when a tool of the specified diameter follows the gcode represented in the path, ignoring cleared space above zmax and path segments that don't affect space within bbox.\n", + "Gets the area cleared when a tool of the specified diameter follows the gcode represented in the path, ignoring cleared space above zmax and path segments that don't affect space within the x/y space of bbox.\n", }, { "getRestArea",nullptr,0, diff --git a/src/Mod/Path/Path/Op/Area.py b/src/Mod/Path/Path/Op/Area.py index 5ec88b640f..fd831341b2 100644 --- a/src/Mod/Path/Path/Op/Area.py +++ b/src/Mod/Path/Path/Op/Area.py @@ -246,20 +246,13 @@ class ObjectOp(PathOp.ObjectOp): z = bbox.ZMin sectionClearedAreas = [] for op in self.job.Operations.Group: - print(op.Name) if self in [x.Proxy for x in [op] + op.OutListRecursive if hasattr(x, "Proxy")]: - print("found self") break if hasattr(op, "Active") and op.Active and op.Path: tool = op.Proxy.tool if hasattr(op.Proxy, "tool") else op.ToolController.Proxy.getTool(op.ToolController) diameter = tool.Diameter.getValueAs("mm") - dz = 0 if not hasattr(tool, "TipAngle") else -PathUtils.drillTipLength(tool) # for drills, dz moves to the full width part of the tool + dz = 0 if not hasattr(tool, "TipAngle") else -PathUtils.drillTipLength(tool) # for drills, dz translates to the full width part of the tool sectionClearedAreas.append(section.getClearedArea(op.Path, diameter, z+dz+0.001, bbox)) - # debugZ = -1.5 - # if debugZ -.1 < z and z < debugZ + .1: - # debugObj = obj.Document.addObject("Part::Feature", "Debug_{}_{}".format(debugZ, op.Name)) - # debugObj.Label = "Debug_{}_{}".format(debugZ, op.Label) - # debugObj.Shape = sectionClearedAreas[-1].getShape() restSection = section.getRestArea(sectionClearedAreas, self.tool.Diameter.getValueAs("mm")) if (restSection is not None): restSections.append(restSection) diff --git a/src/Mod/Path/libarea/AreaClipper.cpp b/src/Mod/Path/libarea/AreaClipper.cpp index 48f3dc93af..1ee608bbba 100644 --- a/src/Mod/Path/libarea/AreaClipper.cpp +++ b/src/Mod/Path/libarea/AreaClipper.cpp @@ -252,14 +252,14 @@ static void MakeObround(const Point &pt0, const CVertex &vt1, double radius) static void OffsetSpansWithObrounds(const CArea& area, TPolyPolygon &pp_new, double radius) { Clipper c; - c.StrictlySimple(CArea::m_clipper_simple); + c.StrictlySimple(CArea::m_clipper_simple); pp_new.clear(); for(std::list::const_iterator It = area.m_curves.begin(); It != area.m_curves.end(); It++) { - c.Clear(); - c.AddPaths(pp_new, ptSubject, true); - pp_new.clear(); + c.Clear(); + c.AddPaths(pp_new, ptSubject, true); + pp_new.clear(); pts_for_AddVertex.clear(); const CCurve& curve = *It; @@ -282,7 +282,7 @@ static void OffsetSpansWithObrounds(const CArea& area, TPolyPolygon &pp_new, dou } prev_vertex = &vertex; } - c.Execute(ctUnion, pp_new, pftNonZero, pftNonZero); + c.Execute(ctUnion, pp_new, pftNonZero, pftNonZero); } From 15306066db3993c99d1dde0abe618c58e59a00d2 Mon Sep 17 00:00:00 2001 From: David Kaufman Date: Thu, 11 Jan 2024 22:49:11 -0500 Subject: [PATCH 014/135] replace hard coded 1um tolerance with job.GeometryTolerance --- src/Mod/Path/Path/Op/Area.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Path/Path/Op/Area.py b/src/Mod/Path/Path/Op/Area.py index fd831341b2..5a3756cacd 100644 --- a/src/Mod/Path/Path/Op/Area.py +++ b/src/Mod/Path/Path/Op/Area.py @@ -252,7 +252,7 @@ class ObjectOp(PathOp.ObjectOp): tool = op.Proxy.tool if hasattr(op.Proxy, "tool") else op.ToolController.Proxy.getTool(op.ToolController) diameter = tool.Diameter.getValueAs("mm") dz = 0 if not hasattr(tool, "TipAngle") else -PathUtils.drillTipLength(tool) # for drills, dz translates to the full width part of the tool - sectionClearedAreas.append(section.getClearedArea(op.Path, diameter, z+dz+0.001, bbox)) + sectionClearedAreas.append(section.getClearedArea(op.Path, diameter, z+dz+self.job.GeometryTolerance.getValueAs("mm"), bbox)) restSection = section.getRestArea(sectionClearedAreas, self.tool.Diameter.getValueAs("mm")) if (restSection is not None): restSections.append(restSection) From eab628460d92926597ffe8b5fc06c34564da9051 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sun, 14 Jan 2024 10:43:39 +0100 Subject: [PATCH 015/135] Material: fix Qt6 build failure QRegExp has been replaced with QRegularExpression and isn't part of the base of Qt6 any more --- src/Mod/Material/App/MaterialLoader.cpp | 14 +++++--------- src/Mod/Material/App/MaterialValue.cpp | 5 ++--- src/Mod/Material/App/ModelLibrary.cpp | 2 +- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/Mod/Material/App/MaterialLoader.cpp b/src/Mod/Material/App/MaterialLoader.cpp index a6ab7a5a8a..fc0e6f14c7 100644 --- a/src/Mod/Material/App/MaterialLoader.cpp +++ b/src/Mod/Material/App/MaterialLoader.cpp @@ -21,16 +21,12 @@ #include "PreCompiled.h" #ifndef _PreComp_ -#include -#endif - #include #include #include #include - -#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) -#include +#include +#include #endif #include @@ -85,7 +81,7 @@ std::shared_ptr> MaterialYamlEntry::readList(const YAML::Node& n QVariant nodeValue; if (isImageList) { nodeValue = QString::fromStdString(it->as()) - .remove(QRegExp(QString::fromStdString("[\r\n]"))); + .remove(QRegularExpression(QString::fromStdString("[\r\n]"))); } else { nodeValue = QString::fromStdString(it->as()); @@ -247,7 +243,7 @@ void MaterialYamlEntry::addToTree( QString::fromStdString((itp->second).as()); if (type == MaterialValue::Image) { propertyValue = - propertyValue.remove(QRegExp(QString::fromStdString("[\r\n]"))); + propertyValue.remove(QRegularExpression(QString::fromStdString("[\r\n]"))); } finalModel->setPhysicalValue(QString::fromStdString(propertyName), propertyValue); @@ -314,7 +310,7 @@ void MaterialYamlEntry::addToTree( QString::fromStdString((itp->second).as()); if (type == MaterialValue::Image) { propertyValue = - propertyValue.remove(QRegExp(QString::fromStdString("[\r\n]"))); + propertyValue.remove(QRegularExpression(QString::fromStdString("[\r\n]"))); } finalModel->setAppearanceValue(QString::fromStdString(propertyName), propertyValue); diff --git a/src/Mod/Material/App/MaterialValue.cpp b/src/Mod/Material/App/MaterialValue.cpp index a9ccc6513e..f27bfed838 100644 --- a/src/Mod/Material/App/MaterialValue.cpp +++ b/src/Mod/Material/App/MaterialValue.cpp @@ -21,11 +21,10 @@ #include "PreCompiled.h" #ifndef _PreComp_ +#include #include #endif -#include - #include #include #include @@ -264,7 +263,7 @@ QString MaterialValue::getYAMLStringMultiLine() const QString yaml; yaml = QString::fromStdString(" |2"); auto list = - getValue().toString().split(QRegExp(QString::fromStdString("[\r\n]")), Qt::SkipEmptyParts); + getValue().toString().split(QRegularExpression(QString::fromStdString("[\r\n]")), Qt::SkipEmptyParts); for (auto& it : list) { yaml += QString::fromStdString("\n ") + it; } diff --git a/src/Mod/Material/App/ModelLibrary.cpp b/src/Mod/Material/App/ModelLibrary.cpp index baa244ecdd..63d06f2d06 100644 --- a/src/Mod/Material/App/ModelLibrary.cpp +++ b/src/Mod/Material/App/ModelLibrary.cpp @@ -55,7 +55,7 @@ QString LibraryBase::getLocalPath(const QString& path) const QString prefix = QString::fromStdString("/") + getName(); if (cleanPath.startsWith(prefix)) { // Remove the library name from the path - filePath += cleanPath.rightRef(cleanPath.length() - prefix.length()); + filePath += cleanPath.right(cleanPath.length() - prefix.length()); } else { filePath += cleanPath; From 4933be26bd3e78fbed52a84f1e3fda3cc34de4fe Mon Sep 17 00:00:00 2001 From: Syres916 <46537884+Syres916@users.noreply.github.com> Date: Mon, 15 Jan 2024 13:43:58 +0000 Subject: [PATCH 016/135] [Gui] [Tux] Fix Nav Style Icons and theme recognition --- src/Mod/Tux/NavigationIndicatorGui.py | 37 ++++++++++---- src/Mod/Tux/Resources/Tux.qrc | 6 ++- .../icons/NavigationOpenSCAD_dark.svg | 50 +++++++++++++++++++ .../icons/NavigationOpenSCAD_light.svg | 50 +++++++++++++++++++ .../icons/NavigationTinkerCAD_dark.svg | 50 +++++++++++++++++++ .../icons/NavigationTinkerCAD_light.svg | 50 +++++++++++++++++++ 6 files changed, 230 insertions(+), 13 deletions(-) create mode 100644 src/Mod/Tux/Resources/icons/NavigationOpenSCAD_dark.svg create mode 100644 src/Mod/Tux/Resources/icons/NavigationOpenSCAD_light.svg create mode 100644 src/Mod/Tux/Resources/icons/NavigationTinkerCAD_dark.svg create mode 100644 src/Mod/Tux/Resources/icons/NavigationTinkerCAD_light.svg diff --git a/src/Mod/Tux/NavigationIndicatorGui.py b/src/Mod/Tux/NavigationIndicatorGui.py index e20c0be897..c05feb1ba2 100644 --- a/src/Mod/Tux/NavigationIndicatorGui.py +++ b/src/Mod/Tux/NavigationIndicatorGui.py @@ -28,7 +28,7 @@ mw = Gui.getMainWindow() statusBar = mw.statusBar() p = App.ParamGet("User parameter:Tux/NavigationIndicator") pView = App.ParamGet("User parameter:BaseApp/Preferences/View") - +pMWin = App.ParamGet("User parameter:BaseApp/Preferences/MainWindow") try: _encoding = QtGui.QApplication.UnicodeUTF8 @@ -62,6 +62,29 @@ class IndicatorButton(QtGui.QPushButton): if param == "NavigationStyle": setCurrent() + def mousePressEvent(self, event): + RePopulateIcons() + return super(IndicatorButton, self).mousePressEvent(event) + + +def RePopulateIcons(): + curStyleSheet = pMWin.GetString("StyleSheet") + if "dark" in curStyleSheet.lower(): + StyleSheetType = "light" + else: + StyleSheetType = "dark" + + a1.setIcon(QtGui.QIcon(":/icons/NavigationBlender_" + StyleSheetType + ".svg")) + a2.setIcon(QtGui.QIcon(":/icons/NavigationCAD_" + StyleSheetType + ".svg")) + a3.setIcon(QtGui.QIcon(":/icons/NavigationGesture_" + StyleSheetType + ".svg")) + a4.setIcon(QtGui.QIcon(":/icons/NavigationMayaGesture_" + StyleSheetType + ".svg")) + a5.setIcon(QtGui.QIcon(":/icons/NavigationOpenCascade_" + StyleSheetType + ".svg")) + a6.setIcon(QtGui.QIcon(":/icons/NavigationOpenInventor_" + StyleSheetType + ".svg")) + a7.setIcon(QtGui.QIcon(":/icons/NavigationOpenSCAD_" + StyleSheetType + ".svg")) + a8.setIcon(QtGui.QIcon(":/icons/NavigationRevit_" + StyleSheetType + ".svg")) + a9.setIcon(QtGui.QIcon(":/icons/NavigationTinkerCAD_" + StyleSheetType + ".svg")) + a10.setIcon(QtGui.QIcon(":/icons/NavigationTouchpad_" + StyleSheetType + ".svg")) + def retranslateUi(): """Retranslate navigation indicator on language change""" @@ -602,65 +625,57 @@ a0.setData("Undefined ") a0.setObjectName("Indicator_NavigationUndefined") a1 = QtGui.QAction(gStyle) -a1.setIcon(QtGui.QIcon(":/icons/NavigationBlender_dark.svg")) a1.setText("Blender ") a1.setData("Gui::BlenderNavigationStyle") a1.setObjectName("Indicator_NavigationBlender") a2 = QtGui.QAction(gStyle) -a2.setIcon(QtGui.QIcon(":/icons/NavigationCAD_dark.svg")) a2.setText("CAD ") a2.setData("Gui::CADNavigationStyle") a2.setObjectName("Indicator_NavigationCAD") a3 = QtGui.QAction(gStyle) -a3.setIcon(QtGui.QIcon(":/icons/NavigationGesture_dark.svg")) a3.setText("Gesture ") a3.setData("Gui::GestureNavigationStyle") a3.setObjectName("Indicator_NavigationGesture") a4 = QtGui.QAction(gStyle) -a4.setIcon(QtGui.QIcon(":/icons/NavigationMayaGesture_dark.svg")) a4.setText("MayaGesture ") a4.setData("Gui::MayaGestureNavigationStyle") a4.setObjectName("Indicator_NavigationMayaGesture") a5 = QtGui.QAction(gStyle) -a5.setIcon(QtGui.QIcon(":/icons/NavigationOpenCascade_dark.svg")) a5.setText("OpenCascade ") a5.setData("Gui::OpenCascadeNavigationStyle") a5.setObjectName("Indicator_NavigationOpenCascade") a6 = QtGui.QAction(gStyle) -a6.setIcon(QtGui.QIcon(":/icons/NavigationOpenInventor_dark.svg")) a6.setText("OpenInventor ") a6.setData("Gui::InventorNavigationStyle") a6.setObjectName("Indicator_NavigationOpenInventor") a7 = QtGui.QAction(gStyle) -a7.setIcon(QtGui.QIcon(":/icons/NavigationOpenSCAD_dark.svg")) a7.setText("OpenSCAD ") a7.setData("Gui::OpenSCADNavigationStyle") a7.setObjectName("Indicator_NavigationOpenSCAD") a8 = QtGui.QAction(gStyle) -a8.setIcon(QtGui.QIcon(":/icons/NavigationRevit_dark.svg")) a8.setText("Revit ") a8.setData("Gui::RevitNavigationStyle") a8.setObjectName("Indicator_NavigationRevit") a9 = QtGui.QAction(gStyle) -a9.setIcon(QtGui.QIcon(":/icons/NavigationTinkerCAD_dark.svg")) a9.setText("TinkerCAD ") a9.setData("Gui::TinkerCADNavigationStyle") a9.setObjectName("Indicator_NavigationTinkerCAD") a10 = QtGui.QAction(gStyle) -a10.setIcon(QtGui.QIcon(":/icons/NavigationTouchpad_dark.svg")) a10.setText("Touchpad ") a10.setData("Gui::TouchpadNavigationStyle") a10.setObjectName("Indicator_NavigationTouchpad") +RePopulateIcons() + menu.addMenu(menuSettings) menu.addSeparator() menu.addAction(a0) diff --git a/src/Mod/Tux/Resources/Tux.qrc b/src/Mod/Tux/Resources/Tux.qrc index 69dfb4ee3a..483eedd537 100644 --- a/src/Mod/Tux/Resources/Tux.qrc +++ b/src/Mod/Tux/Resources/Tux.qrc @@ -58,13 +58,15 @@ icons/NavigationOpenInventor_dark.svg icons/NavigationOpenInventor_ZoomAlt.svg icons/NavigationOpenInventor_Zoom.svg - icons/NavigationCAD_dark.svg + icons/NavigationOpenSCAD_light.svg + icons/NavigationOpenSCAD_dark.svg icons/NavigationOpenCascade_Select.svg icons/NavigationOpenCascade_PanAlt.svg icons/NavigationOpenCascade_PanAlt.svg icons/NavigationOpenCascade_Select.svg icons/NavigationGesture_Pan.svg - icons/NavigationCAD_dark.svg + icons/NavigationTinkerCAD_light.svg + icons/NavigationTinkerCAD_dark.svg icons/NavigationOpenCascade_Select.svg icons/NavigationOpenCascade_Zoom.svg icons/NavigationGesture_Pan.svg diff --git a/src/Mod/Tux/Resources/icons/NavigationOpenSCAD_dark.svg b/src/Mod/Tux/Resources/icons/NavigationOpenSCAD_dark.svg new file mode 100644 index 0000000000..1ec124fba1 --- /dev/null +++ b/src/Mod/Tux/Resources/icons/NavigationOpenSCAD_dark.svg @@ -0,0 +1,50 @@ + + + + + + + + + image/svg+xml + + + + + + + + + OS + diff --git a/src/Mod/Tux/Resources/icons/NavigationOpenSCAD_light.svg b/src/Mod/Tux/Resources/icons/NavigationOpenSCAD_light.svg new file mode 100644 index 0000000000..c3829d9c93 --- /dev/null +++ b/src/Mod/Tux/Resources/icons/NavigationOpenSCAD_light.svg @@ -0,0 +1,50 @@ + + + + + + + + + image/svg+xml + + + + + + + + + OS + diff --git a/src/Mod/Tux/Resources/icons/NavigationTinkerCAD_dark.svg b/src/Mod/Tux/Resources/icons/NavigationTinkerCAD_dark.svg new file mode 100644 index 0000000000..b21d4bacdf --- /dev/null +++ b/src/Mod/Tux/Resources/icons/NavigationTinkerCAD_dark.svg @@ -0,0 +1,50 @@ + + + + + + + + + image/svg+xml + + + + + + + + + T + diff --git a/src/Mod/Tux/Resources/icons/NavigationTinkerCAD_light.svg b/src/Mod/Tux/Resources/icons/NavigationTinkerCAD_light.svg new file mode 100644 index 0000000000..5d011b3d0d --- /dev/null +++ b/src/Mod/Tux/Resources/icons/NavigationTinkerCAD_light.svg @@ -0,0 +1,50 @@ + + + + + + + + + image/svg+xml + + + + + + + + + T + From 92b4ab2c192bd4dee60ad5f6e07da4dd883af8da Mon Sep 17 00:00:00 2001 From: Syres916 <46537884+Syres916@users.noreply.github.com> Date: Mon, 15 Jan 2024 13:58:27 +0000 Subject: [PATCH 017/135] [Gui] [Tux] Correct text location in icons --- .../Tux/Resources/icons/NavigationOpenSCAD_dark.svg | 4 ++-- .../Tux/Resources/icons/NavigationOpenSCAD_light.svg | 4 ++-- .../Tux/Resources/icons/NavigationTinkerCAD_dark.svg | 10 +++++----- .../Tux/Resources/icons/NavigationTinkerCAD_light.svg | 10 +++++----- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Mod/Tux/Resources/icons/NavigationOpenSCAD_dark.svg b/src/Mod/Tux/Resources/icons/NavigationOpenSCAD_dark.svg index 1ec124fba1..a2eba9ffcc 100644 --- a/src/Mod/Tux/Resources/icons/NavigationOpenSCAD_dark.svg +++ b/src/Mod/Tux/Resources/icons/NavigationOpenSCAD_dark.svg @@ -41,10 +41,10 @@ OS diff --git a/src/Mod/Tux/Resources/icons/NavigationOpenSCAD_light.svg b/src/Mod/Tux/Resources/icons/NavigationOpenSCAD_light.svg index c3829d9c93..d6900e002c 100644 --- a/src/Mod/Tux/Resources/icons/NavigationOpenSCAD_light.svg +++ b/src/Mod/Tux/Resources/icons/NavigationOpenSCAD_light.svg @@ -41,10 +41,10 @@ OS diff --git a/src/Mod/Tux/Resources/icons/NavigationTinkerCAD_dark.svg b/src/Mod/Tux/Resources/icons/NavigationTinkerCAD_dark.svg index b21d4bacdf..aaa6e7faab 100644 --- a/src/Mod/Tux/Resources/icons/NavigationTinkerCAD_dark.svg +++ b/src/Mod/Tux/Resources/icons/NavigationTinkerCAD_dark.svg @@ -40,11 +40,11 @@ T + x="27.425451" + y="45.400135">T diff --git a/src/Mod/Tux/Resources/icons/NavigationTinkerCAD_light.svg b/src/Mod/Tux/Resources/icons/NavigationTinkerCAD_light.svg index 5d011b3d0d..6b4101c00f 100644 --- a/src/Mod/Tux/Resources/icons/NavigationTinkerCAD_light.svg +++ b/src/Mod/Tux/Resources/icons/NavigationTinkerCAD_light.svg @@ -40,11 +40,11 @@ T + x="27.425451" + y="45.400135">T From 5fe2b33bb0bdfa24b3600d440a2c331ae9ec1148 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Mon, 15 Jan 2024 17:36:25 +0100 Subject: [PATCH 018/135] Tests: Avoid use of fmtlib when not needed --- tests/src/App/ComplexGeoData.cpp | 4 ++-- tests/src/Base/Reader.cpp | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/src/App/ComplexGeoData.cpp b/tests/src/App/ComplexGeoData.cpp index 6eab5175d9..4c344495ea 100644 --- a/tests/src/App/ComplexGeoData.cpp +++ b/tests/src/App/ComplexGeoData.cpp @@ -3,7 +3,6 @@ #include "gtest/gtest.h" #include -#include #include #include @@ -318,7 +317,8 @@ TEST_F(ComplexGeoDataTest, elementTypeCharMappedNameWithPrefix) // NOLINT int size {0}; Data::MappedName mappedName; Data::IndexedName indexedName; - auto name = fmt::format("{}TestMappedElement:;", Data::ELEMENT_MAP_PREFIX); + std::string name(Data::ELEMENT_MAP_PREFIX); + name.append("TestMappedElement:;"); std::tie(indexedName, mappedName) = createMappedName(name); // Act diff --git a/tests/src/Base/Reader.cpp b/tests/src/Base/Reader.cpp index 04153c68bd..7ff4f17585 100644 --- a/tests/src/Base/Reader.cpp +++ b/tests/src/Base/Reader.cpp @@ -10,7 +10,6 @@ #include "Base/Reader.h" #include #include -#include #include namespace fs = boost::filesystem; From 7b63799a7e3194368b02166bb5f325214eb9e6f0 Mon Sep 17 00:00:00 2001 From: marioalexis Date: Wed, 10 Jan 2024 16:15:28 -0300 Subject: [PATCH 019/135] Fem: Fix constraint arrows - fixes #6073 --- src/Mod/Fem/App/FemConstraint.cpp | 62 ++++++++++++++++++++++--------- src/Mod/Fem/App/PreCompiled.h | 2 + 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/src/Mod/Fem/App/FemConstraint.cpp b/src/Mod/Fem/App/FemConstraint.cpp index 16a61d26fa..fddf496146 100644 --- a/src/Mod/Fem/App/FemConstraint.cpp +++ b/src/Mod/Fem/App/FemConstraint.cpp @@ -25,12 +25,14 @@ #ifndef _PreComp_ #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -39,6 +41,7 @@ #include #include #include +#include #include #include #include //OvG: Required for log10 @@ -109,8 +112,8 @@ App::DocumentObjectExecReturn* Constraint::execute() // OvG: Provide the ability to determine how big to draw constraint arrows etc. int Constraint::calcDrawScaleFactor(double lparam) const { - return ((int)round(log(lparam) * log(lparam) * log(lparam) / 10) > 1) - ? ((int)round(log(lparam) * log(lparam) * log(lparam) / 10)) + return (static_cast(round(log(lparam) * log(lparam) * log(lparam) / 10)) > 1) + ? (static_cast(round(log(lparam) * log(lparam) * log(lparam) / 10))) : 1; } @@ -123,7 +126,8 @@ int Constraint::calcDrawScaleFactor() const { return 1; } -#define CONSTRAINTSTEPLIMIT 50 + +constexpr int CONSTRAINTSTEPLIMIT = 50; void Constraint::onChanged(const App::Property* prop) { @@ -221,11 +225,11 @@ bool Constraint::getPoints(std::vector& points, // OvG: Increase 10 units distance proportionately to l for larger objects. if (l >= 30) { *scale = this->calcDrawScaleFactor(l); // OvG: setup draw scale for constraint - steps = (int)round(l / (10 * (*scale))); + steps = static_cast(round(l / (10 * (*scale)))); steps = steps < 3 ? 3 : steps; } else if (l >= 20) { - steps = (int)round(l / 10); + steps = static_cast(round(l / 10)); *scale = this->calcDrawScaleFactor(); // OvG: setup draw scale for constraint } else { @@ -309,11 +313,11 @@ bool Constraint::getPoints(std::vector& points, int stepsv; if (lv >= 30) { *scale = this->calcDrawScaleFactor(lv, lu); // OvG: setup draw scale for constraint - stepsv = (int)round(lv / (10 * (*scale))); + stepsv = static_cast(round(lv / (10 * (*scale)))); stepsv = stepsv < 3 ? 3 : stepsv; } else if (lv >= 20.0) { - stepsv = (int)round(lv / 10); + stepsv = static_cast(round(lv / 10)); *scale = this->calcDrawScaleFactor(); // OvG: setup draw scale for constraint } else { @@ -329,11 +333,11 @@ bool Constraint::getPoints(std::vector& points, // OvG: Increase 10 units distance proportionately to lu for larger objects. if (lu >= 30) { *scale = this->calcDrawScaleFactor(lv, lu); // OvG: setup draw scale for constraint - stepsu = (int)round(lu / (10 * (*scale))); + stepsu = static_cast(round(lu / (10 * (*scale)))); stepsu = stepsu < 3 ? 3 : stepsu; } else if (lu >= 20.0) { - stepsu = (int)round(lu / 10); + stepsu = static_cast(round(lu / 10)); *scale = this->calcDrawScaleFactor(); // OvG: setup draw scale for constraint } else { @@ -345,19 +349,43 @@ bool Constraint::getPoints(std::vector& points, stepsu = stepsu > CONSTRAINTSTEPLIMIT ? CONSTRAINTSTEPLIMIT : stepsu; double stepv = (vlp - vfp) / stepsv; double stepu = (ulp - ufp) / stepsu; + // Create points and normals + auto fillPointsAndNormals = [&](Standard_Real u, Standard_Real v) { + gp_Pnt p = surface.Value(u, v); + BRepClass_FaceClassifier classifier(face, p, Precision::Confusion()); + if (classifier.State() != TopAbs_OUT) { + points.emplace_back(p.X(), p.Y(), p.Z()); + props.Normal(u, v, center, normal); + normal.Normalize(); + normals.emplace_back(normal.X(), normal.Y(), normal.Z()); + } + }; + + size_t prevSize = points.size(); for (int i = 0; i < stepsv + 1; i++) { for (int j = 0; j < stepsu + 1; j++) { double v = vfp + i * stepv; double u = ufp + j * stepu; - gp_Pnt p = surface.Value(u, v); - BRepClass_FaceClassifier classifier(face, p, Precision::Confusion()); - if (classifier.State() != TopAbs_OUT) { - points.emplace_back(p.X(), p.Y(), p.Z()); - props.Normal(u, v, center, normal); - normal.Normalize(); - normals.emplace_back(normal.X(), normal.Y(), normal.Z()); - } + fillPointsAndNormals(u, v); + } + } + + // it could happen that on a trimmed surface the steps on the iso-curves + // are outside the surface, so no points are added. + // In that case use points on the outer wire. + // https://github.com/FreeCAD/FreeCAD/issues/6073 + if (prevSize == points.size()) { + BRepAdaptor_CompCurve compCurve(BRepTools::OuterWire(face), Standard_True); + GProp_GProps linProps; + BRepGProp::LinearProperties(compCurve.Wire(), linProps); + double outWireLength = linProps.Mass(); + int stepWire = stepsu + stepsv; + ShapeAnalysis_Surface surfAnalysis(surface.Surface().Surface()); + for (int i = 0; i < stepWire; ++i) { + gp_Pnt p = compCurve.Value(outWireLength * i / stepWire); + gp_Pnt2d pUV = surfAnalysis.ValueOfUV(p, Precision::Confusion()); + fillPointsAndNormals(pUV.X(), pUV.Y()); } } } diff --git a/src/Mod/Fem/App/PreCompiled.h b/src/Mod/Fem/App/PreCompiled.h index cd51febaf4..240207ea65 100644 --- a/src/Mod/Fem/App/PreCompiled.h +++ b/src/Mod/Fem/App/PreCompiled.h @@ -109,6 +109,7 @@ // Opencascade #include +#include #include #include #include @@ -136,6 +137,7 @@ #include #include #include +#include #include #include #include From 3a53a69379927d88ff6365b5b4b89055525f80c1 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Mon, 15 Jan 2024 21:22:57 -0600 Subject: [PATCH 020/135] App/Toponaming: Add Comparator for mapped elements This is the original code from the Toponaming branch, modified slightly to update the method name and correct some grammatical errors in the descriptive comment. Co-authored-by: Chris Hennes --- src/App/MappedElement.cpp | 102 ++++++++++++++++++++++++++++++++++++++ src/App/MappedElement.h | 16 ++++++ 2 files changed, 118 insertions(+) diff --git a/src/App/MappedElement.cpp b/src/App/MappedElement.cpp index 14eb6f99ba..5df937adfe 100644 --- a/src/App/MappedElement.cpp +++ b/src/App/MappedElement.cpp @@ -22,3 +22,105 @@ #include "PreCompiled.h" #include "MappedElement.h" + +using namespace Data; + +bool ElementNameComparator::operator()(const MappedName &a, const MappedName &b) const { + size_t size = std::min(a.size(),b.size()); + if(!size) + return a.size()bc) + res = 1; + } + }else if(std::isxdigit(ac)) + return false; + else + break; + } + if(res < 0) + return true; + else if(res > 0) + return false; + + for (; i bc) + return false; + } + return a.size()bc) + return false; + } else if(!std::isdigit(ac)) { + return false; + } else + break; + } + + // Then compare the following digits part by integer value + int res = 0; + for(;ibc) + res = 1; + } + }else if(std::isdigit(ac)) + return false; + else + break; + } + if(res < 0) + return true; + else if(res > 0) + return false; + + // Finally, compare the remaining tail using lexical order + for (; i bc) + return false; + } + return a.size() Date: Mon, 15 Jan 2024 21:50:32 -0600 Subject: [PATCH 021/135] App/Toponaming: ElementNameComparator linter cleanup Does not address cognitive complexity. --- src/App/MappedElement.cpp | 155 +++++++++++++++++++++++--------------- src/App/MappedElement.h | 2 +- 2 files changed, 94 insertions(+), 63 deletions(-) diff --git a/src/App/MappedElement.cpp b/src/App/MappedElement.cpp index 5df937adfe..25e7abf7cc 100644 --- a/src/App/MappedElement.cpp +++ b/src/App/MappedElement.cpp @@ -25,102 +25,133 @@ using namespace Data; -bool ElementNameComparator::operator()(const MappedName &a, const MappedName &b) const { - size_t size = std::min(a.size(),b.size()); - if(!size) - return a.size()(std::min(leftName.size(), rightName.size())); + if (size == 0U) { + return leftName.size() < rightName.size(); + } + int currentIndex = 0; + if (rightName[0] == '#') { + if (leftName[0] != '#') { return true; + } // If both string starts with '#', compare the following hex digits by // its integer value. int res = 0; - for(i=1;ibc) - res = 1; } - }else if(std::isxdigit(ac)) - return false; - else + if (res == 0) { + if (ac < bc) { + res = -1; + } + else if (ac > bc) { + res = 1; + } + } + } + else if (std::isxdigit(ac) != 0) { + res = 1; + } + else { break; + } } - if(res < 0) + if (res < 0) { return true; - else if(res > 0) - return false; - - for (; i bc) - return false; } - return a.size() 0) { + return false; + } + + for (; currentIndex < size; ++currentIndex) { + char ac = leftName[currentIndex]; + char bc = rightName[currentIndex]; + if (ac < bc) { + return true; + } + if (ac > bc) { + return false; + } + } + return leftName.size() < rightName.size(); } - else if (a[0] == '#') + if (leftName[0] == '#') { return false; + } // If the string does not start with '#', compare the non-digits prefix // using lexical order. - for(i=0;ibc) + } + if (ac > bc) { return false; - } else if(!std::isdigit(ac)) { + } + } + else if (std::isdigit(ac) == 0) { return false; - } else + } + else { break; + } } // Then compare the following digits part by integer value int res = 0; - for(;ibc) - res = 1; } - }else if(std::isdigit(ac)) + if (res == 0) { + if (ac < bc) { + res = -1; + } + else if (ac > bc) { + res = 1; + } + } + } + else if (std::isdigit(ac) != 0) { return false; - else + } + else { break; + } } - if(res < 0) + if (res < 0) { return true; - else if(res > 0) + } + if (res > 0) { return false; + } // Finally, compare the remaining tail using lexical order - for (; i bc) + } + if (ac > bc) { return false; + } } - return a.size() Date: Tue, 16 Jan 2024 13:49:37 -0500 Subject: [PATCH 022/135] [TD]Dimension validation improvements --- src/Mod/TechDraw/Gui/CommandCreateDims.cpp | 4 +- src/Mod/TechDraw/Gui/DimensionValidators.cpp | 48 ++++++++++++++------ src/Mod/TechDraw/Gui/DimensionValidators.h | 3 ++ 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/Mod/TechDraw/Gui/CommandCreateDims.cpp b/src/Mod/TechDraw/Gui/CommandCreateDims.cpp index f786803e32..831d2d58f2 100644 --- a/src/Mod/TechDraw/Gui/CommandCreateDims.cpp +++ b/src/Mod/TechDraw/Gui/CommandCreateDims.cpp @@ -177,7 +177,7 @@ void execRadius(Gui::Command* cmd) StringVector acceptableGeometry({"Edge"}); std::vector minimumCounts({1}); std::vector acceptableDimensionGeometrys( - {isCircle, isEllipse, isBSplineCircle}); + {isCircle, isEllipse, isBSplineCircle, isBSpline}); //what 2d geometry configuration did we receive? DimensionGeometryType geometryRefs2d = validateDimSelection( @@ -299,7 +299,7 @@ void execDiameter(Gui::Command* cmd) StringVector acceptableGeometry({"Edge"}); std::vector minimumCounts({1}); std::vector acceptableDimensionGeometrys( - {isCircle, isEllipse, isBSplineCircle}); + {isCircle, isEllipse, isBSplineCircle, isBSpline}); //what 2d geometry configuration did we receive? DimensionGeometryType geometryRefs2d = validateDimSelection( diff --git a/src/Mod/TechDraw/Gui/DimensionValidators.cpp b/src/Mod/TechDraw/Gui/DimensionValidators.cpp index 7ee54c9e05..38ccb64950 100644 --- a/src/Mod/TechDraw/Gui/DimensionValidators.cpp +++ b/src/Mod/TechDraw/Gui/DimensionValidators.cpp @@ -470,19 +470,18 @@ DimensionGeometryType TechDraw::isValidMultiEdge(ReferenceVector refs) return isInvalid; } + //they all must start with "Edge" + const std::string matchToken{"Edge"}; + if (!refsMatchToken(refs, matchToken)) { + return isInvalid; + } + auto objFeat0(dynamic_cast(refs.at(0).getObject())); if (!objFeat0) { //probably redundant throw Base::RuntimeError("Logic error in isValidMultiEdge"); } - //they all must start with "Edge" - for (auto& ref : refs) { - if (TechDraw::DrawUtil::getGeomTypeFromName(ref.getSubName()) != "Edge") { - return isInvalid; - } - } - if (refs.size() > 2) { //many edges, must be an extent? return isMultiEdge; @@ -525,6 +524,12 @@ DimensionGeometryType TechDraw::isValidMultiEdge3d(DrawViewPart* dvp, ReferenceV return isInvalid; } + //they all must start with "Edge" + const std::string matchToken{"Edge"}; + if (!refsMatchToken(refs, matchToken)) { + return isInvalid; + } + std::vector edges; for (auto& ref : refs) { std::vector shapesAll = ShapeExtractor::getShapesFromObject(ref.getObject()); @@ -532,12 +537,6 @@ DimensionGeometryType TechDraw::isValidMultiEdge3d(DrawViewPart* dvp, ReferenceV //reference has no geometry return isInvalid; } - - //the Name starts with "Edge" - std::string geomName = DrawUtil::getGeomTypeFromName(ref.getSubName().c_str()); - if (geomName != "Edge") { - return isInvalid; - } } std::vector edgesAll; std::vector typeAll; @@ -585,6 +584,11 @@ DimensionGeometryType TechDraw::isValidVertexes(ReferenceVector refs) throw Base::RuntimeError("Logic error in isValidMultiEdge"); } + const std::string matchToken{"Vertex"}; + if (!refsMatchToken(refs, matchToken)) { + return isInvalid; + } + if (refs.size() == 2) { //2 vertices can only make a distance dimension TechDraw::VertexPtr v0 = dvp->getVertex(refs.at(0).getSubName()); @@ -610,6 +614,11 @@ DimensionGeometryType TechDraw::isValidVertexes(ReferenceVector refs) DimensionGeometryType TechDraw::isValidVertexes3d(DrawViewPart* dvp, ReferenceVector refs) { (void)dvp; + const std::string matchToken{"Vertex"}; + if (!refsMatchToken(refs, matchToken)) { + return isInvalid; + } + if (refs.size() == 2) { //2 vertices can only make a distance dimension TopoDS_Shape geometry0 = refs.at(0).getGeometry(); @@ -713,3 +722,16 @@ long int TechDraw::mapGeometryTypeToDimType(long int dimType, DimensionGeometryT } return dimType; } + +//! true if all the input references have subelements that match the geometry +//! type token. +bool TechDraw::refsMatchToken(const ReferenceVector& refs, const std::string& matchToken) +{ + for (auto& entry : refs) { + std::string entryToken = DU::getGeomTypeFromName(entry.getSubName(false)); + if (entryToken != matchToken) { + return false; + } + } + return true; +} diff --git a/src/Mod/TechDraw/Gui/DimensionValidators.h b/src/Mod/TechDraw/Gui/DimensionValidators.h index b47da57c3e..e5e71df9ca 100644 --- a/src/Mod/TechDraw/Gui/DimensionValidators.h +++ b/src/Mod/TechDraw/Gui/DimensionValidators.h @@ -98,6 +98,9 @@ DimensionGeometryType isValidHybrid3d(DrawViewPart* dvp, ReferenceVector refs); long int mapGeometryTypeToDimType(long int dimType, DimensionGeometryType geometry2d, DimensionGeometryType geometry3d); + +bool refsMatchToken(const ReferenceVector& refs, const std::string& matchToken); + } #endif //TECHDRAW_DIMENSIONVALIDATORS_H From 8811a590b055c64da63098e2dac088c04dd424ff Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Mon, 15 Jan 2024 22:41:24 -0500 Subject: [PATCH 023/135] ShapeMapper code from RT branch to new TopoShapeMapper files --- src/Mod/Part/App/CMakeLists.txt | 2 + src/Mod/Part/App/TopoShape.h | 56 ++++++ src/Mod/Part/App/TopoShapeMapper.cpp | 75 ++++++++ src/Mod/Part/App/TopoShapeMapper.h | 255 +++++++++++++++++++++++++++ 4 files changed, 388 insertions(+) create mode 100644 src/Mod/Part/App/TopoShapeMapper.cpp create mode 100644 src/Mod/Part/App/TopoShapeMapper.h diff --git a/src/Mod/Part/App/CMakeLists.txt b/src/Mod/Part/App/CMakeLists.txt index bf0ce716eb..1a85f3b620 100644 --- a/src/Mod/Part/App/CMakeLists.txt +++ b/src/Mod/Part/App/CMakeLists.txt @@ -533,6 +533,8 @@ SET(Part_SRCS TopoShapeCache.cpp TopoShapeCache.h TopoShapeExpansion.cpp + TopoShapeMapper.h + TopoShapeMapper.cpp TopoShapeOpCode.h edgecluster.cpp edgecluster.h diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index ad4ef2a780..a80ce07946 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -613,6 +613,27 @@ public: bool hasPendingElementMap() const; + /** Helper class to return the generated and modified shape given an input shape + * + * Shape history information is extracted using OCCT APIs + * BRepBuilderAPI_MakeShape::Generated/Modified(). However, there is often + * some glitches in various derived class. So we use this class as an + * abstraction, and create various derived classes to deal with the glitches. + */ + struct PartExport Mapper { + /// Helper vector for temporary storage of both generated and modified shapes + mutable std::vector _res; + virtual ~Mapper() {} + /// Return a list of shape generated from the given input shape + virtual const std::vector &generated(const TopoDS_Shape &) const { + return _res; + } + /// Return a list of shape modified from the given input shape + virtual const std::vector &modified(const TopoDS_Shape &) const { + return _res; + } + }; + /** Make a compound shape * * @param shapes: input shapes @@ -629,6 +650,41 @@ public: */ TopoShape &makeElementCompound(const std::vector &shapes, const char *op=nullptr, bool force=true); + struct BRepFillingParams; + + /** Provides information about the continuity of a curve. + * Corresponds to OCCT type GeomAbs_Shape + */ + enum class Continuity { + /// Only geometric continuity + C0, + /** for each point on the curve, the tangent vectors 'on the right' and 'on + * the left' are collinear with the same orientation. + */ + G1, + /** Continuity of the first derivative. The 'C1' curve is also 'G1' but, in + * addition, the tangent vectors 'on the right' and 'on the left' are equal. + */ + C1, + + /** For each point on the curve, the normalized normal vectors 'on the + * right' and 'on the left' are equal. + */ + G2, + + /// Continuity of the second derivative. + C2, + + /// Continuity of the third derivative. + C3, + + /** Continuity of the N-th derivative, whatever is the value given for N + * (infinite order of continuity). Also provides information about the + * continuity of a surface. + */ + CN, + }; + friend class TopoShapeCache; private: diff --git a/src/Mod/Part/App/TopoShapeMapper.cpp b/src/Mod/Part/App/TopoShapeMapper.cpp new file mode 100644 index 0000000000..89ea456a40 --- /dev/null +++ b/src/Mod/Part/App/TopoShapeMapper.cpp @@ -0,0 +1,75 @@ +#include "TopoShapeMapper.h" + +namespace Part +{ + +void ShapeMapper::expand(const TopoDS_Shape &d, std::vector &shapes) +{ + if (d.IsNull()) return; + for(TopExp_Explorer xp(d, TopAbs_FACE);xp.More();xp.Next()) + shapes.push_back(xp.Current()); + for(TopExp_Explorer xp(d, TopAbs_EDGE, TopAbs_FACE);xp.More();xp.Next()) + shapes.push_back(xp.Current()); + for(TopExp_Explorer xp(d, TopAbs_VERTEX, TopAbs_EDGE);xp.More();xp.Next()) + shapes.push_back(xp.Current()); +} + +void ShapeMapper::populate(bool generated, + const TopTools_ListOfShape &src, + const TopTools_ListOfShape &dst) +{ + for(TopTools_ListIteratorOfListOfShape it(src);it.More();it.Next()) + populate(generated, it.Value(), dst); +} + +void ShapeMapper::populate(bool generated, + const TopoShape &src, + const TopTools_ListOfShape &dst) +{ + if(src.isNull()) + return; + std::vector dstShapes; + for(TopTools_ListIteratorOfListOfShape it(dst);it.More();it.Next()) + expand(it.Value(), dstShapes); + insert(generated, src.getShape(), dstShapes); +} + +void ShapeMapper::insert(bool generated, const TopoDS_Shape &s, const TopoDS_Shape &d) +{ + if (s.IsNull() || d.IsNull()) return; + // Prevent an element shape from being both generated and modified + if (generated) { + if (_modifiedShapes.count(d)) + return; + _generatedShapes.insert(d); + } else { + if( _generatedShapes.count(d)) + return; + _modifiedShapes.insert(d); + } + auto &entry = generated?_generated[s]:_modified[s]; + if(entry.shapeSet.insert(d).second) + entry.shapes.push_back(d); +}; + +void ShapeMapper::insert(bool generated, const TopoDS_Shape &s, const std::vector &d) +{ + if (s.IsNull() || d.empty()) return; + auto &entry = generated?_generated[s]:_modified[s]; + for(auto &shape : d) { + // Prevent an element shape from being both generated and modified + if (generated) { + if (_modifiedShapes.count(shape)) + continue; + _generatedShapes.insert(shape); + } else { + if( _generatedShapes.count(shape)) + continue; + _modifiedShapes.insert(shape); + } + if(entry.shapeSet.insert(shape).second) + entry.shapes.push_back(shape); + } +}; + +} diff --git a/src/Mod/Part/App/TopoShapeMapper.h b/src/Mod/Part/App/TopoShapeMapper.h new file mode 100644 index 0000000000..736aebc21c --- /dev/null +++ b/src/Mod/Part/App/TopoShapeMapper.h @@ -0,0 +1,255 @@ +#include +#include +#include + +#include +#include +#include "TopoShape.h" + +class BRepBuilderAPI_MakeShape; +class BRepTools_History; +class BRepTools_ReShape; +class ShapeFix_Root; + +namespace Part +{ + +/// Shape hasher that ignore orientation +struct ShapeHasher { + inline size_t operator()(const TopoShape &s) const { + return std::hash {}(s.getShape()); + // return TopTools_ShapeMapHasher{}(s.getShape()); + } + inline size_t operator()(const TopoDS_Shape &s) const { + return std::hash {}(s); + } + inline bool operator()(const TopoShape &a, const TopoShape &b) const { + return a.getShape().IsSame(b.getShape()); + } + inline bool operator()(const TopoDS_Shape &a, const TopoDS_Shape &b) const { + return a.IsSame(b); + } + template + static inline void hash_combine(std::size_t& seed, const T& v) + { + // copied from boost::hash_combine + std::hash hasher; + seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); + } + inline size_t operator()(const std::pair &s) const { + size_t res = std::hash {}(s.first.getShape()); + hash_combine(res, std::hash {}(s.second.getShape())); + return res; + } + inline size_t operator()(const std::pair &s) const { + size_t res = std::hash {}(s.first); + hash_combine(res, std::hash {}(s.second)); + return res; + } + inline bool operator()(const std::pair &a, + const std::pair &b) const { + return a.first.getShape().IsSame(b.first.getShape()) + && a.second.getShape().IsSame(b.second.getShape()); + } + inline bool operator()(const std::pair &a, + const std::pair &b) const { + return a.first.IsSame(b.first) + && a.second.IsSame(b.second); + } +}; + +/** Shape mapper for generic BRepBuilderAPI_MakeShape derived class + * + * Uses BRepBuilderAPI_MakeShape::Modified/Generated() function to extract + * shape history for generating mapped element names + */ +struct PartExport MapperMaker: TopoShape::Mapper { + BRepBuilderAPI_MakeShape &maker; + MapperMaker(BRepBuilderAPI_MakeShape &maker) + :maker(maker) + {} + virtual const std::vector &modified(const TopoDS_Shape &s) const override; + virtual const std::vector &generated(const TopoDS_Shape &s) const override; +}; + +/** Shape mapper for BRepTools_History + * + * Uses BRepTools_History::Modified/Generated() function to extract + * shape history for generating mapped element names + */ +struct PartExport MapperHistory: TopoShape::Mapper { + Handle(BRepTools_History) history; + MapperHistory(const Handle(BRepTools_History) &history); + MapperHistory(const Handle(BRepTools_ReShape) &reshape); + MapperHistory(ShapeFix_Root &fix); + virtual const std::vector &modified(const TopoDS_Shape &s) const override; + virtual const std::vector &generated(const TopoDS_Shape &s) const override; +}; + +/** Shape mapper for user defined shape mapping + */ +struct PartExport ShapeMapper: TopoShape::Mapper { + virtual ~ShapeMapper() noexcept = default; + + /** Populate mapping from a source shape to a list of shape + * + * @param generated: whether the shape is generated + * @param src: source shape + * @param dst: a list of sub shapes in the new shape + * + * The source will be expanded into sub shapes of faces, edges and vertices + * before being inserted into the map. + */ + void populate(bool generated, const TopoShape &src, const TopTools_ListOfShape &dst); + /** Populate mapping from a source sub shape to a list of shape + * + * @param generated: whether the shape is generated + * @param src: a list of sub shapes in the source shape + * @param dst: a list of sub shapes in the new shape + * + * The source will be expanded into sub shapes of faces, edges and vertices + * before being inserted into the map. + */ + void populate(bool generated, const TopTools_ListOfShape &src, const TopTools_ListOfShape &dst); + + /** Populate mapping from a source sub shape to a list of shape + * + * @param generated: whether the shape is generated + * @param src: a list of sub shapes in the source shape + * @param dst: a list of sub shapes in the new shape + * + * The source will be expanded into sub shapes of faces, edges and vertices + * before being inserted into the map. + */ + void populate(bool generated, const std::vector &src, const std::vector &dst) + { + for(auto &s : src) + populate(generated,s,dst); + } + + /** Populate mapping from a source sub shape to a list of shape + * + * @param generated: whether the shape is generated + * @param src: a sub shape of the source shape + * @param dst: a list of sub shapes in the new shape + * + * The source will be expanded into sub shapes of faces, edges and vertices + * before being inserted into the map. + */ + void populate(bool generated, const TopoShape &src, const std::vector &dst) + { + if(src.isNull()) + return; + std::vector dstShapes; + for(auto &d : dst) + expand(d.getShape(), dstShapes); + insert(generated, src.getShape(), dstShapes); + } + + /** Expand a shape into faces, edges and vertices + * @params d: shape to expand + * @param shapes: output sub shapes of faces, edges and vertices + */ + void expand(const TopoDS_Shape &d, std::vector &shapes); + + /** Insert a map entry from a sub shape in the source to a list of sub shapes in the new shape + * + * @params generated: whether the sub shapes are generated or modified + * @param s: a sub shape in the source + * @param d: a list of sub shapes in the new shape + */ + void insert(bool generated, const TopoDS_Shape &s, const std::vector &d); + + /** Insert a map entry from a sub shape in the source to a sub shape in the new shape + * + * @params generated: whether the sub shapes are generated or modified + * @param s: a sub shape in the source + * @param d: a list of sub shapes in the new shape + */ + void insert(bool generated, const TopoDS_Shape &s, const TopoDS_Shape &d); + + virtual const std::vector &generated(const TopoDS_Shape &s) const override { + auto iter = _generated.find(s); + if(iter != _generated.end()) + return iter->second.shapes; + return _res; + } + + virtual const std::vector &modified(const TopoDS_Shape &s) const override { + auto iter = _modified.find(s); + if(iter != _modified.end()) + return iter->second.shapes; + return _res; + } + + std::vector shapes; + std::unordered_set shapeSet; + + struct ShapeValue { + std::vector shapes; + std::unordered_set shapeSet; + }; + typedef std::unordered_map ShapeMap; + ShapeMap _generated; + std::unordered_set _generatedShapes; + ShapeMap _modified; + std::unordered_set _modifiedShapes; +}; + +/// Parameters for TopoShape::makEFilledFace() +struct PartExport TopoShape::BRepFillingParams { + /** Optional initial surface to begin the construction of the surface for the filled face. + * + * It is useful if the surface resulting from construction for the + * algorithm is likely to be complex. The support surface of the face + * under construction is computed by a deformation of Surf which satisfies + * the given constraints. The set of bounding edges defines the wire of + * the face. If no initial surface is given, the algorithm computes it + * automatically. If the set of edges is not connected (Free constraint), + * missing edges are automatically computed. Important: the initial + * surface must have orthogonal local coordinates, i.e. partial + * derivatives dS/du and dS/dv must be orthogonal at each point of + * surface. If this condition breaks, distortions of resulting surface are + * possible + */ + TopoShape surface; + /** Optional map from input edge to continutity order. The default + * continuity order is TopoShape::Continuity::C0. + */ + std::unordered_map orders; + /// Optional map from input shape to face used as support + std::unordered_map supports; + /// Optional begin index to the input shapes to be used as the boundary of the filled face. + int boundary_begin = -1; + /// Optional end index (last index + 1) to the input shapes to be used as the boundary of the filled face. + int boundary_end = -1; + /// The energe minimizing criterion degree; + unsigned int degree = 3; + /// The number of points on the curve NbPntsOnCur + unsigned int ptsoncurve = 15; + /// The number of iterations NbIter + unsigned int numiter = 2; + /// The Boolean Anisotropie + bool anisotropy = false; + /// The 2D tolerance Tol2d + double tol2d = 1e-5; + /// The 3D tolerance Tol3d + double tol3d = 1e-4; + /// The angular tolerance TolAng + double tolG1 = 0.01; + /// The tolerance for curvature TolCur + double tolG2 = 0.1; + /// The highest polynomial degree MaxDeg + unsigned int maxdeg = 8; + /** The greatest number of segments MaxSeg. + * + * If the Boolean Anistropie is true, the algorithm's performance is better + * in cases where the ratio of the length U and the length V indicate a + * great difference between the two. In other words, when the surface is, + * for example, extremely long. + */ + unsigned int maxseg = 9; +}; + + +} From 51ee04ca34047f37fcda30f9936dd461ee42ca30 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Tue, 16 Jan 2024 19:13:29 -0500 Subject: [PATCH 024/135] Tests for transferred TopoShapeMapper objects --- tests/src/Mod/Part/App/CMakeLists.txt | 1 + tests/src/Mod/Part/App/TopoShapeMapper.cpp | 121 +++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 tests/src/Mod/Part/App/TopoShapeMapper.cpp diff --git a/tests/src/Mod/Part/App/CMakeLists.txt b/tests/src/Mod/Part/App/CMakeLists.txt index f2467f3b12..9c642e65e2 100644 --- a/tests/src/Mod/Part/App/CMakeLists.txt +++ b/tests/src/Mod/Part/App/CMakeLists.txt @@ -15,4 +15,5 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/TopoShape.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeCache.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeExpansion.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TopoShapeMapper.cpp ) diff --git a/tests/src/Mod/Part/App/TopoShapeMapper.cpp b/tests/src/Mod/Part/App/TopoShapeMapper.cpp new file mode 100644 index 0000000000..047dd1c3a9 --- /dev/null +++ b/tests/src/Mod/Part/App/TopoShapeMapper.cpp @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "gtest/gtest.h" +#include "src/App/InitApplication.h" +#include +#include "Mod/Part/App/TopoShapeMapper.h" + +#include +#include +#include +#include +#include +#include +#include + +// NOLINTBEGIN(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) + +class TopoShapeMapperTest: public ::testing::Test +{ +protected: + static void SetUpTestSuite() + { + tests::initApplication(); + } + + void SetUp() override + { + _docName = App::GetApplication().getUniqueDocumentName("test"); + App::GetApplication().newDocument(_docName.c_str(), "testUser"); + } + + void TearDown() override + { + App::GetApplication().closeDocument(_docName.c_str()); + } + +private: + std::string _docName; +}; + +TEST_F(TopoShapeMapperTest, shapeHasherTests) +{ + // Arrange + auto edge = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge(); + Part::TopoShape topoShape {edge}; + auto edge2 = BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge(); + Part::TopoShape topoShape2 {edge2}; + std::pair pair1(topoShape, topoShape2); + std::pair pair2(topoShape, topoShape); + std::pair pair3(edge, edge2); + std::pair pair4(edge, edge); + struct Part::ShapeHasher hasher; + + // Act + size_t hash1 = hasher(topoShape); + size_t hash2 = hasher(topoShape2); + size_t hash3 = hasher(edge); + size_t hash4 = hasher(edge2); + size_t hash5 = hasher(topoShape, topoShape); + size_t hash6 = hasher(topoShape, topoShape2); + size_t hash7 = hasher(edge, edge); + size_t hash8 = hasher(edge, edge2); + size_t hash9 = hasher(pair1); + size_t hash10 = hasher(pair2); + size_t hash11 = hasher(pair3); + size_t hash12 = hasher(pair4); + size_t hash13 = hasher(pair1, pair1); + size_t hash14 = hasher(pair1, pair2); + size_t hash15 = hasher(pair3, pair3); + size_t hash16 = hasher(pair3, pair4); + + // Assert + EXPECT_EQ(hash1, hash3); + EXPECT_EQ(hash2, hash4); + EXPECT_NE(hash1, hash2); + EXPECT_TRUE(hash5); + EXPECT_FALSE(hash6); + EXPECT_TRUE(hash7); + EXPECT_FALSE(hash8); + EXPECT_EQ(hash9, hash11); + EXPECT_EQ(hash10, hash12); + EXPECT_NE(hash9, hash10); + EXPECT_TRUE(hash13); + EXPECT_FALSE(hash14); + EXPECT_TRUE(hash15); + EXPECT_FALSE(hash16); +} + +TEST_F(TopoShapeMapperTest, mapperMakerTests) +{ + // How can this be tested? +} + +TEST_F(TopoShapeMapperTest, mapperHistoryTests) +{ + // How can this be tested? +} + +TEST_F(TopoShapeMapperTest, shapeMapperTests) +{ + // Arrange + auto mapper = Part::ShapeMapper(); + auto boxMaker1 = BRepPrimAPI_MakeBox(1.0, 1.0, 1.0); + boxMaker1.Build(); + auto box1 = boxMaker1.Shape(); + Part::TopoShape topoShape1 {box1}; + + // Act + auto e = topoShape1.getSubTopoShapes(TopAbs_EDGE); + mapper.populate(false, box1, {e[0], e[1], e[2], e[3]}); + mapper.populate(true, box1, {e[4], e[5], e[6]}); + std::vector vec1 = mapper.modified(box1); + std::vector vec2 = mapper.generated(box1); + + // Assert + EXPECT_EQ(vec1.size(), 4); + EXPECT_EQ(vec2.size(), 3); +} + + +// // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From 36451d1add8146a4e58f2ab50646dd618e1bcb03 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Tue, 16 Jan 2024 19:05:40 -0600 Subject: [PATCH 025/135] Tests/Toponaming: Add tests for ElementNameComparator This verifies the existing functionality, but does not alter it. Two tests are disabled because they represent cases that the current code does not handle correctly. They are edge cases that are not expected in real code. --- tests/src/App/MappedElement.cpp | 88 +++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/tests/src/App/MappedElement.cpp b/tests/src/App/MappedElement.cpp index 0ea03de6af..36588a8d92 100644 --- a/tests/src/App/MappedElement.cpp +++ b/tests/src/App/MappedElement.cpp @@ -127,3 +127,91 @@ TEST_F(MappedElementTest, lessThanOperator) EXPECT_FALSE(mappedElement2A < mappedElement1B); EXPECT_FALSE(mappedElement2B < mappedElement2BDuplicate); } + +TEST_F(MappedElementTest, comparatorBothAreZeroSize) +{ + // Arrange + Data::MappedName mappedName1 {""}; + Data::MappedName mappedName2 {""}; + auto comp = Data::ElementNameComparator(); + + // Act & Assert + EXPECT_FALSE(comp(mappedName1, mappedName2)); +} + +TEST_F(MappedElementTest, comparatorOneIsZeroSize) +{ + // Arrange + Data::MappedName mappedName1 {""}; + Data::MappedName mappedName2 {"#12345"}; + auto comp = Data::ElementNameComparator(); + + // Act & Assert + EXPECT_TRUE(comp(mappedName1, mappedName2)); +} + +TEST_F(MappedElementTest, comparatorBothStartWithHexDigitsThatDiffer) +{ + // Arrange + Data::MappedName mappedName1 {"#fed;B"}; + Data::MappedName mappedName2 {"#abcdef;A"}; + auto comp = Data::ElementNameComparator(); + + // Act & Assert + EXPECT_TRUE(comp(mappedName1, mappedName2)); +} + +TEST_F(MappedElementTest, comparatorBothStartWithTheSameHexDigits) +{ + // Arrange + Data::MappedName mappedName1 {"#12345;B"}; + Data::MappedName mappedName2 {"#12345;A"}; + auto comp = Data::ElementNameComparator(); + + // Act & Assert + EXPECT_FALSE(comp(mappedName1, mappedName2)); +} + +TEST_F(MappedElementTest, DISABLED_comparatorHexWithoutTerminatorIsBroken) +{ + // Arrange + Data::MappedName mappedName1 {"#fed"}; + Data::MappedName mappedName2 {"#abcdef"}; + auto comp = Data::ElementNameComparator(); + + // Act & Assert + EXPECT_FALSE(comp(mappedName1, mappedName2)); +} + +TEST_F(MappedElementTest, comparatorNoHexDigitsLexicalCompare) +{ + // Arrange + Data::MappedName mappedName1 {"A"}; + Data::MappedName mappedName2 {"B"}; + auto comp = Data::ElementNameComparator(); + + // Act & Assert + EXPECT_TRUE(comp(mappedName1, mappedName2)); +} + +TEST_F(MappedElementTest, comparatorNoHexDigitsSameStringNumericCompare) +{ + // Arrange + Data::MappedName mappedName1 {"Edge123456;"}; + Data::MappedName mappedName2 {"Edge321;"}; + auto comp = Data::ElementNameComparator(); + + // Act & Assert + EXPECT_FALSE(comp(mappedName1, mappedName2)); +} + +TEST_F(MappedElementTest, DISABLED_comparatorIntegerWithoutTerminatorIsBroken) +{ + // Arrange + Data::MappedName mappedName1 {"Edge123456"}; + Data::MappedName mappedName2 {"Edge321"}; + auto comp = Data::ElementNameComparator(); + + // Act & Assert + EXPECT_FALSE(comp(mappedName1, mappedName2)); +} From 160ad0dc8186b50a3e059fb87a8e8ade0526a6d7 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Tue, 16 Jan 2024 22:43:39 -0500 Subject: [PATCH 026/135] Restore RT hash code for OCC < 7.8.0 --- src/Mod/Part/App/TopoShapeMapper.h | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Mod/Part/App/TopoShapeMapper.h b/src/Mod/Part/App/TopoShapeMapper.h index 736aebc21c..f7f4a8e92a 100644 --- a/src/Mod/Part/App/TopoShapeMapper.h +++ b/src/Mod/Part/App/TopoShapeMapper.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include "TopoShape.h" @@ -17,11 +18,18 @@ namespace Part /// Shape hasher that ignore orientation struct ShapeHasher { inline size_t operator()(const TopoShape &s) const { +#if OCC_VERSION_HEX >= 0x070800 return std::hash {}(s.getShape()); - // return TopTools_ShapeMapHasher{}(s.getShape()); +#else + return s.getShape().HashCode(INT_MAX); +#endif } inline size_t operator()(const TopoDS_Shape &s) const { +#if OCC_VERSION_HEX >= 0x070800 return std::hash {}(s); +#else + return s.HashCode(INT_MAX); +#endif } inline bool operator()(const TopoShape &a, const TopoShape &b) const { return a.getShape().IsSame(b.getShape()); @@ -37,13 +45,23 @@ struct ShapeHasher { seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); } inline size_t operator()(const std::pair &s) const { +#if OCC_VERSION_HEX >= 0x070800 size_t res = std::hash {}(s.first.getShape()); hash_combine(res, std::hash {}(s.second.getShape())); +#else + size_t res = s.first.getShape().HashCode(INT_MAX); + hash_combine(res, s.second.getShape().HashCode(INT_MAX)); +#endif return res; } inline size_t operator()(const std::pair &s) const { +#if OCC_VERSION_HEX >= 0x070800 size_t res = std::hash {}(s.first); hash_combine(res, std::hash {}(s.second)); +#else + size_t res = s.first.HashCode(INT_MAX); + hash_combine(res, s.second.HashCode(INT_MAX)); +#endif return res; } inline bool operator()(const std::pair &a, From 744ef198665d4591b2417e0607ff2c8210a5a38c Mon Sep 17 00:00:00 2001 From: Roy-043 Date: Wed, 17 Jan 2024 11:00:40 +0100 Subject: [PATCH 027/135] Draft: Task panel checkboxes were repositioned by accident In a previous PR of mine I have reorganized some lines of code in DraftGui.py without realizing that this would reposition checkboxes. This PR fixes that. --- src/Mod/Draft/DraftGui.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Mod/Draft/DraftGui.py b/src/Mod/Draft/DraftGui.py index c41b38ae30..6974b3b1ca 100644 --- a/src/Mod/Draft/DraftGui.py +++ b/src/Mod/Draft/DraftGui.py @@ -399,16 +399,22 @@ class DraftToolBar: self.selectButton = self._pushbutton("selectButton", bl, icon='view-select') # update modes from parameters: - self.continueMode = params.get_param("ContinueMode") self.relativeMode = params.get_param("RelativeMode") self.globalMode = params.get_param("GlobalMode") self.fillmode = params.get_param("fillmode") + self.continueMode = params.get_param("ContinueMode") + + # Note: The order of the calls to self._checkbox() below controls + # the position of the checkboxes in the task panel. # update checkboxes with parameters and internal modes: - self.continueCmd = self._checkbox("continueCmd", self.layout, checked=self.continueMode) self.isRelative = self._checkbox("isRelative", self.layout, checked=self.relativeMode) self.isGlobal = self._checkbox("isGlobal", self.layout, checked=self.globalMode) self.hasFill = self._checkbox("hasFill", self.layout, checked=self.fillmode) + self.continueCmd = self._checkbox("continueCmd", self.layout, checked=self.continueMode) + + # update checkboxes without parameters and without internal modes: + self.occOffset = self._checkbox("occOffset", self.layout, checked=False) # update checkboxes with parameters but without internal modes: # self.isCopy is also updated in modUi ("CopyMode") and offsetUi ("OffsetCopyMode") @@ -419,9 +425,6 @@ class DraftToolBar: self.layout, checked=params.get_param("SubelementMode")) - # update checkboxes without parameters and without internal modes: - self.occOffset = self._checkbox("occOffset", self.layout, checked=False) - # spacer spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) From 0cb513f0840f013ce4f0faf87ffcc687beb6f261 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Wed, 17 Jan 2024 08:55:16 -0500 Subject: [PATCH 028/135] Move MapperMaker and MapperHistory out to future PR --- src/Mod/Part/App/TopoShapeMapper.h | 28 ---------------------- tests/src/Mod/Part/App/TopoShapeMapper.cpp | 12 +--------- 2 files changed, 1 insertion(+), 39 deletions(-) diff --git a/src/Mod/Part/App/TopoShapeMapper.h b/src/Mod/Part/App/TopoShapeMapper.h index f7f4a8e92a..89080ae46e 100644 --- a/src/Mod/Part/App/TopoShapeMapper.h +++ b/src/Mod/Part/App/TopoShapeMapper.h @@ -76,34 +76,6 @@ struct ShapeHasher { } }; -/** Shape mapper for generic BRepBuilderAPI_MakeShape derived class - * - * Uses BRepBuilderAPI_MakeShape::Modified/Generated() function to extract - * shape history for generating mapped element names - */ -struct PartExport MapperMaker: TopoShape::Mapper { - BRepBuilderAPI_MakeShape &maker; - MapperMaker(BRepBuilderAPI_MakeShape &maker) - :maker(maker) - {} - virtual const std::vector &modified(const TopoDS_Shape &s) const override; - virtual const std::vector &generated(const TopoDS_Shape &s) const override; -}; - -/** Shape mapper for BRepTools_History - * - * Uses BRepTools_History::Modified/Generated() function to extract - * shape history for generating mapped element names - */ -struct PartExport MapperHistory: TopoShape::Mapper { - Handle(BRepTools_History) history; - MapperHistory(const Handle(BRepTools_History) &history); - MapperHistory(const Handle(BRepTools_ReShape) &reshape); - MapperHistory(ShapeFix_Root &fix); - virtual const std::vector &modified(const TopoDS_Shape &s) const override; - virtual const std::vector &generated(const TopoDS_Shape &s) const override; -}; - /** Shape mapper for user defined shape mapping */ struct PartExport ShapeMapper: TopoShape::Mapper { diff --git a/tests/src/Mod/Part/App/TopoShapeMapper.cpp b/tests/src/Mod/Part/App/TopoShapeMapper.cpp index 047dd1c3a9..6f23f19b8d 100644 --- a/tests/src/Mod/Part/App/TopoShapeMapper.cpp +++ b/tests/src/Mod/Part/App/TopoShapeMapper.cpp @@ -86,16 +86,6 @@ TEST_F(TopoShapeMapperTest, shapeHasherTests) EXPECT_FALSE(hash16); } -TEST_F(TopoShapeMapperTest, mapperMakerTests) -{ - // How can this be tested? -} - -TEST_F(TopoShapeMapperTest, mapperHistoryTests) -{ - // How can this be tested? -} - TEST_F(TopoShapeMapperTest, shapeMapperTests) { // Arrange @@ -118,4 +108,4 @@ TEST_F(TopoShapeMapperTest, shapeMapperTests) } -// // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) +// NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From e5dcbf1f456afad4634fd4a7b1cc21bfdebb2847 Mon Sep 17 00:00:00 2001 From: Paddle Date: Wed, 17 Jan 2024 08:06:10 +0100 Subject: [PATCH 029/135] Sketcher: add isPeriodicBsplineCurve helper function --- src/Mod/Sketcher/Gui/Utils.cpp | 9 +++++++++ src/Mod/Sketcher/Gui/Utils.h | 1 + 2 files changed, 10 insertions(+) diff --git a/src/Mod/Sketcher/Gui/Utils.cpp b/src/Mod/Sketcher/Gui/Utils.cpp index dc76c2ae1c..2c8af981d8 100644 --- a/src/Mod/Sketcher/Gui/Utils.cpp +++ b/src/Mod/Sketcher/Gui/Utils.cpp @@ -88,6 +88,15 @@ bool Sketcher::isBSplineCurve(const Part::Geometry& geom) return geom.is(); } +bool Sketcher::isPeriodicBSplineCurve(const Part::Geometry& geom) +{ + if (geom.is()) { + auto* spline = static_cast(&geom); + return spline->isPeriodic(); + } + return false; +} + bool Sketcher::isPoint(const Part::Geometry& geom) { return geom.is(); diff --git a/src/Mod/Sketcher/Gui/Utils.h b/src/Mod/Sketcher/Gui/Utils.h index 09f984110a..07d6a6acff 100644 --- a/src/Mod/Sketcher/Gui/Utils.h +++ b/src/Mod/Sketcher/Gui/Utils.h @@ -61,6 +61,7 @@ bool isLineSegment(const Part::Geometry&); bool isArcOfHyperbola(const Part::Geometry&); bool isArcOfParabola(const Part::Geometry&); bool isBSplineCurve(const Part::Geometry&); +bool isPeriodicBSplineCurve(const Part::Geometry&); bool isPoint(const Part::Geometry&); bool isCircleOrArc(const Part::Geometry& geo); From ec7e3ea364cbb0b3ebdca74dc584848e3326fd6e Mon Sep 17 00:00:00 2001 From: Paddle Date: Sun, 7 Jan 2024 21:26:19 +0100 Subject: [PATCH 030/135] Sketcher: Define double click on edges : Select the whole wire. --- src/Mod/Sketcher/Gui/ViewProviderSketch.cpp | 78 ++++++++++++++++++++- src/Mod/Sketcher/Gui/ViewProviderSketch.h | 3 + 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index ca815cdee5..dd3b963bc0 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -937,8 +937,9 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe -16000, -16000);// certainly far away from any clickable place, to avoid // re-trigger of double-click if next click happens fast. - - Mode = STATUS_NONE; + if (Mode != STATUS_SELECT_Wire) { + Mode = STATUS_NONE; + } } else { DoubleClick::prvClickTime = SbTime::getTimeOfDay(); @@ -1031,6 +1032,11 @@ bool ViewProviderSketch::mouseButtonPressed(int Button, bool pressed, const SbVe } Mode = STATUS_NONE; return true; + case STATUS_SELECT_Wire: { + toggleWireSelelection(preselection.PreselectCurve); + Mode = STATUS_NONE; + return true; + } case STATUS_SELECT_Constraint: if (pp) { auto sels = preselection.PreselectConstraintSet; @@ -1251,7 +1257,9 @@ void ViewProviderSketch::editDoubleClicked() Base::Console().Log("double click point:%d\n", preselection.PreselectPoint); } else if (preselection.isPreselectCurveValid()) { - Base::Console().Log("double click edge:%d\n", preselection.PreselectCurve); + // We cannot do toggleWireSelelection directly here because the released event with + //STATUS_NONE return false which clears the selection. + Mode = STATUS_SELECT_Wire; } else if (preselection.isCrossPreselected()) { Base::Console().Log("double click cross:%d\n", @@ -1278,6 +1286,70 @@ void ViewProviderSketch::editDoubleClicked() } } +void ViewProviderSketch::toggleWireSelelection(int clickedGeoId) +{ + Sketcher::SketchObject* obj = getSketchObject(); + + const Part::Geometry* geo1 = obj->getGeometry(clickedGeoId); + if (isPoint(*geo1) || isCircle(*geo1) || isEllipse(*geo1) || isPeriodicBSplineCurve(*geo1)) { + return; + } + + const char* type1 = (clickedGeoId >= 0) ? "Edge" : "ExternalEdge"; + std::stringstream ss1; + ss1 << type1 << clickedGeoId + 1; + bool selecting = isSelected(ss1.str()); + + std::vector connectedEdges = { clickedGeoId }; + bool partHasBeenAdded = true; + while (partHasBeenAdded) { + partHasBeenAdded = false; + for (int geoId = 0; geoId <= obj->getHighestCurveIndex(); geoId++) { + if (geoId == clickedGeoId || std::find(connectedEdges.begin(), connectedEdges.end(), geoId) != connectedEdges.end()) { + continue; + } + + const Part::Geometry* geo = obj->getGeometry(geoId); + if (isPoint(*geo) || isCircle(*geo) || isEllipse(*geo) || isPeriodicBSplineCurve(*geo1)) { + continue; + } + + Base::Vector3d p11 = obj->getPoint(geoId, PointPos::start); + Base::Vector3d p12 = obj->getPoint(geoId, PointPos::end); + bool connected = false; + for (auto conGeoId : connectedEdges) { + Base::Vector3d p21 = obj->getPoint(conGeoId, PointPos::start); + Base::Vector3d p22 = obj->getPoint(conGeoId, PointPos::end); + if ((p11 - p21).Length() < Precision::Confusion() + || (p11 - p22).Length() < Precision::Confusion() + || (p12 - p21).Length() < Precision::Confusion() + || (p12 - p22).Length() < Precision::Confusion()) { + connected = true; + } + } + + if (connected) { + connectedEdges.push_back(geoId); + partHasBeenAdded = true; + break; + } + } + } + + for (auto geoId : connectedEdges) { + std::stringstream ss; + const char* type = (geoId >= 0) ? "Edge" : "ExternalEdge"; + ss << type << geoId + 1; + if (!selecting && isSelected(ss.str())) { + rmvSelection(ss.str()); + } + else if (selecting && !isSelected(ss.str())) { + addSelection2(ss.str()); + } + } + +} + bool ViewProviderSketch::mouseMove(const SbVec2s& cursorPos, Gui::View3DInventorViewer* viewer) { // maximum radius for mouse moves when selecting a geometry before switching to drag mode diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.h b/src/Mod/Sketcher/Gui/ViewProviderSketch.h index 71f770aec7..e8e16c5c08 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.h +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.h @@ -549,6 +549,7 @@ public: STATUS_SELECT_Edge, /**< enum value an edge was selected. */ STATUS_SELECT_Constraint, /**< enum value a constraint was selected. */ STATUS_SELECT_Cross, /**< enum value the base coordinate system was selected. */ + STATUS_SELECT_Wire, /**< enum value and edge was double clicked. */ STATUS_SKETCH_DragPoint, /**< enum value while dragging a point. */ STATUS_SKETCH_DragCurve, /**< enum value while dragging a curve. */ STATUS_SKETCH_DragConstraint, /**< enum value while dragging a compatible constraint. */ @@ -720,6 +721,8 @@ protected: void deactivateHandler(); /// get called if a subelement is double clicked while editing void editDoubleClicked(); + /// get called when an edge is double clicked to select/unselect the whole wire + void toggleWireSelelection(int geoId); //@} /** @name Solver Information */ From 60ae251b61b2d8554933d99ca004b41279207f62 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Sun, 14 Jan 2024 12:19:58 -0500 Subject: [PATCH 031/135] Initial code of mapSubElement Test --- src/Mod/Part/App/TopoShape.h | 2 +- tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 87 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index ad4ef2a780..61a63bca40 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -609,7 +609,7 @@ public: void copyElementMap(const TopoShape & topoShape, const char *op=nullptr); bool canMapElement(const TopoShape &other) const; void mapSubElement(const TopoShape &other,const char *op=nullptr, bool forceHasher=false); - void mapSubElement(const std::vector &shapes, const char *op); + void mapSubElement(const std::vector &shapes, const char *op=nullptr); bool hasPendingElementMap() const; diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index 964f346f88..360898d323 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -161,4 +161,91 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoCubes) // 26 subshapes each } + +TEST_F(TopoShapeExpansionTest, mapSubElement) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + Part::TopoShape cube1TS {cube1}; + cube1TS.Tag = 1; + Part::TopoShape cube2TS {cube2}; + cube2TS.Tag = 2; + auto [cube3, cube4] = CreateTwoCubes(); + Part::TopoShape cube3TS {cube3}; + cube3TS.Tag = 3; + Part::TopoShape cube4TS {cube4}; + cube4TS.Tag = 4; + std::vector subShapes = cube1TS.getSubTopoShapes(TopAbs_FACE); + Part::TopoShape face1 = subShapes.front(); + face1.Tag = 3; + Part::TopoShape topoShape, topoShape1; + + // Act + int fs1 = topoShape.findShape(cube1TS.getShape()); + topoShape.setShape(cube1TS); + // cube1TS.mapSubElement(face1); // This throws an exception. Is that right? + // The cache ancestry only works on TopAbs_SHAPE so this is likely an invalid call, + // but do we defend against it or expect the exception? + + topoShape.mapSubElement( + cube1TS); // Once we do this map, it's in the ancestry cache forevermore + int fs2 = topoShape.findShape(cube1TS.getShape()); + int fs3 = topoShape.findShape(face1.getShape()); + int size1 = topoShape.getElementMap().size(); + int size0 = cube1TS.getElementMap().size(); + + // Assert + EXPECT_EQ(fs1, 0); + EXPECT_EQ(fs2, 1); + EXPECT_EQ(fs3, 1); + EXPECT_EQ(0, size0); + EXPECT_EQ(26, size1); + + // Act + topoShape.setShape(TopoDS_Shape()); + int fs4 = topoShape.findShape(cube1TS.getShape()); + topoShape.setShape(cube1, true); + // No mapSubElement required, it keeps finding it now + int fs5 = topoShape.findShape(cube1TS.getShape()); + topoShape.setShape(cube1, false); + int fs6 = topoShape.findShape(cube1TS.getShape()); + // Assert + EXPECT_EQ(fs4, 0); + EXPECT_EQ(fs5, 1); + EXPECT_EQ(fs6, 1); + + // Act + Part::TopoShape topoShape2, topoShape3; + topoShape2.setShape(cube2TS); + topoShape2.mapSubElement(cube2TS, nullptr, true); + int fs7 = topoShape2.findShape(cube2TS.getShape()); + int fs8 = topoShape2.findShape(face1.getShape()); + + topoShape3.setShape(cube3TS); + topoShape3.mapSubElement(cube3TS, "Sample", true); + int fs9 = topoShape3.findShape(cube3TS.getShape()); + int fs10 = topoShape3.findShape(face1.getShape()); + + topoShape.makeElementCompound({cube1TS, cube2TS}); + int fs11 = topoShape.findShape(cube2TS.getShape()); + int size2 = topoShape.getElementMap().size(); + // Assert + EXPECT_EQ(fs7, 1); + EXPECT_EQ(fs8, 0); + EXPECT_EQ(fs9, 1); + EXPECT_EQ(fs10, 0); + EXPECT_EQ(fs11, 2); + EXPECT_EQ(52, size2); + + // Act + topoShape2.makeElementCompound({cube3TS, cube4TS}); + topoShape2.mapSubElement(cube3TS, nullptr, true); + topoShape3.makeElementCompound({topoShape, topoShape2}); + int fs12 = topoShape3.findShape(cube4TS.getShape()); + int size4 = topoShape3.getElementMap().size(); + // Assert + EXPECT_EQ(104, size4); + EXPECT_EQ(fs12, 4); +} + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From 56bf4e7ebdb258b61eadd3563de6afbd3aaca6f1 Mon Sep 17 00:00:00 2001 From: Paddle Date: Wed, 17 Jan 2024 17:00:55 +0100 Subject: [PATCH 032/135] Core : Tree: Add 'Properties' action in contextual menu. Opens a property dialog. --- src/Gui/PropertyView.cpp | 6 ++--- src/Gui/PropertyView.h | 2 +- src/Gui/Tree.cpp | 27 +++++++++++++++++++++++ src/Gui/Tree.h | 2 ++ src/Gui/propertyeditor/PropertyEditor.cpp | 6 ++++- src/Gui/propertyeditor/PropertyEditor.h | 2 +- 6 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/Gui/PropertyView.cpp b/src/Gui/PropertyView.cpp index 535f54fa2c..fb0dda47d1 100644 --- a/src/Gui/PropertyView.cpp +++ b/src/Gui/PropertyView.cpp @@ -67,7 +67,7 @@ static ParameterGrp::handle _GetParam() { * Provides two Gui::PropertyEditor::PropertyEditor widgets, for "View" and "Data", * in two tabs. */ -PropertyView::PropertyView(QWidget *parent) +PropertyView::PropertyView(QWidget *parent, int sizeOfFirstColumn) : QWidget(parent), SelectionObserver(false, ResolveMode::NoResolve) { auto pLayout = new QGridLayout( this ); @@ -83,12 +83,12 @@ PropertyView::PropertyView(QWidget *parent) tabs->setTabPosition(QTabWidget::South); pLayout->addWidget(tabs, 0, 0); - propertyEditorView = new Gui::PropertyEditor::PropertyEditor(); + propertyEditorView = new Gui::PropertyEditor::PropertyEditor(parent, sizeOfFirstColumn); propertyEditorView->setAutomaticDocumentUpdate(_GetParam()->GetBool("AutoTransactionView", false)); propertyEditorView->setAutomaticExpand(_GetParam()->GetBool("AutoExpandView", false)); tabs->addTab(propertyEditorView, tr("View")); - propertyEditorData = new Gui::PropertyEditor::PropertyEditor(); + propertyEditorData = new Gui::PropertyEditor::PropertyEditor(parent, sizeOfFirstColumn); propertyEditorData->setAutomaticDocumentUpdate(_GetParam()->GetBool("AutoTransactionData", true)); propertyEditorData->setAutomaticExpand(_GetParam()->GetBool("AutoExpandData", false)); tabs->addTab(propertyEditorData, tr("Data")); diff --git a/src/Gui/PropertyView.h b/src/Gui/PropertyView.h index a058fcd04e..cf539df318 100644 --- a/src/Gui/PropertyView.h +++ b/src/Gui/PropertyView.h @@ -56,7 +56,7 @@ class PropertyView : public QWidget, public Gui::SelectionObserver Q_OBJECT public: - explicit PropertyView(QWidget *parent=nullptr); + explicit PropertyView(QWidget *parent=nullptr, int sizeOfFirstColumn = 0); ~PropertyView() override; Gui::PropertyEditor::PropertyEditor* propertyEditorView; diff --git a/src/Gui/Tree.cpp b/src/Gui/Tree.cpp index 4a657680d4..3b2cc0717c 100644 --- a/src/Gui/Tree.cpp +++ b/src/Gui/Tree.cpp @@ -61,6 +61,7 @@ #include "Macro.h" #include "MainWindow.h" #include "MenuManager.h" +#include "PropertyView.h" #include "TreeParams.h" #include "View3DInventor.h" #include "ViewProviderDocumentObject.h" @@ -523,6 +524,13 @@ TreeWidget::TreeWidget(const char* name, QWidget* parent) connect(this->relabelObjectAction, &QAction::triggered, this, &TreeWidget::onRelabelObject); + this->objectPropertyAction = new QAction(this); +#ifndef Q_OS_MAC + this->objectPropertyAction->setShortcut(Qt::Key_F6); +#endif + connect(this->objectPropertyAction, &QAction::triggered, + this, &TreeWidget::onObjectProperty); + this->finishEditingAction = new QAction(this); connect(this->finishEditingAction, &QAction::triggered, this, &TreeWidget::onFinishEditing); @@ -961,6 +969,7 @@ void TreeWidget::contextMenuEvent(QContextMenuEvent* e) // relabeling is only possible for a single selected document if (SelectedObjectsList.size() == 1) contextMenu.addAction(this->relabelObjectAction); + contextMenu.addAction(this->objectPropertyAction); auto selItems = this->selectedItems(); // if only one item is selected, setup the edit menu @@ -1082,6 +1091,21 @@ void TreeWidget::onRelabelObject() editItem(item); } +void TreeWidget::onObjectProperty() +{ + int sizeOfFirstColumn = 200; + auto prop = new PropertyView(this, sizeOfFirstColumn); + QDialog* propertyDialog = new QDialog(this); + propertyDialog->setWindowTitle(QString::fromLatin1("Properties")); + propertyDialog->resize(700, 500); + QVBoxLayout* layout = new QVBoxLayout(propertyDialog); + layout->addWidget(prop); + propertyDialog->setLayout(layout); + QPoint cursorPos = QCursor::pos() - QPoint(0, 300); + propertyDialog->move(cursorPos); + propertyDialog->show(); +} + void TreeWidget::onStartEditing() { auto action = qobject_cast(sender()); @@ -2965,6 +2989,9 @@ void TreeWidget::setupText() this->relabelObjectAction->setText(tr("Rename")); this->relabelObjectAction->setStatusTip(tr("Rename object")); + this->objectPropertyAction->setText(tr("Properties")); + this->objectPropertyAction->setStatusTip(tr("Properties of the selected object")); + this->finishEditingAction->setText(tr("Finish editing")); this->finishEditingAction->setStatusTip(tr("Finish editing object")); diff --git a/src/Gui/Tree.h b/src/Gui/Tree.h index 6d35edfec2..877cddf664 100644 --- a/src/Gui/Tree.h +++ b/src/Gui/Tree.h @@ -154,6 +154,7 @@ protected: protected Q_SLOTS: void onCreateGroup(); void onRelabelObject(); + void onObjectProperty(); void onActivateDocument(QAction*); void onStartEditing(); void onFinishEditing(); @@ -208,6 +209,7 @@ private: private: QAction* createGroupAction; QAction* relabelObjectAction; + QAction* objectPropertyAction; QAction* finishEditingAction; QAction* selectDependentsAction; QAction* skipRecomputeAction; diff --git a/src/Gui/propertyeditor/PropertyEditor.cpp b/src/Gui/propertyeditor/PropertyEditor.cpp index b0b0e3489b..184d0420dd 100644 --- a/src/Gui/propertyeditor/PropertyEditor.cpp +++ b/src/Gui/propertyeditor/PropertyEditor.cpp @@ -51,7 +51,7 @@ FC_LOG_LEVEL_INIT("PropertyView", true, true) using namespace Gui::PropertyEditor; -PropertyEditor::PropertyEditor(QWidget *parent) +PropertyEditor::PropertyEditor(QWidget *parent, int sizeOfFirstColumn) : QTreeView(parent) , autoexpand(false) , autoupdate(false) @@ -99,6 +99,10 @@ PropertyEditor::PropertyEditor(QWidget *parent) setHeaderHidden(true); viewport()->installEventFilter(this); viewport()->setMouseTracking(true); + + if (sizeOfFirstColumn != 0) { + header()->resizeSection(0, sizeOfFirstColumn); + } } PropertyEditor::~PropertyEditor() diff --git a/src/Gui/propertyeditor/PropertyEditor.h b/src/Gui/propertyeditor/PropertyEditor.h index bd01c2f99e..5c5342d129 100644 --- a/src/Gui/propertyeditor/PropertyEditor.h +++ b/src/Gui/propertyeditor/PropertyEditor.h @@ -67,7 +67,7 @@ class PropertyEditor : public QTreeView Q_PROPERTY(QBrush itemBackground READ itemBackground WRITE setItemBackground DESIGNABLE true SCRIPTABLE true) // clazy:exclude=qproperty-without-notify public: - PropertyEditor(QWidget *parent = nullptr); + PropertyEditor(QWidget *parent = nullptr, int sizeOfFirstColumn = 0); ~PropertyEditor() override; /** Builds up the list view with the properties. */ From 0e53aafa8651ac9e9cd66016bbcf6afabaae510e Mon Sep 17 00:00:00 2001 From: wandererfan Date: Tue, 16 Jan 2024 21:00:53 -0500 Subject: [PATCH 033/135] [Import]fix rounding errors in dxf export --- src/Mod/Import/App/dxf/dxf.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Mod/Import/App/dxf/dxf.cpp b/src/Mod/Import/App/dxf/dxf.cpp index cbde2326a9..5d9920620a 100644 --- a/src/Mod/Import/App/dxf/dxf.cpp +++ b/src/Mod/Import/App/dxf/dxf.cpp @@ -63,6 +63,10 @@ CDxfWrite::CDxfWrite(const char* filepath) return; } m_ofs->imbue(std::locale("C")); + + // use lots of digits to avoid rounding errors + m_ssEntity->setf(std::ios::fixed); + m_ssEntity->precision(9); } CDxfWrite::~CDxfWrite() From 8b888814e6e540b82621406e0634ef06e555089e Mon Sep 17 00:00:00 2001 From: Paddle Date: Wed, 17 Jan 2024 10:27:17 +0100 Subject: [PATCH 034/135] Sketcher: bspline: fix ESC exit tool --- src/Mod/Sketcher/Gui/DrawSketchHandlerBSpline.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerBSpline.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerBSpline.h index 65ab45d998..080ffac50b 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerBSpline.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerBSpline.h @@ -331,6 +331,9 @@ public: } // TODO: On pressing, say, W, modify last pole's weight // TODO: On pressing, say, M, modify next knot's multiplicity + else { + DrawSketchHandler::registerPressedKey(pressed, key); + } return; } From af76942a0bca0c8fe5355cc14b54f7aaf8edd2f1 Mon Sep 17 00:00:00 2001 From: Paddle Date: Wed, 17 Jan 2024 10:27:50 +0100 Subject: [PATCH 035/135] Sketcher: polyline: fix ESC exit tool --- src/Mod/Sketcher/Gui/DrawSketchHandlerLineSet.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerLineSet.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerLineSet.h index 299f196d96..54a9fce65b 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerLineSet.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerLineSet.h @@ -98,11 +98,8 @@ public: void registerPressedKey(bool pressed, int key) override { - if (Mode != STATUS_SEEK_Second) { - return; // SegmentMode can be changed only in STATUS_SEEK_Second mode - } - - if (key == SoKeyboardEvent::M && pressed && previousCurve != -1) { + if (Mode == STATUS_SEEK_Second && key == SoKeyboardEvent::M && pressed + && previousCurve != -1) { // loop through the following modes: // SEGMENT_MODE_Line, TRANSITION_MODE_Free / TRANSITION_MODE_Tangent // SEGMENT_MODE_Line, TRANSITION_MODE_Perpendicular_L @@ -184,6 +181,9 @@ public: } mouseMove(onSketchPos); // trigger an update of EditCurve } + else { + DrawSketchHandler::registerPressedKey(pressed, key); + } } void mouseMove(Base::Vector2d onSketchPos) override From e425b5b2db4a5d4de44d698a372b47055bc5b67d Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Wed, 17 Jan 2024 20:38:50 -0500 Subject: [PATCH 036/135] Toponaming move makEFace as makeElementFace and dependencies --- src/App/ComplexGeoData.h | 4 + src/App/MappedElement.cpp | 109 +++++++++++++ src/App/MappedElement.h | 17 ++ src/Mod/Part/App/FaceMaker.cpp | 108 ++++++++++++- src/Mod/Part/App/FaceMaker.h | 35 ++-- src/Mod/Part/App/FeatureExtrusion.cpp | 6 +- src/Mod/Part/App/TopoShape.h | 80 ++++++++- src/Mod/Part/App/TopoShapeExpansion.cpp | 206 ++++++++++++++++++++++++ 8 files changed, 543 insertions(+), 22 deletions(-) diff --git a/src/App/ComplexGeoData.h b/src/App/ComplexGeoData.h index 7bf68b3bea..d5630c3560 100644 --- a/src/App/ComplexGeoData.h +++ b/src/App/ComplexGeoData.h @@ -247,6 +247,10 @@ public: } // NOTE: getElementHistory is now in ElementMap + long getElementHistory(const MappedName & name, + MappedName *original=nullptr, std::vector *history=nullptr) const { + return _elementMap->getElementHistory(name, Tag, original, history); + }; void setMappedChildElements(const std::vector & children); std::vector getMappedChildElements() const; diff --git a/src/App/MappedElement.cpp b/src/App/MappedElement.cpp index 14eb6f99ba..41a99f2e02 100644 --- a/src/App/MappedElement.cpp +++ b/src/App/MappedElement.cpp @@ -20,5 +20,114 @@ * * **************************************************************************************************/ +// NOLINTNEXTLINE #include "PreCompiled.h" + +#ifndef _PreComp_ +# include +# include +#endif + #include "MappedElement.h" + +using namespace Data; + +bool ElementNameComp::operator()(const MappedName &a, const MappedName &b) const { + size_t size = std::min(a.size(),b.size()); + if(!size) + return a.size()bc) + res = 1; + } + }else if(std::isxdigit(ac)) + return false; + else + break; + } + if(res < 0) + return true; + else if(res > 0) + return false; + + for (; i bc) + return false; + } + return a.size()bc) + return false; + } else if(!std::isdigit(ac)) { + return false; + } else + break; + } + + // Then compare the following digits part by integer value + int res = 0; + for(;ibc) + res = 1; + } + }else if(std::isdigit(ac)) + return false; + else + break; + } + if(res < 0) + return true; + else if(res > 0) + return false; + + // Finally, compare the remaining tail using lexical order + for (; i bc) + return false; + } + return a.size() #include "FaceMaker.h" +#include #include "TopoShape.h" +#include "TopoShapeOpCode.h" TYPESYSTEM_SOURCE_ABSTRACT(Part::FaceMaker, Base::BaseClass) @@ -46,6 +48,11 @@ void Part::FaceMaker::addWire(const TopoDS_Wire& w) void Part::FaceMaker::addShape(const TopoDS_Shape& sh) { + addTopoShape(sh); +} + +void Part::FaceMaker::addTopoShape(const TopoShape& shape) { + const TopoDS_Shape &sh = shape.getShape(); if(sh.IsNull()) throw Base::ValueError("Input shape is null."); switch(sh.ShapeType()){ @@ -58,11 +65,14 @@ void Part::FaceMaker::addShape(const TopoDS_Shape& sh) case TopAbs_EDGE: this->myWires.push_back(BRepBuilderAPI_MakeWire(TopoDS::Edge(sh)).Wire()); break; + case TopAbs_FACE: + this->myInputFaces.push_back(sh); + break; default: throw Base::TypeError("Shape must be a wire, edge or compound. Something else was supplied."); break; } - this->mySourceShapes.push_back(sh); + this->mySourceShapes.push_back(shape); } void Part::FaceMaker::useCompound(const TopoDS_Compound& comp) @@ -73,14 +83,29 @@ void Part::FaceMaker::useCompound(const TopoDS_Compound& comp) } } +void Part::FaceMaker::useTopoCompound(const TopoShape& comp) +{ + for(auto &s : comp.getSubTopoShapes()) + this->addTopoShape(s); +} + const TopoDS_Face& Part::FaceMaker::Face() { - const TopoDS_Shape &sh = this->Shape(); - if(sh.IsNull()) + return TopoDS::Face(TopoFace().getShape()); +} + +const Part::TopoShape &Part::FaceMaker::TopoFace() const{ + if(this->myTopoShape.isNull()) throw NullShapeException("Part::FaceMaker: result shape is null."); - if (sh.ShapeType() != TopAbs_FACE) + if (this->myTopoShape.getShape().ShapeType() != TopAbs_FACE) throw Base::TypeError("Part::FaceMaker: return shape is not a single face."); - return TopoDS::Face(sh); + return this->myTopoShape; +} + +const Part::TopoShape &Part::FaceMaker::getTopoShape() const{ + if(this->myTopoShape.isNull()) + throw NullShapeException("Part::FaceMaker: result shape is null."); + return this->myTopoShape; } #if OCC_VERSION_HEX >= 0x070600 @@ -90,7 +115,7 @@ void Part::FaceMaker::Build() #endif { this->NotDone(); - this->myShapesToReturn.clear(); + this->myShapesToReturn = this->myInputFaces; this->myGenerated.Clear(); this->Build_Essence();//adds stuff to myShapesToReturn @@ -118,6 +143,7 @@ void Part::FaceMaker::Build() if(this->myShapesToReturn.empty()){ //nothing to do, null shape will be returned. + this->myShape = TopoDS_Shape(); } else if (this->myShapesToReturn.size() == 1){ this->myShape = this->myShapesToReturn[0]; } else { @@ -129,6 +155,72 @@ void Part::FaceMaker::Build() } this->myShape = cmp_res; } + + postBuild(); +} + +struct ElementName { + long tag; + Data::MappedName name; + Data::ElementIDRefs sids; + + ElementName(long t, const Data::MappedName &n, const Data::ElementIDRefs &sids) + :tag(t),name(n), sids(sids) + {} + + inline bool operator<(const ElementName &other) const { + if(tagother.tag) + return false; + return Data::ElementNameComp()(name,other.name); + } +}; + +void Part::FaceMaker::postBuild() { + this->myTopoShape.setShape(this->myShape); + this->myTopoShape.Hasher = this->MyHasher; + this->myTopoShape.mapSubElement(this->mySourceShapes); + int i = 0; + const char *op = this->MyOp; + if(!op) + op = Part::OpCodes::Face; + const auto &faces = this->myTopoShape.getSubTopoShapes(TopAbs_FACE); + // name the face using the edges of its outer wire + for(auto &face : faces) { + ++i; + TopoShape wire = face.splitWires(); + wire.mapSubElement(face); + std::set edgeNames; + int count = wire.countSubShapes(TopAbs_EDGE); + for(int i=1;i<=count;++i) { + Data::ElementIDRefs sids; + Data::MappedName name = face.getMappedName( + Data::IndexedName::fromConst("Edge",i), false, &sids); + if(!name) + continue; + edgeNames.emplace(wire.getElementHistory(name),name,sids); + } + if(edgeNames.empty()) + continue; + + std::vector names; + Data::ElementIDRefs sids; +#if 0 + for (auto &e : edgeNames) { + names.insert(e.name); + sids += e.sids; + } +#else + // We just use the first source element name to make the face name more + // stable + names.push_back(edgeNames.begin()->name); + sids = edgeNames.begin()->sids; +#endif + this->myTopoShape.setElementComboName( + Data::IndexedName::fromConst("Face",i),names,op,nullptr,&sids); + } + this->myTopoShape.initCache(true); this->Done(); } @@ -172,12 +264,12 @@ TYPESYSTEM_SOURCE(Part::FaceMakerSimple, Part::FaceMakerPublic) std::string Part::FaceMakerSimple::getUserFriendlyName() const { - return {QT_TRANSLATE_NOOP("Part_FaceMaker","Simple")}; + return std::string(QT_TRANSLATE_NOOP("Part_FaceMaker","Simple")); } std::string Part::FaceMakerSimple::getBriefExplanation() const { - return {QT_TRANSLATE_NOOP("Part_FaceMaker","Makes separate plane face from every wire independently. No support for holes; wires can be on different planes.")}; + return std::string(QT_TRANSLATE_NOOP("Part_FaceMaker","Makes separate plane face from every wire independently. No support for holes; wires can be on different planes.")); } void Part::FaceMakerSimple::Build_Essence() diff --git a/src/Mod/Part/App/FaceMaker.h b/src/Mod/Part/App/FaceMaker.h index aaf289c2a0..3f80639165 100644 --- a/src/Mod/Part/App/FaceMaker.h +++ b/src/Mod/Part/App/FaceMaker.h @@ -33,6 +33,8 @@ #include #include +#include +#include "TopoShape.h" namespace Part { @@ -47,11 +49,16 @@ namespace Part */ class PartExport FaceMaker: public BRepBuilderAPI_MakeShape, public Base::BaseClass { - TYPESYSTEM_HEADER_WITH_OVERRIDE(); + TYPESYSTEM_HEADER(); public: - FaceMaker() = default; - ~FaceMaker() override = default; + FaceMaker() {} + virtual ~FaceMaker() {} + + void addTopoShape(const TopoShape &s); + void useTopoCompound(const TopoShape &comp); + const TopoShape &getTopoShape() const; + const TopoShape &TopoFace() const; virtual void addWire(const TopoDS_Wire& w); /** @@ -69,6 +76,8 @@ public: */ virtual void useCompound(const TopoDS_Compound &comp); + virtual void setPlane(const gp_Pln &) {} + /** * @brief Face: returns the face (result). If result is not a single face, * throws Base::TypeError. (hint: use .Shape() instead) @@ -77,9 +86,9 @@ public: virtual const TopoDS_Face& Face(); #if OCC_VERSION_HEX >= 0x070600 - void Build(const Message_ProgressRange& theRange = Message_ProgressRange()) override; + virtual void Build(const Message_ProgressRange& theRange = Message_ProgressRange()); #else - void Build() override; + virtual void Build(); #endif //fails to compile, huh! @@ -90,11 +99,16 @@ public: static std::unique_ptr ConstructFromType(const char* className); static std::unique_ptr ConstructFromType(Base::Type type); + const char *MyOp = 0; + App::StringHasherRef MyHasher; + protected: - std::vector mySourceShapes; //wire or compound + std::vector mySourceShapes; //wire or compound std::vector myWires; //wires from mySourceShapes std::vector myCompounds; //compounds, for recursive processing std::vector myShapesToReturn; + std::vector myInputFaces; + TopoShape myTopoShape; /** * @brief Build_Essence: build routine that can assume there is no nesting. @@ -106,6 +120,7 @@ protected: * whole Build(). */ virtual void Build_Essence() = 0; + void postBuild(); static void throwNotImplemented(); }; @@ -115,7 +130,7 @@ protected: */ class PartExport FaceMakerPublic : public FaceMaker { - TYPESYSTEM_HEADER_WITH_OVERRIDE(); + TYPESYSTEM_HEADER(); public: virtual std::string getUserFriendlyName() const = 0; virtual std::string getBriefExplanation() const = 0; @@ -141,10 +156,10 @@ class PartExport FaceMakerSimple : public FaceMakerPublic { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: - std::string getUserFriendlyName() const override; - std::string getBriefExplanation() const override; + virtual std::string getUserFriendlyName() const override; + virtual std::string getBriefExplanation() const override; protected: - void Build_Essence() override; + virtual void Build_Essence() override; }; diff --git a/src/Mod/Part/App/FeatureExtrusion.cpp b/src/Mod/Part/App/FeatureExtrusion.cpp index c3fb5ba21f..bca055c84a 100644 --- a/src/Mod/Part/App/FeatureExtrusion.cpp +++ b/src/Mod/Part/App/FeatureExtrusion.cpp @@ -356,14 +356,14 @@ void FaceMakerExtrusion::Build() if (mySourceShapes.empty()) throw Base::ValueError("No input shapes!"); if (mySourceShapes.size() == 1) { - inputShape = mySourceShapes[0]; + inputShape = mySourceShapes[0].getShape(); } else { TopoDS_Builder builder; TopoDS_Compound cmp; builder.MakeCompound(cmp); - for (const TopoDS_Shape& sh : mySourceShapes) { - builder.Add(cmp, sh); + for (const auto &sh : mySourceShapes) { + builder.Add(cmp, sh.getShape()); } inputShape = cmp; } diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index ad4ef2a780..7a5faa1156 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -387,6 +387,27 @@ public: TopoDS_Shape defeaturing(const std::vector& s) const; TopoDS_Shape makeShell(const TopoDS_Shape&) const; //@} + + /// Wire re-orientation when calling splitWires() + enum SplitWireReorient { + /// Keep original reorientation + NoReorient, + /// Make outer wire forward, and inner wires reversed + Reorient, + /// Make both outer and inner wires forward + ReorientForward, + /// Make both outer and inner wires reversed + ReorientReversed, + }; + /** Return the outer and inner wires of a face + * + * @param inner: optional output of inner wires + * @param reorient: wire reorientation, see SplitWireReorient + * + * @return Return the outer wire + */ + TopoShape splitWires(std::vector *inner = nullptr, + SplitWireReorient reorient = Reorient) const; /** @name Element name mapping aware shape maker * @@ -571,6 +592,11 @@ public: const std::string& shapeName(bool silent = false) const; static std::pair shapeTypeAndIndex(const char* name); + Data::MappedName setElementComboName(const Data::IndexedName & element, + const std::vector &names, + const char *marker=nullptr, + const char *op=nullptr, + const Data::ElementIDRefs *sids=nullptr); /** @name sub shape cached functions * @@ -609,7 +635,7 @@ public: void copyElementMap(const TopoShape & topoShape, const char *op=nullptr); bool canMapElement(const TopoShape &other) const; void mapSubElement(const TopoShape &other,const char *op=nullptr, bool forceHasher=false); - void mapSubElement(const std::vector &shapes, const char *op); + void mapSubElement(const std::vector &shapes, const char *op=nullptr); bool hasPendingElementMap() const; @@ -629,6 +655,58 @@ public: */ TopoShape &makeElementCompound(const std::vector &shapes, const char *op=nullptr, bool force=true); + TopoShape &makeElementFace(const std::vector &shapes, + const char *op = nullptr, + const char *maker = nullptr, + const gp_Pln *pln = nullptr); + /** Make a planar face with the input wire or edge + * + * @param shape: input shape. Can be either edge, wire, or compound of + * those two types + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * @param maker: optional type name of the face maker. If not given, + * default to "Part::FaceMakerBullseye" + * @param pln: optional plane of the face. + * + * @return The function creates a planar face. The original content of this + * TopoShape is discarded and replaced with the new shape. The + * function returns the TopoShape itself as a reference so that + * multiple operations can be carried out for the same shape in the + * same line of code. + */ + TopoShape &makeElementFace(const TopoShape &shape, + const char *op = nullptr, + const char *maker = nullptr, + const gp_Pln *pln = nullptr); + /** Make a planar face using this shape + * + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * @param maker: optional type name of the face maker. If not given, + * default to "Part::FaceMakerBullseye" + * @param pln: optional plane of the face. + * + * @return The function returns a new planar face made using the wire or edge + * inside this shape. The shape itself is not modified. + */ + TopoShape makeElementFace(const char *op = nullptr, + const char *maker = nullptr, + const gp_Pln *pln = nullptr) const { + return TopoShape(0,Hasher).makeElementFace(*this,op,maker,pln); + } + + /// Filling style when making a BSpline face + enum FillingStyle { + /// The style with the flattest patches + FillingStyle_Strech, + /// A rounded style of patch with less depth than those of Curved + FillingStyle_Coons, + /// The style with the most rounded patches + FillingStyle_Curved, + }; + + friend class TopoShapeCache; private: diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index d5b5b5227b..7bf252fdf0 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -26,11 +26,19 @@ #include "PreCompiled.h" #ifndef _PreComp_ #include +# include #include +#include + +# include +# include + #endif #include "TopoShape.h" #include "TopoShapeCache.h" +#include "FaceMaker.h" + FC_LOG_LEVEL_INIT("TopoShape", true, true) // NOLINT @@ -540,4 +548,202 @@ TopoShape::makeElementCompound(const std::vector& shapes, const char* return *this; } + +TopoShape &TopoShape::makeElementFace(const TopoShape &shape, + const char *op, + const char *maker, + const gp_Pln *pln) +{ + std::vector shapes; + if (shape.isNull()) + FC_THROWM(NullShapeException, "Null shape"); + if(shape.getShape().ShapeType() == TopAbs_COMPOUND) + shapes = shape.getSubTopoShapes(); + else + shapes.push_back(shape); + return makeElementFace(shapes,op,maker,pln); +} + +TopoShape &TopoShape::makeElementFace(const std::vector &shapes, + const char *op, + const char *maker, + const gp_Pln *pln) +{ + if(!maker || !maker[0]) maker = "Part::FaceMakerBullseye"; + std::unique_ptr mkFace = FaceMaker::ConstructFromType(maker); + mkFace->MyHasher = Hasher; + mkFace->MyOp = op; + if (pln) + mkFace->setPlane(*pln); + + for(auto &s : shapes) { + if (s.getShape().ShapeType() == TopAbs_COMPOUND) + mkFace->useTopoCompound(s); + else + mkFace->addTopoShape(s); + } + mkFace->Build(); + + const auto &ret = mkFace->getTopoShape(); + setShape(ret._Shape); + Hasher = ret.Hasher; + resetElementMap(ret.elementMap()); + if (!isValid()) { + ShapeFix_ShapeTolerance aSFT; + aSFT.LimitTolerance(getShape(), + Precision::Confusion(), Precision::Confusion(), TopAbs_SHAPE); + + // In some cases, the OCC reports the returned shape having invalid + // tolerance. Not sure about the real cause. + // + // Update: one of the cause is related to OCC bug in + // BRepBuilder_FindPlane, A possible call sequence is, + // + // makEOffset2D() -> TopoShape::findPlane() -> BRepLib_FindSurface + // + // See code comments in findPlane() for the description of the bug and + // work around. + + ShapeFix_Shape fixer(getShape()); + fixer.Perform(); + setShape(fixer.Shape(), false); + + if (!isValid()) + FC_WARN("makeElementFace: resulting face is invalid"); + } + return *this; +} + +Data::MappedName TopoShape::setElementComboName(const Data::IndexedName & element, + const std::vector &names, + const char *marker, + const char *op, + const Data::ElementIDRefs *_sids) +{ + if(names.empty()) + return Data::MappedName(); + std::string _marker; + if(!marker) + marker = elementMapPrefix().c_str(); + else if(!boost::starts_with(marker,elementMapPrefix())){ + _marker = elementMapPrefix() + marker; + marker = _marker.c_str(); + } + auto it = names.begin(); + Data::MappedName newName = *it; + std::ostringstream ss; + Data::ElementIDRefs sids; + if (_sids) + sids = *_sids; + if(names.size() == 1) + ss << marker; + else { + bool first = true; + ss.str(""); + if(!Hasher) + ss << marker; + ss << '('; + for(++it;it!=names.end();++it) { + if(first) + first = false; + else + ss << '|'; + ss << *it; + } + ss << ')'; + if(Hasher) { + sids.push_back(Hasher->getID(ss.str().c_str())); + ss.str(""); + ss << marker << sids.back().toString(); + } + } + elementMap()->encodeElementName(element[0],newName,ss,&sids,Tag,op); + return elementMap()->setElementName(element,newName, Tag, &sids); +} + +TopoShape TopoShape::splitWires(std::vector *inner, + SplitWireReorient reorient) const +{ + // ShapeAnalysis::OuterWire() is un-reliable for some reason. OCC source + // code shows it works by creating face using each wire, and then test using + // BRepTopAdaptor_FClass2d::PerformInfinitePoint() to check if it is an out + // bound wire. And practice shows it sometimes returns the incorrect + // result. Need more investigation. Note that this may be related to + // unreliable solid face orientation + // (https://forum.freecadweb.org/viewtopic.php?p=446006#p445674) + // + // Use BrepTools::OuterWire() instead. OCC source code shows it is + // implemented using simple bound box checking. This should be a + // reliable method, especially so for a planar face. + + TopoDS_Shape tmp; + if (shapeType(true) == TopAbs_FACE) + tmp = BRepTools::OuterWire(TopoDS::Face(_Shape)); + else if (countSubShapes(TopAbs_FACE) == 1) + tmp = BRepTools::OuterWire( + TopoDS::Face(getSubShape(TopAbs_FACE, 1))); + if (tmp.IsNull()) + return TopoShape(); + const auto & wires = getSubTopoShapes(TopAbs_WIRE); + auto it = wires.begin(); + + TopAbs_Orientation orientOuter, orientInner; + switch(reorient) { + case ReorientReversed: + orientOuter = orientInner = TopAbs_REVERSED; + break; + case ReorientForward: + orientOuter = orientInner = TopAbs_FORWARD; + break; + default: + orientOuter = TopAbs_FORWARD; + orientInner = TopAbs_REVERSED; + break; + } + + auto doReorient = [](TopoShape &s, TopAbs_Orientation orient) { + // Special case of single edge wire. Make sure the edge is in the + // required orientation. This is necessary because BRepFill_OffsetWire + // has special handling of circular edge offset, which seem to only + // respect the edge orientation and disregard the wire orientation. The + // orientation is used to determine whether to shrink or expand. + if (s.countSubShapes(TopAbs_EDGE) == 1) { + TopoDS_Shape e = s.getSubShape(TopAbs_EDGE, 1); + if (e.Orientation() == orient) { + if (s._Shape.Orientation() == orient) + return; + } else + e = e.Oriented(orient); + BRepBuilderAPI_MakeWire mkWire(TopoDS::Edge(e)); + s.setShape(mkWire.Shape(), false); + } + else if (s._Shape.Orientation() != orient) + s.setShape(s._Shape.Oriented(orient), false); + }; + + for (; it != wires.end(); ++it) { + auto & wire = *it; + if (wire.getShape().IsSame(tmp)) { + if (inner) { + for (++it; it != wires.end(); ++it) { + inner->push_back(*it); + if (reorient) + doReorient(inner->back(), orientInner); + } + } + auto res = wire; + if (reorient) + doReorient(res, orientOuter); + return res; + } + if (inner) { + inner->push_back(wire); + if (reorient) + doReorient(inner->back(), orientInner); + } + } + return TopoShape(); +} + + } // namespace Part From be03a9cebfe072ee73f0a1c9070bb96387bc7a52 Mon Sep 17 00:00:00 2001 From: Roy-043 Date: Tue, 9 Jan 2024 14:03:55 +0100 Subject: [PATCH 037/135] Arch: implement new get_param functions Additionally 2 Arch_Window bugs were fixed: * If the W1 value was changed the box tracker was not repositioned relative to the cursor. * The WindowColor was not applied because of a typo in the code. De current default color is quite dark BTW. Note that all dimensional values that were not really defaults, but just the last entered values, have been removed from preferences-archdefaults.ui. As a result the layout looks a bit strange. That will be improved in a next PR. --- src/Mod/Arch/ArchAxis.py | 10 +- src/Mod/Arch/ArchBuilding.py | 5 +- src/Mod/Arch/ArchBuildingPart.py | 17 +- src/Mod/Arch/ArchCommands.py | 52 +- src/Mod/Arch/ArchComponent.py | 14 +- src/Mod/Arch/ArchCurtainWall.py | 4 +- src/Mod/Arch/ArchFloor.py | 5 +- src/Mod/Arch/ArchIFCSchema.py | 3 +- src/Mod/Arch/ArchMaterial.py | 12 +- src/Mod/Arch/ArchPanel.py | 26 +- src/Mod/Arch/ArchPipe.py | 5 +- src/Mod/Arch/ArchPrecast.py | 59 +- src/Mod/Arch/ArchProfile.py | 11 +- src/Mod/Arch/ArchRebar.py | 9 +- src/Mod/Arch/ArchReference.py | 4 +- src/Mod/Arch/ArchSchedule.py | 30 +- src/Mod/Arch/ArchSectionPlane.py | 13 +- src/Mod/Arch/ArchSite.py | 9 +- src/Mod/Arch/ArchSpace.py | 20 +- src/Mod/Arch/ArchStairs.py | 13 +- src/Mod/Arch/ArchStructure.py | 39 +- src/Mod/Arch/ArchVRM.py | 3 +- src/Mod/Arch/ArchWall.py | 34 +- src/Mod/Arch/ArchWindow.py | 58 +- .../Resources/ui/preferences-archdefaults.ui | 829 +++++------------- src/Mod/Arch/exportIFC.py | 39 +- src/Mod/Arch/exportIFCHelper.py | 5 +- src/Mod/Arch/importDAE.py | 26 +- src/Mod/Arch/importIFC.py | 3 +- src/Mod/Arch/importIFCHelper.py | 43 +- src/Mod/Arch/importIFClegacy.py | 35 +- src/Mod/Arch/importOBJ.py | 4 +- src/Mod/Arch/importSHP.py | 4 +- src/Mod/Draft/draftutils/params.py | 100 ++- 34 files changed, 597 insertions(+), 946 deletions(-) diff --git a/src/Mod/Arch/ArchAxis.py b/src/Mod/Arch/ArchAxis.py index ecda27a768..b23aff4612 100644 --- a/src/Mod/Arch/ArchAxis.py +++ b/src/Mod/Arch/ArchAxis.py @@ -26,6 +26,8 @@ import ArchCommands import Draft import Part from FreeCAD import Vector +from draftutils import params + if FreeCAD.GuiUp: import FreeCADGui, re from PySide import QtCore, QtGui @@ -224,7 +226,7 @@ class _ViewProviderAxis: def setProperties(self,vobj): - ts = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetFloat("textheight",350) + ts = params.get_param("textheight") pl = vobj.PropertiesList if not "BubbleSize" in pl: vobj.addProperty("App::PropertyLength","BubbleSize","Axis", QT_TRANSLATE_NOOP("App::Property","The size of the axis bubbles")) @@ -251,7 +253,7 @@ class _ViewProviderAxis: vobj.StartNumber = 1 if not "FontName" in pl: vobj.addProperty("App::PropertyFont","FontName","Axis",QT_TRANSLATE_NOOP("App::Property","The font to use for texts")) - vobj.FontName = Draft.getParam("textfont","Arial,Sans") + vobj.FontName = params.get_param("textfont") if not "FontSize" in pl: vobj.addProperty("App::PropertyLength","FontSize","Axis",QT_TRANSLATE_NOOP("App::Property","The font size")) vobj.FontSize = ts @@ -476,7 +478,7 @@ class _ViewProviderAxis: txpos = FreeCAD.Vector(center.x,center.y-fs/2.5,center.z) tr.translation.setValue(tuple(txpos)) fo = coin.SoFont() - fn = Draft.getParam("textfont","Arial,Sans") + fn = params.get_param("textfont") if hasattr(vobj,"FontName"): if vobj.FontName: try: @@ -545,7 +547,7 @@ class _ViewProviderAxis: fs = vobj.BubbleSize*0.75 tr.translation.setValue(tuple(pl.Base)) tr.rotation.setValue(pl.Rotation.Q) - fn = Draft.getParam("textfont","Arial,Sans") + fn = params.get_param("textfont") if hasattr(vobj,"FontName"): if vobj.FontName: try: diff --git a/src/Mod/Arch/ArchBuilding.py b/src/Mod/Arch/ArchBuilding.py index 68e5a9352a..8231f81a34 100644 --- a/src/Mod/Arch/ArchBuilding.py +++ b/src/Mod/Arch/ArchBuilding.py @@ -24,6 +24,8 @@ import FreeCAD import ArchCommands import ArchFloor import Draft +from draftutils import params + if FreeCAD.GuiUp: import FreeCADGui from PySide import QtCore @@ -227,8 +229,7 @@ class _CommandBuilding: def Activated(self): sel = FreeCADGui.Selection.getSelection() - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") - link = p.GetBool("FreeLinking",False) + link = params.get_param_arch("FreeLinking") buildingobj = [] warning = False for obj in sel : diff --git a/src/Mod/Arch/ArchBuildingPart.py b/src/Mod/Arch/ArchBuildingPart.py index f21b7ae935..e327004fcc 100644 --- a/src/Mod/Arch/ArchBuildingPart.py +++ b/src/Mod/Arch/ArchBuildingPart.py @@ -27,6 +27,8 @@ import DraftVecUtils import ArchIFC import tempfile import os +from draftutils import params + if FreeCAD.GuiUp: import FreeCADGui from draftutils.translate import translate @@ -567,10 +569,10 @@ class ViewProviderBuildingPart: vobj.ShowLabel = True if not "FontName" in pl: vobj.addProperty("App::PropertyFont","FontName","BuildingPart",QT_TRANSLATE_NOOP("App::Property","The font to be used for texts")) - vobj.FontName = Draft.getParam("textfont","Arial") + vobj.FontName = params.get_param("textfont") if not "FontSize" in pl: vobj.addProperty("App::PropertyLength","FontSize","BuildingPart",QT_TRANSLATE_NOOP("App::Property","The font size of texts")) - vobj.FontSize = Draft.getParam("textheight",2.0) + vobj.FontSize = params.get_param("textheight") if not "DiffuseColor" in pl: vobj.addProperty("App::PropertyColorList","DiffuseColor","BuildingPart",QT_TRANSLATE_NOOP("App::Property","The individual face colors")) @@ -600,17 +602,16 @@ class ViewProviderBuildingPart: vobj.addProperty("App::PropertyBool","ChildrenOverride","Children",QT_TRANSLATE_NOOP("App::Property","If true, show the objects contained in this Building Part will adopt these line, color and transparency settings")) if not "ChildrenLineWidth" in pl: vobj.addProperty("App::PropertyFloat","ChildrenLineWidth","Children",QT_TRANSLATE_NOOP("App::Property","The line width of child objects")) - vobj.LineWidth = 1 + vobj.ChildrenLineWidth = params.get_param_view("DefaultShapeLineWidth") if not "ChildrenLineColor" in pl: vobj.addProperty("App::PropertyColor","ChildrenLineColor","Children",QT_TRANSLATE_NOOP("App::Property","The line color of child objects")) - c = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View").GetUnsigned("DefaultShapeLineColor",255) - vobj.ChildrenLineColor = (float((c>>24)&0xFF)/255.0,float((c>>16)&0xFF)/255.0,float((c>>8)&0xFF)/255.0,0.0) + vobj.ChildrenLineColor = params.get_param_view("DefaultShapeLineColor") & 0xFFFFFF00 if not "ChildrenShapeColor" in pl: vobj.addProperty("App::PropertyColor","ChildrenShapeColor","Children",QT_TRANSLATE_NOOP("App::Property","The shape color of child objects")) - c = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View").GetUnsigned("DefaultShapeColor",4294967295) - vobj.ChildrenLineColor = (float((c>>24)&0xFF)/255.0,float((c>>16)&0xFF)/255.0,float((c>>8)&0xFF)/255.0,0.0) + vobj.ChildrenShapeColor = params.get_param_view("DefaultShapeColor") & 0xFFFFFF00 if not "ChildrenTransparency" in pl: vobj.addProperty("App::PropertyPercent","ChildrenTransparency","Children",QT_TRANSLATE_NOOP("App::Property","The transparency of child objects")) + vobj.ChildrenTransparency = params.get_param_view("DefaultShapeTransparency") # clip properties if not "CutView" in pl: @@ -790,7 +791,7 @@ class ViewProviderBuildingPart: txt += units.display_external(float(q),None,'Length',vobj.ShowUnit,u) except Exception: q = q.getValueAs(q.getUserPreferred()[2]) - d = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt("Decimals",0) + d = params.get_param("Decimals",path="Units") fmt = "{0:."+ str(d) + "f}" if not vobj.ShowUnit: u = "" diff --git a/src/Mod/Arch/ArchCommands.py b/src/Mod/Arch/ArchCommands.py index 61635d21ab..712d55ed5d 100644 --- a/src/Mod/Arch/ArchCommands.py +++ b/src/Mod/Arch/ArchCommands.py @@ -25,6 +25,8 @@ import ArchComponent import Draft import DraftVecUtils from FreeCAD import Vector +from draftutils import params + if FreeCAD.GuiUp: import FreeCADGui from PySide import QtGui,QtCore @@ -62,33 +64,29 @@ def getStringList(objects): def getDefaultColor(objectType): '''getDefaultColor(string): returns a color value for the given object type (Wall, Structure, Window, WindowGlass)''' - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") transparency = 0.0 if objectType == "Wall": - c = p.GetUnsigned("WallColor",4294967295) + c = params.get_param_arch("WallColor") elif objectType == "Structure": - c = p.GetUnsigned("StructureColor",2847259391) + c = params.get_param_arch("StructureColor") elif objectType == "WindowGlass": - c = p.GetUnsigned("WindowGlassColor",1772731135) - transparency = p.GetInt("WindowTransparency",85)/100.0 + c = params.get_param_arch("WindowGlassColor") + transparency = params.get_param_arch("WindowTransparency") / 100.0 elif objectType == "Rebar": - c = p.GetUnsigned("RebarColor",3111475967) + c = params.get_param_arch("RebarColor") elif objectType == "Panel": - c = p.GetUnsigned("PanelColor",3416289279) + c = params.get_param_arch("PanelColor") elif objectType == "Space": - c = p.GetUnsigned("defaultSpaceColor",4278190080) + c = params.get_param_arch("defaultSpaceColor") elif objectType == "Helpers": - c = p.GetUnsigned("ColorHelpers",674321151) + c = params.get_param_arch("ColorHelpers") elif objectType == "Construction": - c = Draft.getParam("constructioncolor",746455039) + c = params.get_param("constructioncolor") transparency = 0.80 else: - c = p.GetUnsigned("WindowsColor",810781695) - r = float((c>>24)&0xFF)/255.0 - g = float((c>>16)&0xFF)/255.0 - b = float((c>>8)&0xFF)/255.0 - result = (r,g,b,transparency) - return result + c = params.get_param_arch("WindowColor") + r, g, b, _ = Draft.get_rgba_tuple(c) + return (r, g, b, transparency) def addComponents(objectsList,host): '''addComponents(objectsList,hostObject): adds the given object or the objects @@ -251,7 +249,7 @@ def setAsSubcomponent(obj): '''Sets the given object properly to become a subcomponent (addition, subtraction) of an Arch component''' Draft.ungroup(obj) - if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("applyConstructionStyle",True): + if params.get_param_arch("applyConstructionStyle"): if FreeCAD.GuiUp: color = getDefaultColor("Construction") if hasattr(obj.ViewObject,"LineColor"): @@ -662,10 +660,7 @@ def download(url,force=False): from urllib2 import urlopen import os name = url.split('/')[-1] - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro") - macropath = p.GetString("MacroPath","") - if not macropath: - macropath = FreeCAD.ConfigGet("UserAppData") + macropath = FreeCAD.getUserMacroDir(True) filepath = os.path.join(macropath,name) if os.path.exists(filepath) and not(force): return filepath @@ -845,7 +840,7 @@ def survey(callback=False): for o in newsels: if hasattr(o.Object, 'Shape'): n = o.Object.Label - showUnit = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("surveyUnits",True) + showUnit = params.get_param_arch("surveyUnits") t = "" u = FreeCAD.Units.Quantity() if not o.HasSubObjects: @@ -1040,7 +1035,7 @@ class SurveyTaskPanel: if hasattr(FreeCAD,"SurveyObserver"): u = FreeCAD.Units.Quantity(FreeCAD.SurveyObserver.totalLength,FreeCAD.Units.Length) t = u.getUserPreferred()[0] - if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("surveyUnits",True): + if params.get_param_arch("surveyUnits"): QtGui.QApplication.clipboard().setText(t) else: QtGui.QApplication.clipboard().setText(str(u.Value/u.getUserPreferred()[1])) @@ -1050,7 +1045,7 @@ class SurveyTaskPanel: u = FreeCAD.Units.Quantity(FreeCAD.SurveyObserver.totalArea,FreeCAD.Units.Area) t = u.getUserPreferred()[0] t = t.replace("^2","²") - if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("surveyUnits",True): + if params.get_param_arch("surveyUnits"): QtGui.QApplication.clipboard().setText(t) else: QtGui.QApplication.clipboard().setText(str(u.Value/u.getUserPreferred()[1])) @@ -1448,11 +1443,10 @@ class _CommandMeshToShape: if f.InList: if f.InList[0].isDerivedFrom("App::DocumentObjectGroup"): g = f.InList[0] - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") - fast = p.GetBool("ConversionFast",True) - tol = p.GetFloat("ConversionTolerance",0.001) - flat = p.GetBool("ConversionFlat",False) - cut = p.GetBool("ConversionCut",False) + fast = params.get_param_arch("ConversionFast") + tol = params.get_param_arch("ConversionTolerance") + flat = params.get_param_arch("ConversionFlat") + cut = params.get_param_arch("ConversionCut") FreeCAD.ActiveDocument.openTransaction(translate("Arch","Mesh to Shape")) for obj in FreeCADGui.Selection.getSelection(): newobj = meshToShape(obj,True,fast,tol,flat,cut) diff --git a/src/Mod/Arch/ArchComponent.py b/src/Mod/Arch/ArchComponent.py index d6d4e06379..1faaa34111 100644 --- a/src/Mod/Arch/ArchComponent.py +++ b/src/Mod/Arch/ArchComponent.py @@ -35,6 +35,8 @@ import FreeCAD import ArchCommands import ArchIFC import Draft +from draftutils import params + if FreeCAD.GuiUp: import FreeCADGui from PySide import QtGui,QtCore @@ -213,10 +215,10 @@ class Component(ArchIFC.IfcProduct): FreeCAD.Console.PrintMessage("Upgrading "+obj.Label+" BaseMaterial property to Material\n") if not "MoveBase" in pl: obj.addProperty("App::PropertyBool","MoveBase","Component",QT_TRANSLATE_NOOP("App::Property","Specifies if moving this object moves its base instead")) - obj.MoveBase = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("MoveBase",False) + obj.MoveBase = params.get_param_arch("MoveBase") if not "MoveWithHost" in pl: obj.addProperty("App::PropertyBool","MoveWithHost","Component",QT_TRANSLATE_NOOP("App::Property","Specifies if this object must move together when its host is moved")) - obj.MoveWithHost = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("MoveWithHost",False) + obj.MoveWithHost = params.get_param_arch("MoveWithHost") if not "VerticalArea" in pl: obj.addProperty("App::PropertyArea","VerticalArea","Component",QT_TRANSLATE_NOOP("App::Property","The area of all vertical faces of this object")) obj.setEditorMode("VerticalArea",1) @@ -992,7 +994,7 @@ class Component(ArchIFC.IfcProduct): import Part import TechDraw - fmax = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetInt("MaxComputeAreas",20) + fmax = params.get_param_arch("MaxComputeAreas") if len(obj.Shape.Faces) > fmax: obj.VerticalArea = 0 obj.HorizontalArea = 0 @@ -1174,7 +1176,7 @@ class ViewProviderComponent: if not "UseMaterialColor" in vobj.PropertiesList: vobj.addProperty("App::PropertyBool","UseMaterialColor","Component",QT_TRANSLATE_NOOP("App::Property","Use the material color as this object's shape color, if available")) - vobj.UseMaterialColor = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("UseMaterialColor",True) + vobj.UseMaterialColor = params.get_param_arch("UseMaterialColor") def updateData(self,obj,prop): """Method called when the host object has a property changed. @@ -1456,7 +1458,7 @@ class ViewProviderComponent: objlink = getattr(self.Object,link) if objlink: c.append(objlink) - if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("ClaimHosted",True): + if params.get_param_arch("ClaimHosted"): for link in self.Object.Proxy.getHosts(self.Object): c.append(link) @@ -2294,7 +2296,7 @@ if FreeCAD.GuiUp: editor = QtGui.QSpinBox(parent) elif "Real" in ptype: editor = QtGui.QDoubleSpinBox(parent) - editor.setDecimals(FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt("Decimals",2)) + editor.setDecimals(params.get_param("Decimals",path="Units")) elif ("Boolean" in ptype) or ("Logical" in ptype): editor = QtGui.QComboBox(parent) editor.addItems(["True","False"]) diff --git a/src/Mod/Arch/ArchCurtainWall.py b/src/Mod/Arch/ArchCurtainWall.py index 64ce7ea311..6b67f3f929 100644 --- a/src/Mod/Arch/ArchCurtainWall.py +++ b/src/Mod/Arch/ArchCurtainWall.py @@ -29,6 +29,7 @@ import FreeCAD import ArchComponent import ArchCommands import DraftVecUtils +from draftutils import params if FreeCAD.GuiUp: import FreeCADGui @@ -167,13 +168,12 @@ class CurtainWall(ArchComponent.Component): pl = obj.PropertiesList vsize = 50 hsize = 50 - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") if not "Host" in pl: obj.addProperty("App::PropertyLink","Host","CurtainWall",QT_TRANSLATE_NOOP("App::Property","An optional host object for this curtain wall")) if not "Height" in pl: obj.addProperty("App::PropertyLength","Height","CurtainWall", QT_TRANSLATE_NOOP("App::Property","The height of the curtain wall, if based on an edge")) - obj.Height = p.GetFloat("WallHeight",3000) + obj.Height = params.get_param_arch("WallHeight") if not "VerticalMullionNumber" in pl: obj.addProperty("App::PropertyInteger","VerticalMullionNumber","CurtainWall", QT_TRANSLATE_NOOP("App::Property","The number of vertical mullions")) diff --git a/src/Mod/Arch/ArchFloor.py b/src/Mod/Arch/ArchFloor.py index 601abd661b..85c66daf6f 100644 --- a/src/Mod/Arch/ArchFloor.py +++ b/src/Mod/Arch/ArchFloor.py @@ -33,6 +33,8 @@ import ArchCommands import ArchIFC import Draft import DraftVecUtils +from draftutils import params + if FreeCAD.GuiUp: import FreeCADGui from draftutils.translate import translate @@ -135,8 +137,7 @@ class _CommandFloor: """ sel = FreeCADGui.Selection.getSelection() - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") - link = p.GetBool("FreeLinking",False) + link = params.get_param_arch("FreeLinking") floorobj = [] warning = False for obj in sel : diff --git a/src/Mod/Arch/ArchIFCSchema.py b/src/Mod/Arch/ArchIFCSchema.py index 917b0a2c3a..f196545068 100644 --- a/src/Mod/Arch/ArchIFCSchema.py +++ b/src/Mod/Arch/ArchIFCSchema.py @@ -30,9 +30,10 @@ import os import json import FreeCAD +from draftutils import params ifcVersions = ["IFC4", "IFC2X3"] -IfcVersion = ifcVersions[FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetInt("IfcVersion",0)] +IfcVersion = ifcVersions[params.get_param_arch("IfcVersion")] with open(os.path.join(FreeCAD.getResourceDir(), "Mod", "Arch", "Presets", "ifc_contexts_" + IfcVersion + ".json")) as f: diff --git a/src/Mod/Arch/ArchMaterial.py b/src/Mod/Arch/ArchMaterial.py index a9809fb44d..f5d39abcb1 100644 --- a/src/Mod/Arch/ArchMaterial.py +++ b/src/Mod/Arch/ArchMaterial.py @@ -20,6 +20,8 @@ #*************************************************************************** import FreeCAD +from draftutils import params + if FreeCAD.GuiUp: import FreeCADGui, os import Arch_rc # Needed for access to icons # lgtm [py/unused_import] @@ -887,9 +889,8 @@ class _ArchMultiMaterialTaskPanel: self.model.clear() self.model.setHorizontalHeaderLabels([translate("Arch","Name"),translate("Arch","Material"),translate("Arch","Thickness")]) # restore widths - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") - self.form.tree.setColumnWidth(0,p.GetInt("MultiMaterialColumnWidth0",60)) - self.form.tree.setColumnWidth(1,p.GetInt("MultiMaterialColumnWidth1",60)) + self.form.tree.setColumnWidth(0,params.get_param_arch("MultiMaterialColumnWidth0")) + self.form.tree.setColumnWidth(1,params.get_param_arch("MultiMaterialColumnWidth1")) for i in range(len(obj.Names)): item1 = QtGui.QStandardItem(obj.Names[i]) item2 = QtGui.QStandardItem(obj.Materials[i].Label) @@ -974,9 +975,8 @@ class _ArchMultiMaterialTaskPanel: def accept(self): # store widths - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") - p.SetInt("MultiMaterialColumnWidth0",self.form.tree.columnWidth(0)) - p.SetInt("MultiMaterialColumnWidth1",self.form.tree.columnWidth(1)) + params.set_param_arch("MultiMaterialColumnWidth0",self.form.tree.columnWidth(0)) + params.set_param_arch("MultiMaterialColumnWidth1",self.form.tree.columnWidth(1)) if self.obj: mats = [] for m in FreeCAD.ActiveDocument.Objects: diff --git a/src/Mod/Arch/ArchPanel.py b/src/Mod/Arch/ArchPanel.py index 20fb4f4083..36bd428be7 100644 --- a/src/Mod/Arch/ArchPanel.py +++ b/src/Mod/Arch/ArchPanel.py @@ -28,6 +28,8 @@ import Draft import DraftVecUtils import Part from FreeCAD import Vector +from draftutils import params + if FreeCAD.GuiUp: import FreeCADGui from PySide import QtCore, QtGui @@ -142,10 +144,9 @@ class CommandPanel: def Activated(self): - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") - self.Length = p.GetFloat("PanelLength",1000) - self.Width = p.GetFloat("PanelWidth",1000) - self.Thickness = p.GetFloat("PanelThickness",10) + self.Length = params.get_param_arch("PanelLength") + self.Width = params.get_param_arch("PanelWidth") + self.Thickness = params.get_param_arch("PanelThickness") self.Profile = None self.continueCmd = False self.rotated = False @@ -167,7 +168,7 @@ class CommandPanel: # interactive mode import WorkingPlane WorkingPlane.get_working_plane() - + self.points = [] self.tracker = DraftTrackers.boxTracker() self.tracker.width(self.Width) @@ -282,17 +283,17 @@ class CommandPanel: def setWidth(self,d): self.Width = d - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PanelWidth",d) + params.set_param_arch("PanelWidth",d) def setThickness(self,d): self.Thickness = d - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PanelThickness",d) + params.set_param_arch("PanelThickness",d) def setLength(self,d): self.Length = d - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PanelLength",d) + params.set_param_arch("PanelLength",d) def setContinue(self,i): @@ -832,7 +833,7 @@ class PanelCut(Draft.DraftObject): obj.addProperty("App::PropertyAngle","TagRotation","PanelCut",QT_TRANSLATE_NOOP("App::Property","The rotation of the tag text")) if not "FontFile" in pl: obj.addProperty("App::PropertyFile","FontFile","PanelCut",QT_TRANSLATE_NOOP("App::Property","The font of the tag text")) - obj.FontFile = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetString("FontFile","") + obj.FontFile = params.get_param("FontFile") if not "MakeFace" in pl: obj.addProperty("App::PropertyBool","MakeFace","PanelCut",QT_TRANSLATE_NOOP("App::Property","If True, the object is rendered as a face, if possible.")) if not "AllowedAngles" in pl: @@ -1099,7 +1100,6 @@ class PanelSheet(Draft.DraftObject): def setProperties(self,obj): pl = obj.PropertiesList - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") if not "Group" in pl: obj.addProperty("App::PropertyLinkList","Group","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The linked Panel cuts")) if not "TagText" in pl: @@ -1113,13 +1113,13 @@ class PanelSheet(Draft.DraftObject): obj.addProperty("App::PropertyAngle","TagRotation","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The rotation of the tag text")) if not "FontFile" in pl: obj.addProperty("App::PropertyFile","FontFile","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The font of the tag text")) - obj.FontFile = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetString("FontFile",QT_TRANSLATE_NOOP("App::Property","The font file")) + obj.FontFile = params.get_param("FontFile") if not "Width" in pl: obj.addProperty("App::PropertyLength","Width","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The width of the sheet")) - obj.Width = p.GetFloat("PanelLength",1000) + obj.Width = params.get_param_arch("PanelLength") if not "Height" in pl: obj.addProperty("App::PropertyLength","Height","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The height of the sheet")) - obj.Height = p.GetFloat("PanelWidth",1000) + obj.Height = params.get_param_arch("PanelWidth") if not "FillRatio" in pl: obj.addProperty("App::PropertyPercent","FillRatio","PanelSheet",QT_TRANSLATE_NOOP("App::Property","The fill ratio of this sheet")) obj.setEditorMode("FillRatio",2) diff --git a/src/Mod/Arch/ArchPipe.py b/src/Mod/Arch/ArchPipe.py index bdcdfac1ab..596fac7cb0 100644 --- a/src/Mod/Arch/ArchPipe.py +++ b/src/Mod/Arch/ArchPipe.py @@ -22,6 +22,8 @@ import FreeCAD import ArchComponent +from draftutils import params + if FreeCAD.GuiUp: import FreeCADGui import Arch_rc @@ -71,8 +73,7 @@ def makePipe(baseobj=None,diameter=0,length=0,placement=None,name=None): if diameter: obj.Diameter = diameter else: - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") - obj.Diameter = p.GetFloat("PipeDiameter",50) + obj.Diameter = params.get_param_arch("PipeDiameter") if placement: obj.Placement = placement return obj diff --git a/src/Mod/Arch/ArchPrecast.py b/src/Mod/Arch/ArchPrecast.py index 1b7543da25..7bb929b10e 100644 --- a/src/Mod/Arch/ArchPrecast.py +++ b/src/Mod/Arch/ArchPrecast.py @@ -28,6 +28,8 @@ Beams, pillars, slabs and panels""" import ArchCommands,ArchComponent,FreeCAD from FreeCAD import Vector +from draftutils import params + if FreeCAD.GuiUp: from draftutils.translate import translate from PySide.QtCore import QT_TRANSLATE_NOOP @@ -897,21 +899,20 @@ class _PrecastTaskPanel: QtCore.QObject.connect(self.valueTread,QtCore.SIGNAL("valueChanged(double)"),self.setTread) # restore presets - param = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") - self.restoreValue(self.valueChamfer, param.GetFloat("PrecastChamfer", 0.0)) - self.restoreValue(self.valueDentLength, param.GetFloat("PrecastDentLength", 0.0)) - self.restoreValue(self.valueDentWidth, param.GetFloat("PrecastDentWidth", 0.0)) - self.restoreValue(self.valueDentHeight, param.GetFloat("PrecastDentHeight", 0.0)) - self.restoreValue(self.valueBase, param.GetFloat("PrecastBase", 0.0)) - self.restoreValue(self.valueHoleMajor, param.GetFloat("PrecastHoleMajor", 0.0)) - self.restoreValue(self.valueHoleMinor, param.GetFloat("PrecastHoleMinor", 0.0)) - self.restoreValue(self.valueHoleSpacing, param.GetFloat("PrecastHoleSpacing", 0.0)) - self.restoreValue(self.valueGrooveDepth, param.GetFloat("PrecastGrooveDepth", 0.0)) - self.restoreValue(self.valueGrooveHeight, param.GetFloat("PrecastGrooveHeight", 0.0)) - self.restoreValue(self.valueGrooveSpacing, param.GetFloat("PrecastGrooveSpacing", 0.0)) - self.restoreValue(self.valueDownLength, param.GetFloat("PrecastDownLength", 0.0)) - self.restoreValue(self.valueRiser, param.GetFloat("PrecastRiser", 0.0)) - self.restoreValue(self.valueTread, param.GetFloat("PrecastTread", 0.0)) + self.restoreValue(self.valueChamfer, params.get_param_arch("PrecastChamfer")) + self.restoreValue(self.valueDentLength, params.get_param_arch("PrecastDentLength")) + self.restoreValue(self.valueDentWidth, params.get_param_arch("PrecastDentWidth")) + self.restoreValue(self.valueDentHeight, params.get_param_arch("PrecastDentHeight")) + self.restoreValue(self.valueBase, params.get_param_arch("PrecastBase")) + self.restoreValue(self.valueHoleMajor, params.get_param_arch("PrecastHoleMajor")) + self.restoreValue(self.valueHoleMinor, params.get_param_arch("PrecastHoleMinor")) + self.restoreValue(self.valueHoleSpacing, params.get_param_arch("PrecastHoleSpacing")) + self.restoreValue(self.valueGrooveDepth, params.get_param_arch("PrecastGrooveDepth")) + self.restoreValue(self.valueGrooveHeight, params.get_param_arch("PrecastGrooveHeight")) + self.restoreValue(self.valueGrooveSpacing, params.get_param_arch("PrecastGrooveSpacing")) + self.restoreValue(self.valueDownLength, params.get_param_arch("PrecastDownLength")) + self.restoreValue(self.valueRiser, params.get_param_arch("PrecastRiser")) + self.restoreValue(self.valueTread, params.get_param_arch("PrecastTread")) self.retranslateUi(self.form) self.form.hide() @@ -945,59 +946,59 @@ class _PrecastTaskPanel: def setChamfer(self,value): self.Chamfer = value - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PrecastChamfer",value) + params.set_param_arch("PrecastChamfer",value) def setDentLength(self,value): self.DentLength = value - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PrecastDentLength",value) + params.set_param_arch("PrecastDentLength",value) def setDentWidth(self,value): self.DentWidth = value - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PrecastDentWidth",value) + params.set_param_arch("PrecastDentWidth",value) def setDentHeight(self,value): self.DentHeight = value - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PrecastDentHeight",value) + params.set_param_arch("PrecastDentHeight",value) def setBase(self,value): self.Base = value - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PrecastBase",value) + params.set_param_arch("PrecastBase",value) def setHoleMajor(self,value): self.HoleMajor = value - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PrecastHoleMajor",value) + params.set_param_arch("PrecastHoleMajor",value) def setHoleMinor(self,value): self.HoleMinor = value - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PrecastHoleMinor",value) + params.set_param_arch("PrecastHoleMinor",value) def setHoleSpacing(self,value): self.HoleSpacing = value - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PrecastHoleSpacing",value) + params.set_param_arch("PrecastHoleSpacing",value) def setGrooveDepth(self,value): self.GrooveDepth = value - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PrecastGrooveDepth",value) + params.set_param_arch("PrecastGrooveDepth",value) def setGrooveHeight(self,value): self.GrooveHeight = value - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PrecastGrooveHeight",value) + params.set_param_arch("PrecastGrooveHeight",value) def setGrooveSpacing(self,value): self.GrooveSpacing = value - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PrecastGrooveSpacing",value) + params.set_param_arch("PrecastGrooveSpacing",value) def setDownLength(self,value): self.DownLength = value - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PrecastDownLength",value) + params.set_param_arch("PrecastDownLength",value) def setRiser(self,value): self.Riser = value - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PrecastRiser",value) + params.set_param_arch("PrecastRiser",value) def setTread(self,value): self.Tread = value - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetFloat("PrecastTread",value) + params.set_param_arch("PrecastTread",value) def retranslateUi(self, dialog): from PySide import QtGui diff --git a/src/Mod/Arch/ArchProfile.py b/src/Mod/Arch/ArchProfile.py index 923a7c78d2..a8d88689ab 100644 --- a/src/Mod/Arch/ArchProfile.py +++ b/src/Mod/Arch/ArchProfile.py @@ -36,6 +36,7 @@ import os import FreeCAD import Draft from FreeCAD import Vector +from draftutils import params if FreeCAD.GuiUp: import FreeCADGui @@ -169,7 +170,7 @@ class Arch_Profile: self.vPresets.currentIndexChanged.connect(self.setPreset) # restore preset - stored = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetString("StructurePreset","") + stored = params.get_param_arch("StructurePreset") if stored: if ";" in stored: stored = stored.split(";") @@ -206,13 +207,13 @@ class Arch_Profile: if i == 0: self.pSelect = [None] self.vPresets.addItems([" "]) - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetString("StructurePreset","") + params.set_param_arch("StructurePreset","") else: self.pSelect = [p for p in self.Presets if p[1] == self.Categories[i-1]] fpresets = [] for p in self.pSelect: f = FreeCAD.Units.Quantity(p[4],FreeCAD.Units.Length).getUserPreferred() - d = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt("Decimals",2) + d = params.get_param("Decimals",path="Units") s1 = str(round(p[4]/f[1],d)) s2 = str(round(p[5]/f[1],d)) s3 = str(f[2]) @@ -227,7 +228,7 @@ class Arch_Profile: if elt: p=elt[0]-1 # Presets indexes are 1-based self.Profile = self.Presets[p] - FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").SetString("StructurePreset",";".join([str(i) for i in self.Profile])) + params.set_param_arch("StructurePreset",";".join([str(i) for i in self.Profile])) class _Profile(Draft._DraftObject): @@ -565,7 +566,7 @@ class ProfileTaskPanel: if pre[1] == text: self.currentpresets.append(pre) f = FreeCAD.Units.Quantity(pre[4],FreeCAD.Units.Length).getUserPreferred() - d = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt("Decimals",2) + d = params.get_param("Decimals",path="Units") s1 = str(round(pre[4]/f[1],d)) s2 = str(round(pre[5]/f[1],d)) s3 = str(f[2]) diff --git a/src/Mod/Arch/ArchRebar.py b/src/Mod/Arch/ArchRebar.py index abe3221a72..d60585a60a 100644 --- a/src/Mod/Arch/ArchRebar.py +++ b/src/Mod/Arch/ArchRebar.py @@ -25,6 +25,8 @@ import Draft import ArchComponent import DraftVecUtils import ArchCommands +from draftutils import params + if FreeCAD.GuiUp: import FreeCADGui from draftutils.translate import translate @@ -58,7 +60,6 @@ def makeRebar(baseobj=None,sketch=None,diameter=None,amount=1,offset=None,name=N if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Rebar") obj.Label = name if name else translate("Arch","Rebar") _Rebar(obj) @@ -87,15 +88,15 @@ def makeRebar(baseobj=None,sketch=None,diameter=None,amount=1,offset=None,name=N if diameter: obj.Diameter = diameter else: - obj.Diameter = p.GetFloat("RebarDiameter",6) + obj.Diameter = params.get_param_arch("RebarDiameter") obj.Amount = amount obj.Document.recompute() if offset is not None: obj.OffsetStart = offset obj.OffsetEnd = offset else: - obj.OffsetStart = p.GetFloat("RebarOffset",30) - obj.OffsetEnd = p.GetFloat("RebarOffset",30) + obj.OffsetStart = params.get_param_arch("RebarOffset") + obj.OffsetEnd = params.get_param_arch("RebarOffset") obj.Mark = obj.Label return obj diff --git a/src/Mod/Arch/ArchReference.py b/src/Mod/Arch/ArchReference.py index ae2e209d5f..2c474f42f8 100644 --- a/src/Mod/Arch/ArchReference.py +++ b/src/Mod/Arch/ArchReference.py @@ -28,6 +28,8 @@ import FreeCAD import os import zipfile import re +from draftutils import params + if FreeCAD.GuiUp: import FreeCADGui from PySide import QtCore, QtGui @@ -404,7 +406,7 @@ class ViewProviderArchReference: # Check for file change every minute self.timer = QtCore.QTimer() self.timer.timeout.connect(self.checkChanges) - s = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetInt("ReferenceCheckInterval",60) + s = params.get_param_arch("ReferenceCheckInterval") self.timer.start(1000*s) def dumps(self): diff --git a/src/Mod/Arch/ArchSchedule.py b/src/Mod/Arch/ArchSchedule.py index 5e6e2b0209..7ee88f0b75 100644 --- a/src/Mod/Arch/ArchSchedule.py +++ b/src/Mod/Arch/ArchSchedule.py @@ -21,6 +21,8 @@ #*************************************************************************** import FreeCAD +from draftutils import params + if FreeCAD.GuiUp: import FreeCADGui from PySide import QtCore, QtGui @@ -344,7 +346,7 @@ class _ArchSchedule: tp = FreeCAD.Units.Length # format value - dv = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt("Decimals",2) + dv = params.get_param("Decimals",path="Units") fs = "{:."+str(dv)+"f}" # format string for o in objs: if verbose: @@ -514,13 +516,12 @@ class ArchScheduleTaskPanel: self.form.buttonSelect.setIcon(QtGui.QIcon(":/icons/edit-select-all.svg")) # restore widths - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") - self.form.list.setColumnWidth(0,p.GetInt("ScheduleColumnWidth0",100)) - self.form.list.setColumnWidth(1,p.GetInt("ScheduleColumnWidth1",100)) - self.form.list.setColumnWidth(2,p.GetInt("ScheduleColumnWidth2",50)) - self.form.list.setColumnWidth(3,p.GetInt("ScheduleColumnWidth3",100)) - w = p.GetInt("ScheduleDialogWidth",300) - h = p.GetInt("ScheduleDialogHeight",200) + self.form.list.setColumnWidth(0,params.get_param_arch("ScheduleColumnWidth0")) + self.form.list.setColumnWidth(1,params.get_param_arch("ScheduleColumnWidth1")) + self.form.list.setColumnWidth(2,params.get_param_arch("ScheduleColumnWidth2")) + self.form.list.setColumnWidth(3,params.get_param_arch("ScheduleColumnWidth3")) + w = params.get_param_arch("ScheduleDialogWidth") + h = params.get_param_arch("ScheduleDialogHeight") self.form.resize(w,h) # set delegate - Not using custom delegates for now... @@ -712,13 +713,12 @@ class ArchScheduleTaskPanel: """Saves the changes and closes the dialog""" # store widths - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") - p.SetInt("ScheduleColumnWidth0",self.form.list.columnWidth(0)) - p.SetInt("ScheduleColumnWidth1",self.form.list.columnWidth(1)) - p.SetInt("ScheduleColumnWidth2",self.form.list.columnWidth(2)) - p.SetInt("ScheduleColumnWidth3",self.form.list.columnWidth(3)) - p.SetInt("ScheduleDialogWidth",self.form.width()) - p.SetInt("ScheduleDialogHeight",self.form.height()) + params.set_param_arch("ScheduleColumnWidth0",self.form.list.columnWidth(0)) + params.set_param_arch("ScheduleColumnWidth1",self.form.list.columnWidth(1)) + params.set_param_arch("ScheduleColumnWidth2",self.form.list.columnWidth(2)) + params.set_param_arch("ScheduleColumnWidth3",self.form.list.columnWidth(3)) + params.set_param_arch("ScheduleDialogWidth",self.form.width()) + params.set_param_arch("ScheduleDialogHeight",self.form.height()) # commit values self.writeValues() diff --git a/src/Mod/Arch/ArchSectionPlane.py b/src/Mod/Arch/ArchSectionPlane.py index 281c525425..ae3f058c85 100644 --- a/src/Mod/Arch/ArchSectionPlane.py +++ b/src/Mod/Arch/ArchSectionPlane.py @@ -31,6 +31,8 @@ import uuid import time from FreeCAD import Vector +from draftutils import params + if FreeCAD.GuiUp: import FreeCADGui from PySide import QtCore, QtGui @@ -346,7 +348,6 @@ def getSVG(source, windows.append(o) objs = nonspaces - archUserParameters = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") scaledLineWidth = linewidth/scale if renderMode in ["Coin",2,"Coin mono",3]: # don't scale linewidths in coin mode @@ -357,11 +358,11 @@ def getSVG(source, scaledCutLineWidth = cutlinewidth/scale svgCutLineWidth = str(scaledCutLineWidth) + 'px' else: - st = archUserParameters.GetFloat("CutLineThickness",2) + st = params.get_param_arch("CutLineThickness") svgCutLineWidth = str(scaledLineWidth * st) + 'px' - yt = archUserParameters.GetFloat("SymbolLineThickness",0.6) + yt = params.get_param_arch("SymbolLineThickness") svgSymbolLineWidth = str(linewidth * yt) - hiddenPattern = archUserParameters.GetString("archHiddenPattern","30,10") + hiddenPattern = params.get_param_arch("archHiddenPattern") svgHiddenPattern = hiddenPattern.replace(" ","") #fillpattern = '= 0: FreeCADGui.Snapper.setSelectMode(False) self.tracker.length(self.Width) - self.tracker.width(self.Thickness) self.tracker.height(self.Height) + self.tracker.width(self.W1) self.tracker.on() self.pic.hide() self.im.show() @@ -1180,7 +1174,9 @@ class _ViewProviderWindow(ArchComponent.ViewProviderComponent): typeidx = (i*5)+1 if typeidx < len(obj.WindowParts): typ = obj.WindowParts[typeidx] - if typ == WindowPartTypes[2]: # transparent parts + if typ == WindowPartTypes[0]: # "Frame" + ccol = ArchCommands.getDefaultColor("") + elif typ == WindowPartTypes[2]: # "Glass panel" ccol = ArchCommands.getDefaultColor("WindowGlass") if not ccol: ccol = base diff --git a/src/Mod/Arch/Resources/ui/preferences-archdefaults.ui b/src/Mod/Arch/Resources/ui/preferences-archdefaults.ui index 09c08af4e3..7ce6a3b397 100644 --- a/src/Mod/Arch/Resources/ui/preferences-archdefaults.ui +++ b/src/Mod/Arch/Resources/ui/preferences-archdefaults.ui @@ -25,135 +25,62 @@ Walls - + - - - - - Width: - - - - - - - mm - - - 9999.989999999999782 - - - 200.000000000000000 - - - WallWidth - - - Mod/Arch - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Height: - - - - - - - mm - - - 9999.989999999999782 - - - 3000.000000000000000 - - - WallHeight - - - Mod/Arch - - - - + + + Use sketches + + + true + + + WallSketches + + + Mod/Arch + + - - - - - Use sketches - - - true - - - WallSketches - - - Mod/Arch - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Color: - - - - - - - This is the default color for new Wall objects - - - - 214 - 214 - 214 - - - - WallColor - - - Mod/Arch - - - - + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Color: + + + + + + + This is the default color for new Wall objects + + + + 214 + 214 + 214 + + + + WallColor + + + Mod/Arch + + @@ -163,158 +90,46 @@ Structures - + - - - - - Length: - - - - - - - mm - - - 9999.989999999999782 - - - 100.000000000000000 - - - StructureLength - - - Mod/Arch - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Width: - - - - - - - mm - - - 9999.989999999999782 - - - 100.000000000000000 - - - StructureWidth - - - Mod/Arch - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Height: - - - - - - - mm - - - 9999.989999999999782 - - - 1000.000000000000000 - - - StructureHeight - - - Mod/Arch - - - - + + + Qt::Horizontal + + + + 40 + 20 + + + - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Color: - - - - - - - This is the default color for new Structure objects - - - - 150 - 169 - 186 - - - - StructureColor - - - Mod/Arch - - - - + + + Color: + + + + + + + This is the default color for new Structure objects + + + + 150 + 169 + 186 + + + + StructureColor + + + Mod/Arch + + @@ -443,208 +258,87 @@ Windows - + - - - - - Width: - - - - - - - The default width for new windows - - - mm - - - 99999.990000000005239 - - - 1000.000000000000000 - - - WindowWidth - - - Mod/Arch - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Height: - - - - - - - The default height for new windows - - - mm - - - 99999.990000000005239 - - - 1000.000000000000000 - - - WindowHeight - - - Mod/Arch - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Thickness: - - - - - - - The default thickness for new windows - - - mm - - - 99999.990000000005239 - - - 100.000000000000000 - - - WindowThickness - - - Mod/Arch - - - - + + + Qt::Horizontal + + + + 40 + 20 + + + - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Transparency: - - - - - - - 85 - - - WindowTransparency - - - Mod/Arch - - - - - - - Frame color: - - - - - - - - 33 - 45 - 66 - - - - WindowColor - - - Mod/Arch - - - - - - - Glass color: - - - - - - - - 93 - 183 - 203 - - - - WindowGlassColor - - - Mod/Arch - - - - + + + Transparency: + + + + + + + 85 + + + WindowTransparency + + + Mod/Arch + + + + + + + Frame color: + + + + + + + + 33 + 45 + 66 + + + + WindowColor + + + Mod/Arch + + + + + + + Glass color: + + + + + + + + 93 + 183 + 203 + + + + WindowGlassColor + + + Mod/Arch + + @@ -811,155 +505,43 @@ Panels - + - - - - - Length: - - - - - - - mm - - - 99999.990000000005239 - - - 1000.000000000000000 - - - PanelLength - - - Mod/Arch - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Width: - - - - - - - mm - - - 9999.989999999999782 - - - 1000.000000000000000 - - - PanelWidth - - - Mod/Arch - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Thickness - - - - - - - mm - - - 9999.989999999999782 - - - 10.000000000000000 - - - PanelThickness - - - Mod/Arch - - - - + + + Qt::Horizontal + + + + 40 + 20 + + + - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Color: - - - - - - - - 203 - 160 - 111 - - - - PanelColor - - - Mod/Arch - - - - + + + Color: + + + + + + + + 203 + 160 + 111 + + + + PanelColor + + + Mod/Arch + + @@ -1181,6 +763,13 @@ + + + + Qt::Vertical + + + diff --git a/src/Mod/Arch/exportIFC.py b/src/Mod/Arch/exportIFC.py index c10938cd90..e4e8dec5f6 100644 --- a/src/Mod/Arch/exportIFC.py +++ b/src/Mod/Arch/exportIFC.py @@ -44,6 +44,7 @@ import exportIFCStructuralTools from DraftGeomUtils import vec from importIFCHelper import dd2dms +from draftutils import params from draftutils.messages import _msg, _err if FreeCAD.GuiUp: @@ -107,12 +108,10 @@ END-ISO-10303-21; def getPreferences(): """Retrieve the IFC preferences available in import and export.""" - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") - - if FreeCAD.GuiUp and p.GetBool("ifcShowDialog", False): + if FreeCAD.GuiUp and params.get_param_arch("ifcShowDialog"): FreeCADGui.showPreferences("Import-Export", 1) - ifcunit = p.GetInt("ifcUnit", 0) + ifcunit = params.get_param_arch("ifcUnit") # Factor to multiply the dimension in millimeters # mm x 0.001 = metre @@ -142,20 +141,20 @@ def getPreferences(): # some objects may be "unreferenced" and won't belong to the `IfcProject`. # Some applications may fail at importing these unreferenced objects. preferences = { - 'DEBUG': p.GetBool("ifcDebug", False), - 'CREATE_CLONES': p.GetBool("ifcCreateClones", True), - 'FORCE_BREP': p.GetBool("ifcExportAsBrep", False), - 'STORE_UID': p.GetBool("ifcStoreUid", True), - 'SERIALIZE': p.GetBool("ifcSerialize", False), - 'EXPORT_2D': p.GetBool("ifcExport2D", True), - 'FULL_PARAMETRIC': p.GetBool("IfcExportFreeCADProperties", False), - 'ADD_DEFAULT_SITE': p.GetBool("IfcAddDefaultSite", True), - 'ADD_DEFAULT_BUILDING': p.GetBool("IfcAddDefaultBuilding", True), - 'ADD_DEFAULT_STOREY': p.GetBool("IfcAddDefaultStorey", True), + 'DEBUG': params.get_param_arch("ifcDebug"), + 'CREATE_CLONES': params.get_param_arch("ifcCreateClones"), + 'FORCE_BREP': params.get_param_arch("ifcExportAsBrep"), + 'STORE_UID': params.get_param_arch("ifcStoreUid"), + 'SERIALIZE': params.get_param_arch("ifcSerialize"), + 'EXPORT_2D': params.get_param_arch("ifcExport2D"), + 'FULL_PARAMETRIC': params.get_param_arch("IfcExportFreeCADProperties"), + 'ADD_DEFAULT_SITE': params.get_param_arch("IfcAddDefaultSite"), + 'ADD_DEFAULT_BUILDING': params.get_param_arch("IfcAddDefaultBuilding"), + 'ADD_DEFAULT_STOREY': params.get_param_arch("IfcAddDefaultStorey"), 'IFC_UNIT': u, 'SCALE_FACTOR': f, - 'GET_STANDARD': p.GetBool("getStandardType", False), - 'EXPORT_MODEL': ['arch', 'struct', 'hybrid'][p.GetInt("ifcExportModel", 0)] + 'GET_STANDARD': params.get_param_arch("getStandardType"), + 'EXPORT_MODEL': ['arch', 'struct', 'hybrid'][params.get_param_arch("ifcExportModel")] } # get ifcopenshell version @@ -175,7 +174,7 @@ def getPreferences(): schema = ifcopenshell.schema_identifier elif ifcos_version >= 0.6: # v0.6 onwards allows to set our own schema - schema = ["IFC4", "IFC2X3"][p.GetInt("IfcVersion", 0)] + schema = ["IFC4", "IFC2X3"][params.get_param_arch("IfcVersion")] else: schema = "IFC2X3" @@ -1869,7 +1868,7 @@ def checkRectangle(edges): or not. It will return True when edges form a rectangular shape or return False when edges do not form a rectangular shape.""" - if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("DisableIfcRectangleProfileDef",False): + if params.get_param_arch("DisableIfcRectangleProfileDef"): return False if len(edges) != 4: return False @@ -2226,8 +2225,8 @@ def getRepresentation( except Base.FreeCADError: pass if curves: - joinfacets = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("ifcJoinCoplanarFacets",False) - usedae = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("ifcUseDaeOptions",False) + joinfacets = params.get_param_arch("ifcJoinCoplanarFacets") + usedae = params.get_param_arch("ifcUseDaeOptions") if joinfacets: result = Arch.removeCurves(fcsolid,dae=usedae) if result: diff --git a/src/Mod/Arch/exportIFCHelper.py b/src/Mod/Arch/exportIFCHelper.py index 926aecf553..cd2f6c0304 100644 --- a/src/Mod/Arch/exportIFCHelper.py +++ b/src/Mod/Arch/exportIFCHelper.py @@ -25,6 +25,7 @@ import math import FreeCAD # import Draft import ifcopenshell +from draftutils import params def getObjectsOfIfcType(objects, ifcType): results = [] @@ -204,8 +205,8 @@ class recycler: def __init__(self,ifcfile,template=True): self.ifcfile = ifcfile - self.compress = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("ifcCompress",True) - self.mergeProfiles = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("ifcMergeProfiles",False) + self.compress = params.get_param_arch("ifcCompress") + self.mergeProfiles = params.get_param_arch("ifcMergeProfiles") self.cartesianpoints = {} self.directions = {} self.axis2placement3ds = {} diff --git a/src/Mod/Arch/importDAE.py b/src/Mod/Arch/importDAE.py index 2dfaad11b7..fea72e98e9 100644 --- a/src/Mod/Arch/importDAE.py +++ b/src/Mod/Arch/importDAE.py @@ -20,6 +20,8 @@ #*************************************************************************** import FreeCAD, Mesh, os, numpy, MeshPart, Arch, Draft +from draftutils import params + if FreeCAD.GuiUp: from draftutils.translate import translate else: @@ -65,15 +67,14 @@ def triangulate(shape): "triangulates the given face" - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") - mesher = p.GetInt("ColladaMesher",0) - tessellation = p.GetFloat("ColladaTessellation",1.0) - grading = p.GetFloat("ColladaGrading",0.3) - segsperedge = p.GetInt("ColladaSegsPerEdge",1) - segsperradius = p.GetInt("ColladaSegsPerRadius",2) - secondorder = p.GetBool("ColladaSecondOrder",False) - optimize = p.GetBool("ColladaOptimize",True) - allowquads = p.GetBool("ColladaAllowQuads",False) + mesher = params.get_param_arch("ColladaMesher") + tessellation = params.get_param_arch("ColladaTessellation") + grading = params.get_param_arch("ColladaGrading") + segsperedge = params.get_param_arch("ColladaSegsPerEdge") + segsperradius = params.get_param_arch("ColladaSegsPerRadius") + secondorder = params.get_param_arch("ColladaSecondOrder") + optimize = params.get_param_arch("ColladaOptimize") + allowquads = params.get_param_arch("ColladaAllowQuads") if mesher == 0: return shape.tessellate(tessellation) elif mesher == 1: @@ -178,12 +179,9 @@ def export(exportList,filename,tessellation=1,colors=None): curved surfaces into triangles.""" if not checkCollada(): return - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") - scale = p.GetFloat("ColladaScalingFactor",1.0) + scale = params.get_param_arch("ColladaScalingFactor") scale = scale * 0.001 # from millimeters (FreeCAD) to meters (Collada) - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View") - c = p.GetUnsigned("DefaultShapeColor",4294967295) - defaultcolor = (float((c>>24)&0xFF)/255.0,float((c>>16)&0xFF)/255.0,float((c>>8)&0xFF)/255.0) + defaultcolor = Draft.get_rgba_tuple(params.get_param_view("DefaultShapeColor"))[:3] colmesh = collada.Collada() colmesh.assetInfo.upaxis = collada.asset.UP_AXIS.Z_UP # authoring info diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py index d05aac8e8d..bf4d65ad70 100644 --- a/src/Mod/Arch/importIFC.py +++ b/src/Mod/Arch/importIFC.py @@ -42,6 +42,7 @@ import ArchIFCSchema import importIFCHelper import importIFCmulticore +from draftutils import params from draftutils.messages import _msg, _err if FreeCAD.GuiUp: @@ -379,7 +380,7 @@ def insert(srcfile, docname, skip=[], only=[], root=None, preferences=None): if preferences['DEBUG']: print(" no layer found", ptype,end="") # checking for full FreeCAD parametric definition, overriding everything else - if psets and FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("IfcImportFreeCADProperties",False): + if psets and params.get_param_arch("IfcImportFreeCADProperties"): if "FreeCADPropertySet" in [ifcfile[pset].Name for pset in psets.keys()]: if preferences['DEBUG']: print(" restoring from parametric definition...",end="") obj,parametrics = importIFCHelper.createFromProperties(psets,ifcfile,parametrics) diff --git a/src/Mod/Arch/importIFCHelper.py b/src/Mod/Arch/importIFCHelper.py index 3e86fd522e..0939e5fdcb 100644 --- a/src/Mod/Arch/importIFCHelper.py +++ b/src/Mod/Arch/importIFCHelper.py @@ -26,11 +26,12 @@ import FreeCAD import Arch import ArchIFC +from draftutils import params +from draftutils.messages import _msg, _wrn + if FreeCAD.GuiUp: import FreeCADGui as Gui -from draftutils.messages import _msg, _wrn - PREDEFINED_RGB = {"black": (0, 0, 0), "red": (1.0, 0, 0), @@ -82,29 +83,27 @@ def getPreferences(): 2 = Part shapes 3 = One compound per storey """ - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") - - if FreeCAD.GuiUp and p.GetBool("ifcShowDialog", False): + if FreeCAD.GuiUp and params.get_param_arch("ifcShowDialog"): Gui.showPreferences("Import-Export", 0) preferences = { - 'DEBUG': p.GetBool("ifcDebug", False), - 'PREFIX_NUMBERS': p.GetBool("ifcPrefixNumbers", False), - 'SKIP': p.GetString("ifcSkip", "").split(","), - 'SEPARATE_OPENINGS': p.GetBool("ifcSeparateOpenings", False), - 'ROOT_ELEMENT': p.GetString("ifcRootElement", "IfcProduct"), - 'GET_EXTRUSIONS': p.GetBool("ifcGetExtrusions", False), - 'MERGE_MATERIALS': p.GetBool("ifcMergeMaterials", False), - 'MERGE_MODE_ARCH': p.GetInt("ifcImportModeArch", 0), - 'MERGE_MODE_STRUCT': p.GetInt("ifcImportModeStruct", 1), - 'CREATE_CLONES': p.GetBool("ifcCreateClones", True), - 'IMPORT_PROPERTIES': p.GetBool("ifcImportProperties", False), - 'SPLIT_LAYERS': p.GetBool("ifcSplitLayers", False), # wall layer, not layer for visual props - 'FITVIEW_ONIMPORT': p.GetBool("ifcFitViewOnImport", False), - 'ALLOW_INVALID': p.GetBool("ifcAllowInvalid", False), - 'REPLACE_PROJECT': p.GetBool("ifcReplaceProject", False), - 'MULTICORE': p.GetInt("ifcMulticore", 0), - 'IMPORT_LAYER': p.GetBool("ifcImportLayer", True) + 'DEBUG': params.get_param_arch("ifcDebug"), + 'PREFIX_NUMBERS': params.get_param_arch("ifcPrefixNumbers"), + 'SKIP': params.get_param_arch("ifcSkip").split(","), + 'SEPARATE_OPENINGS': params.get_param_arch("ifcSeparateOpenings"), + 'ROOT_ELEMENT': params.get_param_arch("ifcRootElement"), + 'GET_EXTRUSIONS': params.get_param_arch("ifcGetExtrusions"), + 'MERGE_MATERIALS': params.get_param_arch("ifcMergeMaterials"), + 'MERGE_MODE_ARCH': params.get_param_arch("ifcImportModeArch"), + 'MERGE_MODE_STRUCT': params.get_param_arch("ifcImportModeStruct"), + 'CREATE_CLONES': params.get_param_arch("ifcCreateClones"), + 'IMPORT_PROPERTIES': params.get_param_arch("ifcImportProperties"), + 'SPLIT_LAYERS': params.get_param_arch("ifcSplitLayers"), # wall layer, not layer for visual props + 'FITVIEW_ONIMPORT': params.get_param_arch("ifcFitViewOnImport"), + 'ALLOW_INVALID': params.get_param_arch("ifcAllowInvalid"), + 'REPLACE_PROJECT': params.get_param_arch("ifcReplaceProject"), + 'MULTICORE': params.get_param_arch("ifcMulticore"), + 'IMPORT_LAYER': params.get_param_arch("ifcImportLayer") } if preferences['MERGE_MODE_ARCH'] > 0: diff --git a/src/Mod/Arch/importIFClegacy.py b/src/Mod/Arch/importIFClegacy.py index bf2e1a36aa..037e889ce5 100644 --- a/src/Mod/Arch/importIFClegacy.py +++ b/src/Mod/Arch/importIFClegacy.py @@ -27,6 +27,7 @@ import FreeCAD, Arch, Draft, os, sys, time, Part, DraftVecUtils, uuid, math, re +from draftutils import params from draftutils.translate import translate __title__="FreeCAD IFC importer" @@ -82,19 +83,18 @@ def getConfig(): global SKIP, CREATE_IFC_GROUPS, ASMESH, PREFIX_NUMBERS, FORCE_PYTHON_PARSER, SEPARATE_OPENINGS, SEPARATE_PLACEMENTS, JOINSOLIDS, AGGREGATE_WINDOWS IMPORT_IFC_FURNITURE = False ASMESH = ["IfcFurnishingElement"] - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") - CREATE_IFC_GROUPS = p.GetBool("createIfcGroups",False) - FORCE_PYTHON_PARSER = p.GetBool("forceIfcPythonParser",False) - DEBUG = p.GetBool("ifcDebug",False) - SEPARATE_OPENINGS = p.GetBool("ifcSeparateOpenings",False) - SEPARATE_PLACEMENTS = p.GetBool("ifcSeparatePlacements",False) - PREFIX_NUMBERS = p.GetBool("ifcPrefixNumbers",False) - JOINSOLIDS = p.GetBool("ifcJoinSolids",False) - AGGREGATE_WINDOWS = p.GetBool("ifcAggregateWindows",False) - skiplist = p.GetString("ifcSkip","") + CREATE_IFC_GROUPS = params.get_param_arch("createIfcGroups") + FORCE_PYTHON_PARSER = params.get_param_arch("forceIfcPythonParser") + DEBUG = params.get_param_arch("ifcDebug") + SEPARATE_OPENINGS = params.get_param_arch("ifcSeparateOpenings") + SEPARATE_PLACEMENTS = params.get_param_arch("ifcSeparatePlacements") + PREFIX_NUMBERS = params.get_param_arch("ifcPrefixNumbers") + JOINSOLIDS = params.get_param_arch("ifcJoinSolids") + AGGREGATE_WINDOWS = params.get_param_arch("ifcAggregateWindows") + skiplist = params.get_param_arch("ifcSkip") if skiplist: SKIP = skiplist.split(",") - asmeshlist = p.GetString("ifcAsMesh","") + asmeshlist = params.get_param_arch("ifcAsMesh") if asmeshlist: ASMESH = asmeshlist.split(",") @@ -811,7 +811,7 @@ def getVector(entity): def getSchema(): "retrieves the express schema" - custom = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetString("CustomIfcSchema","") + custom = params.get_param_arch("CustomIfcSchema") if custom: if os.path.exists(custom): if DEBUG: print("Using custom schema: ",custom.split(os.sep)[-1]) @@ -943,10 +943,9 @@ def export(exportList,filename): # creating base IFC project getConfig() - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") - scaling = p.GetFloat("IfcScalingFactor",1.0) - exporttxt = p.GetBool("IfcExportList",False) - forcebrep = p.GetBool("ifcExportAsBrep",False) + scaling = params.get_param_arch("IfcScalingFactor") + exporttxt = params.get_param_arch("IfcExportList") + forcebrep = params.get_param_arch("ifcExportAsBrep") application = "FreeCAD" ver = FreeCAD.Version() version = ver[0]+"."+ver[1]+" build"+ver[2] @@ -1394,9 +1393,7 @@ class IfcSchema: def __init__(self, filename): self.filename = filename if not os.path.exists(filename): - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro") - p = p.GetString("MacroPath","") - filename = p + os.sep + filename + filename = FreeCAD.getUserMacroDir(True) + os.sep + filename if not os.path.exists(filename): raise ImportError("no IFCSchema file found!") diff --git a/src/Mod/Arch/importOBJ.py b/src/Mod/Arch/importOBJ.py index 6fbfe04eb0..b69d6d92a7 100644 --- a/src/Mod/Arch/importOBJ.py +++ b/src/Mod/Arch/importOBJ.py @@ -31,6 +31,7 @@ import DraftGeomUtils import Mesh import MeshPart import Part +from draftutils import params if FreeCAD.GuiUp: from draftutils.translate import translate @@ -80,8 +81,7 @@ def getIndices(obj,shape,offsetv,offsetvn): except Exception: # unimplemented curve type hascurve = True if hascurve: - param = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Mesh") - tol = param.GetFloat("MaxDeviationExport",0.1) + tol = params.get_param("MaxDeviationExport",path="Mod/Mesh") mesh = Mesh.Mesh() mesh.addFacets(shape.getFaces(tol)) FreeCAD.Console.PrintWarning(translate("Arch","Found a shape containing curves, triangulating")+"\n") diff --git a/src/Mod/Arch/importSHP.py b/src/Mod/Arch/importSHP.py index 6878f50682..4fe6c88eb3 100644 --- a/src/Mod/Arch/importSHP.py +++ b/src/Mod/Arch/importSHP.py @@ -141,9 +141,7 @@ def checkShapeFileLibrary(): FreeCAD.Console.PrintError(translate("Arch","Error: Unable to download from:")+" "+url+"\n") return False b = u.read() - p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro") - fp = p.GetString("MacroPath",os.path.join(FreeCAD.getUserAppDataDir(),"Macros")) - fp = os.path.join(fp,"shapefile.py") + fp = os.path.join(FreeCAD.getUserMacroDir(True),"shapefile.py") f = pythonopen(fp,"wb") f.write(b) f.close() diff --git a/src/Mod/Draft/draftutils/params.py b/src/Mod/Draft/draftutils/params.py index fce3a67fc9..09c47218a6 100644 --- a/src/Mod/Draft/draftutils/params.py +++ b/src/Mod/Draft/draftutils/params.py @@ -393,8 +393,91 @@ def _get_param_dictionary(): # Arch parameters that are not in the preferences: param_dict["Mod/Arch"] = { + "ClaimHosted": ("bool", True), + "CustomIfcSchema": ("string", ""), # importIFClegacy.py + "createIfcGroups": ("bool", False), # importIFClegacy.py + "DoorHeight": ("float", 2100.0), + "DoorPreset": ("int", 5), + "DoorSill": ("float", 0.0), + "DoorWidth": ("float", 1000.0), + "FreeLinking": ("bool", False), + "forceIfcPythonParser": ("bool", False), # importIFClegacy.py + "getStandardType": ("bool", False), + "ifcAggregateWindows": ("bool", False), # importIFClegacy.py + "ifcAsMesh": ("string", ""), # importIFClegacy.py + "IfcExportList": ("bool", False), # importIFClegacy.py + "ifcImportLayer": ("bool", True), + "ifcJoinSolids": ("bool", False), # importIFClegacy.py + "ifcMergeProfiles": ("bool", False), + "IfcScalingFactor": ("float", 1.0), # importIFClegacy.py + "ifcSeparatePlacements": ("bool", False), # importIFClegacy.py + "MultiMaterialColumnWidth0": ("int", 60), + "MultiMaterialColumnWidth1": ("int", 60), + "PanelLength": ("float", 1000.0), + "PanelThickness": ("float", 10.0), + "PanelWidth": ("float", 1000.0), + "PrecastBase": ("float", 0.0), + "PrecastChamfer": ("float", 0.0), + "PrecastDentHeight": ("float", 0.0), + "PrecastDentLength": ("float", 0.0), + "PrecastDentWidth": ("float", 0.0), + "PrecastDownLength": ("float", 0.0), + "PrecastGrooveDepth": ("float", 0.0), + "PrecastGrooveHeight": ("float", 0.0), + "PrecastGrooveSpacing": ("float", 0.0), + "PrecastHoleMajor": ("float", 0.0), + "PrecastHoleMinor": ("float", 0.0), + "PrecastHoleSpacing": ("float", 0.0), + "PrecastRiser": ("float", 0.0), + "PrecastTread": ("float", 0.0), + "ScheduleColumnWidth0": ("int", 100), + "ScheduleColumnWidth1": ("int", 100), + "ScheduleColumnWidth2": ("int", 50), + "ScheduleColumnWidth3": ("int", 100), + "ScheduleDialogHeight": ("int", 200), + "ScheduleDialogWidth": ("int", 300), + "StructureHeight": ("float", 1000.0), + "StructureLength": ("float", 100.0), + "StructurePreset": ("string", ""), + "StructureWidth": ("float", 100.0), + "swallowAdditions": ("bool", True), + "swallowSubtractions": ("bool", True), + "WallAlignment": ("int", 0), + "WallHeight": ("float", 3000.0), + "WallWidth": ("float", 200.0), + "WindowH1": ("float", 50.0), + "WindowH2": ("float", 50.0), + "WindowH3": ("float", 50.0), + "WindowHeight": ("float", 1000.0), + "WindowO1": ("float", 0.0), + "WindowO2": ("float", 50.0), + "WindowPreset": ("int", 0), + "WindowSill": ("float", 0.0), + "WindowW1": ("float", 100.0), + "WindowW2": ("float", 50.0), + "WindowWidth": ("float", 1000.0), + } + # For the Mod/Mesh parameters we do not check the preferences: + param_dict["Mod/Mesh"] = { + "MaxDeviationExport": ("float", 0.1), + } + # For the Mod/TechDraw/PAT parameters we do not check the preferences: + param_dict["Mod/TechDraw/PAT"] = { + "FilePattern": ("string", ""), + "NamePattern": ("string", "Diamant"), + } + + # For the General parameters we do not check the preferences: + param_dict["General"] = { + "ToolbarIconSize": ("int", 24), + } + + # For the Units parameters we do not check the preferences: + param_dict["Units"] = { + "Decimals": ("int", 2), + "UserSchema": ("int", 0), } # For the View parameters we do not check the preferences: @@ -414,23 +497,6 @@ def _get_param_dictionary(): "NewDocumentCameraScale": ("float", 100.0), } - # For the General parameters we do not check the preferences: - param_dict["General"] = { - "ToolbarIconSize": ("int", 24), - } - - # For the Units parameters we do not check the preferences: - param_dict["Units"] = { - "Decimals": ("int", 2), - "UserSchema": ("int", 0), - } - - # For the Mod/TechDraw/PAT parameters we do not check the preferences: - param_dict["Mod/TechDraw/PAT"] = { - "FilePattern": ("string", ""), - "NamePattern": ("string", "Diamant"), - } - # Preferences ui files are stored in resource files. # For the Draft Workbench: /Mod/Draft/Draft_rc.py From 5e67755b4f274f62c226f9be8e10e950e5f8128a Mon Sep 17 00:00:00 2001 From: Roy-043 Date: Fri, 12 Jan 2024 12:24:01 +0100 Subject: [PATCH 038/135] Multiply textheight by DefaultAnnoScaleMultiplier --- src/Mod/Arch/ArchAxis.py | 4 ++-- src/Mod/Arch/ArchBuildingPart.py | 2 +- src/Mod/Arch/ArchSectionPlane.py | 2 +- src/Mod/Arch/ArchSpace.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Mod/Arch/ArchAxis.py b/src/Mod/Arch/ArchAxis.py index b23aff4612..7384fef8ed 100644 --- a/src/Mod/Arch/ArchAxis.py +++ b/src/Mod/Arch/ArchAxis.py @@ -226,11 +226,11 @@ class _ViewProviderAxis: def setProperties(self,vobj): - ts = params.get_param("textheight") + ts = params.get_param("textheight") * params.get_param("DefaultAnnoScaleMultiplier") pl = vobj.PropertiesList if not "BubbleSize" in pl: vobj.addProperty("App::PropertyLength","BubbleSize","Axis", QT_TRANSLATE_NOOP("App::Property","The size of the axis bubbles")) - vobj.BubbleSize = ts*1.42 + vobj.BubbleSize = ts * 1.42 if not "NumberingStyle" in pl: vobj.addProperty("App::PropertyEnumeration","NumberingStyle","Axis", QT_TRANSLATE_NOOP("App::Property","The numbering style")) vobj.NumberingStyle = ["1,2,3","01,02,03","001,002,003","A,B,C","a,b,c","I,II,III","L0,L1,L2"] diff --git a/src/Mod/Arch/ArchBuildingPart.py b/src/Mod/Arch/ArchBuildingPart.py index e327004fcc..c7fa948cf0 100644 --- a/src/Mod/Arch/ArchBuildingPart.py +++ b/src/Mod/Arch/ArchBuildingPart.py @@ -572,7 +572,7 @@ class ViewProviderBuildingPart: vobj.FontName = params.get_param("textfont") if not "FontSize" in pl: vobj.addProperty("App::PropertyLength","FontSize","BuildingPart",QT_TRANSLATE_NOOP("App::Property","The font size of texts")) - vobj.FontSize = params.get_param("textheight") + vobj.FontSize = params.get_param("textheight") * params.get_param("DefaultAnnoScaleMultiplier") if not "DiffuseColor" in pl: vobj.addProperty("App::PropertyColorList","DiffuseColor","BuildingPart",QT_TRANSLATE_NOOP("App::Property","The individual face colors")) diff --git a/src/Mod/Arch/ArchSectionPlane.py b/src/Mod/Arch/ArchSectionPlane.py index ae3f058c85..33596ee4ec 100644 --- a/src/Mod/Arch/ArchSectionPlane.py +++ b/src/Mod/Arch/ArchSectionPlane.py @@ -971,7 +971,7 @@ class _ViewProviderSectionPlane: vobj.FontName = params.get_param("textfont") if not "FontSize" in pl: vobj.addProperty("App::PropertyLength","FontSize","SectionPlane",QT_TRANSLATE_NOOP("App::Property","The size of the text font")) - vobj.FontSize = params.get_param("textheight") + vobj.FontSize = params.get_param("textheight") * params.get_param("DefaultAnnoScaleMultiplier") def onDocumentRestored(self,vobj): diff --git a/src/Mod/Arch/ArchSpace.py b/src/Mod/Arch/ArchSpace.py index ebe2b56fe1..3fcc7af653 100644 --- a/src/Mod/Arch/ArchSpace.py +++ b/src/Mod/Arch/ArchSpace.py @@ -519,10 +519,10 @@ class _ViewProviderSpace(ArchComponent.ViewProviderComponent): vobj.TextColor = (0.0,0.0,0.0,1.0) if not "FontSize" in pl: vobj.addProperty("App::PropertyLength", "FontSize", "Space",QT_TRANSLATE_NOOP("App::Property","The size of the text font")) - vobj.FontSize = params.get_param("textheight") + vobj.FontSize = params.get_param("textheight") * params.get_param("DefaultAnnoScaleMultiplier") if not "FirstLine" in pl: vobj.addProperty("App::PropertyLength", "FirstLine", "Space",QT_TRANSLATE_NOOP("App::Property","The size of the first line of text")) - vobj.FirstLine = params.get_param("textheight") + vobj.FirstLine = params.get_param("textheight") * params.get_param("DefaultAnnoScaleMultiplier") if not "LineSpacing" in pl: vobj.addProperty("App::PropertyFloat", "LineSpacing", "Space",QT_TRANSLATE_NOOP("App::Property","The space between the lines of text")) vobj.LineSpacing = 1.0 From fae1cd569fb92d690937464b3d7f74cebcf1c784 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Thu, 18 Jan 2024 11:46:15 -0500 Subject: [PATCH 039/135] Lint and other cleanups --- src/Mod/Part/App/TopoShapeMapper.cpp | 132 ++++++++++++--------- src/Mod/Part/App/TopoShapeMapper.h | 126 ++++++++++++-------- tests/src/Mod/Part/App/TopoShapeMapper.cpp | 89 ++++++++++---- 3 files changed, 222 insertions(+), 125 deletions(-) diff --git a/src/Mod/Part/App/TopoShapeMapper.cpp b/src/Mod/Part/App/TopoShapeMapper.cpp index 89ea456a40..69f9653d18 100644 --- a/src/Mod/Part/App/TopoShapeMapper.cpp +++ b/src/Mod/Part/App/TopoShapeMapper.cpp @@ -3,73 +3,95 @@ namespace Part { -void ShapeMapper::expand(const TopoDS_Shape &d, std::vector &shapes) +void ShapeMapper::expand(const TopoDS_Shape& d, std::vector& shapes) { - if (d.IsNull()) return; - for(TopExp_Explorer xp(d, TopAbs_FACE);xp.More();xp.Next()) - shapes.push_back(xp.Current()); - for(TopExp_Explorer xp(d, TopAbs_EDGE, TopAbs_FACE);xp.More();xp.Next()) - shapes.push_back(xp.Current()); - for(TopExp_Explorer xp(d, TopAbs_VERTEX, TopAbs_EDGE);xp.More();xp.Next()) - shapes.push_back(xp.Current()); -} - -void ShapeMapper::populate(bool generated, - const TopTools_ListOfShape &src, - const TopTools_ListOfShape &dst) -{ - for(TopTools_ListIteratorOfListOfShape it(src);it.More();it.Next()) - populate(generated, it.Value(), dst); -} - -void ShapeMapper::populate(bool generated, - const TopoShape &src, - const TopTools_ListOfShape &dst) -{ - if(src.isNull()) + if (d.IsNull()) { return; - std::vector dstShapes; - for(TopTools_ListIteratorOfListOfShape it(dst);it.More();it.Next()) - expand(it.Value(), dstShapes); - insert(generated, src.getShape(), dstShapes); + } + for (TopExp_Explorer xp(d, TopAbs_FACE); xp.More(); xp.Next()) { + shapes.push_back(xp.Current()); + } + for (TopExp_Explorer xp(d, TopAbs_EDGE, TopAbs_FACE); xp.More(); xp.Next()) { + shapes.push_back(xp.Current()); + } + for (TopExp_Explorer xp(d, TopAbs_VERTEX, TopAbs_EDGE); xp.More(); xp.Next()) { + shapes.push_back(xp.Current()); + } } -void ShapeMapper::insert(bool generated, const TopoDS_Shape &s, const TopoDS_Shape &d) +void ShapeMapper::populate(MappingStatus status, + const TopTools_ListOfShape& src, + const TopTools_ListOfShape& dst) { - if (s.IsNull() || d.IsNull()) return; + for (TopTools_ListIteratorOfListOfShape it(src); it.More(); it.Next()) { + populate(status, it.Value(), dst); + } +} + +void ShapeMapper::populate(MappingStatus status, + const TopoShape& src, + const TopTools_ListOfShape& dst) +{ + if (src.isNull()) { + return; + } + std::vector dstShapes; + for (TopTools_ListIteratorOfListOfShape it(dst); it.More(); it.Next()) { + expand(it.Value(), dstShapes); + } + insert(status, src.getShape(), dstShapes); +} + +void ShapeMapper::insert(MappingStatus status, const TopoDS_Shape& s, const TopoDS_Shape& d) +{ + if (s.IsNull() || d.IsNull()) { + return; + } // Prevent an element shape from being both generated and modified - if (generated) { - if (_modifiedShapes.count(d)) + if (status == MappingStatus::Generated) { + if (_modifiedShapes.count(d)) { return; + } _generatedShapes.insert(d); - } else { - if( _generatedShapes.count(d)) + } + else { + if (_generatedShapes.count(d)) { return; + } _modifiedShapes.insert(d); } - auto &entry = generated?_generated[s]:_modified[s]; - if(entry.shapeSet.insert(d).second) + auto& entry = (status == MappingStatus::Generated) ? _generated[s] : _modified[s]; + if (entry.shapeSet.insert(d).second) { entry.shapes.push_back(d); -}; - -void ShapeMapper::insert(bool generated, const TopoDS_Shape &s, const std::vector &d) -{ - if (s.IsNull() || d.empty()) return; - auto &entry = generated?_generated[s]:_modified[s]; - for(auto &shape : d) { - // Prevent an element shape from being both generated and modified - if (generated) { - if (_modifiedShapes.count(shape)) - continue; - _generatedShapes.insert(shape); - } else { - if( _generatedShapes.count(shape)) - continue; - _modifiedShapes.insert(shape); - } - if(entry.shapeSet.insert(shape).second) - entry.shapes.push_back(shape); } }; -} +void ShapeMapper::insert(MappingStatus status, + const TopoDS_Shape& s, + const std::vector& d) +{ + if (s.IsNull() || d.empty()) { + return; + } + auto& entry = (status == MappingStatus::Generated) ? _generated[s] : _modified[s]; + for (auto& shape : d) { + // Prevent an element shape from being both generated and modified + if (status == MappingStatus::Generated) { + if (_modifiedShapes.count(shape)) { + continue; + } + _generatedShapes.insert(shape); + } + else { + if (_generatedShapes.count(shape)) { + continue; + } + _modifiedShapes.insert(shape); + } + if (entry.shapeSet.insert(shape).second) { + entry.shapes.push_back(shape); + } + } +}; + +} // namespace Part diff --git a/src/Mod/Part/App/TopoShapeMapper.h b/src/Mod/Part/App/TopoShapeMapper.h index 89080ae46e..5b67c4deb2 100644 --- a/src/Mod/Part/App/TopoShapeMapper.h +++ b/src/Mod/Part/App/TopoShapeMapper.h @@ -12,39 +12,45 @@ class BRepTools_History; class BRepTools_ReShape; class ShapeFix_Root; -namespace Part +namespace Part { /// Shape hasher that ignore orientation -struct ShapeHasher { - inline size_t operator()(const TopoShape &s) const { +struct ShapeHasher +{ + inline size_t operator()(const TopoShape& s) const + { #if OCC_VERSION_HEX >= 0x070800 return std::hash {}(s.getShape()); #else return s.getShape().HashCode(INT_MAX); #endif } - inline size_t operator()(const TopoDS_Shape &s) const { + inline size_t operator()(const TopoDS_Shape& s) const + { #if OCC_VERSION_HEX >= 0x070800 return std::hash {}(s); #else return s.HashCode(INT_MAX); #endif } - inline bool operator()(const TopoShape &a, const TopoShape &b) const { + inline bool operator()(const TopoShape& a, const TopoShape& b) const + { return a.getShape().IsSame(b.getShape()); } - inline bool operator()(const TopoDS_Shape &a, const TopoDS_Shape &b) const { + inline bool operator()(const TopoDS_Shape& a, const TopoDS_Shape& b) const + { return a.IsSame(b); } - template + template static inline void hash_combine(std::size_t& seed, const T& v) { // copied from boost::hash_combine std::hash hasher; - seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); + seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); } - inline size_t operator()(const std::pair &s) const { + inline size_t operator()(const std::pair& s) const + { #if OCC_VERSION_HEX >= 0x070800 size_t res = std::hash {}(s.first.getShape()); hash_combine(res, std::hash {}(s.second.getShape())); @@ -54,7 +60,8 @@ struct ShapeHasher { #endif return res; } - inline size_t operator()(const std::pair &s) const { + inline size_t operator()(const std::pair& s) const + { #if OCC_VERSION_HEX >= 0x070800 size_t res = std::hash {}(s.first); hash_combine(res, std::hash {}(s.second)); @@ -64,130 +71,150 @@ struct ShapeHasher { #endif return res; } - inline bool operator()(const std::pair &a, - const std::pair &b) const { + inline bool operator()(const std::pair& a, + const std::pair& b) const + { return a.first.getShape().IsSame(b.first.getShape()) && a.second.getShape().IsSame(b.second.getShape()); } - inline bool operator()(const std::pair &a, - const std::pair &b) const { - return a.first.IsSame(b.first) - && a.second.IsSame(b.second); + inline bool operator()(const std::pair& a, + const std::pair& b) const + { + return a.first.IsSame(b.first) && a.second.IsSame(b.second); } }; +enum class MappingStatus +{ + Generated, + Modified +}; /** Shape mapper for user defined shape mapping */ -struct PartExport ShapeMapper: TopoShape::Mapper { +struct PartExport ShapeMapper: TopoShape::Mapper +{ virtual ~ShapeMapper() noexcept = default; /** Populate mapping from a source shape to a list of shape * - * @param generated: whether the shape is generated + * @param status: whether the shape is generated * @param src: source shape * @param dst: a list of sub shapes in the new shape * * The source will be expanded into sub shapes of faces, edges and vertices * before being inserted into the map. */ - void populate(bool generated, const TopoShape &src, const TopTools_ListOfShape &dst); + void populate(MappingStatus status, const TopoShape& src, const TopTools_ListOfShape& dst); /** Populate mapping from a source sub shape to a list of shape * - * @param generated: whether the shape is generated + * @param status: whether the shape is generated * @param src: a list of sub shapes in the source shape * @param dst: a list of sub shapes in the new shape * * The source will be expanded into sub shapes of faces, edges and vertices * before being inserted into the map. */ - void populate(bool generated, const TopTools_ListOfShape &src, const TopTools_ListOfShape &dst); + void populate(MappingStatus status, + const TopTools_ListOfShape& src, + const TopTools_ListOfShape& dst); /** Populate mapping from a source sub shape to a list of shape * - * @param generated: whether the shape is generated + * @param status: whether the shape is generated * @param src: a list of sub shapes in the source shape * @param dst: a list of sub shapes in the new shape * * The source will be expanded into sub shapes of faces, edges and vertices * before being inserted into the map. */ - void populate(bool generated, const std::vector &src, const std::vector &dst) + void populate(MappingStatus status, + const std::vector& src, + const std::vector& dst) { - for(auto &s : src) - populate(generated,s,dst); + for (auto& s : src) { + populate(status, s, dst); + } } /** Populate mapping from a source sub shape to a list of shape * - * @param generated: whether the shape is generated + * @param status: whether the shape is generated * @param src: a sub shape of the source shape * @param dst: a list of sub shapes in the new shape * * The source will be expanded into sub shapes of faces, edges and vertices * before being inserted into the map. */ - void populate(bool generated, const TopoShape &src, const std::vector &dst) + void populate(MappingStatus status, const TopoShape& src, const std::vector& dst) { - if(src.isNull()) + if (src.isNull()) { return; + } std::vector dstShapes; - for(auto &d : dst) + for (auto& d : dst) { expand(d.getShape(), dstShapes); - insert(generated, src.getShape(), dstShapes); + } + insert(status, src.getShape(), dstShapes); } /** Expand a shape into faces, edges and vertices * @params d: shape to expand * @param shapes: output sub shapes of faces, edges and vertices */ - void expand(const TopoDS_Shape &d, std::vector &shapes); + void expand(const TopoDS_Shape& d, std::vector& shapes); /** Insert a map entry from a sub shape in the source to a list of sub shapes in the new shape * - * @params generated: whether the sub shapes are generated or modified + * @params status: whether the sub shapes are generated or modified * @param s: a sub shape in the source * @param d: a list of sub shapes in the new shape */ - void insert(bool generated, const TopoDS_Shape &s, const std::vector &d); + void insert(MappingStatus status, const TopoDS_Shape& s, const std::vector& d); /** Insert a map entry from a sub shape in the source to a sub shape in the new shape * - * @params generated: whether the sub shapes are generated or modified + * @params status: whether the sub shapes are generated or modified * @param s: a sub shape in the source * @param d: a list of sub shapes in the new shape */ - void insert(bool generated, const TopoDS_Shape &s, const TopoDS_Shape &d); + void insert(MappingStatus status, const TopoDS_Shape& s, const TopoDS_Shape& d); - virtual const std::vector &generated(const TopoDS_Shape &s) const override { + const std::vector& generated(const TopoDS_Shape& s) const override + { auto iter = _generated.find(s); - if(iter != _generated.end()) + if (iter != _generated.end()) { return iter->second.shapes; + } return _res; } - virtual const std::vector &modified(const TopoDS_Shape &s) const override { + const std::vector& modified(const TopoDS_Shape& s) const override + { auto iter = _modified.find(s); - if(iter != _modified.end()) + if (iter != _modified.end()) { return iter->second.shapes; + } return _res; } std::vector shapes; - std::unordered_set shapeSet; + std::unordered_set shapeSet; - struct ShapeValue { + struct ShapeValue + { std::vector shapes; - std::unordered_set shapeSet; + std::unordered_set shapeSet; }; - typedef std::unordered_map ShapeMap; + typedef std::unordered_map ShapeMap; ShapeMap _generated; - std::unordered_set _generatedShapes; + std::unordered_set _generatedShapes; ShapeMap _modified; - std::unordered_set _modifiedShapes; + std::unordered_set _modifiedShapes; }; -/// Parameters for TopoShape::makEFilledFace() -struct PartExport TopoShape::BRepFillingParams { +/// Parameters for TopoShape::makeElementFilledFace() +struct PartExport TopoShape::BRepFillingParams +{ /** Optional initial surface to begin the construction of the surface for the filled face. * * It is useful if the surface resulting from construction for the @@ -211,7 +238,8 @@ struct PartExport TopoShape::BRepFillingParams { std::unordered_map supports; /// Optional begin index to the input shapes to be used as the boundary of the filled face. int boundary_begin = -1; - /// Optional end index (last index + 1) to the input shapes to be used as the boundary of the filled face. + /// Optional end index (last index + 1) to the input shapes to be used as the boundary of the + /// filled face. int boundary_end = -1; /// The energe minimizing criterion degree; unsigned int degree = 3; @@ -242,4 +270,4 @@ struct PartExport TopoShape::BRepFillingParams { }; -} +} // namespace Part diff --git a/tests/src/Mod/Part/App/TopoShapeMapper.cpp b/tests/src/Mod/Part/App/TopoShapeMapper.cpp index 6f23f19b8d..88c8fa18f6 100644 --- a/tests/src/Mod/Part/App/TopoShapeMapper.cpp +++ b/tests/src/Mod/Part/App/TopoShapeMapper.cpp @@ -38,7 +38,50 @@ private: std::string _docName; }; -TEST_F(TopoShapeMapperTest, shapeHasherTests) +TEST_F(TopoShapeMapperTest, shapeHasherSingle) +{ + // Arrange + auto edge = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge(); + Part::TopoShape topoShape {edge}; + auto edge2 = BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge(); + Part::TopoShape topoShape2 {edge2}; + struct Part::ShapeHasher hasher; + + // Act + size_t hash1 = hasher(topoShape); + size_t hash2 = hasher(topoShape2); + size_t hash3 = hasher(edge); + size_t hash4 = hasher(edge2); + + // Assert + EXPECT_EQ(hash1, hash3); + EXPECT_EQ(hash2, hash4); + EXPECT_NE(hash1, hash2); +} + +TEST_F(TopoShapeMapperTest, shapeHasherDual) +{ + // Arrange + auto edge = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge(); + Part::TopoShape topoShape {edge}; + auto edge2 = BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge(); + Part::TopoShape topoShape2 {edge2}; + struct Part::ShapeHasher hasher; + + // Act + size_t hash5 = hasher(topoShape, topoShape); + size_t hash6 = hasher(topoShape, topoShape2); + size_t hash7 = hasher(edge, edge); + size_t hash8 = hasher(edge, edge2); + + // Assert + EXPECT_TRUE(hash5); + EXPECT_FALSE(hash6); + EXPECT_TRUE(hash7); + EXPECT_FALSE(hash8); +} + +TEST_F(TopoShapeMapperTest, shapeHasherPair) { // Arrange auto edge = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge(); @@ -52,40 +95,44 @@ TEST_F(TopoShapeMapperTest, shapeHasherTests) struct Part::ShapeHasher hasher; // Act - size_t hash1 = hasher(topoShape); - size_t hash2 = hasher(topoShape2); - size_t hash3 = hasher(edge); - size_t hash4 = hasher(edge2); - size_t hash5 = hasher(topoShape, topoShape); - size_t hash6 = hasher(topoShape, topoShape2); - size_t hash7 = hasher(edge, edge); - size_t hash8 = hasher(edge, edge2); size_t hash9 = hasher(pair1); size_t hash10 = hasher(pair2); size_t hash11 = hasher(pair3); size_t hash12 = hasher(pair4); + + // Assert + EXPECT_EQ(hash9, hash11); + EXPECT_EQ(hash10, hash12); + EXPECT_NE(hash9, hash10); +} + +TEST_F(TopoShapeMapperTest, shapeHasherPairs) +{ + // Arrange + auto edge = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge(); + Part::TopoShape topoShape {edge}; + auto edge2 = BRepBuilderAPI_MakeEdge(gp_Pnt(1.0, 0.0, 0.0), gp_Pnt(1.0, 1.0, 0.0)).Edge(); + Part::TopoShape topoShape2 {edge2}; + std::pair pair1(topoShape, topoShape2); + std::pair pair2(topoShape, topoShape); + std::pair pair3(edge, edge2); + std::pair pair4(edge, edge); + struct Part::ShapeHasher hasher; + + // Act size_t hash13 = hasher(pair1, pair1); size_t hash14 = hasher(pair1, pair2); size_t hash15 = hasher(pair3, pair3); size_t hash16 = hasher(pair3, pair4); // Assert - EXPECT_EQ(hash1, hash3); - EXPECT_EQ(hash2, hash4); - EXPECT_NE(hash1, hash2); - EXPECT_TRUE(hash5); - EXPECT_FALSE(hash6); - EXPECT_TRUE(hash7); - EXPECT_FALSE(hash8); - EXPECT_EQ(hash9, hash11); - EXPECT_EQ(hash10, hash12); - EXPECT_NE(hash9, hash10); EXPECT_TRUE(hash13); EXPECT_FALSE(hash14); EXPECT_TRUE(hash15); EXPECT_FALSE(hash16); } + TEST_F(TopoShapeMapperTest, shapeMapperTests) { // Arrange @@ -97,8 +144,8 @@ TEST_F(TopoShapeMapperTest, shapeMapperTests) // Act auto e = topoShape1.getSubTopoShapes(TopAbs_EDGE); - mapper.populate(false, box1, {e[0], e[1], e[2], e[3]}); - mapper.populate(true, box1, {e[4], e[5], e[6]}); + mapper.populate(Part::MappingStatus::Modified, box1, {e[0], e[1], e[2], e[3]}); + mapper.populate(Part::MappingStatus::Generated, box1, {e[4], e[5], e[6]}); std::vector vec1 = mapper.modified(box1); std::vector vec2 = mapper.generated(box1); From 874d421fa825a912583736cd2d9d7259f38eba02 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Thu, 18 Jan 2024 13:15:21 -0500 Subject: [PATCH 040/135] Split test cases --- tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 67 ++++++++++++++----- 1 file changed, 51 insertions(+), 16 deletions(-) diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index 360898d323..3d71be8070 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -162,23 +162,19 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoCubes) } -TEST_F(TopoShapeExpansionTest, mapSubElement) +TEST_F(TopoShapeExpansionTest, mapSubElementOneCube) { // Arrange auto [cube1, cube2] = CreateTwoCubes(); - Part::TopoShape cube1TS {cube1}; - cube1TS.Tag = 1; - Part::TopoShape cube2TS {cube2}; - cube2TS.Tag = 2; auto [cube3, cube4] = CreateTwoCubes(); - Part::TopoShape cube3TS {cube3}; - cube3TS.Tag = 3; - Part::TopoShape cube4TS {cube4}; - cube4TS.Tag = 4; + Part::TopoShape cube1TS {cube1}; + Part::TopoShape cube2TS {cube2}; + cube1TS.Tag = 1; + cube2TS.Tag = 2; std::vector subShapes = cube1TS.getSubTopoShapes(TopAbs_FACE); Part::TopoShape face1 = subShapes.front(); - face1.Tag = 3; - Part::TopoShape topoShape, topoShape1; + face1.Tag = 5; + Part::TopoShape topoShape; // Act int fs1 = topoShape.findShape(cube1TS.getShape()); @@ -187,12 +183,11 @@ TEST_F(TopoShapeExpansionTest, mapSubElement) // The cache ancestry only works on TopAbs_SHAPE so this is likely an invalid call, // but do we defend against it or expect the exception? - topoShape.mapSubElement( - cube1TS); // Once we do this map, it's in the ancestry cache forevermore + topoShape.mapSubElement(cube1TS); int fs2 = topoShape.findShape(cube1TS.getShape()); int fs3 = topoShape.findShape(face1.getShape()); - int size1 = topoShape.getElementMap().size(); int size0 = cube1TS.getElementMap().size(); + int size1 = topoShape.getElementMap().size(); // Assert EXPECT_EQ(fs1, 0); @@ -200,19 +195,58 @@ TEST_F(TopoShapeExpansionTest, mapSubElement) EXPECT_EQ(fs3, 1); EXPECT_EQ(0, size0); EXPECT_EQ(26, size1); +} + +TEST_F(TopoShapeExpansionTest, mapSubElementSetReset) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + auto [cube3, cube4] = CreateTwoCubes(); + Part::TopoShape cube1TS {cube1}; + Part::TopoShape cube2TS {cube2}; + Part::TopoShape cube3TS {cube3}; + Part::TopoShape cube4TS {cube4}; + cube1TS.Tag = 1; + cube2TS.Tag = 2; + cube3TS.Tag = 3; + cube4TS.Tag = 4; + std::vector subShapes = cube1TS.getSubTopoShapes(TopAbs_FACE); + Part::TopoShape face1 = subShapes.front(); + face1.Tag = 5; + Part::TopoShape topoShape, topoShape1; // Act topoShape.setShape(TopoDS_Shape()); int fs4 = topoShape.findShape(cube1TS.getShape()); - topoShape.setShape(cube1, true); + topoShape.setShape(cube1, false); + // topoShape.mapSubElement(cube1TS); // No mapSubElement required, it keeps finding it now int fs5 = topoShape.findShape(cube1TS.getShape()); - topoShape.setShape(cube1, false); + topoShape.setShape(cube1, true); int fs6 = topoShape.findShape(cube1TS.getShape()); // Assert EXPECT_EQ(fs4, 0); EXPECT_EQ(fs5, 1); EXPECT_EQ(fs6, 1); +} + +TEST_F(TopoShapeExpansionTest, mapSubElementCompoundCubes) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + auto [cube3, cube4] = CreateTwoCubes(); + Part::TopoShape cube1TS {cube1}; + Part::TopoShape cube2TS {cube2}; + Part::TopoShape cube3TS {cube3}; + Part::TopoShape cube4TS {cube4}; + cube1TS.Tag = 1; + cube2TS.Tag = 2; + cube3TS.Tag = 3; + cube4TS.Tag = 4; + std::vector subShapes = cube1TS.getSubTopoShapes(TopAbs_FACE); + Part::TopoShape face1 = subShapes.front(); + face1.Tag = 5; + Part::TopoShape topoShape, topoShape1; // Act Part::TopoShape topoShape2, topoShape3; @@ -248,4 +282,5 @@ TEST_F(TopoShapeExpansionTest, mapSubElement) EXPECT_EQ(fs12, 4); } + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From 238e8acda55a4234c4342e0294f2e1ce46659bc6 Mon Sep 17 00:00:00 2001 From: wandererfan Date: Wed, 17 Jan 2024 20:11:59 -0500 Subject: [PATCH 041/135] [TD]fix Section scale for DPGI base views --- src/Mod/TechDraw/App/DrawProjGroupItem.cpp | 10 ++++++ src/Mod/TechDraw/App/DrawProjGroupItem.h | 1 + src/Mod/TechDraw/App/DrawView.h | 1 + src/Mod/TechDraw/Gui/TaskSectionView.cpp | 38 +++++++--------------- 4 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/Mod/TechDraw/App/DrawProjGroupItem.cpp b/src/Mod/TechDraw/App/DrawProjGroupItem.cpp index 17755f4ea8..d49cbedaa4 100644 --- a/src/Mod/TechDraw/App/DrawProjGroupItem.cpp +++ b/src/Mod/TechDraw/App/DrawProjGroupItem.cpp @@ -329,6 +329,16 @@ double DrawProjGroupItem::getScale(void) const return 1.0; } +int DrawProjGroupItem::getScaleType() const +{ + auto pgroup = getPGroup(); + if (pgroup) { + return pgroup->getScaleType(); + } + + return ScaleType.getValue(); +} + void DrawProjGroupItem::unsetupObject() { if (!getPGroup()) { diff --git a/src/Mod/TechDraw/App/DrawProjGroupItem.h b/src/Mod/TechDraw/App/DrawProjGroupItem.h index 7e1a93fe4d..1a15f68241 100644 --- a/src/Mod/TechDraw/App/DrawProjGroupItem.h +++ b/src/Mod/TechDraw/App/DrawProjGroupItem.h @@ -86,6 +86,7 @@ public: const bool flip=true) const override; double getScale() const override; + int getScaleType() const override; void autoPosition(); bool isAnchor() const; diff --git a/src/Mod/TechDraw/App/DrawView.h b/src/Mod/TechDraw/App/DrawView.h index 4082c1fcb3..7bd6adce88 100644 --- a/src/Mod/TechDraw/App/DrawView.h +++ b/src/Mod/TechDraw/App/DrawView.h @@ -100,6 +100,7 @@ public: void showProgressMessage(std::string featureName, std::string text); virtual double getScale(void) const; + virtual int getScaleType() const { return ScaleType.getValue(); }; void checkScale(void); virtual void handleXYLock(void); diff --git a/src/Mod/TechDraw/Gui/TaskSectionView.cpp b/src/Mod/TechDraw/Gui/TaskSectionView.cpp index 059145bacc..3eaa4a937d 100644 --- a/src/Mod/TechDraw/Gui/TaskSectionView.cpp +++ b/src/Mod/TechDraw/Gui/TaskSectionView.cpp @@ -126,11 +126,15 @@ void TaskSectionView::setUiPrimary() // Base::Console().Message("TSV::setUiPrimary()\n"); setWindowTitle(QObject::tr("Create Section View")); + // note DPGI will have a custom scale type and scale = 1.0. In this case, + // we need the values from the parent DPG! ui->sbScale->setValue(m_base->getScale()); - ui->cmbScaleType->setCurrentIndex(m_base->ScaleType.getValue()); + + ui->cmbScaleType->setCurrentIndex(m_base->getScaleType()); //Allow or prevent scale changing initially - if (m_base->ScaleType.isValue("Custom")) { + if (m_base->getScaleType() == 2) { + // custom scale type ui->sbScale->setEnabled(true); } else { @@ -160,7 +164,7 @@ void TaskSectionView::setUiEdit() ui->leSymbol->setText(qTemp); ui->sbScale->setValue(m_section->getScale()); - ui->cmbScaleType->setCurrentIndex(m_section->ScaleType.getValue()); + ui->cmbScaleType->setCurrentIndex(m_section->getScaleType()); //Allow or prevent scale changing initially if (m_section->ScaleType.isValue("Custom")) { ui->sbScale->setEnabled(true); @@ -240,7 +244,7 @@ void TaskSectionView::saveSectionState() if (m_section) { m_saveSymbol = m_section->SectionSymbol.getValue(); m_saveScale = m_section->getScale(); - m_saveScaleType = m_section->ScaleType.getValue(); + m_saveScaleType = m_section->getScaleType(); m_saveNormal = m_section->SectionNormal.getValue(); m_normal = m_saveNormal; m_saveDirection = m_section->Direction.getValue(); @@ -493,7 +497,7 @@ void TaskSectionView::applyAligned() TechDraw::DrawViewSection* TaskSectionView::createSectionView(void) { - // Base::Console().Message("TSV::createSectionView()\n"); + // Base::Console().Message("TSV::createSectionView()\n"); if (!isBaseValid()) { failNoObject(); return nullptr; @@ -535,17 +539,8 @@ TechDraw::DrawViewSection* TaskSectionView::createSectionView(void) m_sectionName.c_str(), ui->sbOrgX->value().getValue(), ui->sbOrgY->value().getValue(), ui->sbOrgZ->value().getValue()); - if (m_scaleEdited) { - // user has changed the scale - Command::doCommand(Command::Doc, "App.ActiveDocument.%s.Scale = %0.7f", + Command::doCommand(Command::Doc, "App.ActiveDocument.%s.Scale = %0.7f", m_sectionName.c_str(), ui->sbScale->value()); - } else { - // scale is untouched, use value from base view - Command::doCommand(Command::Doc, - "App.ActiveDocument.%s.Scale = App.ActiveDocument.%s.Scale", - m_sectionName.c_str(), - baseName.c_str()); - } int scaleType = ui->cmbScaleType->currentIndex(); Command::doCommand(Command::Doc, "App.ActiveDocument.%s.ScaleType = %d", @@ -582,7 +577,7 @@ TechDraw::DrawViewSection* TaskSectionView::createSectionView(void) void TaskSectionView::updateSectionView() { -// Base::Console().Message("TSV::updateSectionView() - m_sectionName: %s\n", m_sectionName.c_str()); + // Base::Console().Message("TSV::updateSectionView() - m_sectionName: %s\n", m_sectionName.c_str()); if (!isSectionValid()) { failNoObject(); return; @@ -611,17 +606,8 @@ void TaskSectionView::updateSectionView() Command::doCommand(Command::Doc, "App.activeDocument().%s.translateLabel('DrawViewSection', 'Section', '%s')", m_sectionName.c_str(), makeSectionLabel(qTemp).c_str()); - if (m_scaleEdited) { - // user has changed the scale - Command::doCommand(Command::Doc, "App.ActiveDocument.%s.Scale = %0.7f", + Command::doCommand(Command::Doc, "App.ActiveDocument.%s.Scale = %0.7f", m_sectionName.c_str(), ui->sbScale->value()); - } else { - // scale is untouched, use value from base view - Command::doCommand(Command::Doc, - "App.ActiveDocument.%s.Scale = App.ActiveDocument.%s.Scale", - m_sectionName.c_str(), - baseName.c_str()); - } int scaleType = ui->cmbScaleType->currentIndex(); Command::doCommand(Command::Doc, "App.ActiveDocument.%s.ScaleType = %d", From 62bd9b5e4ac1bb3fd9d99a95534c3bada611d403 Mon Sep 17 00:00:00 2001 From: wandererfan Date: Wed, 17 Jan 2024 20:41:51 -0500 Subject: [PATCH 042/135] [TD]fix Complex Section scale with DPGI base --- src/Mod/TechDraw/Gui/TaskComplexSection.cpp | 31 +++++---------------- 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/src/Mod/TechDraw/Gui/TaskComplexSection.cpp b/src/Mod/TechDraw/Gui/TaskComplexSection.cpp index df8d8fc3fa..91a7a842c2 100644 --- a/src/Mod/TechDraw/Gui/TaskComplexSection.cpp +++ b/src/Mod/TechDraw/Gui/TaskComplexSection.cpp @@ -138,7 +138,7 @@ void TaskComplexSection::setUiPrimary() setWindowTitle(QObject::tr("New Complex Section")); if (m_baseView) { ui->sbScale->setValue(m_baseView->getScale()); - ui->cmbScaleType->setCurrentIndex(m_baseView->ScaleType.getValue()); + ui->cmbScaleType->setCurrentIndex(m_baseView->getScaleType()); } else { ui->sbScale->setValue(Preferences::scale()); @@ -184,7 +184,7 @@ void TaskComplexSection::setUiEdit() ui->cmbStrategy->setCurrentIndex(m_section->ProjectionStrategy.getValue()); ui->leSymbol->setText(Base::Tools::fromStdString(m_section->SectionSymbol.getValue())); ui->sbScale->setValue(m_section->Scale.getValue()); - ui->cmbScaleType->setCurrentIndex(m_section->ScaleType.getValue()); + ui->cmbScaleType->setCurrentIndex(m_section->getScaleType()); setUiCommon(); @@ -246,7 +246,7 @@ void TaskComplexSection::saveSectionState() if (m_section) { m_saveSymbol = m_section->SectionSymbol.getValue(); m_saveScale = m_section->getScale(); - m_saveScaleType = m_section->ScaleType.getValue(); + m_saveScaleType = m_section->getScaleType(); m_saveNormal = m_section->SectionNormal.getValue(); m_saveDirection = m_section->Direction.getValue(); m_saveXDir = m_section->XDirection.getValue(); @@ -582,17 +582,9 @@ void TaskComplexSection::createComplexSection() m_sectionName.c_str(), ui->sbScale->value()); std::string baseName = m_baseView->getNameInDocument(); - if (m_scaleEdited) { - // user has changed the scale - Command::doCommand(Command::Doc, "App.ActiveDocument.%s.Scale = %0.7f", + + Command::doCommand(Command::Doc, "App.ActiveDocument.%s.Scale = %0.7f", m_sectionName.c_str(), ui->sbScale->value()); - } else { - // scale is untouched, use value from base view - Command::doCommand(Command::Doc, - "App.ActiveDocument.%s.Scale = App.ActiveDocument.%s.Scale", - m_sectionName.c_str(), - baseName.c_str()); - } int scaleType = ui->cmbScaleType->currentIndex(); Command::doCommand(Command::Doc, "App.ActiveDocument.%s.ScaleType = %d", @@ -672,18 +664,9 @@ void TaskComplexSection::updateComplexSection() m_sectionName.c_str(), makeSectionLabel(qTemp).c_str()); std::string baseName = m_baseView->getNameInDocument(); - if (m_scaleEdited) { - // user has changed the scale - Command::doCommand(Command::Doc, "App.ActiveDocument.%s.Scale = %0.7f", - m_sectionName.c_str(), ui->sbScale->value()); - } else { - // scale is untouched, use value from base view - Command::doCommand(Command::Doc, - "App.ActiveDocument.%s.Scale = App.ActiveDocument.%s.Scale", - m_sectionName.c_str(), - baseName.c_str()); - } + Command::doCommand(Command::Doc, "App.ActiveDocument.%s.Scale = %0.7f", + m_sectionName.c_str(), ui->sbScale->value()); int scaleType = ui->cmbScaleType->currentIndex(); Command::doCommand(Command::Doc, "App.ActiveDocument.%s.ScaleType = %d", From 21905c0d624c51013554cb2583ef090e739e7491 Mon Sep 17 00:00:00 2001 From: Paddle Date: Fri, 19 Jan 2024 10:26:00 +0100 Subject: [PATCH 043/135] Core: add property command, that shows property view. --- src/Gui/CommandDoc.cpp | 37 +++++++++++++++++++++++++++++++++++++ src/Gui/Tree.cpp | 31 ++----------------------------- src/Gui/Tree.h | 2 -- src/Gui/Workbench.cpp | 2 +- 4 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/Gui/CommandDoc.cpp b/src/Gui/CommandDoc.cpp index 6e1ffefefc..55d4b91d64 100644 --- a/src/Gui/CommandDoc.cpp +++ b/src/Gui/CommandDoc.cpp @@ -50,6 +50,7 @@ #include "BitmapFactory.h" #include "Command.h" #include "Control.h" +#include "DockWindowManager.h" #include "FileDialog.h" #include "MainWindow.h" #include "Selection.h" @@ -1715,6 +1716,41 @@ bool StdCmdEdit::isActive() return (!Selection().getCompleteSelection().empty()) || (Gui::Control().activeDialog() != nullptr); } +//=========================================================================== +// Std_Properties +//=========================================================================== +DEF_STD_CMD_A(StdCmdProperties) + +StdCmdProperties::StdCmdProperties() + : Command("Std_Properties") +{ + sGroup = "Edit"; + sMenuText = QT_TR_NOOP("Properties"); + sToolTipText = QT_TR_NOOP("Show the property view, which displays the properties of the selected object."); + sWhatsThis = "Std_Properties"; + sStatusTip = sToolTipText; + sAccel = "Alt+Return"; + sPixmap = "document-properties"; + eType = Alter3DView; +} + +void StdCmdProperties::activated(int iMsg) +{ + Q_UNUSED(iMsg); + QWidget* propertyView = Gui::DockWindowManager::instance()->getDockWindow("Property view"); + if (propertyView) { + QWidget* parent = propertyView->parentWidget(); + if (parent && !parent->isVisible()) { + parent->show(); + } + } +} + +bool StdCmdProperties::isActive() +{ + return !Selection().getCompleteSelection().empty(); +} + //====================================================================== // StdCmdExpression //=========================================================================== @@ -1969,6 +2005,7 @@ void CreateDocCommands() rcCmdMgr.addCommand(new StdCmdTransformManip()); rcCmdMgr.addCommand(new StdCmdAlignment()); rcCmdMgr.addCommand(new StdCmdEdit()); + rcCmdMgr.addCommand(new StdCmdProperties()); rcCmdMgr.addCommand(new StdCmdExpression()); } diff --git a/src/Gui/Tree.cpp b/src/Gui/Tree.cpp index 3b2cc0717c..212eb7db24 100644 --- a/src/Gui/Tree.cpp +++ b/src/Gui/Tree.cpp @@ -524,13 +524,6 @@ TreeWidget::TreeWidget(const char* name, QWidget* parent) connect(this->relabelObjectAction, &QAction::triggered, this, &TreeWidget::onRelabelObject); - this->objectPropertyAction = new QAction(this); -#ifndef Q_OS_MAC - this->objectPropertyAction->setShortcut(Qt::Key_F6); -#endif - connect(this->objectPropertyAction, &QAction::triggered, - this, &TreeWidget::onObjectProperty); - this->finishEditingAction = new QAction(this); connect(this->finishEditingAction, &QAction::triggered, this, &TreeWidget::onFinishEditing); @@ -889,7 +882,7 @@ void TreeWidget::contextMenuEvent(QContextMenuEvent* e) MenuItem view; Gui::Application::Instance->setupContextMenu("Tree", &view); - view << "Std_Expressions"; + view << "Std_Properties" << "Separator" << "Std_Expressions"; Workbench::createLinkMenu(&view); QMenu contextMenu; @@ -898,8 +891,7 @@ void TreeWidget::contextMenuEvent(QContextMenuEvent* e) QMenu editMenu; QActionGroup subMenuGroup(&subMenu); subMenuGroup.setExclusive(true); - connect(&subMenuGroup, &QActionGroup::triggered, - this, &TreeWidget::onActivateDocument); + connect(&subMenuGroup, &QActionGroup::triggered, this, &TreeWidget::onActivateDocument); MenuManager::getInstance()->setupContextMenu(&view, contextMenu); // get the current item @@ -969,7 +961,6 @@ void TreeWidget::contextMenuEvent(QContextMenuEvent* e) // relabeling is only possible for a single selected document if (SelectedObjectsList.size() == 1) contextMenu.addAction(this->relabelObjectAction); - contextMenu.addAction(this->objectPropertyAction); auto selItems = this->selectedItems(); // if only one item is selected, setup the edit menu @@ -1091,21 +1082,6 @@ void TreeWidget::onRelabelObject() editItem(item); } -void TreeWidget::onObjectProperty() -{ - int sizeOfFirstColumn = 200; - auto prop = new PropertyView(this, sizeOfFirstColumn); - QDialog* propertyDialog = new QDialog(this); - propertyDialog->setWindowTitle(QString::fromLatin1("Properties")); - propertyDialog->resize(700, 500); - QVBoxLayout* layout = new QVBoxLayout(propertyDialog); - layout->addWidget(prop); - propertyDialog->setLayout(layout); - QPoint cursorPos = QCursor::pos() - QPoint(0, 300); - propertyDialog->move(cursorPos); - propertyDialog->show(); -} - void TreeWidget::onStartEditing() { auto action = qobject_cast(sender()); @@ -2989,9 +2965,6 @@ void TreeWidget::setupText() this->relabelObjectAction->setText(tr("Rename")); this->relabelObjectAction->setStatusTip(tr("Rename object")); - this->objectPropertyAction->setText(tr("Properties")); - this->objectPropertyAction->setStatusTip(tr("Properties of the selected object")); - this->finishEditingAction->setText(tr("Finish editing")); this->finishEditingAction->setStatusTip(tr("Finish editing object")); diff --git a/src/Gui/Tree.h b/src/Gui/Tree.h index 877cddf664..6d35edfec2 100644 --- a/src/Gui/Tree.h +++ b/src/Gui/Tree.h @@ -154,7 +154,6 @@ protected: protected Q_SLOTS: void onCreateGroup(); void onRelabelObject(); - void onObjectProperty(); void onActivateDocument(QAction*); void onStartEditing(); void onFinishEditing(); @@ -209,7 +208,6 @@ private: private: QAction* createGroupAction; QAction* relabelObjectAction; - QAction* objectPropertyAction; QAction* finishEditingAction; QAction* selectDependentsAction; QAction* skipRecomputeAction; diff --git a/src/Gui/Workbench.cpp b/src/Gui/Workbench.cpp index 3f03d97a57..131e938f98 100644 --- a/src/Gui/Workbench.cpp +++ b/src/Gui/Workbench.cpp @@ -647,7 +647,7 @@ MenuItem* StdWorkbench::setupMenuBar() const << "Std_Refresh" << "Std_BoxSelection" << "Std_BoxElementSelection" << "Std_SelectAll" << "Std_Delete" << "Std_SendToPythonConsole" << "Separator" << "Std_Placement" << "Std_TransformManip" << "Std_Alignment" - << "Std_Edit" << "Separator" << "Std_UserEditMode" << "Separator" << "Std_DlgPreferences"; + << "Std_Edit" << "Std_Properties" << "Separator" << "Std_UserEditMode" << "Separator" << "Std_DlgPreferences"; auto axoviews = new MenuItem; axoviews->setCommand("Axonometric"); From f7798327cf9a9c35752bb45fdfd17aeed6145933 Mon Sep 17 00:00:00 2001 From: Paddle Date: Fri, 19 Jan 2024 10:47:10 +0100 Subject: [PATCH 044/135] Core: PropertyEditor : save properly the size of the first column. --- src/Gui/PropertyView.cpp | 6 +++--- src/Gui/PropertyView.h | 2 +- src/Gui/Tree.cpp | 4 ++-- src/Gui/propertyeditor/PropertyEditor.cpp | 11 ++++++++--- src/Gui/propertyeditor/PropertyEditor.h | 2 +- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Gui/PropertyView.cpp b/src/Gui/PropertyView.cpp index fb0dda47d1..535f54fa2c 100644 --- a/src/Gui/PropertyView.cpp +++ b/src/Gui/PropertyView.cpp @@ -67,7 +67,7 @@ static ParameterGrp::handle _GetParam() { * Provides two Gui::PropertyEditor::PropertyEditor widgets, for "View" and "Data", * in two tabs. */ -PropertyView::PropertyView(QWidget *parent, int sizeOfFirstColumn) +PropertyView::PropertyView(QWidget *parent) : QWidget(parent), SelectionObserver(false, ResolveMode::NoResolve) { auto pLayout = new QGridLayout( this ); @@ -83,12 +83,12 @@ PropertyView::PropertyView(QWidget *parent, int sizeOfFirstColumn) tabs->setTabPosition(QTabWidget::South); pLayout->addWidget(tabs, 0, 0); - propertyEditorView = new Gui::PropertyEditor::PropertyEditor(parent, sizeOfFirstColumn); + propertyEditorView = new Gui::PropertyEditor::PropertyEditor(); propertyEditorView->setAutomaticDocumentUpdate(_GetParam()->GetBool("AutoTransactionView", false)); propertyEditorView->setAutomaticExpand(_GetParam()->GetBool("AutoExpandView", false)); tabs->addTab(propertyEditorView, tr("View")); - propertyEditorData = new Gui::PropertyEditor::PropertyEditor(parent, sizeOfFirstColumn); + propertyEditorData = new Gui::PropertyEditor::PropertyEditor(); propertyEditorData->setAutomaticDocumentUpdate(_GetParam()->GetBool("AutoTransactionData", true)); propertyEditorData->setAutomaticExpand(_GetParam()->GetBool("AutoExpandData", false)); tabs->addTab(propertyEditorData, tr("Data")); diff --git a/src/Gui/PropertyView.h b/src/Gui/PropertyView.h index cf539df318..a058fcd04e 100644 --- a/src/Gui/PropertyView.h +++ b/src/Gui/PropertyView.h @@ -56,7 +56,7 @@ class PropertyView : public QWidget, public Gui::SelectionObserver Q_OBJECT public: - explicit PropertyView(QWidget *parent=nullptr, int sizeOfFirstColumn = 0); + explicit PropertyView(QWidget *parent=nullptr); ~PropertyView() override; Gui::PropertyEditor::PropertyEditor* propertyEditorView; diff --git a/src/Gui/Tree.cpp b/src/Gui/Tree.cpp index 212eb7db24..fe830d468f 100644 --- a/src/Gui/Tree.cpp +++ b/src/Gui/Tree.cpp @@ -61,7 +61,6 @@ #include "Macro.h" #include "MainWindow.h" #include "MenuManager.h" -#include "PropertyView.h" #include "TreeParams.h" #include "View3DInventor.h" #include "ViewProviderDocumentObject.h" @@ -891,7 +890,8 @@ void TreeWidget::contextMenuEvent(QContextMenuEvent* e) QMenu editMenu; QActionGroup subMenuGroup(&subMenu); subMenuGroup.setExclusive(true); - connect(&subMenuGroup, &QActionGroup::triggered, this, &TreeWidget::onActivateDocument); + connect(&subMenuGroup, &QActionGroup::triggered, + this, &TreeWidget::onActivateDocument); MenuManager::getInstance()->setupContextMenu(&view, contextMenu); // get the current item diff --git a/src/Gui/propertyeditor/PropertyEditor.cpp b/src/Gui/propertyeditor/PropertyEditor.cpp index 184d0420dd..e9aa88ca2d 100644 --- a/src/Gui/propertyeditor/PropertyEditor.cpp +++ b/src/Gui/propertyeditor/PropertyEditor.cpp @@ -51,7 +51,7 @@ FC_LOG_LEVEL_INIT("PropertyView", true, true) using namespace Gui::PropertyEditor; -PropertyEditor::PropertyEditor(QWidget *parent, int sizeOfFirstColumn) +PropertyEditor::PropertyEditor(QWidget *parent) : QTreeView(parent) , autoexpand(false) , autoupdate(false) @@ -100,8 +100,10 @@ PropertyEditor::PropertyEditor(QWidget *parent, int sizeOfFirstColumn) viewport()->installEventFilter(this); viewport()->setMouseTracking(true); - if (sizeOfFirstColumn != 0) { - header()->resizeSection(0, sizeOfFirstColumn); + auto hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/DockWindows/PropertyView"); + int firstColumnSize = hGrp->GetInt("FirstColumnSize", 0); + if (firstColumnSize != 0) { + header()->resizeSection(0, firstColumnSize); } } @@ -872,6 +874,9 @@ bool PropertyEditor::eventFilter(QObject* object, QEvent* event) { else if (mouse_event->type() == QEvent::MouseButtonRelease && mouse_event->button() == Qt::LeftButton && dragInProgress) { dragInProgress = false; + + auto hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/DockWindows/PropertyView"); + hGrp->SetInt("FirstColumnSize", header()->sectionSize(0)); return true; } } diff --git a/src/Gui/propertyeditor/PropertyEditor.h b/src/Gui/propertyeditor/PropertyEditor.h index 5c5342d129..bd01c2f99e 100644 --- a/src/Gui/propertyeditor/PropertyEditor.h +++ b/src/Gui/propertyeditor/PropertyEditor.h @@ -67,7 +67,7 @@ class PropertyEditor : public QTreeView Q_PROPERTY(QBrush itemBackground READ itemBackground WRITE setItemBackground DESIGNABLE true SCRIPTABLE true) // clazy:exclude=qproperty-without-notify public: - PropertyEditor(QWidget *parent = nullptr, int sizeOfFirstColumn = 0); + PropertyEditor(QWidget *parent = nullptr); ~PropertyEditor() override; /** Builds up the list view with the properties. */ From 865212fc858216608fca2fcf33804efda5ac4315 Mon Sep 17 00:00:00 2001 From: Paddle Date: Tue, 16 Jan 2024 15:30:59 +0100 Subject: [PATCH 045/135] Sketcher Fix radius arc helpers. --- .../Gui/EditModeConstraintCoinManager.cpp | 183 ++++++++++-------- .../Gui/EditModeConstraintCoinManager.h | 7 + 2 files changed, 106 insertions(+), 84 deletions(-) diff --git a/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp index 5dc22f984a..3aa0bedcc4 100644 --- a/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp +++ b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp @@ -1333,51 +1333,43 @@ Restart: double helperStartAngle = 0.; double helperRange = 0.; - if (Constr->First != GeoEnum::GeoUndef) { - const Part::Geometry* geo = - geolistfacade.getGeometryFromGeoId(Constr->First); + if (Constr->First == GeoEnum::GeoUndef) { + break; + } - if (geo->is()) { - auto* arc = static_cast(geo); - double radius = arc->getRadius(); - double angle = (double)Constr->LabelPosition; - double startAngle, endAngle; - arc->getRange(startAngle, endAngle, /*emulateCCW=*/true); - if (angle == 10) { - angle = (startAngle + endAngle) / 2; - } - if (!(angle > startAngle && angle < endAngle)) { - if (angle < startAngle - && startAngle - angle < angle + 2 * M_PI - endAngle) { - helperStartAngle = angle; - helperRange = startAngle - angle; - } - else { - if (angle < endAngle) { - angle += 2 * M_PI; - } - helperStartAngle = endAngle; - helperRange = angle - endAngle; - } - } - 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.); + const Part::Geometry* geo = geolistfacade.getGeometryFromGeoId(Constr->First); + + if (geo->is()) { + auto* arc = static_cast(geo); + double radius = arc->getRadius(); + double angle = (double)Constr->LabelPosition; // between -pi and pi + double startAngle, endAngle; // between 0 and 2*pi + arc->getRange(startAngle, endAngle, /*emulateCCW=*/true); + + if (angle == 10) { + angle = (startAngle + endAngle) / 2; } - else if (geo->is()) { - auto* 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; + + findHelperAngles(helperStartAngle, + helperRange, + angle, + startAngle, + endAngle); + + 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->is()) { + auto* 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; @@ -1415,54 +1407,45 @@ Restart: double helperStartAngle = 0.; double helperRange = 0.; - if (Constr->First != GeoEnum::GeoUndef) { - const Part::Geometry* geo = - geolistfacade.getGeometryFromGeoId(Constr->First); + if (Constr->First == GeoEnum::GeoUndef) { + break; + } + const Part::Geometry* geo = geolistfacade.getGeometryFromGeoId(Constr->First); - if (geo->is()) { - auto* arc = static_cast(geo); - double radius = arc->getRadius(); - double angle = (double)Constr->LabelPosition; - double startAngle, endAngle; - arc->getRange(startAngle, endAngle, /*emulateCCW=*/true); - if (angle == 10) { - angle = (startAngle + endAngle) / 2; - } - if (!(angle > startAngle && angle < endAngle)) { - if (angle < startAngle - && startAngle - angle < angle + 2 * M_PI - endAngle) { - helperStartAngle = angle; - helperRange = startAngle - angle; - } - else { - if (angle < endAngle) { - angle += 2 * M_PI; - } - helperStartAngle = endAngle; - helperRange = angle - endAngle; - } - } - pnt1 = arc->getCenter(); - pnt2 = pnt1 + radius * Base::Vector3d(cos(angle), sin(angle), 0.); + if (geo->is()) { + auto* arc = static_cast(geo); + double radius = arc->getRadius(); + double angle = (double)Constr->LabelPosition; // between -pi and pi + double startAngle, endAngle; // between 0 and 2*pi + arc->getRange(startAngle, endAngle, /*emulateCCW=*/true); + + if (angle == 10) { + angle = (startAngle + endAngle) / 2; } - else if (geo->is()) { - auto* circle = static_cast(geo); - auto gf = GeometryFacade::getFacade(geo); - double radius; + findHelperAngles(helperStartAngle, + helperRange, + angle, + startAngle, + endAngle); - radius = circle->getRadius(); + pnt1 = arc->getCenter(); + pnt2 = pnt1 + radius * Base::Vector3d(cos(angle), sin(angle), 0.); + } + else if (geo->is()) { + auto* circle = static_cast(geo); + auto gf = GeometryFacade::getFacade(geo); - 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; + 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; @@ -1518,6 +1501,38 @@ Restart: } } +void EditModeConstraintCoinManager::findHelperAngles(double& helperStartAngle, + double& helperRange, + double angle, + double startAngle, + double endAngle) +{ + if (angle < 0) { + angle = angle + 2 * M_PI; + } + // endAngle can be more than 2*pi as its startAngle + arcAngle + if (endAngle > 2 * M_PI && angle < endAngle - 2 * M_PI) { + angle = angle + 2 * M_PI; + } + if (!(angle > startAngle && angle < endAngle)) { + if ((angle < startAngle && startAngle - angle < angle + 2 * M_PI - endAngle) + || (angle > endAngle && startAngle + 2 * M_PI - angle < angle - endAngle)) { + if (angle > startAngle) { + angle -= 2 * M_PI; + } + helperStartAngle = angle; + helperRange = startAngle - angle; + } + else { + if (angle < endAngle) { + angle += 2 * M_PI; + } + helperStartAngle = endAngle; + helperRange = angle - endAngle; + } + } +} + Base::Vector3d EditModeConstraintCoinManager::seekConstraintPosition(const Base::Vector3d& origPos, const Base::Vector3d& norm, const Base::Vector3d& dir, diff --git a/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.h b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.h index 657bd6adbb..8db7b6d798 100644 --- a/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.h +++ b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.h @@ -250,6 +250,13 @@ private: /// Essentially a version of sendConstraintIconToCoin, with a blank icon void clearCoinImage(SoImage* soImagePtr); + + /// Find helper angle for radius/diameter constraint + void findHelperAngles(double& helperStartAngle, + double& helperAngle, + double angle, + double startAngle, + double endAngle); //@} private: From edc247cd54245870691b9cf403219918ab6c2199 Mon Sep 17 00:00:00 2001 From: Paddle Date: Tue, 16 Jan 2024 15:58:08 +0100 Subject: [PATCH 046/135] Sketcher : improve single line angle rendering. --- src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp index 3aa0bedcc4..2e7d05e6a4 100644 --- a/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp +++ b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp @@ -1281,18 +1281,20 @@ Restart: const Part::Geometry* geo = geolistfacade.getGeometryFromGeoId(Constr->First); if (geo->is()) { - const Part::GeomLineSegment* lineSeg = - static_cast(geo); + auto* lineSeg = static_cast(geo); p0 = Base::convertTo( (lineSeg->getEndPoint() + lineSeg->getStartPoint()) / 2); + double l1 = 2 * distance + - (lineSeg->getEndPoint() - lineSeg->getStartPoint()).Length() / 2; + endLineLength1 = 2 * distance; + endLineLength2 = l1 > 0. ? l1 : 0.; Base::Vector3d dir = lineSeg->getEndPoint() - lineSeg->getStartPoint(); startangle = 0.; range = atan2(dir.y, dir.x); } else if (geo->is()) { - const Part::GeomArcOfCircle* arc = - static_cast(geo); + auto* arc = static_cast(geo); p0 = Base::convertTo(arc->getCenter()); double endangle; From 772d2940bd60cb29386389bcc69ef47768948e8f Mon Sep 17 00:00:00 2001 From: Paddle Date: Tue, 16 Jan 2024 16:09:13 +0100 Subject: [PATCH 047/135] Sketcher : arc angle render improvement. --- src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp index 2e7d05e6a4..8c32200450 100644 --- a/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp +++ b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp @@ -1297,6 +1297,9 @@ Restart: auto* arc = static_cast(geo); p0 = Base::convertTo(arc->getCenter()); + endLineLength1 = 2 * distance - arc->getRadius(); + endLineLength2 = endLineLength1; + double endangle; arc->getRange(startangle, endangle, /*emulateCCWXY=*/true); range = endangle - startangle; From 3b9a1f7949bd4962ef0b631992f7099480c9b7d4 Mon Sep 17 00:00:00 2001 From: Paddle Date: Tue, 16 Jan 2024 17:59:38 +0100 Subject: [PATCH 048/135] SoDatumLabel : introduce distance arc helpers. --- src/Gui/SoDatumLabel.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ src/Gui/SoDatumLabel.h | 3 +++ 2 files changed, 43 insertions(+) diff --git a/src/Gui/SoDatumLabel.cpp b/src/Gui/SoDatumLabel.cpp index 2e2b44b3c4..52f1823317 100644 --- a/src/Gui/SoDatumLabel.cpp +++ b/src/Gui/SoDatumLabel.cpp @@ -93,6 +93,9 @@ SoDatumLabel::SoDatumLabel() SO_NODE_ADD_FIELD(param2, (0.f)); SO_NODE_ADD_FIELD(param4, (0.f)); SO_NODE_ADD_FIELD(param5, (0.f)); + SO_NODE_ADD_FIELD(param6, (0.f)); + SO_NODE_ADD_FIELD(param7, (0.f)); + SO_NODE_ADD_FIELD(param8, (0.f)); useAntialiasing = true; @@ -989,6 +992,43 @@ void SoDatumLabel::GLRender(SoGLRenderAction * action) glVertex2f(ar3[0], ar3[1]); glVertex2f(ar4[0], ar4[1]); glEnd(); + + + if (this->datumtype.getValue() == DISTANCE) { + // Draw arc helpers if needed + float range1 = this->param4.getValue(); + if (range1 != 0.0) { + float startangle1 = this->param3.getValue(); + float radius1 = this->param5.getValue(); + SbVec3f center = points[2]; + int countSegments = std::max(6, abs(int(50.0 * range1 / (2 * M_PI)))); + double segment = range1 / (countSegments - 1); + + glBegin(GL_LINE_STRIP); + for (int i = 0; i < countSegments; i++) { + double theta = startangle1 + segment * i; + SbVec3f v1 = center + SbVec3f(radius1 * cos(theta), radius1 * sin(theta), 0); + glVertex2f(v1[0], v1[1]); + } + glEnd(); + } + float range2 = this->param7.getValue(); + if (range2 != 0.0) { + float startangle2 = this->param6.getValue(); + float radius2 = this->param8.getValue(); + SbVec3f center = points[3]; + int countSegments = std::max(6, abs(int(50.0 * range2 / (2 * M_PI)))); + double segment = range2 / (countSegments - 1); + + glBegin(GL_LINE_STRIP); + for (int i = 0; i < countSegments; i++) { + double theta = startangle2 + segment * i; + SbVec3f v1 = center + SbVec3f(radius2 * cos(theta), radius2 * sin(theta), 0); + glVertex2f(v1[0], v1[1]); + } + glEnd(); + } + } } else if (this->datumtype.getValue() == RADIUS || this->datumtype.getValue() == DIAMETER) { // Get the Points diff --git a/src/Gui/SoDatumLabel.h b/src/Gui/SoDatumLabel.h index 077069236a..2e55ffeb8e 100644 --- a/src/Gui/SoDatumLabel.h +++ b/src/Gui/SoDatumLabel.h @@ -77,6 +77,9 @@ public: SoSFFloat param3; SoSFFloat param4; SoSFFloat param5; + SoSFFloat param6; + SoSFFloat param7; + SoSFFloat param8; SoMFVec3f pnts; SoSFVec3f norm; SoSFImage image; From 88883faaf856076d7e8ddc5fec166815c9919d84 Mon Sep 17 00:00:00 2001 From: Paddle Date: Sun, 26 Nov 2023 07:50:02 +0100 Subject: [PATCH 049/135] Sketcher: Add copy/cut/paste sketcher commands --- src/Mod/Sketcher/Gui/CommandSketcherTools.cpp | 260 ++++++++++++++++++ src/Mod/Sketcher/Gui/ShortcutListener.cpp | 8 +- src/Mod/Sketcher/Gui/ShortcutListener.h | 9 - src/Mod/Sketcher/Gui/ViewProviderSketch.h | 5 +- src/Mod/Sketcher/Gui/Workbench.cpp | 6 +- 5 files changed, 267 insertions(+), 21 deletions(-) diff --git a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp index c08e135aa2..2fc791558f 100644 --- a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp +++ b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -33,6 +34,8 @@ #include #include +#include +#include #include #include #include @@ -140,6 +143,260 @@ Sketcher::SketchObject* getSketchObject() // ================================================================================ +// Copy + +bool copySelectionToClipboard() { + + std::vector selection = Gui::Command::getSelection().getSelectionEx(); + Sketcher::SketchObject* obj = static_cast(selection[0].getObject()); + // only one sketch with its subelements are allowed to be selected + if (selection.size() != 1) { return false; } + std::vector SubNames = selection[0].getSubNames(); + if (SubNames.empty()) { return false; } + + //First we need listOfGeoId and sort them. + std::vector listOfGeoId; + for (std::vector::const_iterator it = SubNames.begin(); it != SubNames.end(); ++it) { + int GeoId = -1; + // only handle non-external edges + if (it->size() > 4 && it->substr(0, 4) == "Edge") { + GeoId = std::atoi(it->substr(4, 4000).c_str()) - 1; + if (GeoId >= 0) { + listOfGeoId.push_back(GeoId); + } + } + else if (it->size() > 6 && it->substr(0, 6) == "Vertex") { + // only if it is a GeomPoint + int VtId = std::atoi(it->substr(6, 4000).c_str()) - 1; + Sketcher::PointPos PosId; + obj->getGeoVertexIndex(VtId, GeoId, PosId); + if (obj->getGeometry(GeoId)->getTypeId() == Part::GeomPoint::getClassTypeId()) { + if (GeoId >= 0) { + listOfGeoId.push_back(GeoId); + } + } + } + } + if (listOfGeoId.empty()) { return false; } + sort(listOfGeoId.begin(), listOfGeoId.end()); + + Base::StringWriter writer; + + //Export selected geometries as a formated string. + std::vector< Part::Geometry* > newVals; + for (auto geoId : listOfGeoId) { + Part::Geometry* geoNew = obj->getGeometry(geoId)->copy(); + newVals.push_back(geoNew); + } + Part::PropertyGeometryList geoToCopy; + geoToCopy.setValues(std::move(newVals)); + geoToCopy.Save(writer); + + //add constraints to the stream string. + std::vector< Sketcher::Constraint* > newConstrVals; + for (auto constr : obj->Constraints.getValues()) { + + auto isSelectedGeoOrAxis = [](const std::vector& vec, int value) { + return (std::find(vec.begin(), vec.end(), value) != vec.end()) + || value == GeoEnum::GeoUndef || value == GeoEnum::RtPnt + || value == GeoEnum::VAxis || value == GeoEnum::HAxis; + }; + + if (!isSelectedGeoOrAxis(listOfGeoId, constr->First) + || !isSelectedGeoOrAxis(listOfGeoId, constr->Second) + || !isSelectedGeoOrAxis(listOfGeoId, constr->Third)) { + continue; + } + + Constraint* temp = constr->copy(); + for (size_t j = 0; j < listOfGeoId.size(); j++) { + if (temp->First == listOfGeoId[j]) { + temp->First = j; + } + if (temp->Second == listOfGeoId[j]) { + temp->Second = j; + } + if (temp->Third == listOfGeoId[j]) { + temp->Third = j; + } + } + newConstrVals.push_back(temp); + } + Sketcher::PropertyConstraintList constToCopy; + constToCopy.setValues(std::move(newConstrVals)); + constToCopy.Save(writer); + + std::string exportedData = writer.getString(); + + if (!exportedData.empty()) { + QClipboard* clipboard = QGuiApplication::clipboard(); + clipboard->setText(QString::fromStdString(exportedData)); + return true; + } + return false; +} + +DEF_STD_CMD_A(CmdSketcherCopyClipboard) + +CmdSketcherCopyClipboard::CmdSketcherCopyClipboard() + : Command("Sketcher_CopyClipboard") +{ + sAppModule = "Sketcher"; + sGroup = "Sketcher"; + sMenuText = QT_TR_NOOP("C&opy in sketcher"); + sToolTipText = QT_TR_NOOP("Copy selected geometries and constraints to the clipboard"); + sWhatsThis = "Sketcher_CopyClipboard"; + sStatusTip = sToolTipText; + sPixmap = "edit-copy"; + sAccel = keySequenceToAccel(QKeySequence::Copy); + eType = ForEdit; +} + +void CmdSketcherCopyClipboard::activated(int iMsg) +{ + Q_UNUSED(iMsg); + copySelectionToClipboard(); +} + +bool CmdSketcherCopyClipboard::isActive() +{ + return isCommandActive(getActiveGuiDocument(), true); +} + +// ================================================================================ + +// Cut + +DEF_STD_CMD_A(CmdSketcherCut) + +CmdSketcherCut::CmdSketcherCut() + : Command("Sketcher_Cut") +{ + sAppModule = "Sketcher"; + sGroup = "Sketcher"; + sMenuText = QT_TR_NOOP("C&ut in sketcher"); + sToolTipText = QT_TR_NOOP("Cut selected geometries and constraints to the clipboard"); + sWhatsThis = "Sketcher_Cut"; + sStatusTip = sToolTipText; + sPixmap = "edit-cut"; + sAccel = keySequenceToAccel(QKeySequence::Cut); + eType = ForEdit; +} + +void CmdSketcherCut::activated(int iMsg) +{ + Q_UNUSED(iMsg); + if (copySelectionToClipboard()) { + + Gui::Document* doc = getActiveGuiDocument(); + ReleaseHandler(doc); + auto* vp = static_cast(doc->getInEdit()); + + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Cut in Sketcher")); + vp->deleteSelected(); + Gui::Command::commitCommand(); + } +} + +bool CmdSketcherCut::isActive() +{ + return isCommandActive(getActiveGuiDocument(), true); +} + +// ================================================================================ + +// Paste + +DEF_STD_CMD_A(CmdSketcherPaste) + +CmdSketcherPaste::CmdSketcherPaste() + : Command("Sketcher_Paste") +{ + sAppModule = "Sketcher"; + sGroup = "Sketcher"; + sMenuText = QT_TR_NOOP("P&aste in sketcher"); + sToolTipText = QT_TR_NOOP("Paste selected geometries and constraints from the clipboard"); + sWhatsThis = "Sketcher_Paste"; + sStatusTip = sToolTipText; + sPixmap = "edit-paste"; + sAccel = keySequenceToAccel(QKeySequence::Paste); + eType = ForEdit; +} + +void CmdSketcherPaste::activated(int iMsg) +{ + Q_UNUSED(iMsg); + Gui::Document* doc = getActiveGuiDocument(); + ReleaseHandler(doc); + auto* vp = static_cast(doc->getInEdit()); + Sketcher::SketchObject* Obj = vp->getSketchObject(); + + std::string data = QGuiApplication::clipboard()->text().toStdString(); + int importedFirstGeoId = Obj->getHighestCurveIndex() + 1; + + std::string geoString; + if (data.find("", 0) != std::string::npos) { + geoString = data.substr(0, data.find("", 0) + 16); + } + else { return ; } + + std::istringstream istream(geoString); + Base::XMLReader reader("importingGeo", istream); + Part::PropertyGeometryList geoToCopy; + geoToCopy.Restore(reader); + + const std::vector& geos = geoToCopy.getValues(); + if (geos.empty()) { return; } + + Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Paste in Sketcher")); + + for (auto geo : geos) { + Part::Geometry* geocopy = geo->copy(); + Obj->addGeometry(geocopy); + } + + if (data.find("copy(); + //update the geoIds of the constraints + if (!isAxisOrRoot(constraintToAdd->First)) { + constraintToAdd->First += importedFirstGeoId; + } + if (!isAxisOrRoot(constraintToAdd->Second)) { + constraintToAdd->Second += importedFirstGeoId; + } + if (!isAxisOrRoot(constraintToAdd->Third)) { + constraintToAdd->Third += importedFirstGeoId; + } + Obj->addConstraint(constraintToAdd); + } + } + + Obj->solve(true); + vp->draw(false, false); // Redraw + + Gui::Command::commitCommand(); +} + +bool CmdSketcherPaste::isActive() +{ + return isCommandActive(getActiveGuiDocument(), false); +} + +// ================================================================================ + // Select Constraints of selected elements DEF_STD_CMD_A(CmdSketcherSelectConstraints) @@ -2432,5 +2689,8 @@ void CreateSketcherCommandsConstraintAccel() rcCmdMgr.addCommand(new CmdSketcherDeleteAllGeometry()); rcCmdMgr.addCommand(new CmdSketcherDeleteAllConstraints()); rcCmdMgr.addCommand(new CmdSketcherRemoveAxesAlignment()); + rcCmdMgr.addCommand(new CmdSketcherCopyClipboard()); + rcCmdMgr.addCommand(new CmdSketcherCut()); + rcCmdMgr.addCommand(new CmdSketcherPaste()); } // clang-format on diff --git a/src/Mod/Sketcher/Gui/ShortcutListener.cpp b/src/Mod/Sketcher/Gui/ShortcutListener.cpp index 805e54f1f4..b161cb4194 100644 --- a/src/Mod/Sketcher/Gui/ShortcutListener.cpp +++ b/src/Mod/Sketcher/Gui/ShortcutListener.cpp @@ -31,12 +31,6 @@ using namespace SketcherGui; -// ******************** ViewProvider attorney *********************************************// -inline void ViewProviderSketchShortcutListenerAttorney::deleteSelected(ViewProviderSketch& vp) -{ - vp.deleteSelected(); -}; - // ******************** ShortcutListener *********************************************// ShortcutListener::ShortcutListener(ViewProviderSketch* vp) { @@ -55,7 +49,7 @@ bool ShortcutListener::eventFilter(QObject* obj, QEvent* event) switch (kevent->key()) { case Qt::Key_Delete: kevent->accept(); - ViewProviderSketchShortcutListenerAttorney::deleteSelected(*pViewProvider); + pViewProvider->deleteSelected(); return true; default: break; diff --git a/src/Mod/Sketcher/Gui/ShortcutListener.h b/src/Mod/Sketcher/Gui/ShortcutListener.h index 414da71fa3..70366adc83 100644 --- a/src/Mod/Sketcher/Gui/ShortcutListener.h +++ b/src/Mod/Sketcher/Gui/ShortcutListener.h @@ -31,15 +31,6 @@ namespace SketcherGui class ViewProviderSketch; -class ViewProviderSketchShortcutListenerAttorney -{ -private: - static inline void deleteSelected(ViewProviderSketch& vp); - - - friend class ShortcutListener; -}; - class ShortcutListener: public QObject { // Q_OBJECT diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.h b/src/Mod/Sketcher/Gui/ViewProviderSketch.h index e8e16c5c08..bcadc5bed6 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.h +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.h @@ -672,6 +672,7 @@ public: const Gui::View3DInventorViewer* viewer) override; //@} + void deleteSelected(); /// Control the overlays appearing on the Tree and reflecting different sketcher states QIcon mergeColorfulOverlayIcons(const QIcon& orig) const override; @@ -702,7 +703,6 @@ public: friend class ViewProviderSketchDrawSketchHandlerAttorney; friend class ViewProviderSketchCoinAttorney; friend class ViewProviderSketchSnapAttorney; - friend class ViewProviderSketchShortcutListenerAttorney; //@} protected: /** @name enter/exit edit mode */ @@ -867,9 +867,6 @@ private: bool isConstraintSelected(int constraintId) const; - //********* ViewProviderSketchShortcutListenerAttorney ***********// - void deleteSelected(); - //********* ViewProviderSketchDrawSketchHandlerAttorney **********// void setConstraintSelectability(bool enabled = true); void setPositionText(const Base::Vector2d& Pos, const SbString& txt); diff --git a/src/Mod/Sketcher/Gui/Workbench.cpp b/src/Mod/Sketcher/Gui/Workbench.cpp index 5c2d74ed2e..c1be2c1ac5 100644 --- a/src/Mod/Sketcher/Gui/Workbench.cpp +++ b/src/Mod/Sketcher/Gui/Workbench.cpp @@ -543,7 +543,11 @@ inline void SketcherAddWorkbenchTools(Gui::MenuItem& consaccel) << "Sketcher_RemoveAxesAlignment" << "Separator" << "Sketcher_DeleteAllGeometry" - << "Sketcher_DeleteAllConstraints"; + << "Sketcher_DeleteAllConstraints" + << "Separator" + << "Sketcher_CopyClipboard" + << "Sketcher_Cut" + << "Sketcher_Paste"; } template<> From f0c491ef79680fe44ba20c95d7d793dbf57c1f2a Mon Sep 17 00:00:00 2001 From: Paddle Date: Thu, 11 Jan 2024 18:31:25 +0100 Subject: [PATCH 050/135] Sketcher: python converter : add possibility to add lastGeoId to the constraint process. --- src/Mod/Sketcher/App/PythonConverter.cpp | 318 ++++++++++++++--------- src/Mod/Sketcher/App/PythonConverter.h | 14 +- 2 files changed, 203 insertions(+), 129 deletions(-) diff --git a/src/Mod/Sketcher/App/PythonConverter.cpp b/src/Mod/Sketcher/App/PythonConverter.cpp index d063378161..fe5def95dd 100644 --- a/src/Mod/Sketcher/App/PythonConverter.cpp +++ b/src/Mod/Sketcher/App/PythonConverter.cpp @@ -59,11 +59,11 @@ std::string PythonConverter::convert(const Part::Geometry* geo, Mode mode) return command; } -std::string PythonConverter::convert(const Sketcher::Constraint* constraint) +std::string PythonConverter::convert(const Sketcher::Constraint* constraint, bool useLastGeoIdVar) { // addConstraint(Sketcher.Constraint('Distance',%d,%f)) std::string command; - auto cg = process(constraint); + auto cg = process(constraint, useLastGeoIdVar); command = boost::str(boost::format("addConstraint(%s)\n") % cg); @@ -85,13 +85,13 @@ std::string PythonConverter::convert(const std::string& doc, if (ngeos > 0) { if (construction) { command = boost::str( - boost::format("constrGeoList = []\n%s\n%s.addGeometry(constrGeoList,%s)\ndel " - "constrGeoList") + boost::format("constrGeoList = []\n%s%s.addGeometry(constrGeoList,%s)\n" + "del constrGeoList\n") % geolist % doc % "True"); } else { command = boost::str( - boost::format("geoList = []\n%s\n%s.addGeometry(geoList,%s)\ndel geoList") + boost::format("geoList = []\n%s%s.addGeometry(geoList,%s)\ndel geoList\n") % geolist % doc % "False"); } } @@ -134,10 +134,10 @@ std::string PythonConverter::convert(const std::string& doc, if (sg.construction) { geolist = - boost::str(boost::format("%s\nconstrGeoList.append(%s)\n") % geolist % sg.creation); + boost::str(boost::format("%sconstrGeoList.append(%s)\n") % geolist % sg.creation); } else { - geolist = boost::str(boost::format("%s\ngeoList.append(%s)\n") % geolist % sg.creation); + geolist = boost::str(boost::format("%sgeoList.append(%s)\n") % geolist % sg.creation); } ngeos++; @@ -165,7 +165,8 @@ std::string PythonConverter::convert(const std::string& doc, } std::string PythonConverter::convert(const std::string& doc, - const std::vector& constraints) + const std::vector& constraints, + bool useLastGeoIdVar) { if (constraints.size() == 1) { auto cg = convert(constraints[0]); @@ -176,7 +177,7 @@ std::string PythonConverter::convert(const std::string& doc, std::string constraintlist = "constraintList = []"; for (auto constraint : constraints) { - auto cg = process(constraint); + auto cg = process(constraint, useLastGeoIdVar); constraintlist = boost::str(boost::format("%s\nconstraintList.append(%s)") % constraintlist % cg); @@ -200,7 +201,8 @@ PythonConverter::SingleGeometry PythonConverter::process(const Part::Geometry* g auto sgeo = static_cast(geo); SingleGeometry sg; sg.creation = boost::str( - boost::format("Part.LineSegment(App.Vector(%f,%f,%f),App.Vector(%f,%f,%f))") + boost::format( + "Part.LineSegment(App.Vector(%f, %f, %f),App.Vector(%f, %f, %f))") % sgeo->getStartPoint().x % sgeo->getStartPoint().y % sgeo->getStartPoint().z % sgeo->getEndPoint().x % sgeo->getEndPoint().y % sgeo->getEndPoint().z); sg.construction = Sketcher::GeometryFacade::getConstruction(geo); @@ -225,7 +227,7 @@ PythonConverter::SingleGeometry PythonConverter::process(const Part::Geometry* g auto sgeo = static_cast(geo); SingleGeometry sg; sg.creation = - boost::str(boost::format("Part.Point(App.Vector(%f,%f,%f))") + boost::str(boost::format("Part.Point(App.Vector(%f, %f, %f))") % sgeo->getPoint().x % sgeo->getPoint().y % sgeo->getPoint().z); sg.construction = Sketcher::GeometryFacade::getConstruction(geo); return sg; @@ -309,10 +311,10 @@ PythonConverter::SingleGeometry PythonConverter::process(const Part::Geometry* g controlpoints.append(1, ']'); SingleGeometry sg; - sg.creation = - boost::str(boost::format("Part.BSplineCurve (%s,None,None,%s,%d,None,False)") - % controlpoints.c_str() % (bSpline->isPeriodic() ? "True" : "False") - % bSpline->getDegree()); + sg.creation = boost::str( + boost::format("Part.BSplineCurve (%s, None, None, %s, %d, None, False)") + % controlpoints.c_str() % (bSpline->isPeriodic() ? "True" : "False") + % bSpline->getDegree()); sg.construction = Sketcher::GeometryFacade::getConstruction(geo); return sg; }}, @@ -342,128 +344,158 @@ PythonConverter::SingleGeometry PythonConverter::process(const Part::Geometry* g return creator(geo); } -std::string PythonConverter::process(const Sketcher::Constraint* constraint) +std::string PythonConverter::process(const Sketcher::Constraint* constraint, bool useLastIdVar) { - static std::map> + std::string geoId1 = (useLastIdVar ? "lastGeoId + " : "") + std::to_string(constraint->First); + std::string geoId2 = (useLastIdVar ? "lastGeoId + " : "") + std::to_string(constraint->Second); + std::string geoId3 = (useLastIdVar ? "lastGeoId + " : "") + std::to_string(constraint->Third); + + static std::map< + const Sketcher::ConstraintType, + std::function< + std::string(const Sketcher::Constraint*, std::string&, std::string&, std::string&)>> converterMap = { {Sketcher::Coincident, - [](const Sketcher::Constraint* constr) { + [](const Sketcher::Constraint* constr, + std::string& geoId1, + std::string& geoId2, + [[maybe_unused]] std::string& geoId3) { return boost::str( - boost::format("Sketcher.Constraint('Coincident', %i, %i, %i, %i)") - % constr->First % static_cast(constr->FirstPos) % constr->Second + boost::format("Sketcher.Constraint('Coincident', %s, %i, %s, %i)") % geoId1 + % static_cast(constr->FirstPos) % geoId2 % static_cast(constr->SecondPos)); }}, {Sketcher::Horizontal, - [](const Sketcher::Constraint* constr) { + [](const Sketcher::Constraint* constr, + std::string& geoId1, + std::string& geoId2, + [[maybe_unused]] std::string& geoId3) { if (constr->Second == GeoEnum::GeoUndef) { - return boost::str(boost::format("Sketcher.Constraint('Horizontal', %i)") - % constr->First); + return boost::str(boost::format("Sketcher.Constraint('Horizontal', %s)") + % geoId1); } else { return boost::str( - boost::format("Sketcher.Constraint('Horizontal', %i, %i, %i, %i)") - % constr->First % static_cast(constr->FirstPos) % constr->Second + boost::format("Sketcher.Constraint('Horizontal', %s, %i, %s, %i)") % geoId1 + % static_cast(constr->FirstPos) % geoId2 % static_cast(constr->SecondPos)); } }}, {Sketcher::Vertical, - [](const Sketcher::Constraint* constr) { + [](const Sketcher::Constraint* constr, + std::string& geoId1, + std::string& geoId2, + [[maybe_unused]] std::string& geoId3) { if (constr->Second == GeoEnum::GeoUndef) { - return boost::str(boost::format("Sketcher.Constraint('Vertical', %i)") - % constr->First); + return boost::str(boost::format("Sketcher.Constraint('Vertical', %s)") + % geoId1); } else { return boost::str( - boost::format("Sketcher.Constraint('Vertical', %i, %i, %i, %i)") - % constr->First % static_cast(constr->FirstPos) % constr->Second + boost::format("Sketcher.Constraint('Vertical', %s, %i, %s, %i)") % geoId1 + % static_cast(constr->FirstPos) % geoId2 % static_cast(constr->SecondPos)); } }}, {Sketcher::Block, - [](const Sketcher::Constraint* constr) { - return boost::str(boost::format("Sketcher.Constraint('Block', %i)") - % constr->First); + []([[maybe_unused]] const Sketcher::Constraint* constr, + std::string& geoId1, + [[maybe_unused]] std::string& geoId2, + [[maybe_unused]] std::string& geoId3) { + return boost::str(boost::format("Sketcher.Constraint('Block', %s)") % geoId1); }}, {Sketcher::Tangent, - [](const Sketcher::Constraint* constr) { + [](const Sketcher::Constraint* constr, + std::string& geoId1, + std::string& geoId2, + [[maybe_unused]] std::string& geoId3) { if (constr->FirstPos == Sketcher::PointPos::none) { - return boost::str(boost::format("Sketcher.Constraint('Tangent', %i, %i)") - % constr->First % constr->Second); + return boost::str(boost::format("Sketcher.Constraint('Tangent', %s, %s)") + % geoId1 % geoId2); } else if (constr->SecondPos == Sketcher::PointPos::none) { - return boost::str(boost::format("Sketcher.Constraint('Tangent', %i, %i, %i)") - % constr->First % static_cast(constr->FirstPos) - % constr->Second); + return boost::str(boost::format("Sketcher.Constraint('Tangent', %s, %i, %s)") + % geoId1 % static_cast(constr->FirstPos) % geoId2); } else { return boost::str( - boost::format("Sketcher.Constraint('Tangent', %i, %i, %i, %i)") - % constr->First % static_cast(constr->FirstPos) % constr->Second + boost::format("Sketcher.Constraint('Tangent', %s, %i, %s, %i)") % geoId1 + % static_cast(constr->FirstPos) % geoId2 % static_cast(constr->SecondPos)); } }}, {Sketcher::Parallel, - [](const Sketcher::Constraint* constr) { - return boost::str(boost::format("Sketcher.Constraint('Parallel', %i, %i)") - % constr->First % constr->Second); + []([[maybe_unused]] const Sketcher::Constraint* constr, + std::string& geoId1, + std::string& geoId2, + [[maybe_unused]] std::string& geoId3) { + return boost::str(boost::format("Sketcher.Constraint('Parallel', %s, %s)") % geoId1 + % geoId2); }}, {Sketcher::Perpendicular, - [](const Sketcher::Constraint* constr) { + []([[maybe_unused]] const Sketcher::Constraint* constr, + std::string& geoId1, + std::string& geoId2, + [[maybe_unused]] std::string& geoId3) { if (constr->FirstPos == Sketcher::PointPos::none) { - return boost::str(boost::format("Sketcher.Constraint('Perpendicular', %i, %i)") - % constr->First % constr->Second); + return boost::str(boost::format("Sketcher.Constraint('Perpendicular', %s, %s)") + % geoId1 % geoId2); } else if (constr->SecondPos == Sketcher::PointPos::none) { return boost::str( - boost::format("Sketcher.Constraint('Perpendicular', %i, %i, %i)") - % constr->First % static_cast(constr->FirstPos) % constr->Second); + boost::format("Sketcher.Constraint('Perpendicular', %s, %i, %s)") % geoId1 + % static_cast(constr->FirstPos) % geoId2); } else { return boost::str( - boost::format("Sketcher.Constraint('Perpendicular', %i, %i, %i, %i)") - % constr->First % static_cast(constr->FirstPos) % constr->Second + boost::format("Sketcher.Constraint('Perpendicular', %s, %i, %s, %i)") + % geoId1 % static_cast(constr->FirstPos) % geoId2 % static_cast(constr->SecondPos)); } }}, {Sketcher::Equal, - [](const Sketcher::Constraint* constr) { - return boost::str(boost::format("Sketcher.Constraint('Equal', %i, %i)") - % constr->First % constr->Second); + []([[maybe_unused]] const Sketcher::Constraint* constr, + std::string& geoId1, + std::string& geoId2, + [[maybe_unused]] std::string& geoId3) { + return boost::str(boost::format("Sketcher.Constraint('Equal', %s, %s)") % geoId1 + % geoId2); }}, {Sketcher::InternalAlignment, - [](const Sketcher::Constraint* constr) { + [](const Sketcher::Constraint* constr, + std::string& geoId1, + std::string& geoId2, + [[maybe_unused]] std::string& geoId3) { if (constr->AlignmentType == EllipseMajorDiameter || constr->AlignmentType == EllipseMinorDiameter || constr->AlignmentType == HyperbolaMajor || constr->AlignmentType == HyperbolaMinor || constr->AlignmentType == ParabolaFocalAxis) { return boost::str( - boost::format("Sketcher.Constraint('InternalAlignment:%s', %i, %i)") - % constr->internalAlignmentTypeToString() % constr->First - % constr->Second); + boost::format("Sketcher.Constraint('InternalAlignment:%s', %s, %s)") + % constr->internalAlignmentTypeToString() % geoId1 % geoId2); } else if (constr->AlignmentType == EllipseFocus1 || constr->AlignmentType == EllipseFocus2 || constr->AlignmentType == HyperbolaFocus || constr->AlignmentType == ParabolaFocus) { return boost::str( - boost::format("Sketcher.Constraint('InternalAlignment:%s', %i, %i, %i)") - % constr->internalAlignmentTypeToString() % constr->First - % static_cast(constr->FirstPos) % constr->Second); + boost::format("Sketcher.Constraint('InternalAlignment:%s', %s, %i, %s)") + % constr->internalAlignmentTypeToString() % geoId1 + % static_cast(constr->FirstPos) % geoId2); } else if (constr->AlignmentType == BSplineControlPoint) { return boost::str( boost::format( - "Sketcher.Constraint('InternalAlignment:%s', %i, %i, %i, %i)") - % constr->internalAlignmentTypeToString() % constr->First - % static_cast(constr->FirstPos) % constr->Second + "Sketcher.Constraint('InternalAlignment:%s', %s, %i, %s, %i)") + % constr->internalAlignmentTypeToString() % geoId1 + % static_cast(constr->FirstPos) % geoId2 % constr->InternalAlignmentIndex); } else if (constr->AlignmentType == BSplineKnotPoint) { return boost::str( - boost::format("Sketcher.Constraint('InternalAlignment:%s', %i, 1, %i, %i)") - % constr->internalAlignmentTypeToString() % constr->First % constr->Second + boost::format("Sketcher.Constraint('InternalAlignment:%s', %s, 1, %s, %i)") + % constr->internalAlignmentTypeToString() % geoId1 % geoId2 % constr->InternalAlignmentIndex); } @@ -471,124 +503,160 @@ std::string PythonConverter::process(const Sketcher::Constraint* constraint) "PythonConverter: Constraint Alignment Type not supported") }}, {Sketcher::Distance, - [](const Sketcher::Constraint* constr) { + [](const Sketcher::Constraint* constr, + std::string& geoId1, + std::string& geoId2, + [[maybe_unused]] std::string& geoId3) { if (constr->Second == GeoEnum::GeoUndef) { - return boost::str(boost::format("Sketcher.Constraint('Distance', %i, %f)") - % constr->First % constr->getValue()); + return boost::str(boost::format("Sketcher.Constraint('Distance', %s, %f)") + % geoId1 % constr->getValue()); } else if (constr->FirstPos == Sketcher::PointPos::none) { - return boost::str(boost::format("Sketcher.Constraint('Distance', %i, %i, %f)") - % constr->First % constr->Second % constr->getValue()); + return boost::str(boost::format("Sketcher.Constraint('Distance', %s, %s, %f)") + % geoId1 % geoId2 % constr->getValue()); } else if (constr->SecondPos == Sketcher::PointPos::none) { return boost::str( - boost::format("Sketcher.Constraint('Distance', %i, %i, %i, %f)") - % constr->First % static_cast(constr->FirstPos) % constr->Second - % constr->getValue()); + boost::format("Sketcher.Constraint('Distance', %s, %i, %s, %f)") % geoId1 + % static_cast(constr->FirstPos) % geoId2 % constr->getValue()); } else { return boost::str( - boost::format("Sketcher.Constraint('Distance', %i, %i, %i, %i, %f)") - % constr->First % static_cast(constr->FirstPos) % constr->Second + boost::format("Sketcher.Constraint('Distance', %s, %i, %s, %i, %f)") + % geoId1 % static_cast(constr->FirstPos) % geoId2 % static_cast(constr->SecondPos) % constr->getValue()); } }}, {Sketcher::Angle, - [](const Sketcher::Constraint* constr) { + [](const Sketcher::Constraint* constr, + std::string& geoId1, + std::string& geoId2, + std::string& geoId3) { if (constr->Second == GeoEnum::GeoUndef) { - return boost::str(boost::format("Sketcher.Constraint('Angle', %i, %f)") - % constr->First % constr->getValue()); + return boost::str(boost::format("Sketcher.Constraint('Angle', %s, %f)") + % geoId1 % constr->getValue()); } - else if (constr->SecondPos == Sketcher::PointPos::none) { - return boost::str(boost::format("Sketcher.Constraint('Angle', %i, %i, %f)") - % constr->First % constr->Second % constr->getValue()); + else if (constr->Third == GeoEnum::GeoUndef) { + if (constr->SecondPos == Sketcher::PointPos::none) { + return boost::str(boost::format("Sketcher.Constraint('Angle', %s, %s, %f)") + % geoId1 % geoId2 % constr->getValue()); + } + else { + return boost::str( + boost::format("Sketcher.Constraint('Angle', %s, %i, %s, %i, %f)") + % geoId1 % static_cast(constr->FirstPos) % geoId2 + % static_cast(constr->SecondPos) % constr->getValue()); + } } else { return boost::str( - boost::format("Sketcher.Constraint('Angle', %i, %i, %i, %i, %f)") - % constr->First % static_cast(constr->FirstPos) % constr->Second - % static_cast(constr->SecondPos) % constr->getValue()); + boost::format("Sketcher.Constraint('AngleViaPoint', %s, %s, %s, %i, %f)") + % geoId1 % geoId2 % geoId3 % static_cast(constr->ThirdPos) + % constr->getValue()); } }}, {Sketcher::DistanceX, - [](const Sketcher::Constraint* constr) { + [](const Sketcher::Constraint* constr, + std::string& geoId1, + std::string& geoId2, + std::string& geoId3) { if (constr->Second == GeoEnum::GeoUndef) { - return boost::str(boost::format("Sketcher.Constraint('DistanceX', %i, %f)") - % constr->First % constr->getValue()); + return boost::str(boost::format("Sketcher.Constraint('DistanceX', %s, %f)") + % geoId1 % constr->getValue()); } else if (constr->SecondPos == Sketcher::PointPos::none) { - return boost::str(boost::format("Sketcher.Constraint('DistanceX', %i, %i, %f)") - % constr->First % static_cast(constr->FirstPos) + return boost::str(boost::format("Sketcher.Constraint('DistanceX', %s, %i, %f)") + % geoId1 % static_cast(constr->FirstPos) % constr->getValue()); } else { return boost::str( - boost::format("Sketcher.Constraint('DistanceX', %i, %i, %i, %i, %f)") - % constr->First % static_cast(constr->FirstPos) % constr->Second + boost::format("Sketcher.Constraint('DistanceX', %s, %i, %s, %i, %f)") + % geoId1 % static_cast(constr->FirstPos) % geoId2 % static_cast(constr->SecondPos) % constr->getValue()); } }}, {Sketcher::DistanceY, - [](const Sketcher::Constraint* constr) { + [](const Sketcher::Constraint* constr, + std::string& geoId1, + std::string& geoId2, + [[maybe_unused]] std::string& geoId3) { if (constr->Second == GeoEnum::GeoUndef) { - return boost::str(boost::format("Sketcher.Constraint('DistanceY', %i, %f)") - % constr->First % constr->getValue()); + return boost::str(boost::format("Sketcher.Constraint('DistanceY', %s, %f)") + % geoId1 % constr->getValue()); } else if (constr->SecondPos == Sketcher::PointPos::none) { - return boost::str(boost::format("Sketcher.Constraint('DistanceY', %i, %i, %f)") - % constr->First % static_cast(constr->FirstPos) + return boost::str(boost::format("Sketcher.Constraint('DistanceY', %s, %i, %f)") + % geoId1 % static_cast(constr->FirstPos) % constr->getValue()); } else { return boost::str( - boost::format("Sketcher.Constraint('DistanceY', %i, %i, %i, %i, %f)") - % constr->First % static_cast(constr->FirstPos) % constr->Second + boost::format("Sketcher.Constraint('DistanceY', %s, %i, %s, %i, %f)") + % geoId1 % static_cast(constr->FirstPos) % geoId2 % static_cast(constr->SecondPos) % constr->getValue()); } }}, {Sketcher::Radius, - [](const Sketcher::Constraint* constr) { - return boost::str(boost::format("Sketcher.Constraint('Radius', %i, %f)") - % constr->First % constr->getValue()); + [](const Sketcher::Constraint* constr, + std::string& geoId1, + [[maybe_unused]] std::string& geoId2, + [[maybe_unused]] std::string& geoId3) { + return boost::str(boost::format("Sketcher.Constraint('Radius', %s, %f)") % geoId1 + % constr->getValue()); }}, {Sketcher::Diameter, - [](const Sketcher::Constraint* constr) { - return boost::str(boost::format("Sketcher.Constraint('Diameter', %i, %f)") - % constr->First % constr->getValue()); + [](const Sketcher::Constraint* constr, + std::string& geoId1, + [[maybe_unused]] std::string& geoId2, + [[maybe_unused]] std::string& geoId3) { + return boost::str(boost::format("Sketcher.Constraint('Diameter', %s, %f)") % geoId1 + % constr->getValue()); }}, {Sketcher::Weight, - [](const Sketcher::Constraint* constr) { - return boost::str(boost::format("Sketcher.Constraint('Weight', %i, %f)") - % constr->First % constr->getValue()); + [](const Sketcher::Constraint* constr, + std::string& geoId1, + [[maybe_unused]] std::string& geoId2, + [[maybe_unused]] std::string& geoId3) { + return boost::str(boost::format("Sketcher.Constraint('Weight', %s, %f)") % geoId1 + % constr->getValue()); }}, {Sketcher::PointOnObject, - [](const Sketcher::Constraint* constr) { - return boost::str(boost::format("Sketcher.Constraint('PointOnObject', %i, %i, %i)") - % constr->First % static_cast(constr->FirstPos) - % constr->Second); + [](const Sketcher::Constraint* constr, + std::string& geoId1, + std::string& geoId2, + [[maybe_unused]] std::string& geoId3) { + return boost::str(boost::format("Sketcher.Constraint('PointOnObject', %s, %i, %s)") + % geoId1 % static_cast(constr->FirstPos) % geoId2); }}, {Sketcher::Symmetric, - [](const Sketcher::Constraint* constr) { + [](const Sketcher::Constraint* constr, + std::string& geoId1, + std::string& geoId2, + std::string& geoId3) { if (constr->ThirdPos == Sketcher::PointPos::none) { return boost::str( - boost::format("Sketcher.Constraint('Symmetric', %i, %i, %i, %i, %i)") - % constr->First % static_cast(constr->FirstPos) % constr->Second - % static_cast(constr->SecondPos) % constr->Third); + boost::format("Sketcher.Constraint('Symmetric', %s, %i, %s, %i, %s)") + % geoId1 % static_cast(constr->FirstPos) % geoId2 + % static_cast(constr->SecondPos) % geoId3); } else { return boost::str( - boost::format("Sketcher.Constraint('Symmetric', %i, %i, %i, %i, %i, %i)") - % constr->First % static_cast(constr->FirstPos) % constr->Second - % static_cast(constr->SecondPos) % constr->Third + boost::format("Sketcher.Constraint('Symmetric', %s, %i, %s, %i, %s, %i)") + % geoId1 % static_cast(constr->FirstPos) % geoId2 + % static_cast(constr->SecondPos) % geoId3 % static_cast(constr->ThirdPos)); } }}, {Sketcher::SnellsLaw, - [](const Sketcher::Constraint* constr) { + [](const Sketcher::Constraint* constr, + std::string& geoId1, + std::string& geoId2, + std::string& geoId3) { return boost::str( - boost::format("Sketcher.Constraint('SnellsLaw', %i, %i, %i, %i, %i, %f)") - % constr->First % static_cast(constr->FirstPos) % constr->Second - % static_cast(constr->SecondPos) % constr->Third % constr->getValue()); + boost::format("Sketcher.Constraint('SnellsLaw', %s, %i, %s, %i, %s, %f)") + % geoId1 % static_cast(constr->FirstPos) % geoId2 + % static_cast(constr->SecondPos) % geoId3 % constr->getValue()); }}, }; @@ -600,7 +668,7 @@ std::string PythonConverter::process(const Sketcher::Constraint* constraint) auto creator = result->second; - return creator(constraint); + return creator(constraint, geoId1, geoId2, geoId3); } std::vector PythonConverter::multiLine(std::string&& singlestring) diff --git a/src/Mod/Sketcher/App/PythonConverter.h b/src/Mod/Sketcher/App/PythonConverter.h index 98535583b1..050d09dada 100644 --- a/src/Mod/Sketcher/App/PythonConverter.h +++ b/src/Mod/Sketcher/App/PythonConverter.h @@ -24,6 +24,9 @@ #ifndef SKETCHER_PythonConverter_H #define SKETCHER_PythonConverter_H +#include +#include + #include namespace Part @@ -57,7 +60,7 @@ public: OmitInternalGeometry }; - explicit PythonConverter() = delete; + PythonConverter() = delete; ~PythonConverter() = delete; /// Convert a geometry into the string representing the command creating it @@ -68,17 +71,20 @@ public: const std::vector& geos, Mode mode = Mode::CreateInternalGeometry); - static std::string convert(const Sketcher::Constraint* constraint); + static std::string convert(const Sketcher::Constraint* constraint, + bool useLastGeoIdVar = false); static std::string convert(const std::string& doc, - const std::vector& constraints); + const std::vector& constraints, + bool useLastGeoIdVar = false); static std::vector multiLine(std::string&& singlestring); private: static SingleGeometry process(const Part::Geometry* geo); - static std::string process(const Sketcher::Constraint* constraint); + static std::string process(const Sketcher::Constraint* constraint, + bool useLastGeoIdVar = false); }; } // namespace Sketcher From 7654c4446b1c9f16c3e4d044be516a32a83ec494 Mon Sep 17 00:00:00 2001 From: Paddle Date: Thu, 11 Jan 2024 18:31:57 +0100 Subject: [PATCH 051/135] Sketcher: copy paste : move from XML to python. --- src/Mod/Sketcher/Gui/CommandSketcherTools.cpp | 124 ++++-------------- 1 file changed, 25 insertions(+), 99 deletions(-) diff --git a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp index 2fc791558f..a16c6db99f 100644 --- a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp +++ b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -145,55 +146,24 @@ Sketcher::SketchObject* getSketchObject() // Copy -bool copySelectionToClipboard() { - - std::vector selection = Gui::Command::getSelection().getSelectionEx(); - Sketcher::SketchObject* obj = static_cast(selection[0].getObject()); - // only one sketch with its subelements are allowed to be selected - if (selection.size() != 1) { return false; } - std::vector SubNames = selection[0].getSubNames(); - if (SubNames.empty()) { return false; } - - //First we need listOfGeoId and sort them. - std::vector listOfGeoId; - for (std::vector::const_iterator it = SubNames.begin(); it != SubNames.end(); ++it) { - int GeoId = -1; - // only handle non-external edges - if (it->size() > 4 && it->substr(0, 4) == "Edge") { - GeoId = std::atoi(it->substr(4, 4000).c_str()) - 1; - if (GeoId >= 0) { - listOfGeoId.push_back(GeoId); - } - } - else if (it->size() > 6 && it->substr(0, 6) == "Vertex") { - // only if it is a GeomPoint - int VtId = std::atoi(it->substr(6, 4000).c_str()) - 1; - Sketcher::PointPos PosId; - obj->getGeoVertexIndex(VtId, GeoId, PosId); - if (obj->getGeometry(GeoId)->getTypeId() == Part::GeomPoint::getClassTypeId()) { - if (GeoId >= 0) { - listOfGeoId.push_back(GeoId); - } - } - } - } +bool copySelectionToClipboard(Sketcher::SketchObject* obj) { + std::vector listOfGeoId = getListOfSelectedGeoIds(true); if (listOfGeoId.empty()) { return false; } sort(listOfGeoId.begin(), listOfGeoId.end()); - Base::StringWriter writer; - - //Export selected geometries as a formated string. - std::vector< Part::Geometry* > newVals; + //Export selected geometries as a formatted string. + std::vector shapeGeometry; for (auto geoId : listOfGeoId) { Part::Geometry* geoNew = obj->getGeometry(geoId)->copy(); - newVals.push_back(geoNew); + shapeGeometry.push_back(geoNew); } - Part::PropertyGeometryList geoToCopy; - geoToCopy.setValues(std::move(newVals)); - geoToCopy.Save(writer); + std::string geosAsStr = Sketcher::PythonConverter::convert( + "objectStr", + shapeGeometry, + Sketcher::PythonConverter::Mode::OmitInternalGeometry); - //add constraints to the stream string. - std::vector< Sketcher::Constraint* > newConstrVals; + // Export constraints of selected geos. + std::vector shapeConstraints; for (auto constr : obj->Constraints.getValues()) { auto isSelectedGeoOrAxis = [](const std::vector& vec, int value) { @@ -220,13 +190,12 @@ bool copySelectionToClipboard() { temp->Third = j; } } - newConstrVals.push_back(temp); + shapeConstraints.push_back(temp); } - Sketcher::PropertyConstraintList constToCopy; - constToCopy.setValues(std::move(newConstrVals)); - constToCopy.Save(writer); + std::string cstrAsStr = Sketcher::PythonConverter::convert("objectStr", shapeConstraints, true); - std::string exportedData = writer.getString(); + std::string exportedData = "# Copied from sketcher. From:\n#objectStr = " + Gui::Command::getObjectCmd(obj) + "\n" + + geosAsStr + "\n" + cstrAsStr; if (!exportedData.empty()) { QClipboard* clipboard = QGuiApplication::clipboard(); @@ -255,7 +224,7 @@ CmdSketcherCopyClipboard::CmdSketcherCopyClipboard() void CmdSketcherCopyClipboard::activated(int iMsg) { Q_UNUSED(iMsg); - copySelectionToClipboard(); + copySelectionToClipboard(getSketchObject()); } bool CmdSketcherCopyClipboard::isActive() @@ -286,7 +255,7 @@ CmdSketcherCut::CmdSketcherCut() void CmdSketcherCut::activated(int iMsg) { Q_UNUSED(iMsg); - if (copySelectionToClipboard()) { + if (copySelectionToClipboard(getSketchObject())) { Gui::Document* doc = getActiveGuiDocument(); ReleaseHandler(doc); @@ -329,63 +298,20 @@ void CmdSketcherPaste::activated(int iMsg) Gui::Document* doc = getActiveGuiDocument(); ReleaseHandler(doc); auto* vp = static_cast(doc->getInEdit()); - Sketcher::SketchObject* Obj = vp->getSketchObject(); + Sketcher::SketchObject* obj = vp->getSketchObject(); std::string data = QGuiApplication::clipboard()->text().toStdString(); - int importedFirstGeoId = Obj->getHighestCurveIndex() + 1; - - std::string geoString; - if (data.find("", 0) != std::string::npos) { - geoString = data.substr(0, data.find("", 0) + 16); + if (data.find("# Copied from sketcher.", 0) == std::string::npos) { + return; } - else { return ; } - - std::istringstream istream(geoString); - Base::XMLReader reader("importingGeo", istream); - Part::PropertyGeometryList geoToCopy; - geoToCopy.Restore(reader); - - const std::vector& geos = geoToCopy.getValues(); - if (geos.empty()) { return; } + data = "objectStr = " + Gui::Command::getObjectCmd(obj) +"\n" + data; Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Paste in Sketcher")); - for (auto geo : geos) { - Part::Geometry* geocopy = geo->copy(); - Obj->addGeometry(geocopy); - } + Gui::Command::doCommand(Gui::Command::Doc, data.c_str()); - if (data.find("copy(); - //update the geoIds of the constraints - if (!isAxisOrRoot(constraintToAdd->First)) { - constraintToAdd->First += importedFirstGeoId; - } - if (!isAxisOrRoot(constraintToAdd->Second)) { - constraintToAdd->Second += importedFirstGeoId; - } - if (!isAxisOrRoot(constraintToAdd->Third)) { - constraintToAdd->Third += importedFirstGeoId; - } - Obj->addConstraint(constraintToAdd); - } - } - - Obj->solve(true); - vp->draw(false, false); // Redraw + obj->solve(true); + vp->draw(false, false); Gui::Command::commitCommand(); } From e3860080e58e34ae6a18dc8478a243a867e87a05 Mon Sep 17 00:00:00 2001 From: Paddle Date: Tue, 16 Jan 2024 18:00:27 +0100 Subject: [PATCH 052/135] Sketcher : Distance constraint : introduce arc helpers for conic distances. --- .../Gui/EditModeConstraintCoinManager.cpp | 135 +++++++++++++++--- 1 file changed, 116 insertions(+), 19 deletions(-) diff --git a/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp index 8c32200450..401766eea1 100644 --- a/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp +++ b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp @@ -814,6 +814,17 @@ Restart: case DistanceY: { assert(Constr->First >= -extGeoCount && Constr->First < intGeoCount); + double helperStartAngle1 = 0.; // for arc helpers + double helperStartAngle2 = 0.; + double helperRange1 = 0.; + double helperRange2 = 0.; + double radius1 = 0.; + double radius2 = 0.; + Base::Vector3d center1(0., 0., 0.); + Base::Vector3d center2(0., 0., 0.); + + int numPoints = 2; + // pnt1 will be initialized to (0,0,0) if only one point is given auto pnt1 = geolistfacade.getPoint(Constr->First, Constr->FirstPos); @@ -838,17 +849,15 @@ Restart: pnt2.ProjectToLine(pnt1 - l2p1, l2p2 - l2p1); pnt2 += pnt1; } - else { - if (isCircleOrArc(*geo1)) { - // circular to line distance - auto [radius, ct] = getRadiusCenterCircleArc(geo1); - // project the center on the line (translated to origin) - pnt1.ProjectToLine(ct - l2p1, l2p2 - l2p1); - Base::Vector3d dir = pnt1; - dir.Normalize(); - pnt1 += ct; - pnt2 = ct + dir * radius; - } + else if (isCircleOrArc(*geo1)) { + // circular to line distance + auto [radius, ct] = getRadiusCenterCircleArc(geo1); + // project the center on the line (translated to origin) + pnt1.ProjectToLine(ct - l2p1, l2p2 - l2p1); + Base::Vector3d dir = pnt1; + dir.Normalize(); + pnt1 += ct; + pnt2 = ct + dir * radius; } } else if (isCircleOrArc(*geo2)) { @@ -890,10 +899,8 @@ Restart: break; } - // NOLINTBEGIN - SoDatumLabel* asciiText = static_cast( - sep->getChild(static_cast(ConstraintNodePosition::DatumLabelIndex))); - // NOLINTEND + int index = static_cast(ConstraintNodePosition::DatumLabelIndex); + auto* asciiText = static_cast(sep->getChild(index)); // NOLINT // Get presentation string (w/o units if option is set) asciiText->string = @@ -909,13 +916,102 @@ Restart: asciiText->datumtype = SoDatumLabel::DISTANCEY; } + // Check if arc helpers are needed + if (Constr->Second != GeoEnum::GeoUndef + && Constr->SecondPos == Sketcher::PointPos::none) { + auto geo1 = geolistfacade.getGeometryFromGeoId(Constr->First); + auto geo2 = geolistfacade.getGeometryFromGeoId(Constr->Second); + + if (isArcOfCircle(*geo1)) { + auto arc = static_cast(geo1); // NOLINT + radius1 = arc->getRadius(); + center1 = arc->getCenter(); + + double angle = + toVector2d(isLineSegment(*geo2) ? pnt2 - center1 : pnt1 - center1) + .Angle(); + double startAngle, endAngle; + arc->getRange(startAngle, endAngle, /*emulateCCW=*/true); + + findHelperAngles(helperStartAngle1, + helperRange1, + angle, + startAngle, + endAngle); + + if (helperRange1 != 0.) { + // We override to draw the full helper as it does not look good + // otherwise We still use findHelperAngles before to find if helper + // is needed. + helperStartAngle1 = endAngle; + helperRange1 = 2 * M_PI - (endAngle - startAngle); + + numPoints++; + } + } + if (isArcOfCircle(*geo2)) { + auto arc = static_cast(geo2); // NOLINT + radius2 = arc->getRadius(); + center2 = arc->getCenter(); + + double angle = + toVector2d(pnt2 - center2).Angle(); // between -pi and pi + double startAngle, endAngle; // between 0 and 2*pi + arc->getRange(startAngle, endAngle, /*emulateCCW=*/true); + + findHelperAngles(helperStartAngle2, + helperRange2, + angle, + startAngle, + endAngle); + + if (helperRange2 != 0.) { + helperStartAngle2 = endAngle; + helperRange2 = 2 * M_PI - (endAngle - startAngle); + + numPoints++; + } + } + } + // Assign the Datum Points - asciiText->pnts.setNum(2); + asciiText->pnts.setNum(numPoints); SbVec3f* verts = asciiText->pnts.startEditing(); verts[0] = SbVec3f(pnt1.x, pnt1.y, zConstrH); verts[1] = SbVec3f(pnt2.x, pnt2.y, zConstrH); + if (numPoints > 2) { + if (helperRange1 != 0.) { + verts[2] = SbVec3f(center1.x, center1.y, zConstrH); + asciiText->param3 = helperStartAngle1; + asciiText->param4 = helperRange1; + asciiText->param5 = radius1; + } + else { + verts[2] = SbVec3f(center2.x, center2.y, zConstrH); + asciiText->param3 = helperStartAngle2; + asciiText->param4 = helperRange2; + asciiText->param5 = radius2; + } + if (numPoints > 3) { + verts[3] = SbVec3f(center2.x, center2.y, zConstrH); + asciiText->param6 = helperStartAngle2; + asciiText->param7 = helperRange2; + asciiText->param8 = radius2; + } + else { + asciiText->param6 = 0.; + asciiText->param7 = 0.; + asciiText->param8 = 0.; + } + } + else { + asciiText->param3 = 0.; + asciiText->param4 = 0.; + asciiText->param5 = 0.; + } + asciiText->pnts.finishEditing(); // Assign the Label Distance @@ -1512,6 +1608,7 @@ void EditModeConstraintCoinManager::findHelperAngles(double& helperStartAngle, double startAngle, double endAngle) { + double margin = 0.2; // about 10deg if (angle < 0) { angle = angle + 2 * M_PI; } @@ -1525,15 +1622,15 @@ void EditModeConstraintCoinManager::findHelperAngles(double& helperStartAngle, if (angle > startAngle) { angle -= 2 * M_PI; } - helperStartAngle = angle; - helperRange = startAngle - angle; + helperStartAngle = angle - margin; + helperRange = startAngle - angle + margin; } else { if (angle < endAngle) { angle += 2 * M_PI; } helperStartAngle = endAngle; - helperRange = angle - endAngle; + helperRange = angle - endAngle + margin; } } } From 1a4639f71f1a8416c8df266809d04f6e45829276 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Thu, 18 Jan 2024 23:28:13 +0100 Subject: [PATCH 053/135] Gui: Fix scroll behaviour in preference pages selector This fixes problem with scroll jumping after selecting page on bottom of the pages tree view. It was caused by collapsing group of the current item and then re-expanding it few lines after. Fixes: #12000 --- src/Gui/DlgPreferencesImp.cpp | 45 +++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/src/Gui/DlgPreferencesImp.cpp b/src/Gui/DlgPreferencesImp.cpp index 1c31064a85..cd37e6fc88 100644 --- a/src/Gui/DlgPreferencesImp.cpp +++ b/src/Gui/DlgPreferencesImp.cpp @@ -56,6 +56,28 @@ using namespace Gui::Dialog; +bool isParentOf(const QModelIndex& parent, const QModelIndex& child) +{ + for (auto it = child; it.isValid(); it = it.parent()) { + if (it == parent) { + return true; + } + } + + return false; +} + +QModelIndex findRootIndex(const QModelIndex& index) +{ + auto root = index; + + while (root.parent().isValid()) { + root = root.parent(); + } + + return root; +} + QWidget* PreferencesPageItem::getWidget() const { return _widget; @@ -714,17 +736,6 @@ void DlgPreferencesImp::showEvent(QShowEvent* ev) } } -QModelIndex findRootIndex(const QModelIndex& index) -{ - auto root = index; - - while (root.parent().isValid()) { - root = root.parent(); - } - - return root; -} - void DlgPreferencesImp::onPageSelected(const QModelIndex& index) { auto* currentItem = static_cast(_model.itemFromIndex(index)); @@ -790,14 +801,20 @@ void DlgPreferencesImp::onStackWidgetChange(int index) return; } - ui->groupsTreeView->selectionModel()->select(currentItem->index(), QItemSelectionModel::ClearAndSelect); + auto currentIndex = currentItem->index(); auto root = _model.invisibleRootItem(); for (int i = 0; i < root->rowCount(); i++) { auto currentGroup = static_cast(root->child(i)); + auto currentGroupIndex = currentGroup->index(); + + // don't do anything to group of selected item + if (isParentOf(currentGroupIndex, currentIndex)) { + continue; + } if (!currentGroup->isExpanded()) { - ui->groupsTreeView->collapse(currentGroup->index()); + ui->groupsTreeView->collapse(currentGroupIndex); } } @@ -807,6 +824,8 @@ void DlgPreferencesImp::onStackWidgetChange(int index) ui->groupsTreeView->expand(parentItem->index()); parentItem->setExpanded(wasExpanded); } + + ui->groupsTreeView->selectionModel()->select(currentIndex, QItemSelectionModel::ClearAndSelect); } void DlgPreferencesImp::changeEvent(QEvent *e) From ab3bb9fb71ecf6daba2993ce6e9e06280ec00f86 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Fri, 19 Jan 2024 10:49:47 -0500 Subject: [PATCH 054/135] Improve tests --- src/Mod/Part/App/TopoShapeExpansion.cpp | 2 + tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 181 +++++++++--------- 2 files changed, 94 insertions(+), 89 deletions(-) diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index d5b5b5227b..17a33b4edd 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -376,6 +376,8 @@ void TopoShape::mapSubElementTypeForShape(const TopoShape& other, } std::ostringstream ss; char elementType {shapeName(type)[0]}; + if ( ! elementMap() ) + FC_THROWM(NullShapeException, "No element map"); elementMap()->encodeElementName(elementType, name, ss, &sids, Tag, op, other.Tag); elementMap()->setElementName(element, name, Tag, &sids); } diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index 3d71be8070..d58efa4492 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -162,125 +162,128 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoCubes) } -TEST_F(TopoShapeExpansionTest, mapSubElementOneCube) +TEST_F(TopoShapeExpansionTest, mapSubElementNames) { // Arrange auto [cube1, cube2] = CreateTwoCubes(); - auto [cube3, cube4] = CreateTwoCubes(); Part::TopoShape cube1TS {cube1}; Part::TopoShape cube2TS {cube2}; cube1TS.Tag = 1; cube2TS.Tag = 2; + Part::TopoShape topoShape, topoShape1; + + // Act std::vector subShapes = cube1TS.getSubTopoShapes(TopAbs_FACE); Part::TopoShape face1 = subShapes.front(); face1.Tag = 5; - Part::TopoShape topoShape; - // Act - int fs1 = topoShape.findShape(cube1TS.getShape()); - topoShape.setShape(cube1TS); - // cube1TS.mapSubElement(face1); // This throws an exception. Is that right? - // The cache ancestry only works on TopAbs_SHAPE so this is likely an invalid call, - // but do we defend against it or expect the exception? + int fs1 = topoShape1.findShape(cube1); + topoShape.setShape(cube2TS); + topoShape1.makeElementCompound({cube1TS, cube2TS}); + int fs2 = topoShape1.findShape(cube1); - topoShape.mapSubElement(cube1TS); - int fs2 = topoShape.findShape(cube1TS.getShape()); - int fs3 = topoShape.findShape(face1.getShape()); - int size0 = cube1TS.getElementMap().size(); - int size1 = topoShape.getElementMap().size(); + TopoDS_Shape tds1 = topoShape.findShape("SubShape1"); + TopoDS_Shape tds2 = topoShape.findShape("SubShape2"); // Nonexistent + TopoDS_Shape tds3 = topoShape1.findShape("SubShape1"); + TopoDS_Shape tds4 = topoShape1.findShape("SubShape2"); + TopoDS_Shape tds5 = topoShape1.findShape("NonExistentName"); // Invalid Name // Assert + EXPECT_THROW(cube1TS.mapSubElement(face1), Part::NullShapeException); // No subshapes EXPECT_EQ(fs1, 0); EXPECT_EQ(fs2, 1); - EXPECT_EQ(fs3, 1); - EXPECT_EQ(0, size0); - EXPECT_EQ(26, size1); + EXPECT_FALSE(tds1.IsNull()); + EXPECT_TRUE(tds2.IsNull()); + EXPECT_FALSE(tds3.IsNull()); + EXPECT_FALSE(tds4.IsNull()); + EXPECT_TRUE(tds5.IsNull()); } -TEST_F(TopoShapeExpansionTest, mapSubElementSetReset) +TEST_F(TopoShapeExpansionTest, mapSubElementCacheType) { // Arrange auto [cube1, cube2] = CreateTwoCubes(); - auto [cube3, cube4] = CreateTwoCubes(); Part::TopoShape cube1TS {cube1}; Part::TopoShape cube2TS {cube2}; - Part::TopoShape cube3TS {cube3}; - Part::TopoShape cube4TS {cube4}; cube1TS.Tag = 1; cube2TS.Tag = 2; - cube3TS.Tag = 3; - cube4TS.Tag = 4; - std::vector subShapes = cube1TS.getSubTopoShapes(TopAbs_FACE); - Part::TopoShape face1 = subShapes.front(); - face1.Tag = 5; - Part::TopoShape topoShape, topoShape1; - - // Act - topoShape.setShape(TopoDS_Shape()); - int fs4 = topoShape.findShape(cube1TS.getShape()); - topoShape.setShape(cube1, false); - // topoShape.mapSubElement(cube1TS); - // No mapSubElement required, it keeps finding it now - int fs5 = topoShape.findShape(cube1TS.getShape()); - topoShape.setShape(cube1, true); - int fs6 = topoShape.findShape(cube1TS.getShape()); - // Assert - EXPECT_EQ(fs4, 0); - EXPECT_EQ(fs5, 1); - EXPECT_EQ(fs6, 1); -} - -TEST_F(TopoShapeExpansionTest, mapSubElementCompoundCubes) -{ - // Arrange - auto [cube1, cube2] = CreateTwoCubes(); - auto [cube3, cube4] = CreateTwoCubes(); - Part::TopoShape cube1TS {cube1}; - Part::TopoShape cube2TS {cube2}; - Part::TopoShape cube3TS {cube3}; - Part::TopoShape cube4TS {cube4}; - cube1TS.Tag = 1; - cube2TS.Tag = 2; - cube3TS.Tag = 3; - cube4TS.Tag = 4; - std::vector subShapes = cube1TS.getSubTopoShapes(TopAbs_FACE); - Part::TopoShape face1 = subShapes.front(); - face1.Tag = 5; - Part::TopoShape topoShape, topoShape1; - - // Act - Part::TopoShape topoShape2, topoShape3; - topoShape2.setShape(cube2TS); - topoShape2.mapSubElement(cube2TS, nullptr, true); - int fs7 = topoShape2.findShape(cube2TS.getShape()); - int fs8 = topoShape2.findShape(face1.getShape()); - - topoShape3.setShape(cube3TS); - topoShape3.mapSubElement(cube3TS, "Sample", true); - int fs9 = topoShape3.findShape(cube3TS.getShape()); - int fs10 = topoShape3.findShape(face1.getShape()); - + Part::TopoShape topoShape; topoShape.makeElementCompound({cube1TS, cube2TS}); - int fs11 = topoShape.findShape(cube2TS.getShape()); - int size2 = topoShape.getElementMap().size(); - // Assert - EXPECT_EQ(fs7, 1); - EXPECT_EQ(fs8, 0); - EXPECT_EQ(fs9, 1); - EXPECT_EQ(fs10, 0); - EXPECT_EQ(fs11, 2); - EXPECT_EQ(52, size2); + topoShape.mapSubElement(cube2TS, "Name", false); + + // Act, Assert + for (int i = 1; i <= 12; i++) { + TopoDS_Shape dshape1 = topoShape.findShape(TopAbs_FACE, i); + EXPECT_FALSE(dshape1.IsNull()) << "Face num " << i; + } + TopoDS_Shape dshape1 = topoShape.findShape(TopAbs_FACE, 13); + EXPECT_TRUE(dshape1.IsNull()); +} + + +TEST_F(TopoShapeExpansionTest, mapSubElementCacheAncestor) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + Part::TopoShape cube1TS {cube1}; + Part::TopoShape cube2TS {cube2}; + cube1TS.Tag = 1; + cube2TS.Tag = 2; + Part::TopoShape topoShape; + topoShape.makeElementCompound({cube1TS, cube2TS}); + topoShape.mapSubElement(cube2TS, "Name", false); // Act - topoShape2.makeElementCompound({cube3TS, cube4TS}); - topoShape2.mapSubElement(cube3TS, nullptr, true); - topoShape3.makeElementCompound({topoShape, topoShape2}); - int fs12 = topoShape3.findShape(cube4TS.getShape()); - int size4 = topoShape3.getElementMap().size(); + int fa1 = topoShape.findAncestor(cube2, TopAbs_COMPOUND); + TopoDS_Shape tds1 = topoShape.findAncestorShape(cube1, TopAbs_COMPOUND); + // Assert - EXPECT_EQ(104, size4); - EXPECT_EQ(fs12, 4); + EXPECT_EQ(fa1, 1); + EXPECT_TRUE(tds1.IsEqual(topoShape.getShape())); } +TEST_F(TopoShapeExpansionTest, mapSubElementCacheAncestors) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + auto [cube3, cube4] = CreateTwoCubes(); + auto tr {gp_Trsf()}; + tr.SetTranslation(gp_Vec(gp_XYZ(0, 1, 0))); + cube3.Move(TopLoc_Location(tr)); + cube4.Move(TopLoc_Location(tr)); + Part::TopoShape cube1TS {cube1}; + Part::TopoShape cube2TS {cube2}; + Part::TopoShape cube3TS {cube3}; + Part::TopoShape cube4TS {cube4}; + cube1TS.Tag = 1; + cube2TS.Tag = 2; + cube3TS.Tag = 3; + cube4TS.Tag = 4; + Part::TopoShape topoShape, topoShape1, topoShape2; + topoShape.makeElementCompound({cube1TS, cube2TS}); + topoShape.mapSubElement(cube2TS, nullptr, false); + topoShape1.makeElementCompound({cube3TS, cube4TS}); + topoShape1.mapSubElement(cube3TS, nullptr, true); + topoShape2.makeElementCompound({topoShape, topoShape1}); + topoShape2.mapSubElement(cube2TS, nullptr, false); + + // Act + auto ancestorList = topoShape2.findAncestors(cube2, TopAbs_COMPOUND); + auto ancestorShapeList = topoShape2.findAncestorsShapes(cube2, TopAbs_COMPOUND); + + // Assert + EXPECT_EQ(ancestorList.size(), 1); + EXPECT_EQ(ancestorList.front(), 1); + EXPECT_EQ(ancestorShapeList.size(), 1); + // EXPECT_TRUE(ancestorShapeList.front().IsEqual(topoShape.getShape())); + // EXPECT_TRUE(ancestorShapeList.back().IsEqual(topoShape1.getShape())); +} + +// void initCache(int reset = 0) const; // Can't see any path to visibility to test if this worked. +// std::vector findAncestors(const TopoDS_Shape& subshape, TopAbs_ShapeEnum type) const; // +// DONE std::vector findAncestorsShapes(const TopoDS_Shape& subshape, +// TopAbs_ShapeEnum type) const; // UNSURE + + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From c155edcd878e30ec94843c769d4ee87af5fb0653 Mon Sep 17 00:00:00 2001 From: Roy-043 Date: Fri, 19 Jan 2024 18:31:11 +0100 Subject: [PATCH 055/135] Draft: DraftGui.py make type of self.facecolor consistent Since V0.19 self.facecolor had two types: integer (initial value) or QColor. With this PR it is again always a QColor. --- src/Mod/Draft/DraftGui.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Mod/Draft/DraftGui.py b/src/Mod/Draft/DraftGui.py index 6974b3b1ca..326b7a5a1c 100644 --- a/src/Mod/Draft/DraftGui.py +++ b/src/Mod/Draft/DraftGui.py @@ -163,10 +163,8 @@ class DraftToolBar: self.pointcallback = None # OBSOLETE BUT STILL USED BY SOME ADDONS AND MACROS - self.paramcolor = utils.rgba_to_argb(params.get_param_view("DefaultShapeLineColor")) - self.color = QtGui.QColor(self.paramcolor) - # ToDo: in setStyleButton() self.facecolor is assigned a QColor - self.facecolor = utils.rgba_to_argb(params.get_param_view("DefaultShapeColor")) + self.color = QtGui.QColor(utils.rgba_to_argb(params.get_param_view("DefaultShapeLineColor"))) + self.facecolor = QtGui.QColor(utils.rgba_to_argb(params.get_param_view("DefaultShapeColor"))) self.linewidth = params.get_param_view("DefaultShapeLineWidth") self.fontsize = params.get_param("textheight") From 8e48165b16a572f15aea0b4cca2171d22f21d1d4 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Fri, 19 Jan 2024 11:31:53 -0600 Subject: [PATCH 056/135] fixes #12029 --- src/Mod/Path/Path/Main/Gui/Job.py | 49 +++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/src/Mod/Path/Path/Main/Gui/Job.py b/src/Mod/Path/Path/Main/Gui/Job.py index aec6fe875f..faa6b1c2e9 100644 --- a/src/Mod/Path/Path/Main/Gui/Job.py +++ b/src/Mod/Path/Path/Main/Gui/Job.py @@ -621,25 +621,29 @@ class TaskPanel: vUnit = FreeCAD.Units.Quantity(1, FreeCAD.Units.Velocity).getUserPreferred()[2] self.form.toolControllerList.horizontalHeaderItem(1).setText("#") self.form.toolControllerList.horizontalHeaderItem(2).setText( - translate("Path", "H","H is horizontal feed rate. Must be as short as possible") + translate( + "Path", "H", "H is horizontal feed rate. Must be as short as possible" + ) ) self.form.toolControllerList.horizontalHeaderItem(3).setText( - translate("Path", "V","V is vertical feed rate. Must be as short as possible") + translate( + "Path", "V", "V is vertical feed rate. Must be as short as possible" + ) ) self.form.toolControllerList.horizontalHeader().setResizeMode( 0, QtGui.QHeaderView.Stretch ) self.form.toolControllerList.horizontalHeaderItem(1).setToolTip( - translate("Path", "Tool number") + ' ' + translate("Path", "Tool number") + " " ) self.form.toolControllerList.horizontalHeaderItem(2).setToolTip( - translate("Path", "Horizontal feedrate")+ ' ' + vUnit + translate("Path", "Horizontal feedrate") + " " + vUnit ) self.form.toolControllerList.horizontalHeaderItem(3).setToolTip( - translate("Path", "Vertical feedrate")+ ' ' + vUnit + translate("Path", "Vertical feedrate") + " " + vUnit ) self.form.toolControllerList.horizontalHeaderItem(4).setToolTip( - translate("Path", "Spindle RPM")+ ' ' + translate("Path", "Spindle RPM") + " " ) # ensure correct ellisis behaviour on tool controller names. @@ -804,11 +808,31 @@ class TaskPanel: self.setupOps.getFields() def selectComboBoxText(self, widget, text): - index = widget.findText(text, QtCore.Qt.MatchFixedString) - if index >= 0: + """selectInComboBox(name, combo) ... + helper function to select a specific value in a combo box.""" + index = widget.currentIndex() # Save initial index + + # Search using currentData and return if found + newindex = widget.findData(text) + if newindex >= 0: + widget.blockSignals(True) - widget.setCurrentIndex(index) + widget.setCurrentIndex(newindex) widget.blockSignals(False) + return + + # if not found, search using current text + newindex = widget.findText(text, QtCore.Qt.MatchFixedString) + if newindex >= 0: + widget.blockSignals(True) + widget.setCurrentIndex(newindex) + widget.blockSignals(False) + return + + widget.blockSignals(True) + widget.setCurrentIndex(index) + widget.blockSignals(False) + return def updateToolController(self): tcRow = self.form.toolControllerList.currentRow() @@ -900,10 +924,9 @@ class TaskPanel: self.form.operationsList.addItem(item) self.form.jobModel.clear() - for name, count in Counter([ - self.obj.Proxy.baseObject(self.obj, o).Label - for o in self.obj.Model.Group - ]).items(): + for name, count in Counter( + [self.obj.Proxy.baseObject(self.obj, o).Label for o in self.obj.Model.Group] + ).items(): if count == 1: self.form.jobModel.addItem(name) else: From 3266c38c960a5a5770fff8566e139189763b2b90 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Fri, 19 Jan 2024 20:59:19 -0500 Subject: [PATCH 057/135] Improve tests --- tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 73 +++++++++++++------ 1 file changed, 49 insertions(+), 24 deletions(-) diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index d58efa4492..1fd54c23b9 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -12,6 +12,7 @@ // NOLINTBEGIN(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) + class TopoShapeExpansionTest: public ::testing::Test { protected: @@ -162,7 +163,23 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoCubes) } -TEST_F(TopoShapeExpansionTest, mapSubElementNames) +TEST_F(TopoShapeExpansionTest, mapSubElementInvalidParm) +{ + // Arrange + auto [cube1, cube2] = CreateTwoCubes(); + Part::TopoShape cube1TS {cube1}; + cube1TS.Tag = 1; + + // Act + std::vector subShapes = cube1TS.getSubTopoShapes(TopAbs_FACE); + Part::TopoShape face1 = subShapes.front(); + face1.Tag = 2; + + // Assert + EXPECT_THROW(cube1TS.mapSubElement(face1), Part::NullShapeException); // No subshapes +} + +TEST_F(TopoShapeExpansionTest, mapSubElementFindShapeByNames) { // Arrange auto [cube1, cube2] = CreateTwoCubes(); @@ -173,10 +190,6 @@ TEST_F(TopoShapeExpansionTest, mapSubElementNames) Part::TopoShape topoShape, topoShape1; // Act - std::vector subShapes = cube1TS.getSubTopoShapes(TopAbs_FACE); - Part::TopoShape face1 = subShapes.front(); - face1.Tag = 5; - int fs1 = topoShape1.findShape(cube1); topoShape.setShape(cube2TS); topoShape1.makeElementCompound({cube1TS, cube2TS}); @@ -189,7 +202,6 @@ TEST_F(TopoShapeExpansionTest, mapSubElementNames) TopoDS_Shape tds5 = topoShape1.findShape("NonExistentName"); // Invalid Name // Assert - EXPECT_THROW(cube1TS.mapSubElement(face1), Part::NullShapeException); // No subshapes EXPECT_EQ(fs1, 0); EXPECT_EQ(fs2, 1); EXPECT_FALSE(tds1.IsNull()); @@ -199,7 +211,7 @@ TEST_F(TopoShapeExpansionTest, mapSubElementNames) EXPECT_TRUE(tds5.IsNull()); } -TEST_F(TopoShapeExpansionTest, mapSubElementCacheType) +TEST_F(TopoShapeExpansionTest, mapSubElementFindShapeByType) { // Arrange auto [cube1, cube2] = CreateTwoCubes(); @@ -221,7 +233,7 @@ TEST_F(TopoShapeExpansionTest, mapSubElementCacheType) } -TEST_F(TopoShapeExpansionTest, mapSubElementCacheAncestor) +TEST_F(TopoShapeExpansionTest, mapSubElementFindAncestor) { // Arrange auto [cube1, cube2] = CreateTwoCubes(); @@ -243,7 +255,7 @@ TEST_F(TopoShapeExpansionTest, mapSubElementCacheAncestor) } -TEST_F(TopoShapeExpansionTest, mapSubElementCacheAncestors) +TEST_F(TopoShapeExpansionTest, mapSubElementFindAncestors) { // Arrange auto [cube1, cube2] = CreateTwoCubes(); @@ -261,29 +273,42 @@ TEST_F(TopoShapeExpansionTest, mapSubElementCacheAncestors) cube3TS.Tag = 3; cube4TS.Tag = 4; Part::TopoShape topoShape, topoShape1, topoShape2; + Part::TopoShape topoShape3, topoShape4, topoShape5, topoShape6; topoShape.makeElementCompound({cube1TS, cube2TS}); - topoShape.mapSubElement(cube2TS, nullptr, false); topoShape1.makeElementCompound({cube3TS, cube4TS}); - topoShape1.mapSubElement(cube3TS, nullptr, true); - topoShape2.makeElementCompound({topoShape, topoShape1}); - topoShape2.mapSubElement(cube2TS, nullptr, false); + topoShape2.makeElementCompound({cube1TS, cube3TS}); + topoShape3.makeElementCompound({cube2TS, cube4TS}); + topoShape4.makeElementCompound({topoShape, topoShape1}); + topoShape5.makeElementCompound({topoShape2, topoShape3}); + topoShape6.makeElementCompound({topoShape4, topoShape5}); + topoShape6.mapSubElement(cube2TS, nullptr, false); // Act - auto ancestorList = topoShape2.findAncestors(cube2, TopAbs_COMPOUND); - auto ancestorShapeList = topoShape2.findAncestorsShapes(cube2, TopAbs_COMPOUND); + auto ancestorList = topoShape6.findAncestors(cube3, TopAbs_COMPOUND); + auto ancestorShapeList = topoShape6.findAncestorsShapes(cube3, TopAbs_COMPOUND); + + // FIXME: It seems very strange that both of these ancestors calls return lists of two items + // that contain the same thing twice. What I expect is that the ancestors of cube3 would be + // topoShape6 topoShape5, topoShape3, topoShape2, and topoShape1. + // + // This is a very convoluted hierarchy, and the only way I could get more than one result from + // findAncestors. I guess it's possible that it's only intended to return a single result in + // almost all cases; that would mean that what it returns is the shape at the top of the tree. + // But that's exactly the shape we use to call it in the first place, so we already have it. + // + // Note that in the RT branch, findAncestorsShapes is called by GenericShapeMapper::init, + // TopoShape::makEChamfer and MapperPrism + // findAncestors is used in a dozen places. + // // Assert - EXPECT_EQ(ancestorList.size(), 1); + EXPECT_EQ(ancestorList.size(), 2); EXPECT_EQ(ancestorList.front(), 1); - EXPECT_EQ(ancestorShapeList.size(), 1); - // EXPECT_TRUE(ancestorShapeList.front().IsEqual(topoShape.getShape())); - // EXPECT_TRUE(ancestorShapeList.back().IsEqual(topoShape1.getShape())); + EXPECT_EQ(ancestorList.back(), 1); + EXPECT_EQ(ancestorShapeList.size(), 2); + EXPECT_TRUE(ancestorShapeList.front().IsEqual(topoShape6.getShape())); + EXPECT_TRUE(ancestorShapeList.back().IsEqual(topoShape6.getShape())); } -// void initCache(int reset = 0) const; // Can't see any path to visibility to test if this worked. -// std::vector findAncestors(const TopoDS_Shape& subshape, TopAbs_ShapeEnum type) const; // -// DONE std::vector findAncestorsShapes(const TopoDS_Shape& subshape, -// TopAbs_ShapeEnum type) const; // UNSURE - // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From 85b5a343f01c9b1e6c8092103e2f74fc39e7af63 Mon Sep 17 00:00:00 2001 From: Paddle Date: Sat, 20 Jan 2024 06:49:32 +0100 Subject: [PATCH 058/135] Sketcher: PythonConverter: Replace useLastGeoIdVar bool by enum class --- src/Mod/Sketcher/App/PythonConverter.cpp | 17 +++++++++-------- src/Mod/Sketcher/App/PythonConverter.h | 12 +++++++++--- src/Mod/Sketcher/Gui/CommandSketcherTools.cpp | 2 +- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/Mod/Sketcher/App/PythonConverter.cpp b/src/Mod/Sketcher/App/PythonConverter.cpp index fe5def95dd..506463ae62 100644 --- a/src/Mod/Sketcher/App/PythonConverter.cpp +++ b/src/Mod/Sketcher/App/PythonConverter.cpp @@ -59,11 +59,11 @@ std::string PythonConverter::convert(const Part::Geometry* geo, Mode mode) return command; } -std::string PythonConverter::convert(const Sketcher::Constraint* constraint, bool useLastGeoIdVar) +std::string PythonConverter::convert(const Sketcher::Constraint* constraint, GeoIdMode geoIdMode) { // addConstraint(Sketcher.Constraint('Distance',%d,%f)) std::string command; - auto cg = process(constraint, useLastGeoIdVar); + auto cg = process(constraint, geoIdMode); command = boost::str(boost::format("addConstraint(%s)\n") % cg); @@ -166,7 +166,7 @@ std::string PythonConverter::convert(const std::string& doc, std::string PythonConverter::convert(const std::string& doc, const std::vector& constraints, - bool useLastGeoIdVar) + GeoIdMode geoIdMode) { if (constraints.size() == 1) { auto cg = convert(constraints[0]); @@ -177,7 +177,7 @@ std::string PythonConverter::convert(const std::string& doc, std::string constraintlist = "constraintList = []"; for (auto constraint : constraints) { - auto cg = process(constraint, useLastGeoIdVar); + auto cg = process(constraint, geoIdMode); constraintlist = boost::str(boost::format("%s\nconstraintList.append(%s)") % constraintlist % cg); @@ -344,11 +344,12 @@ PythonConverter::SingleGeometry PythonConverter::process(const Part::Geometry* g return creator(geo); } -std::string PythonConverter::process(const Sketcher::Constraint* constraint, bool useLastIdVar) +std::string PythonConverter::process(const Sketcher::Constraint* constraint, GeoIdMode geoIdMode) { - std::string geoId1 = (useLastIdVar ? "lastGeoId + " : "") + std::to_string(constraint->First); - std::string geoId2 = (useLastIdVar ? "lastGeoId + " : "") + std::to_string(constraint->Second); - std::string geoId3 = (useLastIdVar ? "lastGeoId + " : "") + std::to_string(constraint->Third); + bool addLastIdVar = geoIdMode == GeoIdMode::AddLastGeoIdToGeoIds; + std::string geoId1 = (addLastIdVar ? "lastGeoId + " : "") + std::to_string(constraint->First); + std::string geoId2 = (addLastIdVar ? "lastGeoId + " : "") + std::to_string(constraint->Second); + std::string geoId3 = (addLastIdVar ? "lastGeoId + " : "") + std::to_string(constraint->Third); static std::map< const Sketcher::ConstraintType, diff --git a/src/Mod/Sketcher/App/PythonConverter.h b/src/Mod/Sketcher/App/PythonConverter.h index 050d09dada..8062064589 100644 --- a/src/Mod/Sketcher/App/PythonConverter.h +++ b/src/Mod/Sketcher/App/PythonConverter.h @@ -60,6 +60,12 @@ public: OmitInternalGeometry }; + enum class GeoIdMode + { + DoNotChangeGeoIds, + AddLastGeoIdToGeoIds, + }; + PythonConverter() = delete; ~PythonConverter() = delete; @@ -72,11 +78,11 @@ public: Mode mode = Mode::CreateInternalGeometry); static std::string convert(const Sketcher::Constraint* constraint, - bool useLastGeoIdVar = false); + GeoIdMode geoIdMode = GeoIdMode::DoNotChangeGeoIds); static std::string convert(const std::string& doc, const std::vector& constraints, - bool useLastGeoIdVar = false); + GeoIdMode geoIdMode = GeoIdMode::DoNotChangeGeoIds); static std::vector multiLine(std::string&& singlestring); @@ -84,7 +90,7 @@ private: static SingleGeometry process(const Part::Geometry* geo); static std::string process(const Sketcher::Constraint* constraint, - bool useLastGeoIdVar = false); + GeoIdMode geoIdMode = GeoIdMode::DoNotChangeGeoIds); }; } // namespace Sketcher diff --git a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp index a16c6db99f..3905fdcaaa 100644 --- a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp +++ b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp @@ -192,7 +192,7 @@ bool copySelectionToClipboard(Sketcher::SketchObject* obj) { } shapeConstraints.push_back(temp); } - std::string cstrAsStr = Sketcher::PythonConverter::convert("objectStr", shapeConstraints, true); + std::string cstrAsStr = Sketcher::PythonConverter::convert("objectStr", shapeConstraints, Sketcher::PythonConverter::GeoIdMode::AddLastGeoIdToGeoIds); std::string exportedData = "# Copied from sketcher. From:\n#objectStr = " + Gui::Command::getObjectCmd(obj) + "\n" + geosAsStr + "\n" + cstrAsStr; From 56d4214317b3b56aed9e79e6dacac3538dc66774 Mon Sep 17 00:00:00 2001 From: marioalexis Date: Fri, 19 Jan 2024 17:54:16 -0300 Subject: [PATCH 059/135] Fem: Improve self weight constraint - fixes #11652 --- .../constraint_selfweight_cantilever.py | 2 +- .../Fem/femobjects/constraint_selfweight.py | 69 ++++++++++++------- .../calculix/write_constraint_selfweight.py | 8 +-- .../elmer/equations/deformation_writer.py | 8 +-- .../elmer/equations/elasticity_writer.py | 8 +-- .../constraint_selfweight_cantilever.inp | 2 +- 6 files changed, 59 insertions(+), 38 deletions(-) diff --git a/src/Mod/Fem/femexamples/constraint_selfweight_cantilever.py b/src/Mod/Fem/femexamples/constraint_selfweight_cantilever.py index ee6e3c572a..6473c0b959 100644 --- a/src/Mod/Fem/femexamples/constraint_selfweight_cantilever.py +++ b/src/Mod/Fem/femexamples/constraint_selfweight_cantilever.py @@ -124,7 +124,7 @@ def setup(doc=None, solvertype="ccxtools"): # constraint selfweight con_selfweight = ObjectsFem.makeConstraintSelfWeight(doc, "ConstraintSelfWeight") - con_selfweight.Gravity_z = -1.00 + con_selfweight.GravityDirection = (0, 0, -1) analysis.addObject(con_selfweight) # mesh diff --git a/src/Mod/Fem/femobjects/constraint_selfweight.py b/src/Mod/Fem/femobjects/constraint_selfweight.py index 843457bd38..6972ad2d9d 100644 --- a/src/Mod/Fem/femobjects/constraint_selfweight.py +++ b/src/Mod/Fem/femobjects/constraint_selfweight.py @@ -29,7 +29,9 @@ __url__ = "https://www.freecad.org" # \ingroup FEM # \brief constraint self weight object +import FreeCAD from . import base_fempythonobject +from femtools import constants class ConstraintSelfWeight(base_fempythonobject.BaseFemPythonObject): @@ -42,33 +44,52 @@ class ConstraintSelfWeight(base_fempythonobject.BaseFemPythonObject): def __init__(self, obj): super(ConstraintSelfWeight, self).__init__(obj) - obj.addProperty( - "App::PropertyFloat", - "Gravity_x", - "Gravity", - "Gravity direction: set the x-component of the normalized gravity vector" - ) - - obj.addProperty( - "App::PropertyFloat", - "Gravity_y", - "Gravity", - "Gravity direction: set the y-component of the normalized gravity vector" - ) - - obj.addProperty( - "App::PropertyFloat", - "Gravity_z", - "Gravity", - "Gravity direction: set the z-component of the normalized gravity vector" - ) - - obj.Gravity_x = 0.0 - obj.Gravity_y = 0.0 - obj.Gravity_z = -1.0 + self.addProperty(obj) # https://wiki.freecad.org/Scripted_objects#Property_Type # https://forum.freecad.org/viewtopic.php?f=18&t=13460&start=20#p109709 # https://forum.freecad.org/viewtopic.php?t=25524 # obj.setEditorMode("References", 1) # read only in PropertyEditor, but writeable by Python obj.setEditorMode("References", 2) # do not show in Editor + + def addProperty(self, obj): + obj.addProperty("App::PropertyAcceleration", + "GravityAcceleration", + "Gravity", + "Gravity acceleration") + obj.GravityAcceleration = constants.gravity() + + obj.addProperty("App::PropertyVector", + "GravityDirection", + "Gravity", + "Normalized gravity direction") + obj.GravityDirection = FreeCAD.Vector(0, 0, -1) + + obj.setPropertyStatus("NormalDirection", "Hidden") + + def onDocumentRestored(self, obj): + # migrate old App::PropertyFloat "Gravity_*" if exists + try: + grav_x = obj.getPropertyByName("Gravity_x") + grav_y = obj.getPropertyByName("Gravity_y") + grav_z = obj.getPropertyByName("Gravity_z") + grav = FreeCAD.Vector(grav_x, grav_y, grav_z) + + self.addProperty(obj) + obj.GravityAcceleration = constants.gravity() + obj.GravityAcceleration *= grav.Length + obj.GravityDirection = grav.normalize() + + obj.removeProperty("Gravity_x") + obj.removeProperty("Gravity_y") + obj.removeProperty("Gravity_z") + + return + + except: + return + + def execute(self, obj): + obj.GravityDirection.normalize() + + return False diff --git a/src/Mod/Fem/femsolver/calculix/write_constraint_selfweight.py b/src/Mod/Fem/femsolver/calculix/write_constraint_selfweight.py index a4eeeb495a..ee366df41c 100644 --- a/src/Mod/Fem/femsolver/calculix/write_constraint_selfweight.py +++ b/src/Mod/Fem/femsolver/calculix/write_constraint_selfweight.py @@ -52,10 +52,10 @@ def write_constraint(f, femobj, selwei_obj, ccxwriter): "{},GRAV,{:.13G},{:.13G},{:.13G},{:.13G}\n" .format( ccxwriter.ccx_eall, - ccxwriter.gravity, # actual magnitude of gravity vector - selwei_obj.Gravity_x, # coordinate x of normalized gravity vector - selwei_obj.Gravity_y, # y - selwei_obj.Gravity_z # z + selwei_obj.GravityAcceleration.getValueAs("mm/s^2").Value, # actual magnitude of gravity vector + selwei_obj.GravityDirection.x, # coordinate x of normalized gravity vector + selwei_obj.GravityDirection.y, # y + selwei_obj.GravityDirection.z # z ) ) f.write("\n") diff --git a/src/Mod/Fem/femsolver/elmer/equations/deformation_writer.py b/src/Mod/Fem/femsolver/elmer/equations/deformation_writer.py index 080976218d..f76dd39fe0 100644 --- a/src/Mod/Fem/femsolver/elmer/equations/deformation_writer.py +++ b/src/Mod/Fem/femsolver/elmer/equations/deformation_writer.py @@ -148,7 +148,7 @@ class DeformationWriter: obj = self.write.getSingleMember("Fem::ConstraintSelfWeight") if obj is not None: for name in bodies: - gravity = self.write.convert(self.write.constsdef["Gravity"], "L/T^2") + gravity = self.write.convert(obj.GravityAcceleration.toStr(), "L/T^2") if self.write.getBodyMaterial(name) is None: raise general_writer.WriteError( "The body {} is not referenced in any material.\n\n".format(name) @@ -164,9 +164,9 @@ class DeformationWriter: dimension = "M/L^2" density = self.write.convert(densityQuantity, dimension) - force1 = gravity * obj.Gravity_x * density - force2 = gravity * obj.Gravity_y * density - force3 = gravity * obj.Gravity_z * density + force1 = gravity * obj.GravityDirection.x * density + force2 = gravity * obj.GravityDirection.y * density + force3 = gravity * obj.GravityDirection.z * density self.write.bodyForce(name, "Stress Bodyforce 1", force1) self.write.bodyForce(name, "Stress Bodyforce 2", force2) self.write.bodyForce(name, "Stress Bodyforce 3", force3) diff --git a/src/Mod/Fem/femsolver/elmer/equations/elasticity_writer.py b/src/Mod/Fem/femsolver/elmer/equations/elasticity_writer.py index bc71a67368..4424e903e7 100644 --- a/src/Mod/Fem/femsolver/elmer/equations/elasticity_writer.py +++ b/src/Mod/Fem/femsolver/elmer/equations/elasticity_writer.py @@ -364,7 +364,7 @@ class ElasticityWriter: obj = self.write.getSingleMember("Fem::ConstraintSelfWeight") if obj is not None: for name in bodies: - gravity = self.write.convert(self.write.constsdef["Gravity"], "L/T^2") + gravity = self.write.convert(obj.GravityAcceleration.toStr(), "L/T^2") if self.write.getBodyMaterial(name) is None: raise general_writer.WriteError( "The body {} is not referenced in any material.\n\n".format(name) @@ -380,9 +380,9 @@ class ElasticityWriter: dimension = "M/L^2" density = self.write.convert(densityQuantity, dimension) - force1 = gravity * obj.Gravity_x * density - force2 = gravity * obj.Gravity_y * density - force3 = gravity * obj.Gravity_z * density + force1 = gravity * obj.GravityDirection.x * density + force2 = gravity * obj.GravityDirection.y * density + force3 = gravity * obj.GravityDirection.z * density self.write.bodyForce(name, "Stress Bodyforce 1", force1) self.write.bodyForce(name, "Stress Bodyforce 2", force2) self.write.bodyForce(name, "Stress Bodyforce 3", force3) diff --git a/src/Mod/Fem/femtest/data/calculix/constraint_selfweight_cantilever.inp b/src/Mod/Fem/femtest/data/calculix/constraint_selfweight_cantilever.inp index 4fda9cf903..1721a498ea 100644 --- a/src/Mod/Fem/femtest/data/calculix/constraint_selfweight_cantilever.inp +++ b/src/Mod/Fem/femtest/data/calculix/constraint_selfweight_cantilever.inp @@ -2170,7 +2170,7 @@ ConstraintFixed,3 ** Self weight Constraint ** ConstraintSelfWeight *DLOAD -Eall,GRAV,9806,0,0,-1 +Eall,GRAV,9806.65,0,0,-1 *********************************************************** From 8777b809cd33c48a5fff9697b56af2e2f028964b Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Fri, 19 Jan 2024 23:48:49 +0100 Subject: [PATCH 060/135] Sketcher: Draw proper checkbox control in Elements UI This ensures that checkboxes drawn in the Elements UI of the sketcher can be styled using the correct style. Three changes are introduced: 1. paint() method uses style provided by the widget, not application. 2. Indicator is drawn using PE_IndicatorCheckBox not CE_CheckBox - it allows to use ::indicator in qss to style it properly. This works the same as TreeView. 3. Fix for minor issues with text alignement and extension of selected items background to cover checkbox. This is how other controls of this kind work. --- src/Mod/Sketcher/Gui/TaskSketcherElements.cpp | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp index 249d05dd88..c9d45ef713 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp @@ -128,7 +128,6 @@ public: const int border = 1; // 1px, looks good around buttons. const int leftMargin = 4;// 4px on the left of icons, looks good. mutable int customIconsMargin = 4; - const int textBottomMargin = 5;// 5px center the text. Q_SIGNALS: void itemHovered(QModelIndex); @@ -773,10 +772,11 @@ void ElementItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o ElementItem* item = getElementtItem(index); if (item) { + auto style = option.widget ? option.widget->style() : QApplication::style(); QStyleOptionButton checkboxstyle; - checkboxstyle.rect = option.rect; + checkboxstyle.rect = option.rect; checkboxstyle.state |= QStyle::State_Enabled; if (item->isVisible()) @@ -784,12 +784,17 @@ void ElementItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o else checkboxstyle.state |= QStyle::State_Off; - QRect checkboxrect = - QApplication::style()->subElementRect(QStyle::SE_CheckBoxIndicator, &checkboxstyle); + QRect checkboxrect + = style->subElementRect(QStyle::SE_CheckBoxIndicator, &checkboxstyle, option.widget); - customIconsMargin = leftMargin + checkboxrect.width(); + checkboxstyle.rect = { + leftMargin, + option.rect.top() + (option.rect.height() - checkboxrect.height()) / 2, // vertically center the checkbox + checkboxrect.width(), + checkboxrect.height() + }; - QApplication::style()->drawControl(QStyle::CE_CheckBox, &checkboxstyle, painter); + customIconsMargin = leftMargin + checkboxrect.width() + leftMargin; int height = option.rect.height(); int width = height;// icons are square. @@ -805,9 +810,9 @@ void ElementItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o painter->fillRect(rect, option.palette.base()); }; - QRect selection = QRect(customIconsMargin, + QRect selection = QRect(option.rect.x(), option.rect.y(), - option.rect.width() - customIconsMargin, + option.rect.width(), option.rect.height()); painter->fillRect(selection, option.palette.highlight());// paint the item as selected @@ -836,14 +841,20 @@ void ElementItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o ElementWidgetIcons::getIcon(item->GeometryType, Sketcher::PointPos::mid, item->State); // getIcon(item->GeometryType); + style->drawPrimitive(QStyle::PE_IndicatorCheckBox, &checkboxstyle, painter, option.widget); + painter->drawPixmap(x0 + border, btny, iconEdge.pixmap(iconsize, iconsize)); painter->drawPixmap(x0 + border + width, btny, iconStart.pixmap(iconsize, iconsize)); painter->drawPixmap(x0 + border + width * 2, btny, iconEnd.pixmap(iconsize, iconsize)); painter->drawPixmap(x0 + border + width * 3, btny, iconMid.pixmap(iconsize, iconsize)); // Label : + auto labelBoundingBox = painter->fontMetrics().tightBoundingRect(item->label); painter->drawText( - x0 + width * 4 + 3 * border, option.rect.y() + height - textBottomMargin, item->label); + x0 + width * 4 + 3 * border, + option.rect.bottom() - (option.rect.height() - labelBoundingBox.height()) / 2, + item->label + ); } } From 899d0d91302140676d57e68f358aa0aade26fa0c Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sat, 20 Jan 2024 00:06:03 +0100 Subject: [PATCH 061/135] Sketcher: Make Constraints UI style aware This adds reference to the actual widget to the painting of Constraints List control of Sketcher. Thanks to this this list can now be styled using standard mechanisms of Qt framework like QSS. --- src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp index b927ec8ac3..363aea9859 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp @@ -262,12 +262,12 @@ public: static QIcon snell_driven( Gui::BitmapFactory().iconFromTheme("Constraint_SnellsLaw_Driven")); - auto selicon = [](const Sketcher::Constraint* constr, + auto selicon = [this](const Sketcher::Constraint* constr, const QIcon& normal, const QIcon& driven) -> QIcon { if (!constr->isActive) { QIcon darkIcon; - int w = QApplication::style()->pixelMetric(QStyle::PM_ListViewIconSize); + int w = listWidget()->style()->pixelMetric(QStyle::PM_ListViewIconSize); darkIcon.addPixmap(normal.pixmap(w, w, QIcon::Disabled, QIcon::Off), QIcon::Normal, QIcon::Off); @@ -466,7 +466,7 @@ protected: QStyleOptionViewItem options = option; initStyleOption(&options, index); - options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter); + options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter, option.widget); ConstraintItem* item = dynamic_cast(view->item(index.row())); if (!item || item->sketch->Constraints.getSize() <= item->ConstraintNbr) From d1297ebf851b344d3dab3883410836480e64a36d Mon Sep 17 00:00:00 2001 From: wandererfan Date: Fri, 19 Jan 2024 13:22:57 -0500 Subject: [PATCH 062/135] [TD]handle equal section line end points --- src/Mod/TechDraw/Gui/QGIViewPart.cpp | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/Mod/TechDraw/Gui/QGIViewPart.cpp b/src/Mod/TechDraw/Gui/QGIViewPart.cpp index 5c32b1f9dd..50e1b88110 100644 --- a/src/Mod/TechDraw/Gui/QGIViewPart.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewPart.cpp @@ -652,6 +652,16 @@ void QGIViewPart::drawSectionLine(TechDraw::DrawViewSection* viewSection, bool b } if (b) { + //find the ends of the section line + double scale = viewPart->getScale(); + std::pair sLineEnds = viewSection->sectionLineEnds(); + Base::Vector3d l1 = Rez::guiX(sLineEnds.first) * scale; + Base::Vector3d l2 = Rez::guiX(sLineEnds.second) * scale; + if (l1.IsEqual(l2, EWTOLERANCE) ) { + Base::Console().Message("QGIVP::drawSectionLine - line endpoints are equal. No section line created.\n"); + return; + } + QGISectionLine* sectionLine = new QGISectionLine(); addToGroup(sectionLine); sectionLine->setSymbol(const_cast(viewSection->SectionSymbol.getValue())); @@ -660,11 +670,6 @@ void QGIViewPart::drawSectionLine(TechDraw::DrawViewSection* viewSection, bool b sectionLine->setSectionColor(color.asValue()); sectionLine->setPathMode(false); - //find the ends of the section line - double scale = viewPart->getScale(); - std::pair sLineEnds = viewSection->sectionLineEnds(); - Base::Vector3d l1 = Rez::guiX(sLineEnds.first) * scale; - Base::Vector3d l2 = Rez::guiX(sLineEnds.second) * scale; //make the section line a little longer double fudge = 2.0 * Preferences::dimFontSizeMM(); Base::Vector3d lineDir = l2 - l1; @@ -722,6 +727,15 @@ void QGIViewPart::drawComplexSectionLine(TechDraw::DrawViewSection* viewSection, } auto dcs = static_cast(viewSection); + std::pair ends = dcs->sectionLineEnds(); + Base::Vector3d vStart = Rez::guiX(ends.first);//already scaled by dcs + Base::Vector3d vEnd = Rez::guiX(ends.second); + if (vStart.IsEqual(vEnd, EWTOLERANCE) ) { + Base::Console().Message("QGIVP::drawComplexSectionLine - line endpoints are equal. No section line created.\n"); + return; + } + + BaseGeomPtrVector edges = dcs->makeSectionLineGeometry(); QPainterPath wirePath; QPainterPath firstSeg = drawPainterPath(edges.front()); @@ -736,9 +750,6 @@ void QGIViewPart::drawComplexSectionLine(TechDraw::DrawViewSection* viewSection, wirePath.connectPath(edgePath); } - std::pair ends = dcs->sectionLineEnds(); - Base::Vector3d vStart = Rez::guiX(ends.first);//already scaled by dcs - Base::Vector3d vEnd = Rez::guiX(ends.second); QGISectionLine* sectionLine = new QGISectionLine(); addToGroup(sectionLine); From a4c55b9ec8ca0e8c22aca8339c959aa18dc169f3 Mon Sep 17 00:00:00 2001 From: wandererfan Date: Fri, 19 Jan 2024 19:32:29 -0500 Subject: [PATCH 063/135] [TD]protect against stretch factor == 0 --- src/Mod/TechDraw/App/DrawViewSection.cpp | 8 ++++++++ src/Mod/TechDraw/App/DrawViewSection.h | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Mod/TechDraw/App/DrawViewSection.cpp b/src/Mod/TechDraw/App/DrawViewSection.cpp index 3d527715ae..01ab923556 100644 --- a/src/Mod/TechDraw/App/DrawViewSection.cpp +++ b/src/Mod/TechDraw/App/DrawViewSection.cpp @@ -125,6 +125,13 @@ const char* DrawViewSection::SectionDirEnums[] = const char* DrawViewSection::CutSurfaceEnums[] = {"Hide", "Color", "SvgHatch", "PatHatch", nullptr}; +constexpr double stretchMinimum{EWTOLERANCE}; +constexpr double stretchMaximum{std::numeric_limits::max()}; +constexpr double stretchStep{0.1}; + +App::PropertyFloatConstraint::Constraints DrawViewSection::stretchRange = { + stretchMinimum, stretchMaximum, stretchStep}; + //=========================================================================== // DrawViewSection //=========================================================================== @@ -230,6 +237,7 @@ DrawViewSection::DrawViewSection() ADD_PROPERTY_TYPE(SectionLineStretch, (1.0), agroup, App::Prop_None, "Adjusts the length of the section line. 1.0 is normal length. 1.1 would be 10% longer, 0.9 would be 10% shorter."); + SectionLineStretch.setConstraints(&stretchRange); getParameters(); diff --git a/src/Mod/TechDraw/App/DrawViewSection.h b/src/Mod/TechDraw/App/DrawViewSection.h index 09eb87cfd7..ed311fe27b 100644 --- a/src/Mod/TechDraw/App/DrawViewSection.h +++ b/src/Mod/TechDraw/App/DrawViewSection.h @@ -104,7 +104,7 @@ public: App::PropertyBool TrimAfterCut;//new v021 App::PropertyBool UsePreviousCut; // new v022 - App::PropertyFloat SectionLineStretch; // new v022 + App::PropertyFloatConstraint SectionLineStretch; // new v022 bool isReallyInBox(const Base::Vector3d v, const Base::BoundBox3d bb) const; @@ -205,6 +205,9 @@ protected: bool m_waitingForCut; TopoDS_Shape m_cuttingTool; double m_shapeSize; + + static App::PropertyFloatConstraint::Constraints stretchRange; + }; using DrawViewSectionPython = App::FeaturePythonT; From 04bc6f80063bbb5a964b61a3fd76f63c5ee4a783 Mon Sep 17 00:00:00 2001 From: Ajinkya Dahale Date: Fri, 5 Jan 2024 20:54:31 +0530 Subject: [PATCH 064/135] [Sketcher] Visualize B-spline in edit mode Instead of the control polygon, or "join the dots" for interpolated splines. --- .../Sketcher/Gui/DrawSketchHandlerBSpline.h | 43 +++++++++++++++++-- .../DrawSketchHandlerBSplineByInterpolation.h | 25 +++++++++-- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerBSpline.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerBSpline.h index 080ffac50b..2cae185c6e 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerBSpline.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerBSpline.h @@ -90,7 +90,7 @@ public: } else if (Mode == STATUS_SEEK_ADDITIONAL_CONTROLPOINTS) { - drawControlPolygonToPosition(onSketchPos); + drawBSplineToPosition(onSketchPos); drawCursorToPosition(onSketchPos); @@ -313,7 +313,7 @@ public: // run this in the end to draw lines and position text - drawControlPolygonToPosition(prevCursorPosition); + drawBSplineToPosition(prevCursorPosition); drawCursorToPosition(prevCursorPosition); } catch (const Base::Exception&) { @@ -414,13 +414,50 @@ private: void drawControlPolygonToPosition(Base::Vector2d position) { - std::vector editcurve(BSplinePoles); editcurve.push_back(position); drawEdit(editcurve); } + void drawBSplineToPosition(Base::Vector2d position) + { + std::vector editcurve; + for (auto& pole : BSplinePoles) { + editcurve.emplace_back(pole.x, pole.y, 0.0); + } + editcurve.emplace_back(position.x, position.y, 0.0); + size_t degree = std::min(editcurve.size() - 1, static_cast(SplineDegree)); + bool periodic = (ConstrMethod != 0); + + std::vector weights(editcurve.size(), 1.0); + std::vector knots; + std::vector mults; + if (!periodic) { + for (size_t i = 0; i < editcurve.size() - degree + 1; ++i) { + knots.push_back(i); + } + mults.resize(editcurve.size() - degree + 1, 1); + mults.front() = degree + 1; + mults.back() = degree + 1; + } + else { + for (size_t i = 0; i < editcurve.size() + 1; ++i) { + knots.push_back(i); + } + mults.resize(editcurve.size() + 1, 1); + } + + // TODO: This maybe optimized by storing the spline as an attribute. + Part::GeomBSplineCurve editBSpline(editcurve, weights, knots, mults, degree, periodic); + editBSpline.setPoles(editcurve); + + std::vector editBSplines; + editBSplines.push_back(&editBSpline); + + drawEdit(editBSplines); + } + void drawCursorToPosition(Base::Vector2d position) { if (!BSplinePoles.empty()) { diff --git a/src/Mod/Sketcher/Gui/DrawSketchHandlerBSplineByInterpolation.h b/src/Mod/Sketcher/Gui/DrawSketchHandlerBSplineByInterpolation.h index 593c5f9cf9..3880a0c741 100644 --- a/src/Mod/Sketcher/Gui/DrawSketchHandlerBSplineByInterpolation.h +++ b/src/Mod/Sketcher/Gui/DrawSketchHandlerBSplineByInterpolation.h @@ -90,7 +90,7 @@ public: } else if (Mode == STATUS_SEEK_ADDITIONAL_POINTS) { - drawControlPolygonToPosition(onSketchPos); + drawBSplineToPosition(onSketchPos); drawCursorToPosition(onSketchPos); @@ -311,7 +311,7 @@ public: // run this in the end to draw lines and position text - drawControlPolygonToPosition(prevCursorPosition); + drawBSplineToPosition(prevCursorPosition); drawCursorToPosition(prevCursorPosition); } catch (const Base::Exception&) { @@ -409,13 +409,32 @@ private: // NOTE: In this context, it is not a control polygon, but a 1-degree interpolation void drawControlPolygonToPosition(Base::Vector2d position) { - std::vector editcurve(BSplineKnots); editcurve.push_back(position); drawEdit(editcurve); } + void drawBSplineToPosition(Base::Vector2d position) + { + std::vector editcurve(BSplineKnots); + editcurve.push_back(position); + + std::vector editCurveForOCCT; + for (auto& p : editcurve) { + editCurveForOCCT.emplace_back(p.x, p.y, 0.0); + } + + // TODO: This maybe optimized by storing the spline as an attribute. + Part::GeomBSplineCurve editBSpline; + editBSpline.interpolate(editCurveForOCCT, ConstrMethod != 0); + + std::vector editBSplines; + editBSplines.push_back(&editBSpline); + + drawEdit(editBSplines); + } + void drawCursorToPosition(Base::Vector2d position) { if (!BSplineKnots.empty()) { From 336bf03ada63347ec850862ecfbbece1cc7b0e57 Mon Sep 17 00:00:00 2001 From: marioalexis Date: Sat, 20 Jan 2024 12:03:46 -0300 Subject: [PATCH 065/135] Fem: Call base class handleChangedProperyType function --- src/Mod/Fem/App/FemConstraintDisplacement.cpp | 3 +++ src/Mod/Fem/App/FemConstraintForce.cpp | 3 +++ src/Mod/Fem/App/FemConstraintInitialTemperature.cpp | 3 +++ src/Mod/Fem/App/FemConstraintPressure.cpp | 3 +++ src/Mod/Fem/App/FemConstraintTemperature.cpp | 3 +++ src/Mod/Fem/App/FemConstraintTransform.cpp | 3 +++ src/Mod/Fem/App/FemPostFilter.cpp | 3 +++ 7 files changed, 21 insertions(+) diff --git a/src/Mod/Fem/App/FemConstraintDisplacement.cpp b/src/Mod/Fem/App/FemConstraintDisplacement.cpp index 4fcb5f4f01..4ef6497c70 100644 --- a/src/Mod/Fem/App/FemConstraintDisplacement.cpp +++ b/src/Mod/Fem/App/FemConstraintDisplacement.cpp @@ -141,6 +141,9 @@ void ConstraintDisplacement::handleChangedPropertyType(Base::XMLReader& reader, zRotationProperty.Restore(reader); zRotation.setValue(zRotationProperty.getValue()); } + else { + Constraint::handleChangedPropertyType(reader, TypeName, prop); + } } void ConstraintDisplacement::onChanged(const App::Property* prop) diff --git a/src/Mod/Fem/App/FemConstraintForce.cpp b/src/Mod/Fem/App/FemConstraintForce.cpp index 1a54f680aa..79b9d8c746 100644 --- a/src/Mod/Fem/App/FemConstraintForce.cpp +++ b/src/Mod/Fem/App/FemConstraintForce.cpp @@ -80,6 +80,9 @@ void ConstraintForce::handleChangedPropertyType(Base::XMLReader& reader, // e.g. "2.5" must become 2500 to result in 2.5 N Force.setValue(ForceProperty.getValue() * 1000); } + else { + Constraint::handleChangedPropertyType(reader, TypeName, prop); + } } void ConstraintForce::onChanged(const App::Property* prop) diff --git a/src/Mod/Fem/App/FemConstraintInitialTemperature.cpp b/src/Mod/Fem/App/FemConstraintInitialTemperature.cpp index 32b3203c10..cb2af6aa78 100644 --- a/src/Mod/Fem/App/FemConstraintInitialTemperature.cpp +++ b/src/Mod/Fem/App/FemConstraintInitialTemperature.cpp @@ -74,6 +74,9 @@ void ConstraintInitialTemperature::handleChangedPropertyType(Base::XMLReader& re initialTemperatureProperty.Restore(reader); initialTemperature.setValue(initialTemperatureProperty.getValue()); } + else { + Constraint::handleChangedPropertyType(reader, TypeName, prop); + } } void ConstraintInitialTemperature::onChanged(const App::Property* prop) diff --git a/src/Mod/Fem/App/FemConstraintPressure.cpp b/src/Mod/Fem/App/FemConstraintPressure.cpp index e6a06c4efb..911d555056 100644 --- a/src/Mod/Fem/App/FemConstraintPressure.cpp +++ b/src/Mod/Fem/App/FemConstraintPressure.cpp @@ -71,6 +71,9 @@ void ConstraintPressure::handleChangedPropertyType(Base::XMLReader& reader, // therefore we must convert the value with a factor 1000 Pressure.setValue(PressureProperty.getValue() * 1000.0); } + else { + Constraint::handleChangedPropertyType(reader, TypeName, prop); + } } void ConstraintPressure::onChanged(const App::Property* prop) diff --git a/src/Mod/Fem/App/FemConstraintTemperature.cpp b/src/Mod/Fem/App/FemConstraintTemperature.cpp index dc4037482f..6c906333c8 100644 --- a/src/Mod/Fem/App/FemConstraintTemperature.cpp +++ b/src/Mod/Fem/App/FemConstraintTemperature.cpp @@ -86,6 +86,9 @@ void ConstraintTemperature::handleChangedPropertyType(Base::XMLReader& reader, CFluxProperty.Restore(reader); CFlux.setValue(CFluxProperty.getValue()); } + else { + Constraint::handleChangedPropertyType(reader, TypeName, prop); + } } void ConstraintTemperature::onChanged(const App::Property* prop) diff --git a/src/Mod/Fem/App/FemConstraintTransform.cpp b/src/Mod/Fem/App/FemConstraintTransform.cpp index 6ee538c62f..53947104b2 100644 --- a/src/Mod/Fem/App/FemConstraintTransform.cpp +++ b/src/Mod/Fem/App/FemConstraintTransform.cpp @@ -110,6 +110,9 @@ void ConstraintTransform::handleChangedPropertyType(Base::XMLReader& reader, Z_rotProperty.Restore(reader); Z_rot.setValue(Z_rotProperty.getValue()); } + else { + Constraint::handleChangedPropertyType(reader, TypeName, prop); + } } void ConstraintTransform::onChanged(const App::Property* prop) diff --git a/src/Mod/Fem/App/FemPostFilter.cpp b/src/Mod/Fem/App/FemPostFilter.cpp index 3b5c600680..1e3a1ca27c 100644 --- a/src/Mod/Fem/App/FemPostFilter.cpp +++ b/src/Mod/Fem/App/FemPostFilter.cpp @@ -217,6 +217,9 @@ void FemPostDataAlongLineFilter::handleChangedPropertyType(Base::XMLReader& read Point2Property.Restore(reader); Point2.setValue(Point2Property.getValue()); } + else { + FemPostFilter::handleChangedPropertyType(reader, TypeName, prop); + } } void FemPostDataAlongLineFilter::onChanged(const Property* prop) From f178206a2fcd55fdae70cf2a09b6c31b50251ec8 Mon Sep 17 00:00:00 2001 From: wandererfan Date: Sat, 20 Jan 2024 12:00:23 -0500 Subject: [PATCH 066/135] [TD]handle View rotation in ExtensionPack circle centerlines --- src/Mod/TechDraw/Gui/CommandExtensionPack.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Mod/TechDraw/Gui/CommandExtensionPack.cpp b/src/Mod/TechDraw/Gui/CommandExtensionPack.cpp index 8da7f5120c..c3008c25c2 100644 --- a/src/Mod/TechDraw/Gui/CommandExtensionPack.cpp +++ b/src/Mod/TechDraw/Gui/CommandExtensionPack.cpp @@ -194,7 +194,6 @@ void execCircleCenterLines(Gui::Command* cmd) if (!_checkSel(cmd, selection, objFeat, QT_TRANSLATE_NOOP("Command","TechDraw Circle Centerlines"))) return; Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Circle Centerlines")); - double scale = objFeat->getScale(); const std::vector SubNames = selection[0].getSubNames(); for (const std::string& Name : SubNames) { int GeoId = TechDraw::DrawUtil::getIndexFromName(Name); @@ -203,15 +202,19 @@ void execCircleCenterLines(Gui::Command* cmd) if (GeoType == "Edge") { if (geom->getGeomType() == TechDraw::CIRCLE || geom->getGeomType() == TechDraw::ARCOFCIRCLE) { TechDraw::CirclePtr cgen = std::static_pointer_cast(geom); + // center is a scaled and rotated point Base::Vector3d center = cgen->center; center.y = -center.y; - float radius = cgen->radius; + center = CosmeticVertex::makeCanonicalPoint(objFeat, center); + double radius = cgen->radius / objFeat->getScale(); + // right, left, top, bottom are formed from a canonical point (center) + // so they do not need to be changed to canonical form. Base::Vector3d right(center.x + radius + 2.0, center.y, 0.0); Base::Vector3d top(center.x, center.y + radius + 2.0, 0.0); Base::Vector3d left(center.x - radius - 2.0, center.y, 0.0); Base::Vector3d bottom(center.x, center.y - radius - 2.0, 0.0); - std::string line1tag = objFeat->addCosmeticEdge(right / scale, left / scale); - std::string line2tag = objFeat->addCosmeticEdge(top / scale, bottom / scale); + std::string line1tag = objFeat->addCosmeticEdge(right, left); + std::string line2tag = objFeat->addCosmeticEdge(top, bottom); TechDraw::CosmeticEdge* horiz = objFeat->getCosmeticEdge(line1tag); _setLineAttributes(horiz); TechDraw::CosmeticEdge* vert = objFeat->getCosmeticEdge(line2tag); From 411b7573cc0b874d452e4d49c1a6bc7c3ae698a1 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Wed, 29 Mar 2023 15:49:45 +0200 Subject: [PATCH 067/135] Base: Use std::recursive_mutex --- src/Base/CMakeLists.txt | 2 -- src/Base/Mutex.cpp | 36 ------------------------------------ src/Base/Mutex.h | 39 --------------------------------------- src/Base/PreCompiled.h | 3 +-- src/Base/Sequencer.cpp | 31 ++++++++++++++----------------- 5 files changed, 15 insertions(+), 96 deletions(-) delete mode 100644 src/Base/Mutex.cpp delete mode 100644 src/Base/Mutex.h diff --git a/src/Base/CMakeLists.txt b/src/Base/CMakeLists.txt index 478bb59a8b..f2528e7155 100644 --- a/src/Base/CMakeLists.txt +++ b/src/Base/CMakeLists.txt @@ -243,7 +243,6 @@ SET(FreeCADBase_CPP_SRCS Matrix.cpp MatrixPyImp.cpp MemDebug.cpp - Mutex.cpp Observer.cpp Parameter.xsd Parameter.cpp @@ -314,7 +313,6 @@ SET(FreeCADBase_HPP_SRCS Interpreter.h Matrix.h MemDebug.h - Mutex.h Observer.h Parameter.h Persistence.h diff --git a/src/Base/Mutex.cpp b/src/Base/Mutex.cpp deleted file mode 100644 index e3d07c4a4e..0000000000 --- a/src/Base/Mutex.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2022 Werner Mayer * - * * - * 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" - -#include "Mutex.h" - - -#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) -QRecursiveMutex::QRecursiveMutex() - : QMutex(QMutex::Recursive) -{} - -QRecursiveMutex::~QRecursiveMutex() -{} -#endif diff --git a/src/Base/Mutex.h b/src/Base/Mutex.h deleted file mode 100644 index e65db63092..0000000000 --- a/src/Base/Mutex.h +++ /dev/null @@ -1,39 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2022 Werner Mayer * - * * - * 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 BASE_MUTEX_H -#define BASE_MUTEX_H - -#include -#include - -#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) -class BaseExport QRecursiveMutex: public QMutex -{ -public: - QRecursiveMutex(); - ~QRecursiveMutex(); -}; -#endif - -#endif // BASE_MUTEX_H diff --git a/src/Base/PreCompiled.h b/src/Base/PreCompiled.h index f81b2e771b..b38b44a68d 100644 --- a/src/Base/PreCompiled.h +++ b/src/Base/PreCompiled.h @@ -73,6 +73,7 @@ #include #include #include +#include #include // streams @@ -130,8 +131,6 @@ #include #include #include -#include -#include #include #include diff --git a/src/Base/Sequencer.cpp b/src/Base/Sequencer.cpp index 4d41dc0009..3ab27b74be 100644 --- a/src/Base/Sequencer.cpp +++ b/src/Base/Sequencer.cpp @@ -24,12 +24,11 @@ #include "PreCompiled.h" #ifndef _PreComp_ -#include +#include +#include #endif #include "Sequencer.h" -#include "Mutex.h" - using namespace Base; @@ -40,7 +39,7 @@ struct SequencerP // members static std::vector _instances; /**< A vector of all created instances */ static SequencerLauncher* _topLauncher; /**< The outermost launcher */ - static QRecursiveMutex mutex; /**< A mutex-locker for the launcher */ + static std::recursive_mutex mutex; /**< A mutex-locker for the launcher */ /** Sets a global sequencer object. * Access to the last registered object is performed by @see Sequencer(). */ @@ -66,7 +65,7 @@ struct SequencerP */ std::vector SequencerP::_instances; SequencerLauncher* SequencerP::_topLauncher = nullptr; -QRecursiveMutex SequencerP::mutex; +std::recursive_mutex SequencerP::mutex; } // namespace Base SequencerBase& SequencerBase::Instance() @@ -160,7 +159,7 @@ bool SequencerBase::isBlocking() const bool SequencerBase::setLocked(bool bLocked) { - QMutexLocker locker(&SequencerP::mutex); + std::lock_guard locker(SequencerP::mutex); bool old = this->_bLocked; this->_bLocked = bLocked; return old; @@ -168,19 +167,19 @@ bool SequencerBase::setLocked(bool bLocked) bool SequencerBase::isLocked() const { - QMutexLocker locker(&SequencerP::mutex); + std::lock_guard locker(SequencerP::mutex); return this->_bLocked; } bool SequencerBase::isRunning() const { - QMutexLocker locker(&SequencerP::mutex); + std::lock_guard locker(SequencerP::mutex); return (SequencerP::_topLauncher != nullptr); } bool SequencerBase::wasCanceled() const { - QMutexLocker locker(&SequencerP::mutex); + std::lock_guard locker(SequencerP::mutex); return this->_bCanceled; } @@ -236,7 +235,7 @@ void ConsoleSequencer::resetData() SequencerLauncher::SequencerLauncher(const char* pszStr, size_t steps) { - QMutexLocker locker(&SequencerP::mutex); + std::lock_guard locker(SequencerP::mutex); // Have we already an instance of SequencerLauncher created? if (!SequencerP::_topLauncher) { SequencerBase::Instance().start(pszStr, steps); @@ -246,24 +245,22 @@ SequencerLauncher::SequencerLauncher(const char* pszStr, size_t steps) SequencerLauncher::~SequencerLauncher() { - QMutexLocker locker(&SequencerP::mutex); + std::lock_guard locker(SequencerP::mutex); if (SequencerP::_topLauncher == this) { SequencerBase::Instance().stop(); - } - if (SequencerP::_topLauncher == this) { SequencerP::_topLauncher = nullptr; } } void SequencerLauncher::setText(const char* pszTxt) { - QMutexLocker locker(&SequencerP::mutex); + std::lock_guard locker(SequencerP::mutex); SequencerBase::Instance().setText(pszTxt); } bool SequencerLauncher::next(bool canAbort) { - QMutexLocker locker(&SequencerP::mutex); + std::lock_guard locker(SequencerP::mutex); if (SequencerP::_topLauncher != this) { return true; // ignore } @@ -272,13 +269,13 @@ bool SequencerLauncher::next(bool canAbort) void SequencerLauncher::setProgress(size_t pos) { - QMutexLocker locker(&SequencerP::mutex); + std::lock_guard locker(SequencerP::mutex); SequencerBase::Instance().setProgress(pos); } size_t SequencerLauncher::numberOfSteps() const { - QMutexLocker locker(&SequencerP::mutex); + std::lock_guard locker(SequencerP::mutex); return SequencerBase::Instance().numberOfSteps(); } From 3a25a66a05b57ee0940cc590a7f16b45d41be068 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Wed, 15 Nov 2023 10:12:42 +0100 Subject: [PATCH 068/135] Base: Do not use else before return --- src/Base/Interpreter.cpp | 5 +--- src/Base/MatrixPyImp.cpp | 8 ++--- src/Base/Parameter.cpp | 6 +--- src/Base/PlacementPyImp.cpp | 18 +++++------- src/Base/PyObjectBase.cpp | 43 ++++++++++++--------------- src/Base/PyTools.c | 58 +++++++++++++++++-------------------- src/Base/QuantityPyImp.cpp | 10 +++---- src/Base/Reader.cpp | 44 ++++++++++++---------------- src/Base/RotationPyImp.cpp | 18 +++++------- src/Base/StackWalker.cpp | 5 +--- src/Base/UnitPyImp.cpp | 24 ++++++--------- src/Base/UnitsApi.cpp | 1 + src/Base/VectorPyImp.cpp | 51 ++++++++++++-------------------- 13 files changed, 115 insertions(+), 176 deletions(-) diff --git a/src/Base/Interpreter.cpp b/src/Base/Interpreter.cpp index 9c5b7be311..b1e9870f27 100644 --- a/src/Base/Interpreter.cpp +++ b/src/Base/Interpreter.cpp @@ -475,14 +475,11 @@ void InterpreterSingleton::runFile(const char* pxFileName, bool local) if (PyErr_ExceptionMatches(PyExc_SystemExit)) { throw SystemExitException(); } - throw PyException(); } Py_DECREF(result); } - else { - throw FileException("Unknown file", pxFileName); - } + throw FileException("Unknown file", pxFileName); } bool InterpreterSingleton::loadModule(const char* psModName) diff --git a/src/Base/MatrixPyImp.cpp b/src/Base/MatrixPyImp.cpp index 8d0229b0ec..0fdf71b054 100644 --- a/src/Base/MatrixPyImp.cpp +++ b/src/Base/MatrixPyImp.cpp @@ -256,11 +256,9 @@ PyObject* MatrixPy::richCompare(PyObject* v, PyObject* w, int op) Py_INCREF(res); return res; } - else { - // This always returns False - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } + // This always returns False + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; } PyObject* MatrixPy::move(PyObject* args) diff --git a/src/Base/Parameter.cpp b/src/Base/Parameter.cpp index 47163d7259..54a33bf907 100644 --- a/src/Base/Parameter.cpp +++ b/src/Base/Parameter.cpp @@ -501,11 +501,7 @@ std::vector> ParameterGrp::GetGroups() /// test if this group is empty bool ParameterGrp::IsEmpty() const { - if (_pGroupNode && _pGroupNode->getFirstChild()) { - return false; - } - - return true; + return !(_pGroupNode && _pGroupNode->getFirstChild()); } /// test if a special sub group is in this group diff --git a/src/Base/PlacementPyImp.cpp b/src/Base/PlacementPyImp.cpp index fb5367b591..d57791d69f 100644 --- a/src/Base/PlacementPyImp.cpp +++ b/src/Base/PlacementPyImp.cpp @@ -148,22 +148,18 @@ PyObject* PlacementPy::richCompare(PyObject* v, PyObject* w, int op) PyErr_SetString(PyExc_TypeError, "no ordering relation is defined for Placement"); return nullptr; } - else if (op == Py_EQ) { + if (op == Py_EQ) { res = (p1 == p2) ? Py_True : Py_False; Py_INCREF(res); return res; } - else { - res = (p1 != p2) ? Py_True : Py_False; - Py_INCREF(res); - return res; - } - } - else { - // This always returns False - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; + res = (p1 != p2) ? Py_True : Py_False; + Py_INCREF(res); + return res; } + // This always returns False + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; } PyObject* PlacementPy::move(PyObject* args) diff --git a/src/Base/PyObjectBase.cpp b/src/Base/PyObjectBase.cpp index 4f00c0b4ae..0c4000ddfa 100644 --- a/src/Base/PyObjectBase.cpp +++ b/src/Base/PyObjectBase.cpp @@ -365,7 +365,7 @@ int PyObjectBase::__setattro(PyObject *obj, PyObject *attro, PyObject *value) PyErr_Format(PyExc_AttributeError, "Cannot delete attribute: '%s'", attr); return -1; } - else if (!static_cast(obj)->isValid()){ + if (!static_cast(obj)->isValid()){ PyErr_Format(PyExc_ReferenceError, "Cannot access attribute '%s' of deleted object", attr); return -1; } @@ -404,36 +404,32 @@ PyObject *PyObjectBase::_getattr(const char *attr) // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) return reinterpret_cast(Py_TYPE(this)); } - else if (streq(attr, "__members__")) { + if (streq(attr, "__members__")) { // Use __dict__ instead as __members__ is deprecated return nullptr; } - else if (streq(attr,"__dict__")) { + if (streq(attr,"__dict__")) { // Return the default dict PyTypeObject *tp = Py_TYPE(this); Py_XINCREF(tp->tp_dict); return tp->tp_dict; } - else if (streq(attr,"softspace")) { + if (streq(attr,"softspace")) { // Internal Python stuff return nullptr; } - else { - // As fallback solution use Python's default method to get generic attributes - PyObject *w{}; - PyObject *res{}; - w = PyUnicode_InternFromString(attr); - if (w) { - res = PyObject_GenericGetAttr(this, w); - Py_XDECREF(w); - return res; - } else { - // Throw an exception for unknown attributes - PyTypeObject *tp = Py_TYPE(this); - PyErr_Format(PyExc_AttributeError, "%.50s instance has no attribute '%.400s'", tp->tp_name, attr); - return nullptr; - } + // As fallback solution use Python's default method to get generic attributes + PyObject *w{}, *res{}; + w = PyUnicode_InternFromString(attr); + if (w) { + res = PyObject_GenericGetAttr(this, w); + Py_XDECREF(w); + return res; } + // Throw an exception for unknown attributes + PyTypeObject *tp = Py_TYPE(this); + PyErr_Format(PyExc_AttributeError, "%.50s instance has no attribute '%.400s'", tp->tp_name, attr); + return nullptr; } int PyObjectBase::_setattr(const char *attr, PyObject *value) @@ -449,12 +445,11 @@ int PyObjectBase::_setattr(const char *attr, PyObject *value) int res = PyObject_GenericSetAttr(this, w, value); Py_DECREF(w); return res; - } else { - // Throw an exception for unknown attributes - PyTypeObject *tp = Py_TYPE(this); - PyErr_Format(PyExc_AttributeError, "%.50s instance has no attribute '%.400s'", tp->tp_name, attr); - return -1; } + // Throw an exception for unknown attributes + PyTypeObject *tp = Py_TYPE(this); + PyErr_Format(PyExc_AttributeError, "%.50s instance has no attribute '%.400s'", tp->tp_name, attr); + return -1; } /*------------------------------ diff --git a/src/Base/PyTools.c b/src/Base/PyTools.c index 4a90a3becd..e71216a224 100644 --- a/src/Base/PyTools.c +++ b/src/Base/PyTools.c @@ -359,27 +359,25 @@ PP_Convert_Result(PyObject *presult, const char *resFormat, void *resTarget) Py_DECREF(presult); /* procedures and stmts return None */ return 0; } - else if (! PyArg_Parse(presult, resFormat, resTarget)) { /* convert Python->C */ Py_DECREF(presult); /* need not be tuple */ return -1; /* error in convert */ } - else { - if (strcmp(resFormat, "O") != 0) { /* free object unless exported */ - if (strcmp(resFormat, "s") == 0) { /* copy string: caller owns it */ - char **target = (char**) resTarget; + if (strcmp(resFormat, "O") != 0) { /* free object unless exported */ + if (strcmp(resFormat, "s") == 0) { /* copy string: caller owns it */ + char **target = (char**) resTarget; #if defined (__GNUC__) - *target = strdup(*target); + *target = strdup(*target); #else - *target = _strdup(*target); + *target = _strdup(*target); #endif - } - Py_DECREF(presult); } - return 0; /* returns 0=success, -1=failure */ - } /* if 0: C result in *resTarget */ -} /* caller must decref if fmt="O" */ - /* caller must free() if fmt="s" */ + Py_DECREF(presult); + } + return 0; /* returns 0=success, -1=failure */ + /* if 0: C result in *resTarget */ +} /* caller must decref if fmt="O" */ + /* caller must free() if fmt="s" */ int PP_Get_Global(const char *modname, const char *varname, const char *resfmt, void *cresult) @@ -430,27 +428,26 @@ int PP_DEBUG = 0; /* debug embedded code with pdb? */ const char *PP_Init(const char *modname) { Py_Initialize(); /* init python if needed */ - if (modname!=NULL) return modname; - { /* we assume here that the caller frees allocated memory */ - return "__main__"; - } + if (modname) + return modname; + /* we assume here that the caller frees allocated memory */ + return "__main__"; } int -PP_Make_Dummy_Module(const char *modname) /* namespace for strings, if no file */ -{ /* instead of sharing __main__ for all */ - PyObject *module = NULL, *dict = NULL; /* note: __main__ is created in py_init */ +PP_Make_Dummy_Module(const char *modname) /* namespace for strings, if no file */ +{ /* instead of sharing __main__ for all */ + PyObject *module = NULL, *dict = NULL; /* note: __main__ is created in py_init */ Py_Initialize(); module = PyImport_AddModule(modname); /* fetch or make, no load */ if (module == NULL) /* module not incref'd */ return -1; - else { /* module.__dict__ */ - dict = PyModule_GetDict(module); /* ['__dummy__'] = None */ - PyDict_SetItemString(dict, "__dummy__", Py_None); - PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins()); - return 0; - } + /* module.__dict__ */ + dict = PyModule_GetDict(module); /* ['__dummy__'] = None */ + PyDict_SetItemString(dict, "__dummy__", Py_None); + PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins()); + return 0; } @@ -479,17 +476,14 @@ PP_Load_Module(const char *modname) /* modname can be "package.module" for PyDict_GetItemString(PyModule_GetDict(module), "__dummy__")) { return module; /* not increfd */ } - else if (PP_RELOAD && module != NULL && PyModule_Check(module)) { module = PyImport_ReloadModule(module); /* reload file,run code */ Py_XDECREF(module); /* still on sys.modules */ return module; /* not increfd */ } - else { - module = PyImport_ImportModule(modname); /* fetch or load module */ - Py_XDECREF(module); /* still on sys.modules */ - return module; /* not increfd */ - } + module = PyImport_ImportModule(modname); /* fetch or load module */ + Py_XDECREF(module); /* still on sys.modules */ + return module; /* not increfd */ } diff --git a/src/Base/QuantityPyImp.cpp b/src/Base/QuantityPyImp.cpp index 6069342ebf..527c01e2d4 100644 --- a/src/Base/QuantityPyImp.cpp +++ b/src/Base/QuantityPyImp.cpp @@ -508,20 +508,18 @@ PyObject* QuantityPy::number_power_handler(PyObject* self, PyObject* other, PyOb return new QuantityPy(new Quantity(q)); } - else if (PyFloat_Check(other)) { + if (PyFloat_Check(other)) { Base::Quantity* a = static_cast(self)->getQuantityPtr(); double b = PyFloat_AsDouble(other); return new QuantityPy(new Quantity(a->pow(b))); } - else if (PyLong_Check(other)) { + if (PyLong_Check(other)) { Base::Quantity* a = static_cast(self)->getQuantityPtr(); double b = (double)PyLong_AsLong(other); return new QuantityPy(new Quantity(a->pow(b))); } - else { - PyErr_SetString(PyExc_TypeError, "Expected quantity or number"); - return nullptr; - } + PyErr_SetString(PyExc_TypeError, "Expected quantity or number"); + return nullptr; } PY_CATCH } diff --git a/src/Base/Reader.cpp b/src/Base/Reader.cpp index 6b6068bf12..3fb14a803c 100644 --- a/src/Base/Reader.cpp +++ b/src/Base/Reader.cpp @@ -116,12 +116,10 @@ long Base::XMLReader::getAttributeAsInteger(const char* AttrName) const if (pos != AttrMap.end()) { return atol(pos->second.c_str()); } - else { - // wrong name, use hasAttribute if not sure! - std::ostringstream msg; - msg << "XML Attribute: \"" << AttrName << "\" not found"; - throw Base::XMLAttributeError(msg.str()); - } + // wrong name, use hasAttribute if not sure! + std::ostringstream msg; + msg << "XML Attribute: \"" << AttrName << "\" not found"; + throw Base::XMLAttributeError(msg.str()); } unsigned long Base::XMLReader::getAttributeAsUnsigned(const char* AttrName) const @@ -131,12 +129,10 @@ unsigned long Base::XMLReader::getAttributeAsUnsigned(const char* AttrName) cons if (pos != AttrMap.end()) { return strtoul(pos->second.c_str(), nullptr, 10); } - else { - // wrong name, use hasAttribute if not sure! - std::ostringstream msg; - msg << "XML Attribute: \"" << AttrName << "\" not found"; - throw Base::XMLAttributeError(msg.str()); - } + // wrong name, use hasAttribute if not sure! + std::ostringstream msg; + msg << "XML Attribute: \"" << AttrName << "\" not found"; + throw Base::XMLAttributeError(msg.str()); } double Base::XMLReader::getAttributeAsFloat(const char* AttrName) const @@ -146,12 +142,10 @@ double Base::XMLReader::getAttributeAsFloat(const char* AttrName) const if (pos != AttrMap.end()) { return atof(pos->second.c_str()); } - else { - // wrong name, use hasAttribute if not sure! - std::ostringstream msg; - msg << "XML Attribute: \"" << AttrName << "\" not found"; - throw Base::XMLAttributeError(msg.str()); - } + // wrong name, use hasAttribute if not sure! + std::ostringstream msg; + msg << "XML Attribute: \"" << AttrName << "\" not found"; + throw Base::XMLAttributeError(msg.str()); } const char* Base::XMLReader::getAttribute(const char* AttrName) const @@ -161,12 +155,10 @@ const char* Base::XMLReader::getAttribute(const char* AttrName) const if (pos != AttrMap.end()) { return pos->second.c_str(); } - else { - // wrong name, use hasAttribute if not sure! - std::ostringstream msg; - msg << "XML Attribute: \"" << AttrName << "\" not found"; - throw Base::XMLAttributeError(msg.str()); - } + // wrong name, use hasAttribute if not sure! + std::ostringstream msg; + msg << "XML Attribute: \"" << AttrName << "\" not found"; + throw Base::XMLAttributeError(msg.str()); } bool Base::XMLReader::hasAttribute(const char* AttrName) const @@ -216,7 +208,7 @@ void Base::XMLReader::readElement(const char* ElementName) // thus we must stop reading on. break; } - else if (ReadType == EndDocument) { + if (ReadType == EndDocument) { // the end of the document has been reached but we still try to continue on reading throw Base::XMLParseException("End of document reached"); } @@ -276,7 +268,7 @@ void Base::XMLReader::readEndElement(const char* ElementName, int level) && (level < 0 || level == Level)) { return; } - else if (ReadType == EndDocument) { + if (ReadType == EndDocument) { // the end of the document has been reached but we still try to continue on reading throw Base::XMLParseException("End of document reached"); } diff --git a/src/Base/RotationPyImp.cpp b/src/Base/RotationPyImp.cpp index 8d0b019a1c..a4747ac6f1 100644 --- a/src/Base/RotationPyImp.cpp +++ b/src/Base/RotationPyImp.cpp @@ -271,22 +271,18 @@ PyObject* RotationPy::richCompare(PyObject* v, PyObject* w, int op) PyErr_SetString(PyExc_TypeError, "no ordering relation is defined for Rotation"); return nullptr; } - else if (op == Py_EQ) { + if (op == Py_EQ) { res = (r1 == r2) ? Py_True : Py_False; Py_INCREF(res); return res; } - else { - res = (r1 != r2) ? Py_True : Py_False; - Py_INCREF(res); - return res; - } - } - else { - // This always returns False - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; + res = (r1 != r2) ? Py_True : Py_False; + Py_INCREF(res); + return res; } + // This always returns False + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; } PyObject* RotationPy::invert(PyObject* args) diff --git a/src/Base/StackWalker.cpp b/src/Base/StackWalker.cpp index c3c4a7159c..fcc7fa4dc7 100644 --- a/src/Base/StackWalker.cpp +++ b/src/Base/StackWalker.cpp @@ -1118,10 +1118,7 @@ BOOL __stdcall StackWalker::myReadProcMem( //printf("ReadMemory: hProcess: %p, baseAddr: %p, buffer: %p, size: %d, read: %d, result: %d\n", hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, (DWORD) st, (DWORD) bRet); return bRet; } - else - { - return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData); - } + return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData); } void StackWalker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion) diff --git a/src/Base/UnitPyImp.cpp b/src/Base/UnitPyImp.cpp index e8db65865a..364ffb6641 100644 --- a/src/Base/UnitPyImp.cpp +++ b/src/Base/UnitPyImp.cpp @@ -175,10 +175,8 @@ PyObject* UnitPy::number_multiply_handler(PyObject* self, PyObject* other) return new UnitPy(new Unit((*a) * (*b))); } - else { - PyErr_SetString(PyExc_TypeError, "A Unit can only be multiplied by a Unit"); - return nullptr; - } + PyErr_SetString(PyExc_TypeError, "A Unit can only be multiplied by a Unit"); + return nullptr; } PyObject* UnitPy::richCompare(PyObject* v, PyObject* w, int op) @@ -192,22 +190,18 @@ PyObject* UnitPy::richCompare(PyObject* v, PyObject* w, int op) PyErr_SetString(PyExc_TypeError, "no ordering relation is defined for Units"); return nullptr; } - else if (op == Py_EQ) { + if (op == Py_EQ) { res = (*u1 == *u2) ? Py_True : Py_False; // NOLINT Py_INCREF(res); return res; } - else { - res = (*u1 != *u2) ? Py_True : Py_False; // NOLINT - Py_INCREF(res); - return res; - } - } - else { - // This always returns False - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; + res = (*u1 != *u2) ? Py_True : Py_False; // NOLINT + Py_INCREF(res); + return res; } + // This always returns False + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; } Py::String UnitPy::getType() const diff --git a/src/Base/UnitsApi.cpp b/src/Base/UnitsApi.cpp index f0c118bd20..9984a4dee8 100644 --- a/src/Base/UnitsApi.cpp +++ b/src/Base/UnitsApi.cpp @@ -193,6 +193,7 @@ double UnitsApi::toDouble(PyObject* args, const Base::Unit& u) } throw Base::UnitsMismatchError("Wrong unit type!"); } + if (PyFloat_Check(args)) { return PyFloat_AsDouble(args); } diff --git a/src/Base/VectorPyImp.cpp b/src/Base/VectorPyImp.cpp index 8e0ea738e9..bf2b66181b 100644 --- a/src/Base/VectorPyImp.cpp +++ b/src/Base/VectorPyImp.cpp @@ -150,30 +150,24 @@ PyObject* VectorPy::number_multiply_handler(PyObject* self, PyObject* other) Py::Float mult(a * b); return Py::new_reference_to(mult); } - else if (PyNumber_Check(other)) { + if (PyNumber_Check(other)) { double b = PyFloat_AsDouble(other); return new VectorPy(a * b); } - else { - PyErr_SetString(PyExc_TypeError, "A Vector can only be multiplied by Vector or number"); - return nullptr; - } + PyErr_SetString(PyExc_TypeError, "A Vector can only be multiplied by Vector or number"); + return nullptr; } - else if (PyObject_TypeCheck(other, &(VectorPy::Type))) { + if (PyObject_TypeCheck(other, &(VectorPy::Type))) { Base::Vector3d a = static_cast(other)->value(); if (PyNumber_Check(self)) { double b = PyFloat_AsDouble(self); return new VectorPy(a * b); } - else { - PyErr_SetString(PyExc_TypeError, "A Vector can only be multiplied by Vector or number"); - return nullptr; - } - } - else { - PyErr_SetString(PyExc_TypeError, "First or second arg must be Vector"); + PyErr_SetString(PyExc_TypeError, "A Vector can only be multiplied by Vector or number"); return nullptr; } + PyErr_SetString(PyExc_TypeError, "First or second arg must be Vector"); + return nullptr; } Py_ssize_t VectorPy::sequence_length(PyObject* /*unused*/) @@ -243,13 +237,8 @@ PyObject* VectorPy::mapping_subscript(PyObject* self, PyObject* item) } return sequence_item(self, i); } - else if (PySlice_Check(item)) { - Py_ssize_t start = 0; - Py_ssize_t stop = 0; - Py_ssize_t step = 0; - Py_ssize_t slicelength = 0; - Py_ssize_t cur = 0; - Py_ssize_t i = 0; + if (PySlice_Check(item)) { + Py_ssize_t start = 0, stop = 0, step = 0, slicelength = 0, cur = 0, i = 0; PyObject* slice = item; if (PySlice_GetIndicesEx(slice, sequence_length(self), &start, &stop, &step, &slicelength) @@ -260,7 +249,7 @@ PyObject* VectorPy::mapping_subscript(PyObject* self, PyObject* item) if (slicelength <= 0) { return PyTuple_New(0); } - else if (start == 0 && step == 1 && slicelength == sequence_length(self) + if (start == 0 && step == 1 && slicelength == sequence_length(self) && PyObject_TypeCheck(self, &(VectorPy::Type))) { Base::Vector3d v = static_cast(self)->value(); Py::Tuple xyz(3); @@ -269,7 +258,7 @@ PyObject* VectorPy::mapping_subscript(PyObject* self, PyObject* item) xyz.setItem(2, Py::Float(v.z)); return Py::new_reference_to(xyz); } - else if (PyObject_TypeCheck(self, &(VectorPy::Type))) { + if (PyObject_TypeCheck(self, &(VectorPy::Type))) { Base::Vector3d v = static_cast(self)->value(); Py::Tuple xyz(static_cast(slicelength)); @@ -342,22 +331,18 @@ PyObject* VectorPy::richCompare(PyObject* v, PyObject* w, int op) PyErr_SetString(PyExc_TypeError, "no ordering relation is defined for Vector"); return nullptr; } - else if (op == Py_EQ) { + if (op == Py_EQ) { res = (v1 == v2) ? Py_True : Py_False; // NOLINT Py_INCREF(res); return res; } - else { - res = (v1 != v2) ? Py_True : Py_False; // NOLINT - Py_INCREF(res); - return res; - } - } - else { - // This always returns False - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; + res = (v1 != v2) ? Py_True : Py_False; // NOLINT + Py_INCREF(res); + return res; } + // This always returns False + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; } PyObject* VectorPy::isEqual(PyObject* args) From 839cd947c7b0600fb20716878245e6cc7b275957 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sat, 20 Jan 2024 18:18:09 +0100 Subject: [PATCH 069/135] Base: Replace if else with switch statement --- src/Base/Console.h | 30 +++++------ src/Base/Persistence.cpp | 54 ++++++++++--------- src/Base/QuantityPyImp.cpp | 108 +++++++++++++++++-------------------- src/Base/Tools.cpp | 42 ++++++++------- src/Base/VectorPyImp.cpp | 2 +- 5 files changed, 115 insertions(+), 121 deletions(-) diff --git a/src/Base/Console.h b/src/Base/Console.h index ab2a2b3616..ed350bd5ca 100644 --- a/src/Base/Console.h +++ b/src/Base/Console.h @@ -579,23 +579,19 @@ public: */ bool isActive(Base::LogStyle category) const { - if (category == Base::LogStyle::Log) { - return bLog; - } - if (category == Base::LogStyle::Warning) { - return bWrn; - } - if (category == Base::LogStyle::Error) { - return bErr; - } - if (category == Base::LogStyle::Message) { - return bMsg; - } - if (category == Base::LogStyle::Critical) { - return bCritical; - } - if (category == Base::LogStyle::Notification) { - return bNotification; + switch (category) { + case Base::LogStyle::Log: + return bLog; + case Base::LogStyle::Warning: + return bWrn; + case Base::LogStyle::Error: + return bErr; + case Base::LogStyle::Message: + return bMsg; + case Base::LogStyle::Critical: + return bCritical; + case Base::LogStyle::Notification: + return bNotification; } return false; diff --git a/src/Base/Persistence.cpp b/src/Base/Persistence.cpp index 094b85767f..7dc3f7d42e 100644 --- a/src/Base/Persistence.cpp +++ b/src/Base/Persistence.cpp @@ -76,32 +76,34 @@ std::string Persistence::encodeAttribute(const std::string& str) { std::string tmp; for (char it : str) { - if (it == '<') { - tmp += "<"; - } - else if (it == '\"') { - tmp += """; - } - else if (it == '\'') { - tmp += "'"; - } - else if (it == '&') { - tmp += "&"; - } - else if (it == '>') { - tmp += ">"; - } - else if (it == '\r') { - tmp += " "; - } - else if (it == '\n') { - tmp += " "; - } - else if (it == '\t') { - tmp += " "; - } - else { - tmp += it; + switch (it) { + case '<': + tmp += "<"; + break; + case '\"': + tmp += """; + break; + case '\'': + tmp += "'"; + break; + case '&': + tmp += "&"; + break; + case '>': + tmp += ">"; + break; + case '\r': + tmp += " "; + break; + case '\n': + tmp += " "; + break; + case '\t': + tmp += " "; + break; + default: + tmp += it; + break; } } diff --git a/src/Base/QuantityPyImp.cpp b/src/Base/QuantityPyImp.cpp index 527c01e2d4..4a59496f42 100644 --- a/src/Base/QuantityPyImp.cpp +++ b/src/Base/QuantityPyImp.cpp @@ -541,35 +541,31 @@ PyObject* QuantityPy::richCompare(PyObject* v, PyObject* w, int op) const Quantity* u2 = static_cast(w)->getQuantityPtr(); PyObject* res = nullptr; - if (op == Py_NE) { - res = (!(*u1 == *u2)) ? Py_True : Py_False; - Py_INCREF(res); - return res; - } - else if (op == Py_LT) { - res = (*u1 < *u2) ? Py_True : Py_False; - Py_INCREF(res); - return res; - } - else if (op == Py_LE) { - res = (*u1 < *u2) || (*u1 == *u2) ? Py_True : Py_False; - Py_INCREF(res); - return res; - } - else if (op == Py_GT) { - res = (!(*u1 < *u2)) && (!(*u1 == *u2)) ? Py_True : Py_False; - Py_INCREF(res); - return res; - } - else if (op == Py_GE) { - res = (!(*u1 < *u2)) ? Py_True : Py_False; - Py_INCREF(res); - return res; - } - else if (op == Py_EQ) { - res = (*u1 == *u2) ? Py_True : Py_False; - Py_INCREF(res); - return res; + switch (op) { + case Py_NE: + res = (!(*u1 == *u2)) ? Py_True : Py_False; + Py_INCREF(res); + return res; + case Py_LT: + res = (*u1 < *u2) ? Py_True : Py_False; + Py_INCREF(res); + return res; + case Py_LE: + res = (*u1 < *u2) || (*u1 == *u2) ? Py_True : Py_False; + Py_INCREF(res); + return res; + case Py_GT: + res = (!(*u1 < *u2)) && (!(*u1 == *u2)) ? Py_True : Py_False; + Py_INCREF(res); + return res; + case Py_GE: + res = (!(*u1 < *u2)) ? Py_True : Py_False; + Py_INCREF(res); + return res; + case Py_EQ: + res = (*u1 == *u2) ? Py_True : Py_False; + Py_INCREF(res); + return res; } } else if (PyNumber_Check(v) && PyNumber_Check(w)) { @@ -577,35 +573,31 @@ PyObject* QuantityPy::richCompare(PyObject* v, PyObject* w, int op) double u1 = PyFloat_AsDouble(v); double u2 = PyFloat_AsDouble(w); PyObject* res = nullptr; - if (op == Py_NE) { - res = (u1 != u2) ? Py_True : Py_False; - Py_INCREF(res); - return res; - } - else if (op == Py_LT) { - res = (u1 < u2) ? Py_True : Py_False; - Py_INCREF(res); - return res; - } - else if (op == Py_LE) { - res = (u1 <= u2) ? Py_True : Py_False; - Py_INCREF(res); - return res; - } - else if (op == Py_GT) { - res = (u1 > u2) ? Py_True : Py_False; - Py_INCREF(res); - return res; - } - else if (op == Py_GE) { - res = (u1 >= u2) ? Py_True : Py_False; - Py_INCREF(res); - return res; - } - else if (op == Py_EQ) { - res = (u1 == u2) ? Py_True : Py_False; - Py_INCREF(res); - return res; + switch (op) { + case Py_NE: + res = (u1 != u2) ? Py_True : Py_False; + Py_INCREF(res); + return res; + case Py_LT: + res = (u1 < u2) ? Py_True : Py_False; + Py_INCREF(res); + return res; + case Py_LE: + res = (u1 <= u2) ? Py_True : Py_False; + Py_INCREF(res); + return res; + case Py_GT: + res = (u1 > u2) ? Py_True : Py_False; + Py_INCREF(res); + return res; + case Py_GE: + res = (u1 >= u2) ? Py_True : Py_False; + Py_INCREF(res); + return res; + case Py_EQ: + res = (u1 == u2) ? Py_True : Py_False; + Py_INCREF(res); + return res; } } diff --git a/src/Base/Tools.cpp b/src/Base/Tools.cpp index 1f315ce721..9e313be85e 100644 --- a/src/Base/Tools.cpp +++ b/src/Base/Tools.cpp @@ -264,17 +264,19 @@ std::string Base::Tools::escapeEncodeString(const std::string& s) std::string result; size_t len = s.size(); for (size_t i = 0; i < len; ++i) { - if (s.at(i) == '\\') { - result += "\\\\"; - } - else if (s.at(i) == '\"') { - result += "\\\""; - } - else if (s.at(i) == '\'') { - result += "\\\'"; - } - else { - result += s.at(i); + switch (s.at(i)) { + case '\\': + result += "\\\\"; + break; + case '\"': + result += "\\\""; + break; + case '\'': + result += "\\\'"; + break; + default: + result += s.at(i); + break; } } return result; @@ -305,14 +307,16 @@ std::string Base::Tools::escapeEncodeFilename(const std::string& s) std::string result; size_t len = s.size(); for (size_t i = 0; i < len; ++i) { - if (s.at(i) == '\"') { - result += "\\\""; - } - else if (s.at(i) == '\'') { - result += "\\\'"; - } - else { - result += s.at(i); + switch (s.at(i)) { + case '\"': + result += "\\\""; + break; + case '\'': + result += "\\\'"; + break; + default: + result += s.at(i); + break; } } return result; diff --git a/src/Base/VectorPyImp.cpp b/src/Base/VectorPyImp.cpp index bf2b66181b..80d730c39d 100644 --- a/src/Base/VectorPyImp.cpp +++ b/src/Base/VectorPyImp.cpp @@ -250,7 +250,7 @@ PyObject* VectorPy::mapping_subscript(PyObject* self, PyObject* item) return PyTuple_New(0); } if (start == 0 && step == 1 && slicelength == sequence_length(self) - && PyObject_TypeCheck(self, &(VectorPy::Type))) { + && PyObject_TypeCheck(self, &(VectorPy::Type))) { Base::Vector3d v = static_cast(self)->value(); Py::Tuple xyz(3); xyz.setItem(0, Py::Float(v.x)); From abfe38e0e7eee5f01f8877f511bf09330d7f8bd6 Mon Sep 17 00:00:00 2001 From: bdieterm <119257544+bdieterm@users.noreply.github.com> Date: Sun, 21 Jan 2024 17:41:31 +0100 Subject: [PATCH 070/135] Gui: add explicit transparency specification --- src/Gui/ViewProviderOriginFeature.cpp | 1 + src/Mod/Fem/Gui/ViewProviderFemMesh.cpp | 1 + src/Mod/Import/Gui/dxf/ImpExpDxfGui.cpp | 1 + src/Mod/Part/Gui/DlgProjectionOnSurface.cpp | 1 + 4 files changed, 4 insertions(+) diff --git a/src/Gui/ViewProviderOriginFeature.cpp b/src/Gui/ViewProviderOriginFeature.cpp index 6e360c06dd..df57b9b044 100644 --- a/src/Gui/ViewProviderOriginFeature.cpp +++ b/src/Gui/ViewProviderOriginFeature.cpp @@ -51,6 +51,7 @@ ViewProviderOriginFeature::ViewProviderOriginFeature () { QT_TRANSLATE_NOOP("App::Property", "Visual size of the feature")); ShapeColor.setValue ( ViewProviderOrigin::defaultColor ); // Set default color for origin (light-blue) + Transparency.setValue(0); BoundingBox.setStatus(App::Property::Hidden, true); // Hide Boundingbox from the user due to it doesn't make sense // Create node for scaling the origin diff --git a/src/Mod/Fem/Gui/ViewProviderFemMesh.cpp b/src/Mod/Fem/Gui/ViewProviderFemMesh.cpp index cfc8bf172d..b7829b9521 100644 --- a/src/Mod/Fem/Gui/ViewProviderFemMesh.cpp +++ b/src/Mod/Fem/Gui/ViewProviderFemMesh.cpp @@ -197,6 +197,7 @@ ViewProviderFemMesh::ViewProviderFemMesh() LineWidth.setConstraints(&floatRange); ShapeColor.setValue(App::Color(1.0f, 0.7f, 0.0f)); + Transparency.setValue(0); ADD_PROPERTY(BackfaceCulling, (true)); ADD_PROPERTY(ShowInner, (false)); ADD_PROPERTY(MaxFacesShowInner, (50000)); diff --git a/src/Mod/Import/Gui/dxf/ImpExpDxfGui.cpp b/src/Mod/Import/Gui/dxf/ImpExpDxfGui.cpp index a9888d115d..89e04b819b 100644 --- a/src/Mod/Import/Gui/dxf/ImpExpDxfGui.cpp +++ b/src/Mod/Import/Gui/dxf/ImpExpDxfGui.cpp @@ -82,6 +82,7 @@ void ImpExpDxfReadGui::ApplyGuiStyles(Part::Feature* object) view->LineColor.setValue(color); view->PointColor.setValue(color); view->ShapeColor.setValue(color); + view->Transparency.setValue(0); } void ImpExpDxfReadGui::ApplyGuiStyles(App::FeaturePython* object) diff --git a/src/Mod/Part/Gui/DlgProjectionOnSurface.cpp b/src/Mod/Part/Gui/DlgProjectionOnSurface.cpp index 4d0704f981..c36b7b5ef3 100644 --- a/src/Mod/Part/Gui/DlgProjectionOnSurface.cpp +++ b/src/Mod/Part/Gui/DlgProjectionOnSurface.cpp @@ -638,6 +638,7 @@ void PartGui::DlgProjectionOnSurface::show_projected_shapes(const std::vectorLineColor.setValue(0x8ae23400); vp->ShapeColor.setValue(0x8ae23400); vp->PointColor.setValue(0x8ae23400); + vp->Transparency.setValue(0); } } From de4639371149791a71bccd3516b74e866de42ec5 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Sat, 20 Jan 2024 20:06:12 -0600 Subject: [PATCH 071/135] Materials: Correct PreCompiled.h includes in App --- src/Mod/Material/App/MaterialConfigLoader.cpp | 9 ++++----- src/Mod/Material/App/MaterialLibrary.cpp | 4 ++-- src/Mod/Material/App/Materials.cpp | 4 ++-- src/Mod/Material/App/Model.cpp | 3 +-- src/Mod/Material/App/ModelLibrary.cpp | 3 +-- src/Mod/Material/App/ModelLoader.cpp | 4 ++-- src/Mod/Material/App/PreCompiled.h | 12 ++++++++++++ 7 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/Mod/Material/App/MaterialConfigLoader.cpp b/src/Mod/Material/App/MaterialConfigLoader.cpp index fe2090f009..b16b14daa8 100644 --- a/src/Mod/Material/App/MaterialConfigLoader.cpp +++ b/src/Mod/Material/App/MaterialConfigLoader.cpp @@ -24,18 +24,17 @@ #include #include #include -#endif - -#include - #include #include #include #include +#include +#include +#endif + #include #include -#include #include "Exceptions.h" diff --git a/src/Mod/Material/App/MaterialLibrary.cpp b/src/Mod/Material/App/MaterialLibrary.cpp index d1c30730f8..1a1573e388 100644 --- a/src/Mod/Material/App/MaterialLibrary.cpp +++ b/src/Mod/Material/App/MaterialLibrary.cpp @@ -22,10 +22,10 @@ #include "PreCompiled.h" #ifndef _PreComp_ #include -#endif - #include #include +#endif + #include diff --git a/src/Mod/Material/App/Materials.cpp b/src/Mod/Material/App/Materials.cpp index a1d7592125..106bf43bdf 100644 --- a/src/Mod/Material/App/Materials.cpp +++ b/src/Mod/Material/App/Materials.cpp @@ -21,10 +21,10 @@ #include "PreCompiled.h" #ifndef _PreComp_ -#endif - #include #include +#endif + #include #include diff --git a/src/Mod/Material/App/Model.cpp b/src/Mod/Material/App/Model.cpp index 2d30bdadc3..19a4ebb832 100644 --- a/src/Mod/Material/App/Model.cpp +++ b/src/Mod/Material/App/Model.cpp @@ -21,9 +21,8 @@ #include "PreCompiled.h" #ifndef _PreComp_ -#endif - #include +#endif #include diff --git a/src/Mod/Material/App/ModelLibrary.cpp b/src/Mod/Material/App/ModelLibrary.cpp index 63d06f2d06..87a4387263 100644 --- a/src/Mod/Material/App/ModelLibrary.cpp +++ b/src/Mod/Material/App/ModelLibrary.cpp @@ -21,9 +21,8 @@ #include "PreCompiled.h" #ifndef _PreComp_ -#endif - #include +#endif #include diff --git a/src/Mod/Material/App/ModelLoader.cpp b/src/Mod/Material/App/ModelLoader.cpp index 3976f2a28c..a24bea113a 100644 --- a/src/Mod/Material/App/ModelLoader.cpp +++ b/src/Mod/Material/App/ModelLoader.cpp @@ -22,13 +22,13 @@ #include "PreCompiled.h" #ifndef _PreComp_ #include +#include +#include #endif #include #include -#include -#include #include "Model.h" #include "ModelLoader.h" diff --git a/src/Mod/Material/App/PreCompiled.h b/src/Mod/Material/App/PreCompiled.h index c2ca3cd36e..e991f90cda 100644 --- a/src/Mod/Material/App/PreCompiled.h +++ b/src/Mod/Material/App/PreCompiled.h @@ -47,14 +47,26 @@ // STL #include +#include #include +#include #include #include #include // Qt #include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include #endif //_PreComp_ From e11a65813cf15d68ee7324fcc9de52f8c7ccfb67 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Sat, 20 Jan 2024 20:21:34 -0600 Subject: [PATCH 072/135] Materials: Correct PreCompiled.h includes in Gui --- src/Mod/Material/Gui/Array2D.cpp | 3 +-- src/Mod/Material/Gui/Array3D.cpp | 3 +-- src/Mod/Material/Gui/ArrayDelegate.cpp | 3 +-- src/Mod/Material/Gui/ArrayModel.cpp | 3 +-- src/Mod/Material/Gui/BaseDelegate.cpp | 3 +-- src/Mod/Material/Gui/ImageEdit.cpp | 5 ++--- src/Mod/Material/Gui/ListDelegate.cpp | 3 +-- src/Mod/Material/Gui/ListEdit.cpp | 3 +-- src/Mod/Material/Gui/ListModel.cpp | 3 +-- src/Mod/Material/Gui/MaterialDelegate.cpp | 3 +-- src/Mod/Material/Gui/MaterialsEditor.cpp | 3 +-- src/Mod/Material/Gui/PreCompiled.h | 1 + src/Mod/Material/Gui/TextEdit.cpp | 3 +-- 13 files changed, 14 insertions(+), 25 deletions(-) diff --git a/src/Mod/Material/Gui/Array2D.cpp b/src/Mod/Material/Gui/Array2D.cpp index 359160ac13..234c608dfc 100644 --- a/src/Mod/Material/Gui/Array2D.cpp +++ b/src/Mod/Material/Gui/Array2D.cpp @@ -21,11 +21,10 @@ #include "PreCompiled.h" #ifndef _PreComp_ +#include #include #endif -#include - #include #include diff --git a/src/Mod/Material/Gui/Array3D.cpp b/src/Mod/Material/Gui/Array3D.cpp index 37677a5f62..16cebf5931 100644 --- a/src/Mod/Material/Gui/Array3D.cpp +++ b/src/Mod/Material/Gui/Array3D.cpp @@ -21,12 +21,11 @@ #include "PreCompiled.h" #ifndef _PreComp_ +#include #include #include #endif -#include - #include #include diff --git a/src/Mod/Material/Gui/ArrayDelegate.cpp b/src/Mod/Material/Gui/ArrayDelegate.cpp index 4ad6bffe93..fa06605f19 100644 --- a/src/Mod/Material/Gui/ArrayDelegate.cpp +++ b/src/Mod/Material/Gui/ArrayDelegate.cpp @@ -30,9 +30,8 @@ #include #include #include -#endif - #include +#endif #include #include diff --git a/src/Mod/Material/Gui/ArrayModel.cpp b/src/Mod/Material/Gui/ArrayModel.cpp index c7f581316e..5d1dbdd57d 100644 --- a/src/Mod/Material/Gui/ArrayModel.cpp +++ b/src/Mod/Material/Gui/ArrayModel.cpp @@ -21,10 +21,9 @@ #include "PreCompiled.h" #ifndef _PreComp_ -#endif - #include #include +#endif #include #include diff --git a/src/Mod/Material/Gui/BaseDelegate.cpp b/src/Mod/Material/Gui/BaseDelegate.cpp index d018a80e68..3f75d52fb8 100644 --- a/src/Mod/Material/Gui/BaseDelegate.cpp +++ b/src/Mod/Material/Gui/BaseDelegate.cpp @@ -31,9 +31,8 @@ #include #include #include -#endif - #include +#endif #include #include diff --git a/src/Mod/Material/Gui/ImageEdit.cpp b/src/Mod/Material/Gui/ImageEdit.cpp index 7ed319554c..b0c21b1258 100644 --- a/src/Mod/Material/Gui/ImageEdit.cpp +++ b/src/Mod/Material/Gui/ImageEdit.cpp @@ -21,17 +21,16 @@ #include "PreCompiled.h" #ifndef _PreComp_ -#include -#endif - #include #include #include +#include #include #include #include #include #include +#endif #include #include diff --git a/src/Mod/Material/Gui/ListDelegate.cpp b/src/Mod/Material/Gui/ListDelegate.cpp index 1932cb396f..52f0ca6d2c 100644 --- a/src/Mod/Material/Gui/ListDelegate.cpp +++ b/src/Mod/Material/Gui/ListDelegate.cpp @@ -30,9 +30,8 @@ #include #include #include -#endif - #include +#endif #include #include diff --git a/src/Mod/Material/Gui/ListEdit.cpp b/src/Mod/Material/Gui/ListEdit.cpp index cc1a59bad8..b8df1722a7 100644 --- a/src/Mod/Material/Gui/ListEdit.cpp +++ b/src/Mod/Material/Gui/ListEdit.cpp @@ -21,11 +21,10 @@ #include "PreCompiled.h" #ifndef _PreComp_ +#include #include #endif -#include - #include #include diff --git a/src/Mod/Material/Gui/ListModel.cpp b/src/Mod/Material/Gui/ListModel.cpp index edf56e2b68..e89a6dcadc 100644 --- a/src/Mod/Material/Gui/ListModel.cpp +++ b/src/Mod/Material/Gui/ListModel.cpp @@ -21,9 +21,8 @@ #include "PreCompiled.h" #ifndef _PreComp_ -#endif - #include +#endif #include #include diff --git a/src/Mod/Material/Gui/MaterialDelegate.cpp b/src/Mod/Material/Gui/MaterialDelegate.cpp index 38e67cc5e1..d3787e6d46 100644 --- a/src/Mod/Material/Gui/MaterialDelegate.cpp +++ b/src/Mod/Material/Gui/MaterialDelegate.cpp @@ -31,9 +31,8 @@ #include #include #include -#endif - #include +#endif #include #include diff --git a/src/Mod/Material/Gui/MaterialsEditor.cpp b/src/Mod/Material/Gui/MaterialsEditor.cpp index 3c23ec20f8..d2c3cc2508 100644 --- a/src/Mod/Material/Gui/MaterialsEditor.cpp +++ b/src/Mod/Material/Gui/MaterialsEditor.cpp @@ -31,9 +31,8 @@ #include #include #include -#endif - #include +#endif #include #include diff --git a/src/Mod/Material/Gui/PreCompiled.h b/src/Mod/Material/Gui/PreCompiled.h index 36cb312a6f..9d0d6c1884 100644 --- a/src/Mod/Material/Gui/PreCompiled.h +++ b/src/Mod/Material/Gui/PreCompiled.h @@ -44,6 +44,7 @@ // standard #include #include +#include // STL #include diff --git a/src/Mod/Material/Gui/TextEdit.cpp b/src/Mod/Material/Gui/TextEdit.cpp index 93f46b77c3..95c1558855 100644 --- a/src/Mod/Material/Gui/TextEdit.cpp +++ b/src/Mod/Material/Gui/TextEdit.cpp @@ -21,11 +21,10 @@ #include "PreCompiled.h" #ifndef _PreComp_ +#include #include #endif -#include - #include #include From f52c0d12f0aaa95107e3d825e1f184f05da8d793 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sat, 20 Jan 2024 16:55:38 +0100 Subject: [PATCH 073/135] Sketcher: Make Elements UI style aware This commit like previous one introduces ability to style list of elements in the sketcher. It also completely refactors the rendering delegate introducing dedicated method for calculating rects of each element and method for rendering each sub control. Thanks to this refactor we can be sure that areas drawn are the same as areas checked for events in editorEvent. --- src/Mod/Sketcher/Gui/TaskSketcherElements.cpp | 326 ++++++++++++------ 1 file changed, 213 insertions(+), 113 deletions(-) diff --git a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp index c9d45ef713..16b0b25e6c 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp @@ -115,25 +115,37 @@ class ElementItemDelegate: public QStyledItemDelegate { Q_OBJECT public: + /// Enum containing all controls rendered in this item. Controls in that enum MUST be in order. + enum SubControl : int { + CheckBox, + LineSelect, + StartSelect, + EndSelect, + MidSelect, + Label + }; + explicit ElementItemDelegate(ElementView* parent); - ~ElementItemDelegate() override; + ~ElementItemDelegate() override = default; void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) override; - ElementItem* getElementtItem(const QModelIndex& index) const; + ElementItem* getElementItem(const QModelIndex& index) const; - const int border = 1; // 1px, looks good around buttons. - const int leftMargin = 4;// 4px on the left of icons, looks good. - mutable int customIconsMargin = 4; + QRect subControlRect(SubControl element, const QStyleOptionViewItem& option, const QModelIndex& index) const; + void drawSubControl(SubControl element, QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + + const int gap = 4; // 4px of spacing between consecutive elements Q_SIGNALS: void itemHovered(QModelIndex); void itemChecked(QModelIndex, Qt::CheckState state); }; +// clang-format on // helper class to store additional information about the listWidget entry. class ElementItem: public QListWidgetItem { @@ -153,8 +165,13 @@ public: Hidden = 2, }; - ElementItem(int elementnr, int startingVertex, int midVertex, int endVertex, - Base::Type geometryType, GeometryState state, const QString& lab, + ElementItem(int elementnr, + int startingVertex, + int midVertex, + int endVertex, + Base::Type geometryType, + GeometryState state, + const QString& lab, ViewProviderSketch* sketchView) : ElementNbr(elementnr) , StartingVertex(startingVertex) @@ -178,7 +195,6 @@ public: bool isVisible() { - if (State != GeometryState::External) { const auto geo = sketchView->getSketchObject()->getGeometry(ElementNbr); if (geo) { @@ -194,6 +210,35 @@ public: return true; } + QVariant data(int role) const override + { + // In order for content-box to include size of the 4 geometry icons we need to provide + // Qt with information about decoration (icon) size. This is hack to work around Qt + // limitation of not knowing about padding, border and margin boxes of stylesheets + // thus being unable to provide proper sizeHint for stylesheets to render correctly + if (role == Qt::DecorationRole) { + int size = listWidget()->style()->pixelMetric(QStyle::PM_ListViewIconSize); + + return QIcon(QPixmap(QSize(size, size))); + } + + return QListWidgetItem::data(role); + } + + bool isGeometrySelected(Sketcher::PointPos pos) const + { + switch (pos) { + case Sketcher::PointPos::none: + return isLineSelected; + case Sketcher::PointPos::start: + return isStartingPointSelected; + case Sketcher::PointPos::end: + return isEndPointSelected; + case Sketcher::PointPos::mid: + return isMidPointSelected; + } + } + int ElementNbr; int StartingVertex; int MidVertex; @@ -217,6 +262,7 @@ public: private: ViewProviderSketch* sketchView; }; +// clang-format off class ElementFilterList: public QListWidget { @@ -497,6 +543,7 @@ ElementView::~ElementView() void ElementView::changeLayer(int layer) { App::Document* doc = App::GetApplication().getActiveDocument(); + if (!doc) return; @@ -507,7 +554,6 @@ void ElementView::changeLayer(int layer) auto geoids = getGeoIdsOfEdgesFromNames(sketchobject, ft->getSubNames()); - auto geometry = sketchobject->Geometry.getValues(); auto newgeometry(geometry); @@ -737,6 +783,8 @@ void ElementView::deleteSelectedItems() void ElementView::onIndexHovered(QModelIndex index) { + update(index); + Q_EMIT onItemHovered(itemFromIndex(index)); } @@ -755,166 +803,218 @@ ElementItem* ElementView::itemFromIndex(const QModelIndex& index) return static_cast(QListWidget::itemFromIndex(index)); } +// clang-format on /* ElementItem delegate ---------------------------------------------------- */ - ElementItemDelegate::ElementItemDelegate(ElementView* parent) : QStyledItemDelegate(parent) -{// This class relies on the parent being an ElementView, see getElementtItem +{ // This class relies on the parent being an ElementView, see getElementtItem } -ElementItemDelegate::~ElementItemDelegate() -{} - - -void ElementItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, +void ElementItemDelegate::paint(QPainter* painter, + const QStyleOptionViewItem& option, const QModelIndex& index) const { - ElementItem* item = getElementtItem(index); + ElementItem* item = getElementItem(index); - if (item) { - auto style = option.widget ? option.widget->style() : QApplication::style(); + if (!item) { + return; + } - QStyleOptionButton checkboxstyle; + auto style = option.widget ? option.widget->style() : QApplication::style(); - checkboxstyle.rect = option.rect; - checkboxstyle.state |= QStyle::State_Enabled; + QStyleOptionViewItem itemOption = option; - if (item->isVisible()) - checkboxstyle.state |= QStyle::State_On; - else - checkboxstyle.state |= QStyle::State_Off; + initStyleOption(&itemOption, index); - QRect checkboxrect - = style->subElementRect(QStyle::SE_CheckBoxIndicator, &checkboxstyle, option.widget); + if (item->isLineSelected || item->isStartingPointSelected || item->isEndPointSelected + || item->isMidPointSelected) { + itemOption.state |= QStyle::State_Active; + } - checkboxstyle.rect = { - leftMargin, - option.rect.top() + (option.rect.height() - checkboxrect.height()) / 2, // vertically center the checkbox - checkboxrect.width(), - checkboxrect.height() - }; + style->drawPrimitive(QStyle::PE_PanelItemViewItem, &itemOption, painter, option.widget); - customIconsMargin = leftMargin + checkboxrect.width() + leftMargin; + drawSubControl(SubControl::CheckBox, painter, option, index); + drawSubControl(SubControl::LineSelect, painter, option, index); + drawSubControl(SubControl::StartSelect, painter, option, index); + drawSubControl(SubControl::EndSelect, painter, option, index); + drawSubControl(SubControl::MidSelect, painter, option, index); + drawSubControl(SubControl::Label, painter, option, index); +} - int height = option.rect.height(); - int width = height;// icons are square. - int x0 = option.rect.x() + customIconsMargin; - int iconsize = height - 2 * border; - int btny = option.rect.y() + border; +QRect ElementItemDelegate::subControlRect(SubControl element, + const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + auto itemOption = option; - if (item->isLineSelected || item->isStartingPointSelected || item->isEndPointSelected - || item->isMidPointSelected) {// option.state & QStyle::State_Selected + auto style = option.widget ? option.widget->style() : QApplication::style(); - auto unselecticon = [&](int iconnumber) { - QRect rect {x0 + border + width * iconnumber, btny, iconsize, iconsize}; - painter->fillRect(rect, option.palette.base()); - }; + initStyleOption(&itemOption, index); - QRect selection = QRect(option.rect.x(), - option.rect.y(), - option.rect.width(), - option.rect.height()); + QRect checkBoxRect = + style->subElementRect(QStyle::SE_CheckBoxIndicator, &itemOption, option.widget); - painter->fillRect(selection, option.palette.highlight());// paint the item as selected + checkBoxRect.moveTo(gap, + option.rect.top() + (option.rect.height() - checkBoxRect.height()) / 2); - // Repaint individual icons - if (!item->isLineSelected) - unselecticon(0); + if (element == SubControl::CheckBox) { + return checkBoxRect; + } - if (!item->isStartingPointSelected) - unselecticon(1); + QRect selectRect = + style->subElementRect(QStyle::SE_ItemViewItemDecoration, &itemOption, option.widget) + .translated(checkBoxRect.right() + gap, 0); - if (!item->isEndPointSelected) - unselecticon(2); + unsigned pos = element - SubControl::LineSelect; - if (!item->isMidPointSelected) - unselecticon(3); + auto rect = selectRect.translated((selectRect.width() + gap) * pos, 0); + + if (element != SubControl::Label) { + return rect; + } + + rect.setRight(itemOption.rect.right()); + + return rect; +} + +void ElementItemDelegate::drawSubControl(SubControl element, + QPainter* painter, + const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + auto item = getElementItem(index); + auto style = option.widget ? option.widget->style() : QApplication::style(); + + auto rect = subControlRect(element, option, index); + + auto mousePos = option.widget->mapFromGlobal(QCursor::pos()); + auto isHovered = rect.contains(mousePos); + + auto drawSelectIcon = [&](Sketcher::PointPos pos) { + auto icon = ElementWidgetIcons::getIcon(item->GeometryType, pos, item->State); + auto opacity = 0.4f; + + if (isHovered) { + opacity = 0.8f; } - auto& iconEdge = - ElementWidgetIcons::getIcon(item->GeometryType, Sketcher::PointPos::none, item->State); - auto& iconStart = - ElementWidgetIcons::getIcon(item->GeometryType, Sketcher::PointPos::start, item->State); - auto& iconEnd = - ElementWidgetIcons::getIcon(item->GeometryType, Sketcher::PointPos::end, item->State); - auto& iconMid = - ElementWidgetIcons::getIcon(item->GeometryType, Sketcher::PointPos::mid, item->State); - // getIcon(item->GeometryType); + if (item->isGeometrySelected(pos)) { + opacity = 1.0f; + } - style->drawPrimitive(QStyle::PE_IndicatorCheckBox, &checkboxstyle, painter, option.widget); + painter->setOpacity(opacity); + painter->drawPixmap(rect, icon.pixmap(rect.size())); + }; - painter->drawPixmap(x0 + border, btny, iconEdge.pixmap(iconsize, iconsize)); - painter->drawPixmap(x0 + border + width, btny, iconStart.pixmap(iconsize, iconsize)); - painter->drawPixmap(x0 + border + width * 2, btny, iconEnd.pixmap(iconsize, iconsize)); - painter->drawPixmap(x0 + border + width * 3, btny, iconMid.pixmap(iconsize, iconsize)); + painter->save(); - // Label : - auto labelBoundingBox = painter->fontMetrics().tightBoundingRect(item->label); - painter->drawText( - x0 + width * 4 + 3 * border, - option.rect.bottom() - (option.rect.height() - labelBoundingBox.height()) / 2, - item->label - ); + switch (element) { + case SubControl::CheckBox: { + QStyleOptionButton checkboxOption; + + checkboxOption.initFrom(option.widget); + + checkboxOption.state |= QStyle::State_Enabled; + checkboxOption.rect = rect; + + if (item->isVisible()) { + checkboxOption.state |= QStyle::State_On; + } + else { + checkboxOption.state |= QStyle::State_Off; + } + + style->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, + &checkboxOption, + painter, + option.widget); + + break; + } + + case LineSelect: { + drawSelectIcon(Sketcher::PointPos::none); + break; + } + + case StartSelect: { + drawSelectIcon(Sketcher::PointPos::start); + break; + } + + case EndSelect: { + drawSelectIcon(Sketcher::PointPos::end); + break; + } + + case MidSelect: { + drawSelectIcon(Sketcher::PointPos::mid); + break; + } + + case Label: { + QRect rect = subControlRect(SubControl::Label, option, index); + + auto labelBoundingBox = painter->fontMetrics().tightBoundingRect(item->label); + + painter->drawText(rect.x(), + option.rect.bottom() + - (option.rect.height() - labelBoundingBox.height()) / 2, + item->label); + + break; + } } + + painter->restore(); } +// clang-format off bool ElementItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) { - auto getSubElementType = [&](ElementItem* item, int xPos, int width) { - bool label = (xPos > option.rect.x() + customIconsMargin + width * 4 + border); + auto item = getElementItem(index); - if ((xPos < option.rect.x() + customIconsMargin + width + border) - || (item->GeometryType != Part::GeomPoint::getClassTypeId() && label)) + auto getSubElementType = [&](QPoint pos) { + if (subControlRect(SubControl::LineSelect, option, index).contains(pos)) { return SubElementType::edge; - if (xPos < option.rect.x() + customIconsMargin + width * 2 + border - || (item->GeometryType == Part::GeomPoint::getClassTypeId() && label)) + } else if (subControlRect(SubControl::StartSelect, option, index).contains(pos)) { return SubElementType::start; - if (xPos < option.rect.x() + customIconsMargin + width * 3 + border) + } else if (subControlRect(SubControl::EndSelect, option, index).contains(pos)) { return SubElementType::end; - else if (xPos < option.rect.x() + customIconsMargin + width * 4 + border) + } else if (subControlRect(SubControl::MidSelect, option, index).contains(pos)) { return SubElementType::mid; - else - return SubElementType::none; + } else { + // depending on geometry type by default we select either point or edge + return item->GeometryType == Part::GeomPoint::getClassTypeId() ? SubElementType::start : SubElementType::edge; + } }; if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) { - QMouseEvent* mEvent = static_cast(event); - ElementItem* item = getElementtItem(index); + auto mouseEvent = static_cast(event); - int xPos = mEvent->pos().x(); - int width = option.rect.height();// icons are square + item->clickedOn = getSubElementType(mouseEvent->pos()); + item->rightClicked = mouseEvent->button() == Qt::RightButton; - item->clickedOn = getSubElementType(item, xPos, width); + QRect checkboxRect = subControlRect(SubControl::CheckBox, option, index); - if (mEvent->button() == Qt::RightButton) - item->rightClicked = true; - - QRect checkboxrect = QRect( - leftMargin, option.rect.y(), customIconsMargin - leftMargin, option.rect.height()); - - if (mEvent->button() == Qt::LeftButton && checkboxrect.contains(mEvent->pos())) { + if (mouseEvent->button() == Qt::LeftButton && checkboxRect.contains(mouseEvent->pos())) { Q_EMIT itemChecked(index, item->isVisible() ? Qt::Unchecked : Qt::Checked); } } else if (event->type() == QEvent::MouseMove) { - SubElementType typeUnderMouse; - QMouseEvent* mEvent = static_cast(event); - int xPos = mEvent->pos().x(); - int width = option.rect.height();// icons are square + auto mouseEvent = static_cast(event); - ElementItem* item = getElementtItem(index); + item->hovered = getSubElementType(mouseEvent->pos()); - typeUnderMouse = getSubElementType(item, xPos, width); - - item->hovered = typeUnderMouse; Q_EMIT itemHovered(index); } return QStyledItemDelegate::editorEvent(event, model, option, index); } - -ElementItem* ElementItemDelegate::getElementtItem(const QModelIndex& index) const +const QWidget *w +ElementItem* ElementItemDelegate::getElementItem(const QModelIndex& index) const { ElementView* elementView = static_cast(parent()); return elementView->itemFromIndex(index); From 8c2310eaf350986bff097c669bf19f612c838b22 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sun, 21 Jan 2024 00:13:35 +0100 Subject: [PATCH 074/135] Sketcher: Fix checkbox behavior for Element checkboxes This creates new method overload for changing layer of exact ElementItem. This fixes strange behavior of the checkboxes for elements that were dependent not on what user clicked but on what is selected. Fixes: #8814 --- src/Mod/Sketcher/Gui/TaskSketcherElements.cpp | 57 ++++++++++++++++--- src/Mod/Sketcher/Gui/TaskSketcherElements.h | 1 + 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp index 16b0b25e6c..9949d16b2e 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp @@ -239,6 +239,11 @@ public: } } + Sketcher::SketchObject* getSketchObject() const + { + return sketchView->getSketchObject(); + } + int ElementNbr; int StartingVertex; int MidVertex; @@ -586,6 +591,47 @@ void ElementView::changeLayer(int layer) doc->commitTransaction(); } +void ElementView::changeLayer(ElementItem* item, int layer) +{ + App::Document* doc = App::GetApplication().getActiveDocument(); + + if (!doc) { + return; + } + + doc->openTransaction("Geometry Layer Change"); + + auto sketchObject = item->getSketchObject(); + + auto geometry = sketchObject->Geometry.getValues(); + auto newGeometry(geometry); + + auto geoid = item->ElementNbr; + + // currently only internal geometry can be changed from one layer to another + if (geoid >= 0) { + auto currentLayer = getSafeGeomLayerId(geometry[geoid]); + + if (currentLayer != layer) { + auto geo = geometry[geoid]->clone(); + setSafeGeomLayerId(geo, layer); + newGeometry[geoid] = geo; + + sketchObject->Geometry.setValues(std::move(newGeometry)); + sketchObject->solve(); + } + } + else { + Gui::TranslatedUserWarning( + sketchObject, + QObject::tr("Unsupported visual layer operation"), + QObject::tr("It is currently unsupported to move external geometry to another " + "visual layer. External geometry will be omitted")); + } + + doc->commitTransaction(); +} + void ElementView::contextMenuEvent(QContextMenuEvent* event) { QMenu menu; @@ -788,14 +834,11 @@ void ElementView::onIndexHovered(QModelIndex index) Q_EMIT onItemHovered(itemFromIndex(index)); } -void ElementView::onIndexChecked(QModelIndex, Qt::CheckState state) +void ElementView::onIndexChecked(QModelIndex index, Qt::CheckState state) { - if (state == Qt::Checked) { - changeLayer(static_cast(ElementItem::Layer::Default)); - } - else { - changeLayer(static_cast(ElementItem::Layer::Hidden)); - } + auto item = itemFromIndex(index); + + changeLayer(item, static_cast(state == Qt::Checked ? ElementItem::Layer::Default : ElementItem::Layer::Hidden)); } ElementItem* ElementView::itemFromIndex(const QModelIndex& index) diff --git a/src/Mod/Sketcher/Gui/TaskSketcherElements.h b/src/Mod/Sketcher/Gui/TaskSketcherElements.h index 1284a804d2..6965b44ea1 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherElements.h +++ b/src/Mod/Sketcher/Gui/TaskSketcherElements.h @@ -109,6 +109,7 @@ Q_SIGNALS: private: void changeLayer(int layer); + void changeLayer(ElementItem* item, int layer); }; class ElementFilterList; From 993248b4dba710b9b1432f974b03bb6baa3fcda2 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sun, 21 Jan 2024 19:47:42 +0100 Subject: [PATCH 075/135] Sketcher: Disable visibility checkbox for external geometry --- .../images_dark-light/check_dark_disabled.svg | 51 +++++++++++++++++++ .../check_light_disabled.svg | 51 +++++++++++++++++++ src/Mod/Sketcher/Gui/TaskSketcherElements.cpp | 25 ++++++--- 3 files changed, 120 insertions(+), 7 deletions(-) create mode 100644 src/Gui/Stylesheets/images_dark-light/check_dark_disabled.svg create mode 100644 src/Gui/Stylesheets/images_dark-light/check_light_disabled.svg diff --git a/src/Gui/Stylesheets/images_dark-light/check_dark_disabled.svg b/src/Gui/Stylesheets/images_dark-light/check_dark_disabled.svg new file mode 100644 index 0000000000..5355029ad4 --- /dev/null +++ b/src/Gui/Stylesheets/images_dark-light/check_dark_disabled.svg @@ -0,0 +1,51 @@ + + + + + + + + + image/svg+xml + + + + Pablo Gil + + + + + SVG + template + + + + + + + + + + diff --git a/src/Gui/Stylesheets/images_dark-light/check_light_disabled.svg b/src/Gui/Stylesheets/images_dark-light/check_light_disabled.svg new file mode 100644 index 0000000000..71445d475f --- /dev/null +++ b/src/Gui/Stylesheets/images_dark-light/check_light_disabled.svg @@ -0,0 +1,51 @@ + + + + + + + + + image/svg+xml + + + + Pablo Gil + + + + + SVG + template + + + + + + + + + + diff --git a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp index 9949d16b2e..41835ede72 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp @@ -193,7 +193,12 @@ public: ~ElementItem() override {} - bool isVisible() + bool canBeHidden() const + { + return State != GeometryState::External; + } + + bool isVisible() const { if (State != GeometryState::External) { const auto geo = sketchView->getSketchObject()->getGeometry(ElementNbr); @@ -957,10 +962,14 @@ void ElementItemDelegate::drawSubControl(SubControl element, QStyleOptionButton checkboxOption; checkboxOption.initFrom(option.widget); - - checkboxOption.state |= QStyle::State_Enabled; checkboxOption.rect = rect; + checkboxOption.state.setFlag(QStyle::State_Enabled, item->canBeHidden()); + + if (isHovered) { + checkboxOption.state |= QStyle::State_MouseOver; + } + if (item->isVisible()) { checkboxOption.state |= QStyle::State_On; } @@ -1040,10 +1049,12 @@ bool ElementItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, item->clickedOn = getSubElementType(mouseEvent->pos()); item->rightClicked = mouseEvent->button() == Qt::RightButton; - QRect checkboxRect = subControlRect(SubControl::CheckBox, option, index); + if (item->canBeHidden()) { + QRect checkboxRect = subControlRect(SubControl::CheckBox, option, index); - if (mouseEvent->button() == Qt::LeftButton && checkboxRect.contains(mouseEvent->pos())) { - Q_EMIT itemChecked(index, item->isVisible() ? Qt::Unchecked : Qt::Checked); + if (mouseEvent->button() == Qt::LeftButton && checkboxRect.contains(mouseEvent->pos())) { + Q_EMIT itemChecked(index, item->isVisible() ? Qt::Unchecked : Qt::Checked); + } } } else if (event->type() == QEvent::MouseMove) { @@ -1056,7 +1067,7 @@ bool ElementItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, return QStyledItemDelegate::editorEvent(event, model, option, index); } -const QWidget *w + ElementItem* ElementItemDelegate::getElementItem(const QModelIndex& index) const { ElementView* elementView = static_cast(parent()); From d405fdadc470bd8e0774c646903de9f693ae54db Mon Sep 17 00:00:00 2001 From: bgbsww Date: Sun, 21 Jan 2024 15:34:46 -0500 Subject: [PATCH 076/135] Add tests, reformat to modern C++, clean --- src/Mod/Part/App/FaceMaker.h | 6 +- src/Mod/Part/App/TopoShape.h | 2 +- src/Mod/Part/App/TopoShapeExpansion.cpp | 186 +++++++----- tests/src/Mod/Part/App/PartTestHelpers.cpp | 27 +- tests/src/Mod/Part/App/PartTestHelpers.h | 8 +- tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 279 +++++++++++++++++- 6 files changed, 422 insertions(+), 86 deletions(-) diff --git a/src/Mod/Part/App/FaceMaker.h b/src/Mod/Part/App/FaceMaker.h index 3f80639165..fc40e243a1 100644 --- a/src/Mod/Part/App/FaceMaker.h +++ b/src/Mod/Part/App/FaceMaker.h @@ -156,10 +156,10 @@ class PartExport FaceMakerSimple : public FaceMakerPublic { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: - virtual std::string getUserFriendlyName() const override; - virtual std::string getBriefExplanation() const override; + std::string getUserFriendlyName() const override; + std::string getBriefExplanation() const override; protected: - virtual void Build_Essence() override; + void Build_Essence() override; }; diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 7a5faa1156..b5bf372a93 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -81,7 +81,7 @@ class PartExport ShapeSegment: public Data::Segment TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: - ShapeSegment(const TopoDS_Shape& ShapeIn) + explicit ShapeSegment(const TopoDS_Shape& ShapeIn) : Shape(ShapeIn) {} ShapeSegment() = default; diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 7bf252fdf0..c59ada479b 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -26,12 +26,12 @@ #include "PreCompiled.h" #ifndef _PreComp_ #include -# include +#include #include #include -# include -# include +#include +#include #endif @@ -480,7 +480,7 @@ void TopoShape::mapCompoundSubElements(const std::vector& shapes, con ++count; auto subshape = getSubShape(TopAbs_SHAPE, count, /*silent = */ true); if (!subshape.IsPartner(topoShape._Shape)) { - return; // Not a partner shape, don't do any mapping at all + return; // Not a partner shape, don't do any mapping at all } } auto children {createChildMap(count, shapes, op)}; @@ -549,49 +549,59 @@ TopoShape::makeElementCompound(const std::vector& shapes, const char* } -TopoShape &TopoShape::makeElementFace(const TopoShape &shape, - const char *op, - const char *maker, - const gp_Pln *pln) +TopoShape& TopoShape::makeElementFace(const TopoShape& shape, + const char* op, + const char* maker, + const gp_Pln* pln) { std::vector shapes; - if (shape.isNull()) - FC_THROWM(NullShapeException, "Null shape"); - if(shape.getShape().ShapeType() == TopAbs_COMPOUND) + if (shape.isNull()) { + FC_THROWM(NullShapeException, "Null shape"); + } + if (shape.getShape().ShapeType() == TopAbs_COMPOUND) { shapes = shape.getSubTopoShapes(); - else + } + else { shapes.push_back(shape); - return makeElementFace(shapes,op,maker,pln); + } + return makeElementFace(shapes, op, maker, pln); } -TopoShape &TopoShape::makeElementFace(const std::vector &shapes, - const char *op, - const char *maker, - const gp_Pln *pln) +TopoShape& TopoShape::makeElementFace(const std::vector& shapes, + const char* op, + const char* maker, + const gp_Pln* pln) { - if(!maker || !maker[0]) maker = "Part::FaceMakerBullseye"; + if (!maker || !maker[0]) { + maker = "Part::FaceMakerBullseye"; + } std::unique_ptr mkFace = FaceMaker::ConstructFromType(maker); mkFace->MyHasher = Hasher; mkFace->MyOp = op; - if (pln) + if (pln) { mkFace->setPlane(*pln); + } - for(auto &s : shapes) { - if (s.getShape().ShapeType() == TopAbs_COMPOUND) + for (auto& s : shapes) { + if (s.getShape().ShapeType() == TopAbs_COMPOUND) { mkFace->useTopoCompound(s); - else + } + else { mkFace->addTopoShape(s); + } } mkFace->Build(); - const auto &ret = mkFace->getTopoShape(); + const auto& ret = mkFace->getTopoShape(); setShape(ret._Shape); Hasher = ret.Hasher; resetElementMap(ret.elementMap()); if (!isValid()) { ShapeFix_ShapeTolerance aSFT; aSFT.LimitTolerance(getShape(), - Precision::Confusion(), Precision::Confusion(), TopAbs_SHAPE); + Precision::Confusion(), + Precision::Confusion(), + TopAbs_SHAPE); // In some cases, the OCC reports the returned shape having invalid // tolerance. Not sure about the real cause. @@ -608,24 +618,39 @@ TopoShape &TopoShape::makeElementFace(const std::vector &shapes, fixer.Perform(); setShape(fixer.Shape(), false); - if (!isValid()) + if (!isValid()) { FC_WARN("makeElementFace: resulting face is invalid"); + } } return *this; } -Data::MappedName TopoShape::setElementComboName(const Data::IndexedName & element, - const std::vector &names, - const char *marker, - const char *op, - const Data::ElementIDRefs *_sids) +/** + * Encode and set an element name in the elementMap. If a hasher is defined, apply it to the name. + * + * @param element The element name(type) that provides 1 one character suffix to the name IF . + * @param names The subnames to build the name from. If empty, return the TopoShape MappedName. + * @param marker The elementMap name or suffix to start the name with. If null, use the + * elementMapPrefix. + * @param op The op text passed to the element name encoder along with the TopoShape Tag + * @param _sids If defined, records the sub ids processed. + * + * @return The encoded, possibly hashed name. + */ +Data::MappedName TopoShape::setElementComboName(const Data::IndexedName& element, + const std::vector& names, + const char* marker, + const char* op, + const Data::ElementIDRefs* _sids) { - if(names.empty()) + if (names.empty()) { return Data::MappedName(); + } std::string _marker; - if(!marker) + if (!marker) { marker = elementMapPrefix().c_str(); - else if(!boost::starts_with(marker,elementMapPrefix())){ + } + else if (!boost::starts_with(marker, elementMapPrefix())) { _marker = elementMapPrefix() + marker; marker = _marker.c_str(); } @@ -633,36 +658,50 @@ Data::MappedName TopoShape::setElementComboName(const Data::IndexedName & elemen Data::MappedName newName = *it; std::ostringstream ss; Data::ElementIDRefs sids; - if (_sids) + if (_sids) { sids = *_sids; - if(names.size() == 1) + } + if (names.size() == 1) { ss << marker; + } else { bool first = true; ss.str(""); - if(!Hasher) + if (!Hasher) { ss << marker; + } ss << '('; - for(++it;it!=names.end();++it) { - if(first) + for (++it; it != names.end(); ++it) { + if (first) { first = false; - else + } + else { ss << '|'; + } ss << *it; } ss << ')'; - if(Hasher) { + if (Hasher) { sids.push_back(Hasher->getID(ss.str().c_str())); ss.str(""); ss << marker << sids.back().toString(); } } - elementMap()->encodeElementName(element[0],newName,ss,&sids,Tag,op); - return elementMap()->setElementName(element,newName, Tag, &sids); + elementMap()->encodeElementName(element[0], newName, ss, &sids, Tag, op); + return elementMap()->setElementName(element, newName, Tag, &sids); } -TopoShape TopoShape::splitWires(std::vector *inner, - SplitWireReorient reorient) const +/** + * Reorient the outer and inner wires of the TopoShape + * + * @param inner If this is not a nullptr, then any inner wires processed will be returned in this + * vector. + * @param reorient One of NoReorient, Reorient ( Outer forward, inner reversed ), + * ReorientForward ( all forward ), or ReorientReversed ( all reversed ) + * @return The outer wire, or an empty TopoShape if this isn't a Face, has no Face subShapes, or the + * outer wire isn't found. + */ +TopoShape TopoShape::splitWires(std::vector* inner, SplitWireReorient reorient) const { // ShapeAnalysis::OuterWire() is un-reliable for some reason. OCC source // code shows it works by creating face using each wire, and then test using @@ -677,31 +716,33 @@ TopoShape TopoShape::splitWires(std::vector *inner, // reliable method, especially so for a planar face. TopoDS_Shape tmp; - if (shapeType(true) == TopAbs_FACE) + if (shapeType(true) == TopAbs_FACE) { tmp = BRepTools::OuterWire(TopoDS::Face(_Shape)); - else if (countSubShapes(TopAbs_FACE) == 1) - tmp = BRepTools::OuterWire( - TopoDS::Face(getSubShape(TopAbs_FACE, 1))); - if (tmp.IsNull()) + } + else if (countSubShapes(TopAbs_FACE) == 1) { + tmp = BRepTools::OuterWire(TopoDS::Face(getSubShape(TopAbs_FACE, 1))); + } + if (tmp.IsNull()) { return TopoShape(); - const auto & wires = getSubTopoShapes(TopAbs_WIRE); + } + const auto& wires = getSubTopoShapes(TopAbs_WIRE); auto it = wires.begin(); TopAbs_Orientation orientOuter, orientInner; - switch(reorient) { - case ReorientReversed: - orientOuter = orientInner = TopAbs_REVERSED; - break; - case ReorientForward: - orientOuter = orientInner = TopAbs_FORWARD; - break; - default: - orientOuter = TopAbs_FORWARD; - orientInner = TopAbs_REVERSED; - break; + switch (reorient) { + case ReorientReversed: + orientOuter = orientInner = TopAbs_REVERSED; + break; + case ReorientForward: + orientOuter = orientInner = TopAbs_FORWARD; + break; + default: + orientOuter = TopAbs_FORWARD; + orientInner = TopAbs_REVERSED; + break; } - auto doReorient = [](TopoShape &s, TopAbs_Orientation orient) { + auto doReorient = [](TopoShape& s, TopAbs_Orientation orient) { // Special case of single edge wire. Make sure the edge is in the // required orientation. This is necessary because BRepFill_OffsetWire // has special handling of circular edge offset, which seem to only @@ -710,36 +751,43 @@ TopoShape TopoShape::splitWires(std::vector *inner, if (s.countSubShapes(TopAbs_EDGE) == 1) { TopoDS_Shape e = s.getSubShape(TopAbs_EDGE, 1); if (e.Orientation() == orient) { - if (s._Shape.Orientation() == orient) + if (s._Shape.Orientation() == orient) { return; - } else + } + } + else { e = e.Oriented(orient); + } BRepBuilderAPI_MakeWire mkWire(TopoDS::Edge(e)); s.setShape(mkWire.Shape(), false); } - else if (s._Shape.Orientation() != orient) + else if (s._Shape.Orientation() != orient) { s.setShape(s._Shape.Oriented(orient), false); + } }; for (; it != wires.end(); ++it) { - auto & wire = *it; + auto& wire = *it; if (wire.getShape().IsSame(tmp)) { if (inner) { for (++it; it != wires.end(); ++it) { inner->push_back(*it); - if (reorient) + if (reorient) { doReorient(inner->back(), orientInner); + } } } auto res = wire; - if (reorient) + if (reorient) { doReorient(res, orientOuter); + } return res; } if (inner) { inner->push_back(wire); - if (reorient) + if (reorient) { doReorient(inner->back(), orientInner); + } } } return TopoShape(); diff --git a/tests/src/Mod/Part/App/PartTestHelpers.cpp b/tests/src/Mod/Part/App/PartTestHelpers.cpp index 02a3d33d04..24d106f9f8 100644 --- a/tests/src/Mod/Part/App/PartTestHelpers.cpp +++ b/tests/src/Mod/Part/App/PartTestHelpers.cpp @@ -14,6 +14,21 @@ double getVolume(const TopoDS_Shape& shape) return prop.Mass(); } +double getArea(const TopoDS_Shape& shape) +{ + GProp_GProps prop; + BRepGProp::SurfaceProperties(shape, prop); + return prop.Mass(); +} + +double getLength(const TopoDS_Shape& shape) +{ + GProp_GProps prop; + BRepGProp::LinearProperties(shape, prop); + return prop.Mass(); +} + + void PartTestHelperClass::createTestDoc() { _docName = App::GetApplication().getUniqueDocumentName("test"); @@ -48,7 +63,8 @@ _getFilletEdges(const std::vector& edges, double startRadius, double endRad return filletElements; } -void executePython(const std::vector& python) + +void ExecutePython(const std::vector& python) { Base::InterpreterSingleton is = Base::InterpreterSingleton(); @@ -68,16 +84,9 @@ void rectangle(double height, double width, char* name) boost::str(boost::format("V4 = FreeCAD.Vector(0, %d, 0)") % width), "P1 = Part.makePolygon([V1, V2, V3, V4],True)", "F1 = Part.Face(P1)", // Make the face or the volume calc won't work right. - // "L1 = Part.LineSegment(V1, V2)", - // "L2 = Part.LineSegment(V2, V3)", - // "L3 = Part.LineSegment(V3, V4)", - // "L4 = Part.LineSegment(V4, V1)", - // "S1 = Part.Shape([L1,L2,L3,L4])", - // "W1 = Part.Wire(S1.Edges)", - // "F1 = Part.Face(W1)", // Make the face or the volume calc won't work right. boost::str(boost::format("Part.show(F1,'%s')") % name), }; - executePython(rectstring); + ExecutePython(rectstring); } testing::AssertionResult diff --git a/tests/src/Mod/Part/App/PartTestHelpers.h b/tests/src/Mod/Part/App/PartTestHelpers.h index d651951c2f..742eaf26bc 100644 --- a/tests/src/Mod/Part/App/PartTestHelpers.h +++ b/tests/src/Mod/Part/App/PartTestHelpers.h @@ -1,21 +1,25 @@ // SPDX-License-Identifier: LGPL-2.1-or-later #include "gtest/gtest.h" +#include #include #include +#include "Base/Interpreter.h" #include #include "Mod/Part/App/FeaturePartBox.h" #include "Mod/Part/App/FeaturePartFuse.h" #include "Mod/Part/App/FeatureFillet.h" #include -#include "Base/Interpreter.h" -#include namespace PartTestHelpers { double getVolume(const TopoDS_Shape& shape); +double getArea(const TopoDS_Shape& shape); + +double getLength(const TopoDS_Shape& shape); + std::vector _getFilletEdges(const std::vector& edges, double startRadius, double endRadius); diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index 964f346f88..bb793a326b 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -3,12 +3,18 @@ #include "gtest/gtest.h" #include "src/App/InitApplication.h" #include +#include + +#include "PartTestHelpers.h" -#include #include +#include +#include #include +#include +#include +#include #include -#include // NOLINTBEGIN(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) @@ -34,6 +40,7 @@ protected: App::GetApplication().closeDocument(_docName.c_str()); } + private: std::string _docName; Data::ElementIDRefs _sid; @@ -161,4 +168,272 @@ TEST_F(TopoShapeExpansionTest, makeElementCompoundTwoCubes) // 26 subshapes each } +std::tuple +CreateRectFace(float len = 2.0, float wid = 3.0) +{ + auto edge1 = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(len, 0.0, 0.0)).Edge(); + auto edge2 = BRepBuilderAPI_MakeEdge(gp_Pnt(len, 0.0, 0.0), gp_Pnt(len, wid, 0.0)).Edge(); + auto edge3 = BRepBuilderAPI_MakeEdge(gp_Pnt(len, wid, 0.0), gp_Pnt(0.0, wid, 0.0)).Edge(); + auto edge4 = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, wid, 0.0), gp_Pnt(0.0, 0.0, 0.0)).Edge(); + auto wire1 = BRepBuilderAPI_MakeWire({edge1, edge2, edge3, edge4}).Wire(); + auto face1 = BRepBuilderAPI_MakeFace(wire1).Face(); + return {face1, wire1, edge1, edge2, edge3, edge4}; +} + +std::tuple +CreateFaceWithRoundHole(float len = 2.0, float wid = 3.0, float radius = 1.0) +{ + auto [face1, wire1, edge1, edge2, edge3, edge4] = CreateRectFace(len, wid); + auto circ1 = + GC_MakeCircle(gp_Pnt(len / 2.0, wid / 2.0, 0), gp_Dir(0.0, 0.0, 1.0), radius).Value(); + auto edge5 = BRepBuilderAPI_MakeEdge(circ1).Edge(); + auto wire2 = BRepBuilderAPI_MakeWire(edge5).Wire(); + auto face2 = BRepBuilderAPI_MakeFace(face1, wire2).Face(); + return {face2, wire1, wire2}; +} + +TEST_F(TopoShapeExpansionTest, makeElementFaceNull) +{ + // Arrange + const double L = 3, W = 2, R = 1; + auto [face1, wire1, wire2] = CreateFaceWithRoundHole(L, W, R); + Part::TopoShape topoShape {face1}; + double area = PartTestHelpers::getArea(face1); + double area1 = PartTestHelpers::getArea(topoShape.getShape()); + // Act + Part::TopoShape newFace = topoShape.makeElementFace(nullptr); + double area2 = PartTestHelpers::getArea(newFace.getShape()); + double area3 = PartTestHelpers::getArea(topoShape.getShape()); + // Assert + EXPECT_FALSE(face1.IsEqual(newFace.getShape())); + EXPECT_FLOAT_EQ(area, L * W + M_PI * R * R); + EXPECT_FLOAT_EQ(area1, L * W + M_PI * R * R); + EXPECT_FLOAT_EQ(area2, L * W - M_PI * R * R); + EXPECT_FLOAT_EQ(area3, L * W + M_PI * R * R); + EXPECT_STREQ(newFace.shapeName().c_str(), "Face"); +} + +TEST_F(TopoShapeExpansionTest, makeElementFaceSimple) +{ + // Arrange + const double L = 3, W = 2, R = 1; + auto [face1, wire1, wire2] = CreateFaceWithRoundHole(L, W, R); + Part::TopoShape topoShape {face1}; + double area = PartTestHelpers::getArea(face1); + double area1 = PartTestHelpers::getArea(topoShape.getShape()); + // Act + Part::TopoShape newFace = topoShape.makeElementFace(wire1); + double area2 = PartTestHelpers::getArea(newFace.getShape()); + double area3 = PartTestHelpers::getArea(topoShape.getShape()); + // Assert + EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered + EXPECT_FALSE(face1.IsEqual(newFace.getShape())); + EXPECT_FLOAT_EQ(area, L * W + M_PI * R * R); + EXPECT_FLOAT_EQ(area1, L * W + M_PI * R * R); + EXPECT_FLOAT_EQ(area2, L * W); + EXPECT_FLOAT_EQ(area3, L * W); + EXPECT_STREQ(newFace.shapeName().c_str(), "Face"); +} + +TEST_F(TopoShapeExpansionTest, makeElementFaceParams) +{ + // Arrange + const double L = 3, W = 2, R = 1; + auto [face1, wire1, wire2] = CreateFaceWithRoundHole(L, W, R); + Part::TopoShape topoShape {face1, 1L}; + double area = PartTestHelpers::getArea(face1); + double area1 = PartTestHelpers::getArea(topoShape.getShape()); + // Act + Part::TopoShape newFace = + topoShape.makeElementFace(wire1, "Cut", "Part::FaceMakerBullseye", nullptr); + double area2 = PartTestHelpers::getArea(newFace.getShape()); + double area3 = PartTestHelpers::getArea(topoShape.getShape()); + // Assert + EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered + EXPECT_FALSE(face1.IsEqual(newFace.getShape())); + EXPECT_FLOAT_EQ(area, L * W + M_PI * R * R); + EXPECT_FLOAT_EQ(area1, L * W + M_PI * R * R); + EXPECT_FLOAT_EQ(area2, L * W); + EXPECT_FLOAT_EQ(area3, L * W); + EXPECT_STREQ(newFace.shapeName().c_str(), "Face"); +} + +TEST_F(TopoShapeExpansionTest, makeElementFaceFromFace) +{ + // Arrange + const double L = 3, W = 2, R = 1; + auto [face1, wire1, wire2] = CreateFaceWithRoundHole(L, W, R); + Part::TopoShape topoShape {face1, 1L}; + double area = PartTestHelpers::getArea(face1); + double area1 = PartTestHelpers::getArea(topoShape.getShape()); + // Act + Part::TopoShape newFace = + topoShape.makeElementFace(face1, "Cut", "Part::FaceMakerBullseye", nullptr); + double area2 = PartTestHelpers::getArea(newFace.getShape()); + double area3 = PartTestHelpers::getArea(topoShape.getShape()); + // Assert + EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered + EXPECT_FALSE(face1.IsEqual(newFace.getShape())); + EXPECT_FLOAT_EQ(area, L * W + M_PI * R * R); + EXPECT_FLOAT_EQ(area1, L * W + M_PI * R * R); + EXPECT_FLOAT_EQ(area2, L * W - M_PI * R * R); + EXPECT_FLOAT_EQ(area3, L * W - M_PI * R * R); + EXPECT_STREQ(newFace.shapeName().c_str(), "Face"); +} + + +TEST_F(TopoShapeExpansionTest, makeElementFaceOpenWire) +{ + // Arrange + const double L = 3, W = 2, R = 1; + auto [face1, wire1, wire2] = CreateFaceWithRoundHole(L, W, R); + Part::TopoShape topoShape {wire1, 1L}; + double area = PartTestHelpers::getArea(face1); + double area1 = PartTestHelpers::getArea(topoShape.getShape()); + // Act + Part::TopoShape newFace = topoShape.makeElementFace(wire1, "Cut", nullptr, nullptr); + double area2 = PartTestHelpers::getArea(newFace.getShape()); + double area3 = PartTestHelpers::getArea(topoShape.getShape()); + // Assert + EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered + EXPECT_FALSE(face1.IsEqual(newFace.getShape())); + EXPECT_FLOAT_EQ(area, L * W + M_PI * R * R); + EXPECT_FLOAT_EQ(area1, 0); // L * W - M_PI * R * R); + EXPECT_FLOAT_EQ(area2, L * W); + EXPECT_FLOAT_EQ(area3, L * W); + EXPECT_STREQ(newFace.shapeName().c_str(), "Face"); +} + + +TEST_F(TopoShapeExpansionTest, makeElementFaceClosedWire) +{ + // Arrange + const double L = 3, W = 2, R = 1; + auto [face1, wire1, wire2] = CreateFaceWithRoundHole(L, W, R); + Part::TopoShape topoShape {wire2, 1L}; + double area = PartTestHelpers::getArea(face1); + double area1 = PartTestHelpers::getArea(topoShape.getShape()); + // Act + Part::TopoShape newFace = + topoShape.makeElementFace(wire2, "Cut", "Part::FaceMakerBullseye", nullptr); + double area2 = PartTestHelpers::getArea(newFace.getShape()); + double area3 = PartTestHelpers::getArea(topoShape.getShape()); + // Assert + EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered + EXPECT_FALSE(face1.IsEqual(newFace.getShape())); + EXPECT_FLOAT_EQ(area, L * W + M_PI * R * R); + EXPECT_FLOAT_EQ(area1, 0); // L * W - M_PI * R * R); + EXPECT_FLOAT_EQ(area2, M_PI * R * R); + EXPECT_FLOAT_EQ(area3, M_PI * R * R); + EXPECT_STREQ(newFace.shapeName().c_str(), "Face"); +} + +// Possible future makeElementFace tests: +// Overlapping wire +// Compound of wires +// Compound of faces +// Compound of other shape types + + +TEST_F(TopoShapeExpansionTest, setElementComboNameNothing) +{ + // Arrange + Part::TopoShape topoShape(1L); + // Act + Data::MappedName result = topoShape.setElementComboName(Data::IndexedName(), {}); + // ASSERT + EXPECT_STREQ(result.toString().c_str(), ""); +} + + +TEST_F(TopoShapeExpansionTest, setElementComboNameSimple) +{ + // Arrange + auto edge1 = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge(); + Part::TopoShape topoShape(edge1, 1L); + topoShape.setElementMap({}); // Initialize the map to avoid a segfault. + // Also, maybe the end of TopoShape::mapSubElementTypeForShape should enforce that elementMap() + // isn't nullptr to eliminate the segfault. + Data::MappedName edgeName("testname"); + // Act + Data::MappedName result = + topoShape.setElementComboName(Data::IndexedName::fromConst("Edge", 1), {edgeName}); + // Assert + EXPECT_STREQ(result.toString().c_str(), "testname;"); +} + + +TEST_F(TopoShapeExpansionTest, setElementComboName) +{ + // Arrange + Part::TopoShape topoShape(2L); + topoShape.setElementMap({}); + Data::MappedName edgeName = + topoShape.getMappedName(Data::IndexedName::fromConst("Edge", 1), true); + Data::MappedName faceName = + topoShape.getMappedName(Data::IndexedName::fromConst("Face", 7), true); + Data::MappedName faceName2 = + topoShape.getMappedName(Data::IndexedName::fromConst("Face", 8), true); + char* op = "Copy"; + // Act + Data::MappedName result = topoShape.setElementComboName(Data::IndexedName::fromConst("Edge", 1), + {edgeName, faceName, faceName2}, + Part::OpCodes::Common, + op); + // Assert + EXPECT_STREQ(result.toString().c_str(), "Edge1;CMN(Face7|Face8);Copy"); + // The detailed forms of names are covered in encodeElementName tests +} + +TEST_F(TopoShapeExpansionTest, setElementComboNameCompound) +{ + // Arrange + auto edge1 = BRepBuilderAPI_MakeEdge(gp_Pnt(0.0, 0.0, 0.0), gp_Pnt(1.0, 0.0, 0.0)).Edge(); + auto wire1 = BRepBuilderAPI_MakeWire({edge1}).Wire(); + auto wire2 = BRepBuilderAPI_MakeWire({edge1}).Wire(); + Part::TopoShape topoShape(2L); + topoShape.makeElementCompound({wire1, wire2}); // Quality of shape doesn't matter + Data::MappedName edgeName = + topoShape.getMappedName(Data::IndexedName::fromConst("Edge", 1), true); + Data::MappedName faceName = + topoShape.getMappedName(Data::IndexedName::fromConst("Face", 7), true); + Data::MappedName faceName2 = + topoShape.getMappedName(Data::IndexedName::fromConst("Face", 8), true); + char* op = "Copy"; + // Act + Data::MappedName result = topoShape.setElementComboName(Data::IndexedName::fromConst("Edge", 1), + {edgeName, faceName, faceName2}, + Part::OpCodes::Common, + op); + // ASSERT + EXPECT_STREQ(result.toString().c_str(), "Edge1;:H,E;CMN(Face7|Face8);Copy"); + // The detailed forms of names are covered in encodeElementName tests +} + +TEST_F(TopoShapeExpansionTest, splitWires) +{ + // Arrange + const double L = 3, W = 2, R = 1; + auto [face1, wire1, wire2] = CreateFaceWithRoundHole(L, W, R); + Part::TopoShape topoShape {face1, 1L}; + std::vector inner; + // Act + EXPECT_EQ(topoShape.getShape().Orientation(), TopAbs_FORWARD); + Part::TopoShape wire = + topoShape.splitWires(&inner, Part::TopoShape::SplitWireReorient::ReorientReversed); + // Assert + EXPECT_EQ(inner.size(), 1); + EXPECT_FLOAT_EQ(PartTestHelpers::getLength(wire.getShape()), 2 + 2 + 3 + 3); + EXPECT_FLOAT_EQ(PartTestHelpers::getLength(inner.front().getShape()), M_PI * R * 2); + EXPECT_EQ(wire.getShape().Orientation(), TopAbs_REVERSED); + for (Part::TopoShape ts : inner) { + EXPECT_EQ(ts.getShape().Orientation(), TopAbs_FORWARD); + } +} + +// Possible future tests: +// splitWires without inner Wires +// splitWires with allfour reorientation values NoReorient, ReOrient, ReorientForward, +// ReorientRevesed + // NOLINTEND(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers) From 0a4ec777ad209df5ff2eaf11e13a9bc00b1e371c Mon Sep 17 00:00:00 2001 From: bgbsww Date: Sun, 21 Jan 2024 16:06:32 -0500 Subject: [PATCH 077/135] apply Comp to Comparator name change --- src/Mod/Part/App/FaceMaker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Part/App/FaceMaker.cpp b/src/Mod/Part/App/FaceMaker.cpp index fcdef3537f..21bd1d789d 100644 --- a/src/Mod/Part/App/FaceMaker.cpp +++ b/src/Mod/Part/App/FaceMaker.cpp @@ -173,7 +173,7 @@ struct ElementName { return true; if(tag>other.tag) return false; - return Data::ElementNameComp()(name,other.name); + return Data::ElementNameComparator()(name,other.name); } }; From 5b7cca77039486dd66855d74b8d780a0ff637908 Mon Sep 17 00:00:00 2001 From: marioalexis Date: Sun, 21 Jan 2024 15:22:21 -0300 Subject: [PATCH 078/135] Fem: Set PotentialEnabled to False in capacitance_two_balls example - fixes #9707 --- .../equation_electrostatics_capacitance_two_balls.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Mod/Fem/femexamples/equation_electrostatics_capacitance_two_balls.py b/src/Mod/Fem/femexamples/equation_electrostatics_capacitance_two_balls.py index 94ae02167f..ed4624da90 100644 --- a/src/Mod/Fem/femexamples/equation_electrostatics_capacitance_two_balls.py +++ b/src/Mod/Fem/femexamples/equation_electrostatics_capacitance_two_balls.py @@ -138,6 +138,7 @@ def setup(doc=None, solvertype="elmer"): con_elect_pot1 = ObjectsFem.makeConstraintElectrostaticPotential(doc, name_pot1) con_elect_pot1.References = [(geom_obj, "Face1")] con_elect_pot1.ElectricInfinity = True + con_elect_pot1.PotentialEnabled = False analysis.addObject(con_elect_pot1) # constraint potential 2nd @@ -146,6 +147,7 @@ def setup(doc=None, solvertype="elmer"): con_elect_pot2.References = [(geom_obj, "Face2")] con_elect_pot2.CapacitanceBody = 1 con_elect_pot2.CapacitanceBodyEnabled = True + con_elect_pot2.PotentialEnabled = False analysis.addObject(con_elect_pot2) # constraint potential 3rd @@ -154,6 +156,7 @@ def setup(doc=None, solvertype="elmer"): con_elect_pot3.References = [(geom_obj, "Face3")] con_elect_pot3.CapacitanceBody = 2 con_elect_pot3.CapacitanceBodyEnabled = True + con_elect_pot3.PotentialEnabled = False analysis.addObject(con_elect_pot3) # mesh From bf7adc51d10aa0218a1bc27b165b58e4836fb01f Mon Sep 17 00:00:00 2001 From: Ulices Date: Sun, 21 Jan 2024 18:52:34 -0600 Subject: [PATCH 079/135] PartDesign: Update Length/Offset when Occurrences changes (#12069) * Add 'HACK:'/`NOTE:` tags to get special highlightning * [PD] Bugfix: Update Length/Offset when Occurrences changes Fix #12068 --- src/Mod/PartDesign/App/FeatureLinearPattern.cpp | 10 ++++++---- src/Mod/PartDesign/WizardShaft/WizardShaft.py | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Mod/PartDesign/App/FeatureLinearPattern.cpp b/src/Mod/PartDesign/App/FeatureLinearPattern.cpp index 256ea7d29b..2b95e1aca1 100644 --- a/src/Mod/PartDesign/App/FeatureLinearPattern.cpp +++ b/src/Mod/PartDesign/App/FeatureLinearPattern.cpp @@ -221,7 +221,7 @@ const std::list LinearPattern::getTransformations(const std::vector Date: Fri, 19 Jan 2024 19:00:58 +0100 Subject: [PATCH 080/135] CMake: Fix searching for pip installed PySide --- .../SetupShibokenAndPyside.cmake | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cMake/FreeCAD_Helpers/SetupShibokenAndPyside.cmake b/cMake/FreeCAD_Helpers/SetupShibokenAndPyside.cmake index 7556a25a89..aca36bc439 100644 --- a/cMake/FreeCAD_Helpers/SetupShibokenAndPyside.cmake +++ b/cMake/FreeCAD_Helpers/SetupShibokenAndPyside.cmake @@ -45,9 +45,9 @@ macro(SetupShibokenAndPyside) if(NOT SHIBOKEN_INCLUDE_DIR) find_pip_package(Shiboken${SHIBOKEN_MAJOR_VERSION}) if (Shiboken${SHIBOKEN_MAJOR_VERSION}_FOUND) - set(Shiboken_INCLUDE_DIR ${Shiboken${SHIBOKEN_MAJOR_VERSION}_INCLUDE_DIRS}) - set(Shiboken_LIBRARY ${Shiboken${SHIBOKEN_MAJOR_VERSION}_LIBRARIES}) - set(Shiboken_FOUND TRUE) + set(SHIBOKEN_INCLUDE_DIR ${Shiboken${SHIBOKEN_MAJOR_VERSION}_INCLUDE_DIRS}) + set(SHIBOKEN_LIBRARY ${Shiboken${SHIBOKEN_MAJOR_VERSION}_LIBRARIES}) + set(SHIBOKEN_FOUND TRUE) endif() endif() @@ -60,9 +60,9 @@ macro(SetupShibokenAndPyside) if(NOT PYSIDE_INCLUDE_DIR) find_pip_package(PySide${PYSIDE_MAJOR_VERSION}) if (PySide${PYSIDE_MAJOR_VERSION}_FOUND) - set(PySide_INCLUDE_DIR ${PySide${PYSIDE_MAJOR_VERSION}_INCLUDE_DIRS}) - set(PySide_LIBRARY ${PySide${PYSIDE_MAJOR_VERSION}_LIBRARIES}) - set(PySide_FOUND TRUE) + set(PYSIDE_INCLUDE_DIR ${PySide${PYSIDE_MAJOR_VERSION}_INCLUDE_DIRS}) + set(PYSIDE_LIBRARY ${PySide${PYSIDE_MAJOR_VERSION}_LIBRARIES}) + set(PYSIDE_FOUND TRUE) endif() endif() @@ -192,9 +192,9 @@ macro(find_pip_package PACKAGE) endforeach() file(TO_NATIVE_PATH "${PIP_PACKAGE_LOCATION}/${PIP_PACKAGE_NAME}/include" INCLUDE_DIR) file(TO_NATIVE_PATH "${PIP_PACKAGE_LOCATION}/${PIP_PACKAGE_NAME}/lib" LIBRARY) - set(${PACKAGE}_INCLUDE_DIRS ${INCLUDE_DIR} PARENT_SCOPE) - set(${PACKAGE}_LIBRARIES ${LIBRARY} PARENT_SCOPE) - set(${PACKAGE}_FOUND ${LIBRARY} TRUE) + set(${PACKAGE}_INCLUDE_DIRS ${INCLUDE_DIR}) + set(${PACKAGE}_LIBRARIES ${LIBRARY}) + set(${PACKAGE}_FOUND TRUE) message(STATUS "Found pip-installed ${PACKAGE} in ${PIP_PACKAGE_LOCATION}/${PIP_PACKAGE_NAME}") endif() endmacro() From 3dd2fc8f0fea9b13116ecdc1297539e03c94daf7 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Fri, 19 Jan 2024 19:02:58 +0100 Subject: [PATCH 081/135] Gui: Consolidate module error message in PythonWrapper --- src/Gui/PythonWrapper.cpp | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/Gui/PythonWrapper.cpp b/src/Gui/PythonWrapper.cpp index 412b56c47f..c56def3526 100644 --- a/src/Gui/PythonWrapper.cpp +++ b/src/Gui/PythonWrapper.cpp @@ -354,6 +354,13 @@ private: ~WrapperManager() override = default; }; +static std::string formatModuleError(std::string name) +{ + std::string error = "Cannot load " + name + " module"; + PyErr_Print(); + return error; +} + template Py::Object qt_wrapInstance(qttype object, const std::string& className, @@ -363,10 +370,7 @@ Py::Object qt_wrapInstance(qttype object, { PyObject* module = PyImport_ImportModule(shiboken.c_str()); if (!module) { - std::string error = "Cannot load "; - error += shiboken; - error += " module"; - throw Py::Exception(PyExc_ImportError, error); + throw Py::Exception(PyExc_ImportError, formatModuleError(shiboken)); } Py::Module mainmod(module, true); @@ -377,10 +381,7 @@ Py::Object qt_wrapInstance(qttype object, module = PyImport_ImportModule(pyside.c_str()); if (!module) { - std::string error = "Cannot load "; - error += pyside; - error += " module"; - throw Py::Exception(PyExc_ImportError, error); + throw Py::Exception(PyExc_ImportError, formatModuleError(pyside)); } Py::Module qtmod(module); @@ -392,10 +393,7 @@ const char* qt_identifyType(QObject* ptr, const std::string& pyside) { PyObject* module = PyImport_ImportModule(pyside.c_str()); if (!module) { - std::string error = "Cannot load "; - error += pyside; - error += " module"; - throw Py::Exception(PyExc_ImportError, error); + throw Py::Exception(PyExc_ImportError, formatModuleError(pyside)); } Py::Module qtmod(module); @@ -415,10 +413,7 @@ void* qt_getCppPointer(const Py::Object& pyobject, const std::string& shiboken, // https://github.com/PySide/Shiboken/blob/master/shibokenmodule/typesystem_shiboken.xml PyObject* module = PyImport_ImportModule(shiboken.c_str()); if (!module) { - std::string error = "Cannot load "; - error += shiboken; - error += " module"; - throw Py::Exception(PyExc_ImportError, error); + throw Py::Exception(PyExc_ImportError, formatModuleError(shiboken)); } Py::Module mainmod(module, true); From d03a0dfcd7bfd983f391f5f0d3b0671f74790a91 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Sat, 20 Jan 2024 11:58:26 +0100 Subject: [PATCH 082/135] Gui: Consolidate Python -> Qt class conversion --- src/Gui/PythonWrapper.cpp | 60 +++++++-------------------------------- 1 file changed, 11 insertions(+), 49 deletions(-) diff --git a/src/Gui/PythonWrapper.cpp b/src/Gui/PythonWrapper.cpp index c56def3526..490932505a 100644 --- a/src/Gui/PythonWrapper.cpp +++ b/src/Gui/PythonWrapper.cpp @@ -447,9 +447,10 @@ PyTypeObject *getPyTypeObjectForTypeName() } template -qttype* qt_getCppType(PyObject* pyobj) +qttype* qt_getCppType(PyObject* pyobj, const std::string& shiboken) { #if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE) + Q_UNUSED(shiboken) PyTypeObject * type = getPyTypeObjectForTypeName(); if (type) { if (Shiboken::Object::checkType(pyobj)) { @@ -459,7 +460,9 @@ qttype* qt_getCppType(PyObject* pyobj) } } #else - Q_UNUSED(pyobj) + void* ptr = qt_getCppPointer(Py::asObject(pyobj), shiboken, "getCppPointer"); + if (ptr) + return reinterpret_cast(ptr); #endif return nullptr; @@ -531,30 +534,12 @@ bool PythonWrapper::toCString(const Py::Object& pyobject, std::string& str) QObject* PythonWrapper::toQObject(const Py::Object& pyobject) { - // http://pastebin.com/JByDAF5Z -#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE) - return qt_getCppType(pyobject.ptr()); -#else - // Access shiboken/PySide via Python - // - void* ptr = qt_getCppPointer(pyobject, shiboken, "getCppPointer"); - return static_cast(ptr); -#endif - - return nullptr; + return qt_getCppType(pyobject.ptr(), shiboken); } QGraphicsItem* PythonWrapper::toQGraphicsItem(PyObject* pyPtr) { -#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE) - return qt_getCppType(pyPtr); -#else - // Access shiboken/PySide via Python - // - void* ptr = qt_getCppPointer(Py::asObject(pyPtr), shiboken, "getCppPointer"); - return static_cast(ptr); -#endif - return nullptr; + return qt_getCppType(pyPtr, shiboken); } QGraphicsItem* PythonWrapper::toQGraphicsItem(const Py::Object& pyobject) @@ -564,15 +549,7 @@ QGraphicsItem* PythonWrapper::toQGraphicsItem(const Py::Object& pyobject) QGraphicsObject* PythonWrapper::toQGraphicsObject(PyObject* pyPtr) { -#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE) - return qt_getCppType(pyPtr); -#else - // Access shiboken/PySide via Python - // - void* ptr = qt_getCppPointer(Py::asObject(pyPtr), shiboken, "getCppPointer"); - return reinterpret_cast(ptr); -#endif - return nullptr; + return qt_getCppType(pyPtr, shiboken); } QGraphicsObject* PythonWrapper::toQGraphicsObject(const Py::Object& pyobject) @@ -603,12 +580,7 @@ Py::Object PythonWrapper::fromQImage(const QImage& img) QImage *PythonWrapper::toQImage(PyObject *pyobj) { -#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE) - return qt_getCppType(pyobj); -#else - Q_UNUSED(pyobj); -#endif - return nullptr; + return qt_getCppType(pyobj, shiboken); } Py::Object PythonWrapper::fromQIcon(const QIcon* icon) @@ -634,12 +606,7 @@ Py::Object PythonWrapper::fromQIcon(const QIcon* icon) QIcon *PythonWrapper::toQIcon(PyObject *pyobj) { -#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE) - return qt_getCppType(pyobj); -#else - Q_UNUSED(pyobj); -#endif - return nullptr; + return qt_getCppType(pyobj, shiboken); } Py::Object PythonWrapper::fromQDir(const QDir& dir) @@ -663,12 +630,7 @@ Py::Object PythonWrapper::fromQDir(const QDir& dir) QDir* PythonWrapper::toQDir(PyObject* pyobj) { -#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE) - return qt_getCppType(pyobj); -#else - Q_UNUSED(pyobj); -#endif - return nullptr; + return qt_getCppType(pyobj, shiboken); } Py::Object PythonWrapper::fromQPrinter(QPrinter* printer) From 500f3b60860a4e3081aa42eb534e3255e7d8e23e Mon Sep 17 00:00:00 2001 From: marioalexis Date: Mon, 22 Jan 2024 00:52:10 -0300 Subject: [PATCH 083/135] Material: Initialize types --- src/Mod/Material/App/AppMaterial.cpp | 24 ++++++++++++++++++++++++ src/Mod/Material/App/MaterialLibrary.cpp | 4 ++-- src/Mod/Material/App/ModelLibrary.cpp | 2 +- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/Mod/Material/App/AppMaterial.cpp b/src/Mod/Material/App/AppMaterial.cpp index 9d2471c657..b63547925d 100644 --- a/src/Mod/Material/App/AppMaterial.cpp +++ b/src/Mod/Material/App/AppMaterial.cpp @@ -28,6 +28,8 @@ #include // #include "Model.h" +#include "MaterialFilter.h" + #include "MaterialManagerPy.h" #include "MaterialPy.h" #include "ModelManagerPy.h" @@ -71,5 +73,27 @@ PyMOD_INIT_FUNC(Material) Base::Interpreter().addType(&Materials::ModelPy ::Type, module, "Model"); Base::Interpreter().addType(&Materials::UUIDsPy ::Type, module, "UUIDs"); + + // Initialize types + + Materials::Material ::init(); + Materials::MaterialFilter ::init(); + Materials::MaterialManager ::init(); + Materials::Model ::init(); + Materials::ModelManager ::init(); + Materials::ModelUUIDs ::init(); + + Materials::LibraryBase ::init(); + Materials::MaterialLibrary ::init(); + Materials::ModelLibrary ::init(); + Materials::MaterialExternalLibrary ::init(); + + Materials::ModelProperty ::init(); + Materials::MaterialProperty ::init(); + + Materials::MaterialValue ::init(); + Materials::Material2DArray ::init(); + Materials::Material3DArray ::init(); + PyMOD_Return(module); } diff --git a/src/Mod/Material/App/MaterialLibrary.cpp b/src/Mod/Material/App/MaterialLibrary.cpp index 1a1573e388..87de1c2104 100644 --- a/src/Mod/Material/App/MaterialLibrary.cpp +++ b/src/Mod/Material/App/MaterialLibrary.cpp @@ -41,7 +41,7 @@ using namespace Materials; /* TRANSLATOR Material::Materials */ -TYPESYSTEM_SOURCE(Materials::MaterialLibrary, LibraryBase) +TYPESYSTEM_SOURCE(Materials::MaterialLibrary, Materials::LibraryBase) MaterialLibrary::MaterialLibrary(const QString& libraryName, const QString& dir, @@ -352,7 +352,7 @@ MaterialLibrary::getMaterialTree(const MaterialFilter* filter) const return materialTree; } -TYPESYSTEM_SOURCE(Materials::MaterialExternalLibrary, MaterialLibrary::MaterialLibrary) +TYPESYSTEM_SOURCE(Materials::MaterialExternalLibrary, Materials::MaterialLibrary) MaterialExternalLibrary::MaterialExternalLibrary(const QString& libraryName, const QString& dir, diff --git a/src/Mod/Material/App/ModelLibrary.cpp b/src/Mod/Material/App/ModelLibrary.cpp index 87a4387263..40df9b3366 100644 --- a/src/Mod/Material/App/ModelLibrary.cpp +++ b/src/Mod/Material/App/ModelLibrary.cpp @@ -99,7 +99,7 @@ QString LibraryBase::getRelativePath(const QString& path) const return filePath; } -TYPESYSTEM_SOURCE(Materials::ModelLibrary, LibraryBase) +TYPESYSTEM_SOURCE(Materials::ModelLibrary, Materials::LibraryBase) ModelLibrary::ModelLibrary(const QString& libraryName, const QString& dir, const QString& icon) : LibraryBase(libraryName, dir, icon) From 26dbd32c1548101226b5815af7c9eee68ee411bf Mon Sep 17 00:00:00 2001 From: bgbsww Date: Mon, 22 Jan 2024 11:01:19 -0500 Subject: [PATCH 084/135] lint / review cleanups --- src/Mod/Part/App/FaceMaker.cpp | 7 ---- src/Mod/Part/App/FaceMaker.h | 2 +- src/Mod/Part/App/TopoShape.h | 38 ++++++++++--------- src/Mod/Part/App/TopoShapeExpansion.cpp | 20 +++++----- tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 4 +- 5 files changed, 33 insertions(+), 38 deletions(-) diff --git a/src/Mod/Part/App/FaceMaker.cpp b/src/Mod/Part/App/FaceMaker.cpp index 21bd1d789d..5db6f6c640 100644 --- a/src/Mod/Part/App/FaceMaker.cpp +++ b/src/Mod/Part/App/FaceMaker.cpp @@ -206,17 +206,10 @@ void Part::FaceMaker::postBuild() { std::vector names; Data::ElementIDRefs sids; -#if 0 - for (auto &e : edgeNames) { - names.insert(e.name); - sids += e.sids; - } -#else // We just use the first source element name to make the face name more // stable names.push_back(edgeNames.begin()->name); sids = edgeNames.begin()->sids; -#endif this->myTopoShape.setElementComboName( Data::IndexedName::fromConst("Face",i),names,op,nullptr,&sids); } diff --git a/src/Mod/Part/App/FaceMaker.h b/src/Mod/Part/App/FaceMaker.h index fc40e243a1..cd25ada21b 100644 --- a/src/Mod/Part/App/FaceMaker.h +++ b/src/Mod/Part/App/FaceMaker.h @@ -86,7 +86,7 @@ public: virtual const TopoDS_Face& Face(); #if OCC_VERSION_HEX >= 0x070600 - virtual void Build(const Message_ProgressRange& theRange = Message_ProgressRange()); + void Build(const Message_ProgressRange& theRange = Message_ProgressRange()) override; #else virtual void Build(); #endif diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index b5bf372a93..716237de47 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -655,11 +655,11 @@ public: */ TopoShape &makeElementCompound(const std::vector &shapes, const char *op=nullptr, bool force=true); - TopoShape &makeElementFace(const std::vector &shapes, - const char *op = nullptr, - const char *maker = nullptr, - const gp_Pln *pln = nullptr); - /** Make a planar face with the input wire or edge + TopoShape& makeElementFace(const std::vector& shapes, + const char* op = nullptr, + const char* maker = nullptr, + const gp_Pln* pln = nullptr); + /** Make a planar face with the input wire or edge * * @param shape: input shape. Can be either edge, wire, or compound of * those two types @@ -675,11 +675,11 @@ public: * multiple operations can be carried out for the same shape in the * same line of code. */ - TopoShape &makeElementFace(const TopoShape &shape, - const char *op = nullptr, - const char *maker = nullptr, - const gp_Pln *pln = nullptr); - /** Make a planar face using this shape + TopoShape& makeElementFace(const TopoShape& shape, + const char* op = nullptr, + const char* maker = nullptr, + const gp_Pln* pln = nullptr); + /** Make a planar face using this shape * * @param op: optional string to be encoded into topo naming for indicating * the operation @@ -690,20 +690,22 @@ public: * @return The function returns a new planar face made using the wire or edge * inside this shape. The shape itself is not modified. */ - TopoShape makeElementFace(const char *op = nullptr, - const char *maker = nullptr, - const gp_Pln *pln = nullptr) const { - return TopoShape(0,Hasher).makeElementFace(*this,op,maker,pln); + TopoShape makeElementFace(const char* op = nullptr, + const char* maker = nullptr, + const gp_Pln* pln = nullptr) const + { + return TopoShape(0, Hasher).makeElementFace(*this, op, maker, pln); } /// Filling style when making a BSpline face - enum FillingStyle { + enum class FillingStyle + { /// The style with the flattest patches - FillingStyle_Strech, + Stretch, /// A rounded style of patch with less depth than those of Curved - FillingStyle_Coons, + Coons, /// The style with the most rounded patches - FillingStyle_Curved, + Curved, }; diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index c59ada479b..d0109d4e72 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -552,7 +552,7 @@ TopoShape::makeElementCompound(const std::vector& shapes, const char* TopoShape& TopoShape::makeElementFace(const TopoShape& shape, const char* op, const char* maker, - const gp_Pln* pln) + const gp_Pln* plane) { std::vector shapes; if (shape.isNull()) { @@ -564,13 +564,13 @@ TopoShape& TopoShape::makeElementFace(const TopoShape& shape, else { shapes.push_back(shape); } - return makeElementFace(shapes, op, maker, pln); + return makeElementFace(shapes, op, maker, plane); } TopoShape& TopoShape::makeElementFace(const std::vector& shapes, const char* op, const char* maker, - const gp_Pln* pln) + const gp_Pln* plane) { if (!maker || !maker[0]) { maker = "Part::FaceMakerBullseye"; @@ -578,16 +578,16 @@ TopoShape& TopoShape::makeElementFace(const std::vector& shapes, std::unique_ptr mkFace = FaceMaker::ConstructFromType(maker); mkFace->MyHasher = Hasher; mkFace->MyOp = op; - if (pln) { - mkFace->setPlane(*pln); + if (plane) { + mkFace->setPlane(*plane); } - for (auto& s : shapes) { - if (s.getShape().ShapeType() == TopAbs_COMPOUND) { - mkFace->useTopoCompound(s); + for (auto& shape : shapes) { + if (shape.getShape().ShapeType() == TopAbs_COMPOUND) { + mkFace->useTopoCompound(shape); } else { - mkFace->addTopoShape(s); + mkFace->addTopoShape(shape); } } mkFace->Build(); @@ -634,7 +634,7 @@ TopoShape& TopoShape::makeElementFace(const std::vector& shapes, * elementMapPrefix. * @param op The op text passed to the element name encoder along with the TopoShape Tag * @param _sids If defined, records the sub ids processed. - * + * * @return The encoded, possibly hashed name. */ Data::MappedName TopoShape::setElementComboName(const Data::IndexedName& element, diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index bb793a326b..f534e4d02e 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -426,8 +426,8 @@ TEST_F(TopoShapeExpansionTest, splitWires) EXPECT_FLOAT_EQ(PartTestHelpers::getLength(wire.getShape()), 2 + 2 + 3 + 3); EXPECT_FLOAT_EQ(PartTestHelpers::getLength(inner.front().getShape()), M_PI * R * 2); EXPECT_EQ(wire.getShape().Orientation(), TopAbs_REVERSED); - for (Part::TopoShape ts : inner) { - EXPECT_EQ(ts.getShape().Orientation(), TopAbs_FORWARD); + for (Part::TopoShape& shape : inner) { + EXPECT_EQ(shape.getShape().Orientation(), TopAbs_FORWARD); } } From 7f2d2050bd09ef1df1c70669b87314f5388e8d2c Mon Sep 17 00:00:00 2001 From: bgbsww Date: Mon, 22 Jan 2024 11:29:41 -0500 Subject: [PATCH 085/135] Single char constants --- tests/src/Mod/Part/App/TopoShapeExpansion.cpp | 78 +++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp index f534e4d02e..1255a91da6 100644 --- a/tests/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/tests/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -195,8 +195,8 @@ CreateFaceWithRoundHole(float len = 2.0, float wid = 3.0, float radius = 1.0) TEST_F(TopoShapeExpansionTest, makeElementFaceNull) { // Arrange - const double L = 3, W = 2, R = 1; - auto [face1, wire1, wire2] = CreateFaceWithRoundHole(L, W, R); + const double Len = 3, Wid = 2, Rad = 1; + auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); Part::TopoShape topoShape {face1}; double area = PartTestHelpers::getArea(face1); double area1 = PartTestHelpers::getArea(topoShape.getShape()); @@ -206,18 +206,18 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceNull) double area3 = PartTestHelpers::getArea(topoShape.getShape()); // Assert EXPECT_FALSE(face1.IsEqual(newFace.getShape())); - EXPECT_FLOAT_EQ(area, L * W + M_PI * R * R); - EXPECT_FLOAT_EQ(area1, L * W + M_PI * R * R); - EXPECT_FLOAT_EQ(area2, L * W - M_PI * R * R); - EXPECT_FLOAT_EQ(area3, L * W + M_PI * R * R); + EXPECT_FLOAT_EQ(area, Len * Wid + M_PI * Rad * Rad); + EXPECT_FLOAT_EQ(area1, Len * Wid + M_PI * Rad * Rad); + EXPECT_FLOAT_EQ(area2, Len * Wid - M_PI * Rad * Rad); + EXPECT_FLOAT_EQ(area3, Len * Wid + M_PI * Rad * Rad); EXPECT_STREQ(newFace.shapeName().c_str(), "Face"); } TEST_F(TopoShapeExpansionTest, makeElementFaceSimple) { // Arrange - const double L = 3, W = 2, R = 1; - auto [face1, wire1, wire2] = CreateFaceWithRoundHole(L, W, R); + const double Len = 3, Wid = 2, Rad = 1; + auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); Part::TopoShape topoShape {face1}; double area = PartTestHelpers::getArea(face1); double area1 = PartTestHelpers::getArea(topoShape.getShape()); @@ -228,18 +228,18 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceSimple) // Assert EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered EXPECT_FALSE(face1.IsEqual(newFace.getShape())); - EXPECT_FLOAT_EQ(area, L * W + M_PI * R * R); - EXPECT_FLOAT_EQ(area1, L * W + M_PI * R * R); - EXPECT_FLOAT_EQ(area2, L * W); - EXPECT_FLOAT_EQ(area3, L * W); + EXPECT_FLOAT_EQ(area, Len * Wid + M_PI * Rad * Rad); + EXPECT_FLOAT_EQ(area1, Len * Wid + M_PI * Rad * Rad); + EXPECT_FLOAT_EQ(area2, Len * Wid); + EXPECT_FLOAT_EQ(area3, Len * Wid); EXPECT_STREQ(newFace.shapeName().c_str(), "Face"); } TEST_F(TopoShapeExpansionTest, makeElementFaceParams) { // Arrange - const double L = 3, W = 2, R = 1; - auto [face1, wire1, wire2] = CreateFaceWithRoundHole(L, W, R); + const double Len = 3, Wid = 2, Rad = 1; + auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); Part::TopoShape topoShape {face1, 1L}; double area = PartTestHelpers::getArea(face1); double area1 = PartTestHelpers::getArea(topoShape.getShape()); @@ -251,18 +251,18 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceParams) // Assert EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered EXPECT_FALSE(face1.IsEqual(newFace.getShape())); - EXPECT_FLOAT_EQ(area, L * W + M_PI * R * R); - EXPECT_FLOAT_EQ(area1, L * W + M_PI * R * R); - EXPECT_FLOAT_EQ(area2, L * W); - EXPECT_FLOAT_EQ(area3, L * W); + EXPECT_FLOAT_EQ(area, Len * Wid + M_PI * Rad * Rad); + EXPECT_FLOAT_EQ(area1, Len * Wid + M_PI * Rad * Rad); + EXPECT_FLOAT_EQ(area2, Len * Wid); + EXPECT_FLOAT_EQ(area3, Len * Wid); EXPECT_STREQ(newFace.shapeName().c_str(), "Face"); } TEST_F(TopoShapeExpansionTest, makeElementFaceFromFace) { // Arrange - const double L = 3, W = 2, R = 1; - auto [face1, wire1, wire2] = CreateFaceWithRoundHole(L, W, R); + const double Len = 3, Wid = 2, Rad = 1; + auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); Part::TopoShape topoShape {face1, 1L}; double area = PartTestHelpers::getArea(face1); double area1 = PartTestHelpers::getArea(topoShape.getShape()); @@ -274,10 +274,10 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceFromFace) // Assert EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered EXPECT_FALSE(face1.IsEqual(newFace.getShape())); - EXPECT_FLOAT_EQ(area, L * W + M_PI * R * R); - EXPECT_FLOAT_EQ(area1, L * W + M_PI * R * R); - EXPECT_FLOAT_EQ(area2, L * W - M_PI * R * R); - EXPECT_FLOAT_EQ(area3, L * W - M_PI * R * R); + EXPECT_FLOAT_EQ(area, Len * Wid + M_PI * Rad * Rad); + EXPECT_FLOAT_EQ(area1, Len * Wid + M_PI * Rad * Rad); + EXPECT_FLOAT_EQ(area2, Len * Wid - M_PI * Rad * Rad); + EXPECT_FLOAT_EQ(area3, Len * Wid - M_PI * Rad * Rad); EXPECT_STREQ(newFace.shapeName().c_str(), "Face"); } @@ -285,8 +285,8 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceFromFace) TEST_F(TopoShapeExpansionTest, makeElementFaceOpenWire) { // Arrange - const double L = 3, W = 2, R = 1; - auto [face1, wire1, wire2] = CreateFaceWithRoundHole(L, W, R); + const double Len = 3, Wid = 2, Rad = 1; + auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); Part::TopoShape topoShape {wire1, 1L}; double area = PartTestHelpers::getArea(face1); double area1 = PartTestHelpers::getArea(topoShape.getShape()); @@ -297,10 +297,10 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceOpenWire) // Assert EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered EXPECT_FALSE(face1.IsEqual(newFace.getShape())); - EXPECT_FLOAT_EQ(area, L * W + M_PI * R * R); - EXPECT_FLOAT_EQ(area1, 0); // L * W - M_PI * R * R); - EXPECT_FLOAT_EQ(area2, L * W); - EXPECT_FLOAT_EQ(area3, L * W); + EXPECT_FLOAT_EQ(area, Len * Wid + M_PI * Rad * Rad); + EXPECT_FLOAT_EQ(area1, 0); // Len * Wid - M_PI * Rad * Rad); + EXPECT_FLOAT_EQ(area2, Len * Wid); + EXPECT_FLOAT_EQ(area3, Len * Wid); EXPECT_STREQ(newFace.shapeName().c_str(), "Face"); } @@ -308,8 +308,8 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceOpenWire) TEST_F(TopoShapeExpansionTest, makeElementFaceClosedWire) { // Arrange - const double L = 3, W = 2, R = 1; - auto [face1, wire1, wire2] = CreateFaceWithRoundHole(L, W, R); + const double Len = 3, Wid = 2, Rad = 1; + auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); Part::TopoShape topoShape {wire2, 1L}; double area = PartTestHelpers::getArea(face1); double area1 = PartTestHelpers::getArea(topoShape.getShape()); @@ -321,10 +321,10 @@ TEST_F(TopoShapeExpansionTest, makeElementFaceClosedWire) // Assert EXPECT_TRUE(newFace.getShape().IsEqual(topoShape.getShape())); // topoShape was altered EXPECT_FALSE(face1.IsEqual(newFace.getShape())); - EXPECT_FLOAT_EQ(area, L * W + M_PI * R * R); - EXPECT_FLOAT_EQ(area1, 0); // L * W - M_PI * R * R); - EXPECT_FLOAT_EQ(area2, M_PI * R * R); - EXPECT_FLOAT_EQ(area3, M_PI * R * R); + EXPECT_FLOAT_EQ(area, Len * Wid + M_PI * Rad * Rad); + EXPECT_FLOAT_EQ(area1, 0); // Len * Wid - M_PI * Rad * Rad); + EXPECT_FLOAT_EQ(area2, M_PI * Rad * Rad); + EXPECT_FLOAT_EQ(area3, M_PI * Rad * Rad); EXPECT_STREQ(newFace.shapeName().c_str(), "Face"); } @@ -413,8 +413,8 @@ TEST_F(TopoShapeExpansionTest, setElementComboNameCompound) TEST_F(TopoShapeExpansionTest, splitWires) { // Arrange - const double L = 3, W = 2, R = 1; - auto [face1, wire1, wire2] = CreateFaceWithRoundHole(L, W, R); + const double Len = 3, Wid = 2, Rad = 1; + auto [face1, wire1, wire2] = CreateFaceWithRoundHole(Len, Wid, Rad); Part::TopoShape topoShape {face1, 1L}; std::vector inner; // Act @@ -424,7 +424,7 @@ TEST_F(TopoShapeExpansionTest, splitWires) // Assert EXPECT_EQ(inner.size(), 1); EXPECT_FLOAT_EQ(PartTestHelpers::getLength(wire.getShape()), 2 + 2 + 3 + 3); - EXPECT_FLOAT_EQ(PartTestHelpers::getLength(inner.front().getShape()), M_PI * R * 2); + EXPECT_FLOAT_EQ(PartTestHelpers::getLength(inner.front().getShape()), M_PI * Rad * 2); EXPECT_EQ(wire.getShape().Orientation(), TopAbs_REVERSED); for (Part::TopoShape& shape : inner) { EXPECT_EQ(shape.getShape().Orientation(), TopAbs_FORWARD); From e6b5fd0a2148ee34a6813dbe31a53fc350f34c77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Caldas?= Date: Sat, 21 Oct 2023 22:37:58 -0300 Subject: [PATCH 086/135] Does not rely on the pointervalue returned by getNameInDocument() to use as a DAG key. In order to make getNameInDocument() always return a valid string, we implement a getDagKey() method that shall be used instead of getNameInDocument() when we want to use the pointer value as a "key" to identify the DocumentObject. --- src/App/DocumentObject.cpp | 11 ++++++++++- src/App/DocumentObject.h | 2 ++ src/App/Link.cpp | 2 +- src/Gui/ViewProviderLink.cpp | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/App/DocumentObject.cpp b/src/App/DocumentObject.cpp index 2950697434..708b6ea3b9 100644 --- a/src/App/DocumentObject.cpp +++ b/src/App/DocumentObject.cpp @@ -292,6 +292,15 @@ std::string DocumentObject::getFullLabel() const { return name; } +const char* DocumentObject::getDagKey() const +{ + if(!pcNameInDocument) + { + return nullptr; + } + return pcNameInDocument->c_str(); +} + const char *DocumentObject::getNameInDocument() const { // Note: It can happen that we query the internal name of an object even if it is not @@ -808,7 +817,7 @@ DocumentObject *DocumentObject::getSubObject(const char *subname, if(outList.size()!=_outListMap.size()) { _outListMap.clear(); for(auto obj : outList) - _outListMap[obj->getNameInDocument()] = obj; + _outListMap[obj->getDagKey()] = obj; } auto it = _outListMap.find(name.c_str()); if(it != _outListMap.end()) diff --git a/src/App/DocumentObject.h b/src/App/DocumentObject.h index a274a60c0c..b9b745ad54 100644 --- a/src/App/DocumentObject.h +++ b/src/App/DocumentObject.h @@ -132,6 +132,8 @@ public: DocumentObject(); ~DocumentObject() override; + /// returns a value that uniquely identifies this DocumentObject. + const char* getDagKey() const; /// returns the name which is set in the document for this object (not the name property!) const char *getNameInDocument() const; /// Return the object ID that is unique within its owner document diff --git a/src/App/Link.cpp b/src/App/Link.cpp index a63cd4bf8f..4328a2ebe1 100644 --- a/src/App/Link.cpp +++ b/src/App/Link.cpp @@ -468,7 +468,7 @@ void LinkBaseExtension::setOnChangeCopyObject( } } - const char *key = flags.testFlag(OnChangeCopyOptions::ApplyAll) ? "*" : parent->getNameInDocument(); + const char *key = flags.testFlag(OnChangeCopyOptions::ApplyAll) ? "*" : parent->getDagKey(); if (external) prop->setValue(key, exclude ? "" : "+"); else diff --git a/src/Gui/ViewProviderLink.cpp b/src/Gui/ViewProviderLink.cpp index c121fe91b2..61a68d6465 100644 --- a/src/Gui/ViewProviderLink.cpp +++ b/src/Gui/ViewProviderLink.cpp @@ -225,7 +225,7 @@ public: } const char *getLinkedName() const { - return pcLinked->getObject()->getNameInDocument(); + return pcLinked->getObject()->getDagKey(); } const char *getLinkedLabel() const { From fbb114c47c2ced7408dfef0be52cfab5ee4f3bed Mon Sep 17 00:00:00 2001 From: xtemp09 Date: Tue, 19 Dec 2023 18:42:24 +0700 Subject: [PATCH 087/135] Fix occasional segmentation fault when exiting Fixes #11521. --- src/Gui/PythonWrapper.cpp | 68 +++++++++++++++------------------------ 1 file changed, 26 insertions(+), 42 deletions(-) diff --git a/src/Gui/PythonWrapper.cpp b/src/Gui/PythonWrapper.cpp index 490932505a..5608a606e2 100644 --- a/src/Gui/PythonWrapper.cpp +++ b/src/Gui/PythonWrapper.cpp @@ -263,7 +263,7 @@ PyTypeObject *getPyTypeObjectForTypeName(); */ class WrapperManager : public QObject { - std::unordered_map> wrappers; + public: static WrapperManager& instance() @@ -275,52 +275,38 @@ public: * \brief addQObject * \param obj * \param pyobj - * Add the QObject and its Python wrapper to the list. + * Connects destruction event of a QObject with invalidation of its PythonWrapper via a helper QObject. */ void addQObject(QObject* obj, PyObject* pyobj) { - if (wrappers.find(obj) == wrappers.end()) { - QObject::connect(obj, &QObject::destroyed, this, &WrapperManager::destroyed); - } + const auto PyW_unique_name = QString::number(reinterpret_cast (pyobj)); + auto PyW_invalidator = findChild (PyW_unique_name, Qt::FindDirectChildrenOnly); - auto& pylist = wrappers[obj]; - if (std::find_if(pylist.cbegin(), pylist.cend(), - [pyobj](const Py::Object& py) { - return py.ptr() == pyobj; - }) == pylist.end()) { + if (PyW_invalidator == nullptr) { + PyW_invalidator = new QObject(this); + PyW_invalidator->setObjectName(PyW_unique_name); - pylist.emplace_back(pyobj); - } - } + Py_INCREF (pyobj); + } else + PyW_invalidator->disconnect(); + + auto destroyedFun = [pyobj](){ +#if defined (HAVE_SHIBOKEN) + auto sbk_ptr = reinterpret_cast (pyobj); + if (sbk_ptr != nullptr) { + Base::PyGILStateLocker lock; + Shiboken::Object::setValidCpp(sbk_ptr, false); + } else + Base::Console().DeveloperError("WrapperManager", "A QObject has just been destroyed after its Pythonic wrapper.\n"); +#endif + Py_DECREF (pyobj); + }; + + QObject::connect(PyW_invalidator, &QObject::destroyed, this, destroyedFun); + QObject::connect(obj, &QObject::destroyed, PyW_invalidator, &QObject::deleteLater); +} private: - /*! - * \brief destroyed - * \param obj - * The listed QObject is about to be destroyed. Invalidate its Python wrappers now. - */ - void destroyed(QObject* obj = nullptr) - { - if (obj) { -#if defined (HAVE_SHIBOKEN) && defined(HAVE_PYSIDE) - auto key = wrappers.find(obj); - if (key != wrappers.end()) { - Base::PyGILStateLocker lock; - for (const auto& it : key->second) { - auto value = it.ptr(); - Shiboken::Object::setValidCpp(reinterpret_cast(value), false); - } - - wrappers.erase(key); - } -#endif - } - } - void clear() - { - Base::PyGILStateLocker lock; - wrappers.clear(); - } void wrapQApplication() { // We have to explicitly hold a reference to the wrapper of the QApplication @@ -347,8 +333,6 @@ private: WrapperManager() { - connect(QApplication::instance(), &QCoreApplication::aboutToQuit, - this, &WrapperManager::clear); wrapQApplication(); } ~WrapperManager() override = default; From 8889cc1163d4245122c1b982af374c2f814c74af Mon Sep 17 00:00:00 2001 From: Paddle Date: Mon, 8 Jan 2024 10:51:50 +0100 Subject: [PATCH 088/135] Core: tree: prevent object replacement when dragging --- src/Gui/Tree.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Gui/Tree.cpp b/src/Gui/Tree.cpp index fe830d468f..30148e512b 100644 --- a/src/Gui/Tree.cpp +++ b/src/Gui/Tree.cpp @@ -1747,7 +1747,8 @@ void TreeWidget::dragMoveEvent(QDragMoveEvent* event) // let the view provider decide to accept the object or ignore it if (da != Qt::LinkAction && !vp->canDropObjectEx(obj, owner, subname.c_str(), item->mySubs)) { - if (event->possibleActions() & Qt::LinkAction) { + // Cause unexpected bugs : https://github.com/FreeCAD/FreeCAD/issues/11676 + /*if (event->possibleActions() & Qt::LinkAction) { if (items.size() > 1) { TREE_TRACE("Cannot replace with more than one object"); event->ignore(); @@ -1760,7 +1761,7 @@ void TreeWidget::dragMoveEvent(QDragMoveEvent* event) } event->setDropAction(Qt::LinkAction); return; - } + }*/ TREE_TRACE("cannot drop " << obj->getFullName() << ' ' << (owner ? owner->getFullName() : "") << '.' << subname); From 17cab3e3ff278930e4246c5ad8a8f9c6c51d3e18 Mon Sep 17 00:00:00 2001 From: bgbsww <120601209+bgbsww@users.noreply.github.com> Date: Mon, 22 Jan 2024 12:12:21 -0500 Subject: [PATCH 089/135] Update src/Mod/Part/App/TopoShapeExpansion.cpp Co-authored-by: Chris Hennes --- src/Mod/Part/App/TopoShapeExpansion.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index 17a33b4edd..63b4faf718 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -376,8 +376,9 @@ void TopoShape::mapSubElementTypeForShape(const TopoShape& other, } std::ostringstream ss; char elementType {shapeName(type)[0]}; - if ( ! elementMap() ) + if ( ! elementMap() ) { FC_THROWM(NullShapeException, "No element map"); + } elementMap()->encodeElementName(elementType, name, ss, &sids, Tag, op, other.Tag); elementMap()->setElementName(element, name, Tag, &sids); } From a5d980ff59640d8cd8b6cbe1eb7b0e407afad9f3 Mon Sep 17 00:00:00 2001 From: Paddle Date: Mon, 15 Jan 2024 18:38:59 +0100 Subject: [PATCH 090/135] Sketcher: Edges pattern and width by type. --- src/Mod/Sketcher/Gui/AppSketcherGui.cpp | 2 +- src/Mod/Sketcher/Gui/CMakeLists.txt | 2 +- src/Mod/Sketcher/Gui/EditModeCoinManager.cpp | 100 +- src/Mod/Sketcher/Gui/EditModeCoinManager.h | 6 + .../Gui/EditModeCoinManagerParameters.h | 98 +- .../Gui/EditModeConstraintCoinManager.cpp | 26 +- .../Gui/EditModeGeometryCoinConverter.cpp | 149 +-- .../Gui/EditModeGeometryCoinConverter.h | 9 +- .../Gui/EditModeGeometryCoinManager.cpp | 301 +++--- src/Mod/Sketcher/Gui/SketcherSettings.cpp | 162 ++- src/Mod/Sketcher/Gui/SketcherSettings.h | 10 +- .../Gui/SketcherSettingsAppearance.ui | 967 ++++++++++++++++++ .../Sketcher/Gui/SketcherSettingsColors.ui | 835 --------------- 13 files changed, 1565 insertions(+), 1102 deletions(-) create mode 100644 src/Mod/Sketcher/Gui/SketcherSettingsAppearance.ui delete mode 100644 src/Mod/Sketcher/Gui/SketcherSettingsColors.ui diff --git a/src/Mod/Sketcher/Gui/AppSketcherGui.cpp b/src/Mod/Sketcher/Gui/AppSketcherGui.cpp index 2d416d37dc..c45f9b91d9 100644 --- a/src/Mod/Sketcher/Gui/AppSketcherGui.cpp +++ b/src/Mod/Sketcher/Gui/AppSketcherGui.cpp @@ -143,7 +143,7 @@ PyMOD_INIT_FUNC(SketcherGui) QT_TRANSLATE_NOOP("QObject", "Sketcher")); (void)new Gui::PrefPageProducer( QT_TRANSLATE_NOOP("QObject", "Sketcher")); - (void)new Gui::PrefPageProducer( + (void)new Gui::PrefPageProducer( QT_TRANSLATE_NOOP("QObject", "Sketcher")); // add resources and reloads the translators diff --git a/src/Mod/Sketcher/Gui/CMakeLists.txt b/src/Mod/Sketcher/Gui/CMakeLists.txt index 1e1011d8e2..2b8aa6e7f7 100644 --- a/src/Mod/Sketcher/Gui/CMakeLists.txt +++ b/src/Mod/Sketcher/Gui/CMakeLists.txt @@ -39,7 +39,7 @@ set(SketcherGui_UIC_SRCS SketchMirrorDialog.ui SketcherSettings.ui SketcherSettingsGrid.ui - SketcherSettingsColors.ui + SketcherSettingsAppearance.ui SketcherSettingsDisplay.ui SketchRectangularArrayDialog.ui SketcherRegularPolygonDialog.ui diff --git a/src/Mod/Sketcher/Gui/EditModeCoinManager.cpp b/src/Mod/Sketcher/Gui/EditModeCoinManager.cpp index 2dbdff3588..b88aaad185 100644 --- a/src/Mod/Sketcher/Gui/EditModeCoinManager.cpp +++ b/src/Mod/Sketcher/Gui/EditModeCoinManager.cpp @@ -145,6 +145,38 @@ void EditModeCoinManager::ParameterObserver::initParameters() [this](const std::string& param) { updateElementSizeParameters(param); }}, + {"EdgeWidth", + [this, &drawingParameters = Client.drawingParameters](const std::string& param) { + updateWidth(drawingParameters.CurveWidth, param, 2); + }}, + {"ConstructionWidth", + [this, &drawingParameters = Client.drawingParameters](const std::string& param) { + updateWidth(drawingParameters.ConstructionWidth, param, 1); + }}, + {"InternalWidth", + [this, &drawingParameters = Client.drawingParameters](const std::string& param) { + updateWidth(drawingParameters.InternalWidth, param, 1); + }}, + {"ExternalWidth", + [this, &drawingParameters = Client.drawingParameters](const std::string& param) { + updateWidth(drawingParameters.ExternalWidth, param, 1); + }}, + {"EdgePattern", + [this, &drawingParameters = Client.drawingParameters](const std::string& param) { + updatePattern(drawingParameters.CurvePattern, param, 0b1111111111111111); + }}, + {"ConstructionPattern", + [this, &drawingParameters = Client.drawingParameters](const std::string& param) { + updatePattern(drawingParameters.ConstructionPattern, param, 0b1111110011111100); + }}, + {"InternalPattern", + [this, &drawingParameters = Client.drawingParameters](const std::string& param) { + updatePattern(drawingParameters.InternalPattern, param, 0b1111110011111100); + }}, + {"ExternalPattern", + [this, &drawingParameters = Client.drawingParameters](const std::string& param) { + updatePattern(drawingParameters.ExternalPattern, param, 0b1110010011100100); + }}, {"CreateLineColor", [this, drawingParameters = Client.drawingParameters](const std::string& param) { updateColor(drawingParameters.CreateCurveColor, param); @@ -384,6 +416,30 @@ void EditModeCoinManager::ParameterObserver::updateElementSizeParameters( Client.updateInventorNodeSizes(); } +void EditModeCoinManager::ParameterObserver::updateWidth(int& width, + const std::string& parametername, + int def) +{ + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/Sketcher/View"); + + width = hGrp->GetInt(parametername.c_str(), def); + + Client.updateInventorWidths(); +} + +void EditModeCoinManager::ParameterObserver::updatePattern(unsigned int& pattern, + const std::string& parametername, + unsigned int def) +{ + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/Sketcher/View"); + + pattern = hGrp->GetInt(parametername.c_str(), def); + + Client.updateInventorPatterns(); +} + void EditModeCoinManager::ParameterObserver::updateColor(SbColor& sbcolor, const std::string& parametername) { @@ -691,14 +747,17 @@ EditModeCoinManager::detectPreselection(SoPickedPoint* Point, const SbVec2s& cur } // 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); + for (int t = 0; t < geometryLayerParameters.getSubLayerCount(); t++) { + if (tail == editModeScenegraphNodes.CurveSet[l][t]) { + const SoDetail* curve_detail = + Point->getDetail(editModeScenegraphNodes.CurveSet[l][t]); + if (curve_detail && curve_detail->getTypeId() == SoLineDetail::getClassTypeId()) { + // get the index + int curveIndex = static_cast(curve_detail)->getLineIndex(); + result.GeoIndex = coinMapping.getCurveGeoId(curveIndex, l, t); - return result; + return result; + } } } } @@ -1036,14 +1095,14 @@ void EditModeCoinManager::updateInventorNodeSizes() { auto layersconfiguration = viewProvider.VisualLayerList.getValues(); + updateInventorWidths(); + for (int l = 0; l < geometryLayerParameters.getCoinLayerCount(); l++) { editModeScenegraphNodes.PointsDrawStyle[l]->pointSize = 8 * drawingParameters.pixelScalingFactor; editModeScenegraphNodes.PointSet[l]->markerIndex = Gui::Inventor::MarkerBitmaps::getMarkerIndex("CIRCLE_FILLED", drawingParameters.markerSize); - editModeScenegraphNodes.CurvesDrawStyle[l]->lineWidth = - layersconfiguration[l].getLineWidth() * drawingParameters.pixelScalingFactor; } editModeScenegraphNodes.RootCrossDrawStyle->lineWidth = @@ -1064,6 +1123,29 @@ void EditModeCoinManager::updateInventorNodeSizes() pEditModeConstraintCoinManager->rebuildConstraintNodes(); } +void EditModeCoinManager::updateInventorWidths() +{ + editModeScenegraphNodes.CurvesDrawStyle->lineWidth = + drawingParameters.CurveWidth * drawingParameters.pixelScalingFactor; + editModeScenegraphNodes.CurvesConstructionDrawStyle->lineWidth = + drawingParameters.ConstructionWidth * drawingParameters.pixelScalingFactor; + editModeScenegraphNodes.CurvesInternalDrawStyle->lineWidth = + drawingParameters.InternalWidth * drawingParameters.pixelScalingFactor; + editModeScenegraphNodes.CurvesExternalDrawStyle->lineWidth = + drawingParameters.ExternalWidth * drawingParameters.pixelScalingFactor; +} + +void EditModeCoinManager::updateInventorPatterns() +{ + editModeScenegraphNodes.CurvesDrawStyle->linePattern = drawingParameters.CurvePattern; + editModeScenegraphNodes.CurvesConstructionDrawStyle->linePattern = + drawingParameters.ConstructionPattern; + editModeScenegraphNodes.CurvesInternalDrawStyle->linePattern = + drawingParameters.InternalPattern; + editModeScenegraphNodes.CurvesExternalDrawStyle->linePattern = + drawingParameters.ExternalPattern; +} + void EditModeCoinManager::updateInventorColors() { editModeScenegraphNodes.RootCrossMaterials->diffuseColor.set1Value( diff --git a/src/Mod/Sketcher/Gui/EditModeCoinManager.h b/src/Mod/Sketcher/Gui/EditModeCoinManager.h index aaf2a9b14e..338d983452 100644 --- a/src/Mod/Sketcher/Gui/EditModeCoinManager.h +++ b/src/Mod/Sketcher/Gui/EditModeCoinManager.h @@ -141,6 +141,8 @@ class SketcherGuiExport EditModeCoinManager void updateLineRenderingOrderParameters(const std::string& parametername); void updateConstraintPresentationParameters(const std::string& parametername); void updateElementSizeParameters(const std::string& parametername); + void updateWidth(int& width, const std::string& parametername, int def); + void updatePattern(unsigned int& pattern, const std::string& pname, unsigned int def); void updateColor(SbColor& sbcolor, const std::string& parametername); void updateUnit(const std::string& parametername); @@ -282,6 +284,10 @@ private: void updateInventorColors(); + void updateInventorPatterns(); + + void updateInventorWidths(); + /** @name coin nodes creation*/ void createEditModeInventorNodes(); //@} diff --git a/src/Mod/Sketcher/Gui/EditModeCoinManagerParameters.h b/src/Mod/Sketcher/Gui/EditModeCoinManagerParameters.h index a59f7766a6..19a77466e6 100644 --- a/src/Mod/Sketcher/Gui/EditModeCoinManagerParameters.h +++ b/src/Mod/Sketcher/Gui/EditModeCoinManagerParameters.h @@ -40,7 +40,6 @@ #include #include - #include #include @@ -140,6 +139,16 @@ struct DrawingParameters 17; // Font size to be used by SoDatumLabel, which uses a QPainter and a QFont internally int constraintIconSize = 15; // Size of constraint icons int markerSize = 7; // Size used for markers + + int CurveWidth = 2; // width of normal edges + int ConstructionWidth = 1; // width of construction edges + int InternalWidth = 1; // width of internal edges + int ExternalWidth = 1; // width of external edges + + unsigned int CurvePattern = 0b1111111111111111; // pattern of normal edges + unsigned int ConstructionPattern = 0b1111110011111100; // pattern of construction edges + unsigned int InternalPattern = 0b1111110011111100; // pattern of internal edges + unsigned int ExternalPattern = 0b1110010011100100; // pattern of external edges //@} }; @@ -155,9 +164,9 @@ struct GeometryLayerNodes /** @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 + std::vector>& CurvesMaterials; + std::vector>& CurvesCoordinate; + std::vector>& CurveSet; //@} }; @@ -176,9 +185,10 @@ struct GeometryLayerNodes class MultiFieldId { public: - explicit constexpr MultiFieldId(int fieldindex = -1, int layerid = 0) + explicit constexpr MultiFieldId(int fieldindex = -1, int layerid = 0, int geotypeid = 0) : fieldIndex(fieldindex) , layerId(layerid) + , geoTypeId(geotypeid) {} MultiFieldId(const MultiFieldId&) = default; @@ -189,16 +199,19 @@ public: inline bool operator==(const MultiFieldId& obj) const { - return this->fieldIndex == obj.fieldIndex && this->layerId == obj.layerId; + return this->fieldIndex == obj.fieldIndex && this->layerId == obj.layerId + && this->geoTypeId == obj.geoTypeId; } inline bool operator!=(const MultiFieldId& obj) const { - return this->fieldIndex != obj.fieldIndex || this->layerId != obj.layerId; + return this->fieldIndex != obj.fieldIndex || this->layerId != obj.layerId + || this->geoTypeId != obj.geoTypeId; } int fieldIndex = -1; int layerId = 0; + int geoTypeId = 0; static const MultiFieldId Invalid; }; @@ -255,7 +268,16 @@ private: Default = 0 }; + public: + enum class SubLayer + { + Normal = 0, + Construction = 1, + Internal = 2, + External = 3, + }; + void reset() { CoinLayers = 1; @@ -280,8 +302,46 @@ public: CoinLayers = layernumber; } + int inline getSubLayerCount() const + { + return SubLayers; + } + + int inline getSubLayerIndex(const int geoId, const Sketcher::GeometryFacade* geom) const + { + bool isConstruction = geom->getConstruction(); + bool isInternal = geom->isInternalAligned(); + bool isExternal = geoId <= Sketcher::GeoEnum::RefExt; + + return static_cast(isExternal ? SubLayer::External + : isInternal ? SubLayer::Internal + : isConstruction ? SubLayer::Construction + : SubLayer::Normal); + } + + bool isNormalSubLayer(int t) const + { + return t == static_cast(SubLayer::Normal); + } + + bool isConstructionSubLayer(int t) const + { + return t == static_cast(SubLayer::Construction); + } + + bool isInternalSubLayer(int t) const + { + return t == static_cast(SubLayer::Internal); + } + + bool isExternalSubLayer(int t) const + { + return t == static_cast(SubLayer::External); + } + private: int CoinLayers = 1; // defaults to a single Coin Geometry Layer. + int SubLayers = 4; // Normal, Construction, Internal, External. }; /** @brief Struct to hold the results of analysis performed on geometry @@ -340,10 +400,14 @@ struct EditModeScenegraphNodes /** @name Curve nodes*/ //@{ SmSwitchboard* CurvesGroup; - std::vector CurvesMaterials; - std::vector CurvesCoordinate; - std::vector CurvesDrawStyle; - std::vector CurveSet; + std::vector> CurvesMaterials; + std::vector> CurvesCoordinate; + std::vector> CurveSet; + SoDrawStyle* CurvesDrawStyle; + SoDrawStyle* CurvesConstructionDrawStyle; + SoDrawStyle* CurvesInternalDrawStyle; + SoDrawStyle* CurvesExternalDrawStyle; + SoDrawStyle* HiddenCurvesDrawStyle; //@} /** @name Axes nodes*/ @@ -395,7 +459,7 @@ struct EditModeScenegraphNodes }; /** @brief Helper struct containing index conversions (mappings) between - * {GeoId, PointPos} and MF indices per layer, and VertexId and MF indices per layer. + * {GeoId, PointPos} and MF indices per layer, sublayers, and VertexId and MF indices per layer. * * These are updated with every draw of the scenegraph. */ @@ -404,6 +468,9 @@ struct CoinMapping void clear() { + for (size_t l = 0; l < CurvIdToGeoId.size(); ++l) { + CurvIdToGeoId[l].clear(); + } CurvIdToGeoId.clear(); PointIdToGeoId.clear(); GeoElementId2SetId.clear(); @@ -412,9 +479,9 @@ struct CoinMapping /// 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) + int getCurveGeoId(int curveindex, int layerindex, int sublayerindex = 0) { - return CurvIdToGeoId[layerindex][curveindex]; + return CurvIdToGeoId[layerindex][sublayerindex][curveindex]; } /// given the MF index of a point and the coin layer in which it is drawn returns the GeoId of /// the point @@ -461,7 +528,8 @@ struct CoinMapping //* These map a MF index (second index) within a coin layer (first index) for points or curves // to a GeoId */ - std::vector> CurvIdToGeoId; // conversion of SoLineSet index to GeoId + std::vector>> + 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 diff --git a/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp index 5dc22f984a..c8a38dd933 100644 --- a/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp +++ b/src/Mod/Sketcher/Gui/EditModeConstraintCoinManager.cpp @@ -1587,14 +1587,20 @@ void EditModeConstraintCoinManager::updateConstraintColor( std::vector PtNum; std::vector pcolor; // point color - std::vector CurvNum; - std::vector color; // curve color + std::vector> CurvNum; + std::vector> color; // curve color for (int l = 0; l < geometryLayerParameters.getCoinLayerCount(); l++) { PtNum.push_back(editModeScenegraphNodes.PointsMaterials[l]->diffuseColor.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()); + CurvNum.emplace_back(); + color.emplace_back(); + for (int t = 0; t < geometryLayerParameters.getSubLayerCount(); t++) { + CurvNum[l].push_back( + editModeScenegraphNodes.CurvesMaterials[l][t]->diffuseColor.getNum()); + color[l].push_back( + editModeScenegraphNodes.CurvesMaterials[l][t]->diffuseColor.startEditing()); + } } int maxNumberOfConstraints = std::min(editModeScenegraphNodes.constrGroup->getNumChildren(), @@ -1653,9 +1659,11 @@ void EditModeConstraintCoinManager::updateConstraintColor( 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; + int sublayer = multifieldIndex.geoTypeId; + if (layer < static_cast(CurvNum.size()) + && sublayer < static_cast(CurvNum[layer].size()) && index >= 0 + && index < CurvNum[layer][sublayer]) { + color[layer][sublayer][index] = drawingParameters.SelectColor; } } } @@ -1730,7 +1738,9 @@ void EditModeConstraintCoinManager::updateConstraintColor( for (int l = 0; l < geometryLayerParameters.getCoinLayerCount(); l++) { editModeScenegraphNodes.PointsMaterials[l]->diffuseColor.finishEditing(); - editModeScenegraphNodes.CurvesMaterials[l]->diffuseColor.finishEditing(); + for (int t = 0; t < geometryLayerParameters.getSubLayerCount(); t++) { + editModeScenegraphNodes.CurvesMaterials[l][t]->diffuseColor.finishEditing(); + } } } diff --git a/src/Mod/Sketcher/Gui/EditModeGeometryCoinConverter.cpp b/src/Mod/Sketcher/Gui/EditModeGeometryCoinConverter.cpp index d1b0679788..5235a0224c 100644 --- a/src/Mod/Sketcher/Gui/EditModeGeometryCoinConverter.cpp +++ b/src/Mod/Sketcher/Gui/EditModeGeometryCoinConverter.cpp @@ -55,27 +55,30 @@ void EditModeGeometryCoinConverter::convert(const Sketcher::GeoListFacade& geoli arcGeoIds.clear(); // end information layer - Coords.clear(); Points.clear(); + Coords.clear(); Index.clear(); coinMapping.clear(); pointCounter.clear(); - curveCounter.clear(); for (auto l = 0; l < geometryLayerParameters.getCoinLayerCount(); l++) { - Coords.emplace_back(); Points.emplace_back(); + Coords.emplace_back(); Index.emplace_back(); coinMapping.CurvIdToGeoId.emplace_back(); + for (int t = 0; t < geometryLayerParameters.getSubLayerCount(); t++) { + Coords[l].emplace_back(); + Index[l].emplace_back(); + coinMapping.CurvIdToGeoId[l].emplace_back(); + } coinMapping.PointIdToGeoId.emplace_back(); coinMapping.PointIdToVertexId.emplace_back(); } pointCounter.resize(geometryLayerParameters.getCoinLayerCount(), 0); - curveCounter.resize(geometryLayerParameters.getCoinLayerCount(), 0); // RootPoint // TODO: RootPoint is here added in layer0. However, this layer may be hidden. The point should, @@ -83,8 +86,8 @@ void EditModeGeometryCoinConverter::convert(const Sketcher::GeoListFacade& geoli // empty layer. Points[0].emplace_back(0., 0., 0.); coinMapping.PointIdToGeoId[0].push_back(-1); // root point - coinMapping.PointIdToVertexId[0].push_back( - -1); // VertexId is the reference used for point selection/preselection + coinMapping.PointIdToVertexId[0].push_back(-1); + // VertexId is the reference used for point selection/preselection coinMapping.GeoElementId2SetId.emplace(std::piecewise_construct, std::forward_as_tuple(Sketcher::GeoElementId::RtPnt), @@ -93,7 +96,8 @@ void EditModeGeometryCoinConverter::convert(const Sketcher::GeoListFacade& geoli auto setTracking = [this](int geoId, int coinLayer, EditModeGeometryCoinConverter::PointsMode pointmode, - int numberCurves) { + int numberCurves, + int sublayer) { int numberPoints = 0; if (pointmode == PointsMode::InsertSingle) { @@ -153,12 +157,14 @@ void EditModeGeometryCoinConverter::convert(const Sketcher::GeoListFacade& geoli 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)); + std::forward_as_tuple( + static_cast(coinMapping.CurvIdToGeoId[coinLayer][sublayer].size()), + coinLayer, + sublayer)); } for (int i = 0; i < numberCurves; i++) { - coinMapping.CurvIdToGeoId[coinLayer].push_back(geoId); + coinMapping.CurvIdToGeoId[coinLayer][sublayer].push_back(geoId); } }; @@ -168,7 +174,8 @@ void EditModeGeometryCoinConverter::convert(const Sketcher::GeoListFacade& geoli const auto geom = geolistfacade.getGeometryFacadeFromGeoId(GeoId); const auto type = geom->getGeometry()->getTypeId(); - auto layerId = getSafeGeomLayerId(geom); + int layerId = getSafeGeomLayerId(geom); + int subLayerId = geometryLayerParameters.getSubLayerIndex(GeoId, geom); auto coinLayer = geometryLayerParameters.getSafeCoinLayer(layerId); @@ -176,43 +183,55 @@ void EditModeGeometryCoinConverter::convert(const Sketcher::GeoListFacade& geoli convert(geom, GeoId); + EditModeGeometryCoinConverter::AnalyseMode::BoundingBoxMagnitude>(geom, + GeoId, + subLayerId); setTracking(GeoId, coinLayer, EditModeGeometryCoinConverter::PointsMode::InsertSingle, - 0); + 0, + subLayerId); } else if (type == Part::GeomLineSegment::getClassTypeId()) { // add a line convert(geom, GeoId); + EditModeGeometryCoinConverter::AnalyseMode::BoundingBoxMagnitude>(geom, + GeoId, + subLayerId); setTracking(GeoId, coinLayer, EditModeGeometryCoinConverter::PointsMode::InsertStartEnd, - 1); + 1, + subLayerId); } else if (type.isDerivedFrom( Part::GeomConic::getClassTypeId())) { // add a closed curve conic convert(geom, GeoId); + EditModeGeometryCoinConverter::AnalyseMode::BoundingBoxMagnitude>(geom, + GeoId, + subLayerId); setTracking(GeoId, coinLayer, EditModeGeometryCoinConverter::PointsMode::InsertMidOnly, - 1); + 1, + subLayerId); } else if (type.isDerivedFrom( Part::GeomArcOfConic::getClassTypeId())) { // add an arc of conic convert(geom, GeoId); + EditModeGeometryCoinConverter::AnalyseMode::BoundingBoxMagnitude>(geom, + GeoId, + subLayerId); setTracking(GeoId, coinLayer, EditModeGeometryCoinConverter::PointsMode::InsertStartEndMid, - 1); + 1, + subLayerId); arcGeoIds.push_back(GeoId); } else if (type == Part::GeomBSplineCurve::getClassTypeId()) { // add a bspline (a bounded @@ -221,58 +240,53 @@ void EditModeGeometryCoinConverter::convert(const Sketcher::GeoListFacade& geoli EditModeGeometryCoinConverter::PointsMode::InsertStartEnd, EditModeGeometryCoinConverter::CurveMode::OpenCurve, EditModeGeometryCoinConverter::AnalyseMode:: - BoundingBoxMagnitudeAndBSplineCurvature>(geom, GeoId); + BoundingBoxMagnitudeAndBSplineCurvature>(geom, GeoId, subLayerId); setTracking(GeoId, coinLayer, EditModeGeometryCoinConverter::PointsMode::InsertStartEnd, - 1); + 1, + subLayerId); bsplineGeoIds.push_back(GeoId); } } - for (auto l = 0; l < geometryLayerParameters.getCoinLayerCount(); l++) { + // Coin Nodes Editing + int vOrFactor = ViewProviderSketchCoinAttorney::getViewOrientationFactor(viewProvider); + double linez = vOrFactor * drawingParameters.zLowLines; // NOLINT + double pointz = vOrFactor * drawingParameters.zLowPoints; - // 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()); + for (auto l = 0; l < geometryLayerParameters.getCoinLayerCount(); l++) { 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, - ViewProviderSketchCoinAttorney::getViewOrientationFactor(viewProvider) - * drawingParameters.zLowLines); + int i = 0; // setting up the point set + for (auto& point : Points[l]) { + pverts[i++].setValue(point.x, point.y, pointz); } - - 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, - ViewProviderSketchCoinAttorney::getViewOrientationFactor(viewProvider) - * drawingParameters.zLowPoints); - } - - geometryLayerNodes.CurvesCoordinate[l]->point.finishEditing(); - geometryLayerNodes.CurveSet[l]->numVertices.finishEditing(); geometryLayerNodes.PointsCoordinate[l]->point.finishEditing(); + + for (auto t = 0; t < geometryLayerParameters.getSubLayerCount(); t++) { + geometryLayerNodes.CurvesCoordinate[l][t]->point.setNum(Coords[l][t].size()); + geometryLayerNodes.CurveSet[l][t]->numVertices.setNum(Index[l][t].size()); + geometryLayerNodes.CurvesMaterials[l][t]->diffuseColor.setNum(Index[l][t].size()); + + SbVec3f* verts = geometryLayerNodes.CurvesCoordinate[l][t]->point.startEditing(); + int32_t* index = geometryLayerNodes.CurveSet[l][t]->numVertices.startEditing(); + + i = 0; // setting up the line set + for (auto& coord : Coords[l][t]) { + verts[i++].setValue(coord.x, coord.y, linez); // NOLINT + } + + i = 0; // setting up the indexes of the line set + for (auto it : Index[l][t]) { + index[i++] = it; + } + + geometryLayerNodes.CurvesCoordinate[l][t]->point.finishEditing(); + geometryLayerNodes.CurveSet[l][t]->numVertices.finishEditing(); + } } } @@ -281,7 +295,8 @@ template void EditModeGeometryCoinConverter::convert(const Sketcher::GeometryFacade* geometryfacade, - [[maybe_unused]] int geoid) + [[maybe_unused]] int geoid, + int subLayer) { auto geo = static_cast(geometryfacade->getGeometry()); auto layerId = getSafeGeomLayerId(geometryfacade); @@ -317,9 +332,9 @@ void EditModeGeometryCoinConverter::convert(const Sketcher::GeometryFacade* geom // Curves if constexpr (curvemode == CurveMode::StartEndPointsOnly) { - addPoint(Coords[coinLayer], geo->getStartPoint()); - addPoint(Coords[coinLayer], geo->getEndPoint()); - Index[coinLayer].push_back(2); + addPoint(Coords[coinLayer][subLayer], geo->getStartPoint()); + addPoint(Coords[coinLayer][subLayer], geo->getEndPoint()); + Index[coinLayer][subLayer].push_back(2); } else if constexpr (curvemode == CurveMode::ClosedCurve) { int numSegments = drawingParameters.curvedEdgeCountSegments; @@ -331,13 +346,13 @@ void EditModeGeometryCoinConverter::convert(const Sketcher::GeometryFacade* geom for (int i = 0; i < numSegments; i++) { Base::Vector3d pnt = geo->value(i * segment); - addPoint(Coords[coinLayer], pnt); + addPoint(Coords[coinLayer][subLayer], pnt); } Base::Vector3d pnt = geo->value(0); - addPoint(Coords[coinLayer], pnt); + addPoint(Coords[coinLayer][subLayer], pnt); - Index[coinLayer].push_back(numSegments + 1); + Index[coinLayer][subLayer].push_back(numSegments + 1); } else if constexpr (curvemode == CurveMode::OpenCurve) { int numSegments = drawingParameters.curvedEdgeCountSegments; @@ -349,13 +364,13 @@ void EditModeGeometryCoinConverter::convert(const Sketcher::GeometryFacade* geom for (int i = 0; i < numSegments; i++) { Base::Vector3d pnt = geo->value(geo->getFirstParameter() + i * segment); - addPoint(Coords[coinLayer], pnt); + addPoint(Coords[coinLayer][subLayer], pnt); } Base::Vector3d pnt = geo->value(geo->getLastParameter()); - addPoint(Coords[coinLayer], pnt); + addPoint(Coords[coinLayer][subLayer], pnt); - Index[coinLayer].push_back(numSegments + 1); + Index[coinLayer][subLayer].push_back(numSegments + 1); if constexpr (analysemode == AnalyseMode::BoundingBoxMagnitudeAndBSplineCurvature) { //*************************************************************************************************************** diff --git a/src/Mod/Sketcher/Gui/EditModeGeometryCoinConverter.h b/src/Mod/Sketcher/Gui/EditModeGeometryCoinConverter.h index 950635138a..889b5b0a2e 100644 --- a/src/Mod/Sketcher/Gui/EditModeGeometryCoinConverter.h +++ b/src/Mod/Sketcher/Gui/EditModeGeometryCoinConverter.h @@ -152,7 +152,9 @@ public: private: template - void convert(const Sketcher::GeometryFacade* geometryfacade, [[maybe_unused]] int geoId); + void convert(const Sketcher::GeometryFacade* geometryfacade, + [[maybe_unused]] int geoId, + int subLayerId = 0); private: /// Reference to ViewProviderSketch in order to access the public and the Attorney Interface @@ -160,13 +162,12 @@ private: GeometryLayerNodes& geometryLayerNodes; - std::vector> Coords; std::vector> Points; - std::vector> Index; + std::vector>> Coords; + std::vector>> Index; // temporal counters, one per layer std::vector pointCounter; - std::vector curveCounter; // temporal global vertex counter int vertexCounter = 0; diff --git a/src/Mod/Sketcher/Gui/EditModeGeometryCoinManager.cpp b/src/Mod/Sketcher/Gui/EditModeGeometryCoinManager.cpp index 120e8dd352..8c6b030a34 100644 --- a/src/Mod/Sketcher/Gui/EditModeGeometryCoinManager.cpp +++ b/src/Mod/Sketcher/Gui/EditModeGeometryCoinManager.cpp @@ -73,19 +73,21 @@ void EditModeGeometryCoinManager::processGeometry(const GeoListFacade& geolistfa { // enable all layers editModeScenegraphNodes.PointsGroup->enable.setNum(geometryLayerParameters.getCoinLayerCount()); - editModeScenegraphNodes.CurvesGroup->enable.setNum(geometryLayerParameters.getCoinLayerCount()); + editModeScenegraphNodes.CurvesGroup->enable.setNum( + geometryLayerParameters.getCoinLayerCount() * geometryLayerParameters.getSubLayerCount()); SbBool* swsp = editModeScenegraphNodes.PointsGroup->enable.startEditing(); SbBool* swsc = editModeScenegraphNodes.CurvesGroup->enable.startEditing(); - auto setEnableLayer = [swsp, swsc](int l, bool enabled) { - swsp[l] = enabled; // layer defaults to enabled - swsc[l] = enabled; // layer defaults to enabled - }; - auto layersconfigurations = viewProvider.VisualLayerList.getValues(); for (auto l = 0; l < geometryLayerParameters.getCoinLayerCount(); l++) { - setEnableLayer(l, layersconfigurations[l].isVisible()); + auto enabled = layersconfigurations[l].isVisible(); + + swsp[l] = enabled; + int slCount = geometryLayerParameters.getSubLayerCount(); + for (int t = 0; t < slCount; t++) { + swsc[l * slCount + t] = enabled; + } } editModeScenegraphNodes.PointsGroup->enable.finishEditing(); @@ -122,14 +124,6 @@ void EditModeGeometryCoinManager::updateGeometryColor(const GeoListFacade& geoli 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) { @@ -162,6 +156,9 @@ void EditModeGeometryCoinManager::updateGeometryColor(const GeoListFacade& geoli return false; }; + bool sketchFullyConstrained = + ViewProviderSketchCoinAttorney::isSketchFullyConstrained(viewProvider); + // Update Colors SbColor* crosscolor = editModeScenegraphNodes.RootCrossMaterials->diffuseColor.startEditing(); @@ -169,17 +166,11 @@ void EditModeGeometryCoinManager::updateGeometryColor(const GeoListFacade& geoli ViewProviderSketchCoinAttorney::getViewOrientationFactor(viewProvider); for (auto l = 0; l < geometryLayerParameters.getCoinLayerCount(); l++) { - + float x, y, z; int PtNum = editModeScenegraphNodes.PointsMaterials[l]->diffuseColor.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++) { @@ -189,9 +180,7 @@ void EditModeGeometryCoinManager::updateGeometryColor(const GeoListFacade& geoli else { for (int i = 0; i < PtNum; i++) { - if (!(i == 0 && l == 0) - && ViewProviderSketchCoinAttorney::isSketchFullyConstrained( - viewProvider)) { // root point is not coloured + if (!(i == 0 && l == 0) && sketchFullyConstrained) { // root point is not coloured pcolor[i] = drawingParameters.FullyConstrainedColor; } else { @@ -345,98 +334,99 @@ void EditModeGeometryCoinManager::updateGeometryColor(const GeoListFacade& geoli drawingParameters.zMidLines, drawingParameters.zLowLines); - int j = 0; // vertexindex + for (auto t = 0; t < geometryLayerParameters.getSubLayerCount(); t++) { + int CurvNum = editModeScenegraphNodes.CurvesMaterials[l][t]->diffuseColor.getNum(); + SbColor* color = + editModeScenegraphNodes.CurvesMaterials[l][t]->diffuseColor.startEditing(); + SbVec3f* verts = editModeScenegraphNodes.CurvesCoordinate[l][t]->point.startEditing(); - 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]); + int j = 0; // vertexindex + for (int i = 0; i < CurvNum; i++) { + int GeoId = coinMapping.getCurveGeoId(i, l, t); + // CurvId has several vertices associated to 1 material + // edit->CurveSet->numVertices => [i] indicates number of vertex for line i. + int indexes = (editModeScenegraphNodes.CurveSet[l][t]->numVertices[i]); - bool selected = ViewProviderSketchCoinAttorney::isCurveSelected(viewProvider, GeoId); - bool preselected = (preselectcurve == GeoId); + bool selected = + ViewProviderSketchCoinAttorney::isCurveSelected(viewProvider, GeoId); + bool preselected = (preselectcurve == GeoId); + bool constrainedElement = isFullyConstraintElement(GeoId); - bool constrainedElement = isFullyConstraintElement(GeoId); + if (selected || preselected) { + color[i] = selected ? (preselected ? drawingParameters.PreselectSelectedColor + : drawingParameters.SelectColor) + : drawingParameters.PreselectColor; - if (selected && preselected) { - color[i] = drawingParameters.PreselectSelectedColor; - for (int k = j; j < k + indexes; j++) { - verts[j].getValue(x, y, z); - verts[j] = SbVec3f(x, y, viewOrientationFactor * drawingParameters.zHighLine); - } - } - else if (selected) { - color[i] = drawingParameters.SelectColor; - for (int k = j; j < k + indexes; j++) { - verts[j].getValue(x, y, z); - verts[j] = SbVec3f(x, y, viewOrientationFactor * drawingParameters.zHighLine); - } - } - else if (preselected) { - color[i] = drawingParameters.PreselectColor; - for (int k = j; j < k + indexes; j++) { - verts[j].getValue(x, y, z); - verts[j] = SbVec3f(x, y, viewOrientationFactor * drawingParameters.zHighLine); - } - } - else if (GeoId <= Sketcher::GeoEnum::RefExt) { // external Geometry - color[i] = drawingParameters.CurveExternalColor; - for (int k = j; j < k + indexes; j++) { - verts[j].getValue(x, y, z); - verts[j] = SbVec3f(x, y, viewOrientationFactor * zExtLine); - } - } - else if (issketchinvalid) { - color[i] = drawingParameters.InvalidSketchColor; - for (int k = j; j < k + indexes; j++) { - verts[j].getValue(x, y, z); - verts[j] = SbVec3f(x, y, viewOrientationFactor * zNormLine); - } - } - else if (isConstructionGeom(GeoId)) { - if (isInternalAlignedGeom(GeoId)) { - if (constrainedElement) { - color[i] = drawingParameters.FullyConstraintInternalAlignmentColor; + for (int k = j; j < k + indexes; j++) { + verts[j].getValue(x, y, z); + verts[j] = + SbVec3f(x, y, viewOrientationFactor * drawingParameters.zHighLine); } - else { - color[i] = drawingParameters.InternalAlignedGeoColor; + } + else if (geometryLayerParameters.isExternalSubLayer(t)) { + color[i] = drawingParameters.CurveExternalColor; + for (int k = j; j < k + indexes; j++) { + verts[j].getValue(x, y, z); + verts[j] = SbVec3f(x, y, viewOrientationFactor * zExtLine); } } else { - if (constrainedElement) { - color[i] = drawingParameters.FullyConstraintConstructionElementColor; + if (issketchinvalid) { + color[i] = drawingParameters.InvalidSketchColor; + + for (int k = j; j < k + indexes; j++) { + verts[j].getValue(x, y, z); + verts[j] = SbVec3f(x, y, viewOrientationFactor * zNormLine); + } + } + else if (geometryLayerParameters.isConstructionSubLayer(t)) { + if (constrainedElement) { + color[i] = drawingParameters.FullyConstraintConstructionElementColor; + } + else { + color[i] = drawingParameters.CurveDraftColor; + } + + for (int k = j; j < k + indexes; j++) { + verts[j].getValue(x, y, z); + verts[j] = SbVec3f(x, y, viewOrientationFactor * zConstrLine); + } + } + else if (geometryLayerParameters.isInternalSubLayer(t)) { + if (constrainedElement) { + color[i] = drawingParameters.FullyConstraintInternalAlignmentColor; + } + else { + color[i] = drawingParameters.InternalAlignedGeoColor; + } + + for (int k = j; j < k + indexes; j++) { + verts[j].getValue(x, y, z); + verts[j] = SbVec3f(x, y, viewOrientationFactor * zConstrLine); + } } else { - color[i] = drawingParameters.CurveDraftColor; + if (sketchFullyConstrained) { + color[i] = drawingParameters.FullyConstrainedColor; + } + else if (constrainedElement) { + color[i] = drawingParameters.FullyConstraintElementColor; + } + else { + color[i] = drawingParameters.CurveColor; + } + + for (int k = j; j < k + indexes; j++) { + verts[j].getValue(x, y, z); + verts[j] = SbVec3f(x, y, viewOrientationFactor * zNormLine); + } } } + } - for (int k = j; j < k + indexes; j++) { - verts[j].getValue(x, y, z); - verts[j] = SbVec3f(x, y, viewOrientationFactor * zConstrLine); - } - } - else if (ViewProviderSketchCoinAttorney::isSketchFullyConstrained(viewProvider)) { - color[i] = drawingParameters.FullyConstrainedColor; - for (int k = j; j < k + indexes; j++) { - verts[j].getValue(x, y, z); - verts[j] = SbVec3f(x, y, viewOrientationFactor * zNormLine); - } - } - else if (isFullyConstraintElement(GeoId)) { - color[i] = drawingParameters.FullyConstraintElementColor; - for (int k = j; j < k + indexes; j++) { - verts[j].getValue(x, y, z); - verts[j] = SbVec3f(x, y, viewOrientationFactor * zNormLine); - } - } - else { - color[i] = drawingParameters.CurveColor; - for (int k = j; j < k + indexes; j++) { - verts[j].getValue(x, y, z); - verts[j] = SbVec3f(x, y, viewOrientationFactor * zNormLine); - } - } + editModeScenegraphNodes.CurvesMaterials[l][t]->diffuseColor.finishEditing(); + editModeScenegraphNodes.CurvesCoordinate[l][t]->point.finishEditing(); + editModeScenegraphNodes.CurveSet[l][t]->numVertices.finishEditing(); } // colors of the cross @@ -464,11 +454,7 @@ void EditModeGeometryCoinManager::updateGeometryColor(const GeoListFacade& geoli } } - // end editing - editModeScenegraphNodes.CurvesMaterials[l]->diffuseColor.finishEditing(); editModeScenegraphNodes.PointsMaterials[l]->diffuseColor.finishEditing(); - editModeScenegraphNodes.CurvesCoordinate[l]->point.finishEditing(); - editModeScenegraphNodes.CurveSet[l]->numVertices.finishEditing(); } editModeScenegraphNodes.RootCrossMaterials->diffuseColor.finishEditing(); @@ -565,45 +551,80 @@ void EditModeGeometryCoinManager::createEditModePointInventorNodes() void EditModeGeometryCoinManager::createEditModeCurveInventorNodes() { - auto layersconfigurations = viewProvider.VisualLayerList.getValue(); + editModeScenegraphNodes.CurvesDrawStyle = new SoDrawStyle; + editModeScenegraphNodes.CurvesDrawStyle->setName("CurvesDrawStyle"); + editModeScenegraphNodes.CurvesDrawStyle->lineWidth = + drawingParameters.CurveWidth * drawingParameters.pixelScalingFactor; + editModeScenegraphNodes.CurvesDrawStyle->linePattern = drawingParameters.CurvePattern; + editModeScenegraphNodes.CurvesDrawStyle->linePatternScaleFactor = 2; + + editModeScenegraphNodes.CurvesConstructionDrawStyle = new SoDrawStyle; + editModeScenegraphNodes.CurvesConstructionDrawStyle->setName("CurvesConstructionDrawStyle"); + editModeScenegraphNodes.CurvesConstructionDrawStyle->lineWidth = + drawingParameters.ConstructionWidth * drawingParameters.pixelScalingFactor; + editModeScenegraphNodes.CurvesConstructionDrawStyle->linePattern = + drawingParameters.ConstructionPattern; + editModeScenegraphNodes.CurvesConstructionDrawStyle->linePatternScaleFactor = 2; + + editModeScenegraphNodes.CurvesInternalDrawStyle = new SoDrawStyle; + editModeScenegraphNodes.CurvesInternalDrawStyle->setName("CurvesInternalDrawStyle"); + editModeScenegraphNodes.CurvesInternalDrawStyle->lineWidth = + drawingParameters.InternalWidth * drawingParameters.pixelScalingFactor; + editModeScenegraphNodes.CurvesInternalDrawStyle->linePattern = + drawingParameters.InternalPattern; + editModeScenegraphNodes.CurvesInternalDrawStyle->linePatternScaleFactor = 2; + + editModeScenegraphNodes.CurvesExternalDrawStyle = new SoDrawStyle; + editModeScenegraphNodes.CurvesExternalDrawStyle->setName("CurvesExternalDrawStyle"); + editModeScenegraphNodes.CurvesExternalDrawStyle->lineWidth = + drawingParameters.ExternalWidth * drawingParameters.pixelScalingFactor; + editModeScenegraphNodes.CurvesExternalDrawStyle->linePattern = + drawingParameters.ExternalPattern; + editModeScenegraphNodes.CurvesExternalDrawStyle->linePatternScaleFactor = 2; for (int i = 0; i < geometryLayerParameters.getCoinLayerCount(); i++) { - SoSeparator* sep = new SoSeparator; - sep->ref(); + editModeScenegraphNodes.CurvesMaterials.emplace_back(); + editModeScenegraphNodes.CurvesCoordinate.emplace_back(); + editModeScenegraphNodes.CurveSet.emplace_back(); + for (int t = 0; t < geometryLayerParameters.getSubLayerCount(); t++) { + 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 somaterial = new SoMaterial; + somaterial->setName(concat("CurvesMaterials", i * 10 + t).c_str()); + editModeScenegraphNodes.CurvesMaterials[i].push_back(somaterial); + sep->addChild(editModeScenegraphNodes.CurvesMaterials[i][t]); - auto MtlBind = new SoMaterialBinding; - MtlBind->setName(concat("CurvesMaterialsBinding", i).c_str()); - MtlBind->value = SoMaterialBinding::PER_FACE; - sep->addChild(MtlBind); + auto MtlBind = new SoMaterialBinding; + MtlBind->setName(concat("CurvesMaterialsBinding", i * 10 + t).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 coords = new SoCoordinate3; + coords->setName(concat("CurvesCoordinate", i * 10 + t).c_str()); + editModeScenegraphNodes.CurvesCoordinate[i].push_back(coords); + sep->addChild(editModeScenegraphNodes.CurvesCoordinate[i][t]); - auto drawstyle = new SoDrawStyle; - editModeScenegraphNodes.CurvesDrawStyle.push_back(drawstyle); - editModeScenegraphNodes.CurvesDrawStyle[i]->setName(concat("CurvesDrawStyle", i).c_str()); + if (geometryLayerParameters.isConstructionSubLayer(t)) { + sep->addChild(editModeScenegraphNodes.CurvesConstructionDrawStyle); + } + else if (geometryLayerParameters.isInternalSubLayer(t)) { + sep->addChild(editModeScenegraphNodes.CurvesInternalDrawStyle); + } + else if (geometryLayerParameters.isExternalSubLayer(t)) { + sep->addChild(editModeScenegraphNodes.CurvesExternalDrawStyle); + } + else { + sep->addChild(editModeScenegraphNodes.CurvesDrawStyle); + } - editModeScenegraphNodes.CurvesDrawStyle[i]->lineWidth = - layersconfigurations[i].getLineWidth() * drawingParameters.pixelScalingFactor; - editModeScenegraphNodes.CurvesDrawStyle[i]->linePattern = - layersconfigurations[i].getLinePattern(); - editModeScenegraphNodes.CurvesDrawStyle[i]->linePatternScaleFactor = 5; + auto solineset = new SoLineSet; + solineset->setName(concat("CurvesLineSet", i * 10 + t).c_str()); + editModeScenegraphNodes.CurveSet[i].push_back(solineset); + sep->addChild(editModeScenegraphNodes.CurveSet[i][t]); - 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(); + editModeScenegraphNodes.CurvesGroup->addChild(sep); + sep->unref(); + } } } diff --git a/src/Mod/Sketcher/Gui/SketcherSettings.cpp b/src/Mod/Sketcher/Gui/SketcherSettings.cpp index a316235e49..5c30e5fced 100644 --- a/src/Mod/Sketcher/Gui/SketcherSettings.cpp +++ b/src/Mod/Sketcher/Gui/SketcherSettings.cpp @@ -34,7 +34,7 @@ #include "SketcherSettings.h" #include "ui_SketcherSettings.h" -#include "ui_SketcherSettingsColors.h" +#include "ui_SketcherSettingsAppearance.h" #include "ui_SketcherSettingsDisplay.h" #include "ui_SketcherSettingsGrid.h" @@ -43,6 +43,49 @@ using namespace SketcherGui; /* TRANSLATOR SketcherGui::SketcherSettings */ +QList getPenStyles() +{ + QList styles; + styles << 0b1111111111111111 // solid + << 0b1110111011101110 // dashed 3:1 + << 0b1111110011111100 // dashed 6:2 + << 0b0000111100001111 // dashed 4:4 + << 0b1010101010101010 // point 1:1 + << 0b1110010011100100 // dash point + << 0b1111111100111100; // dash long-dash + return styles; +} + +const QVector binaryPatternToDashPattern(int binaryPattern) +{ + QVector dashPattern; + int count = 0; + bool isDash = (binaryPattern & 0x8000) != 0; // Check the highest bit + + for (int i = 0; i < 16; ++i) { + bool currentBit = (binaryPattern & (0x8000 >> i)) != 0; + if (currentBit == isDash) { + ++count; // Counting dashes or spaces + } + else { + // Adjust count to be odd for dashes and even for spaces (see qt doc) + count = (count % 2 == (isDash ? 0 : 1)) ? count + 1 : count; + dashPattern << count; + count = 1; // Reset count for next dash/space + isDash = !isDash; + } + } + count = (count % 2 == (isDash ? 0 : 1)) ? count + 1 : count; + dashPattern << count; // Add the last count + + if ((dashPattern.size() % 2) == 1) { + // prevent this error : qWarning("QPen::setDashPattern: Pattern not of even length"); + dashPattern << 1; + } + + return dashPattern; +} + SketcherSettings::SketcherSettings(QWidget* parent) : PreferencePage(parent) , ui(new Ui_SketcherSettings) @@ -206,17 +249,17 @@ SketcherSettingsGrid::SketcherSettingsGrid(QWidget* parent) { ui->setupUi(this); - QList> styles; - styles << qMakePair(Qt::SolidLine, 0xffff) << qMakePair(Qt::DashLine, 0x0f0f) - << qMakePair(Qt::DotLine, 0xaaaa); + QList styles = getPenStyles(); ui->gridLinePattern->setIconSize(QSize(80, 12)); ui->gridDivLinePattern->setIconSize(QSize(80, 12)); - for (QList>::iterator it = styles.begin(); it != styles.end(); ++it) { + for (auto& style : styles) { QPixmap px(ui->gridLinePattern->iconSize()); px.fill(Qt::transparent); QBrush brush(Qt::black); - QPen pen(it->first); + + QPen pen; + pen.setDashPattern(binaryPatternToDashPattern(style)); pen.setBrush(brush); pen.setWidth(2); @@ -226,8 +269,8 @@ SketcherSettingsGrid::SketcherSettingsGrid(QWidget* parent) painter.drawLine(0, mid, ui->gridLinePattern->iconSize().width(), mid); painter.end(); - ui->gridLinePattern->addItem(QIcon(px), QString(), QVariant(it->second)); - ui->gridDivLinePattern->addItem(QIcon(px), QString(), QVariant(it->second)); + ui->gridLinePattern->addItem(QIcon(px), QString(), QVariant(style)); + ui->gridDivLinePattern->addItem(QIcon(px), QString(), QVariant(style)); } } @@ -273,13 +316,13 @@ void SketcherSettingsGrid::loadSettings() ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( "User parameter:BaseApp/Preferences/Mod/Sketcher/General"); - int pattern = hGrp->GetInt("GridLinePattern", 0x0f0f); + int pattern = hGrp->GetInt("GridLinePattern", 0b0000111100001111); int index = ui->gridLinePattern->findData(QVariant(pattern)); if (index < 0) { index = 1; } ui->gridLinePattern->setCurrentIndex(index); - pattern = hGrp->GetInt("GridDivLinePattern", 0xffff); + pattern = hGrp->GetInt("GridDivLinePattern", 0b1111111111111111); index = ui->gridDivLinePattern->findData(QVariant(pattern)); if (index < 0) { index = 0; @@ -412,24 +455,51 @@ void SketcherSettingsDisplay::onBtnTVApplyClicked(bool) } -/* TRANSLATOR SketcherGui::SketcherSettingsColors */ +/* TRANSLATOR SketcherGui::SketcherSettingsAppearance */ -SketcherSettingsColors::SketcherSettingsColors(QWidget* parent) +SketcherSettingsAppearance::SketcherSettingsAppearance(QWidget* parent) : PreferencePage(parent) - , ui(new Ui_SketcherSettingsColors) + , ui(new Ui_SketcherSettingsAppearance) { ui->setupUi(this); + + QList styles = getPenStyles(); + + ui->EdgePattern->setIconSize(QSize(70, 12)); + ui->ConstructionPattern->setIconSize(QSize(70, 12)); + ui->InternalPattern->setIconSize(QSize(70, 12)); + ui->ExternalPattern->setIconSize(QSize(70, 12)); + for (auto& style : styles) { + QPixmap px(ui->EdgePattern->iconSize()); + px.fill(Qt::transparent); + QBrush brush(Qt::black); + QPen pen; + pen.setDashPattern(binaryPatternToDashPattern(style)); + pen.setBrush(brush); + pen.setWidth(2); + + QPainter painter(&px); + painter.setPen(pen); + double mid = ui->EdgePattern->iconSize().height() / 2.0; + painter.drawLine(0, mid, ui->EdgePattern->iconSize().width(), mid); + painter.end(); + + ui->EdgePattern->addItem(QIcon(px), QString(), QVariant(style)); + ui->ConstructionPattern->addItem(QIcon(px), QString(), QVariant(style)); + ui->InternalPattern->addItem(QIcon(px), QString(), QVariant(style)); + ui->ExternalPattern->addItem(QIcon(px), QString(), QVariant(style)); + } } /** * Destroys the object and frees any allocated resources */ -SketcherSettingsColors::~SketcherSettingsColors() +SketcherSettingsAppearance::~SketcherSettingsAppearance() { // no need to delete child widgets, Qt does it all for us } -void SketcherSettingsColors::saveSettings() +void SketcherSettingsAppearance::saveSettings() { // Sketcher ui->SketchEdgeColor->onSave(); @@ -455,9 +525,32 @@ void SketcherSettingsColors::saveSettings() ui->CursorTextColor->onSave(); ui->CursorCrosshairColor->onSave(); ui->CreateLineColor->onSave(); + + ui->EdgeWidth->onSave(); + ui->ConstructionWidth->onSave(); + ui->InternalWidth->onSave(); + ui->ExternalWidth->onSave(); + + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/Sketcher/View"); + QVariant data = ui->EdgePattern->itemData(ui->EdgePattern->currentIndex()); + int pattern = data.toInt(); + hGrp->SetInt("EdgePattern", pattern); + + data = ui->ConstructionPattern->itemData(ui->ConstructionPattern->currentIndex()); + pattern = data.toInt(); + hGrp->SetInt("ConstructionPattern", pattern); + + data = ui->InternalPattern->itemData(ui->InternalPattern->currentIndex()); + pattern = data.toInt(); + hGrp->SetInt("InternalPattern", pattern); + + data = ui->ExternalPattern->itemData(ui->ExternalPattern->currentIndex()); + pattern = data.toInt(); + hGrp->SetInt("ExternalPattern", pattern); } -void SketcherSettingsColors::loadSettings() +void SketcherSettingsAppearance::loadSettings() { // Sketcher ui->SketchEdgeColor->onRestore(); @@ -483,12 +576,47 @@ void SketcherSettingsColors::loadSettings() ui->CursorTextColor->onRestore(); ui->CursorCrosshairColor->onRestore(); ui->CreateLineColor->onRestore(); + + ui->EdgeWidth->onRestore(); + ui->ConstructionWidth->onRestore(); + ui->InternalWidth->onRestore(); + ui->ExternalWidth->onRestore(); + + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/Sketcher/View"); + int pattern = hGrp->GetInt("EdgePattern", 0b1111111111111111); + int index = ui->EdgePattern->findData(QVariant(pattern)); + if (index < 0) { + index = 0; + } + ui->EdgePattern->setCurrentIndex(index); + + pattern = hGrp->GetInt("ConstructionPattern", 0b1111110011111100); + index = ui->ConstructionPattern->findData(QVariant(pattern)); + if (index < 0) { + index = 0; + } + ui->ConstructionPattern->setCurrentIndex(index); + + pattern = hGrp->GetInt("InternalPattern", 0b1111110011111100); + index = ui->InternalPattern->findData(QVariant(pattern)); + if (index < 0) { + index = 0; + } + ui->InternalPattern->setCurrentIndex(index); + + pattern = hGrp->GetInt("ExternalPattern", 0b1110010011100100); + index = ui->ExternalPattern->findData(QVariant(pattern)); + if (index < 0) { + index = 0; + } + ui->ExternalPattern->setCurrentIndex(index); } /** * Sets the strings of the subwidgets using the current language. */ -void SketcherSettingsColors::changeEvent(QEvent* e) +void SketcherSettingsAppearance::changeEvent(QEvent* e) { if (e->type() == QEvent::LanguageChange) { ui->retranslateUi(this); diff --git a/src/Mod/Sketcher/Gui/SketcherSettings.h b/src/Mod/Sketcher/Gui/SketcherSettings.h index 1d3f52effc..ddc8261635 100644 --- a/src/Mod/Sketcher/Gui/SketcherSettings.h +++ b/src/Mod/Sketcher/Gui/SketcherSettings.h @@ -32,7 +32,7 @@ namespace SketcherGui class Ui_SketcherSettings; class Ui_SketcherSettingsGrid; class Ui_SketcherSettingsDisplay; -class Ui_SketcherSettingsColors; +class Ui_SketcherSettingsAppearance; class SketcherGeneralWidget; /** * The SketcherSettings class implements a preference page to change sketcher settings. @@ -107,13 +107,13 @@ private: * The SketcherSettings class implements a preference page to change sketcher settings. * @author Werner Mayer */ -class SketcherSettingsColors: public Gui::Dialog::PreferencePage +class SketcherSettingsAppearance: public Gui::Dialog::PreferencePage { Q_OBJECT public: - explicit SketcherSettingsColors(QWidget* parent = nullptr); - ~SketcherSettingsColors() override; + explicit SketcherSettingsAppearance(QWidget* parent = nullptr); + ~SketcherSettingsAppearance() override; void saveSettings() override; void loadSettings() override; @@ -122,7 +122,7 @@ protected: void changeEvent(QEvent* e) override; private: - std::unique_ptr ui; + std::unique_ptr ui; }; } // namespace SketcherGui diff --git a/src/Mod/Sketcher/Gui/SketcherSettingsAppearance.ui b/src/Mod/Sketcher/Gui/SketcherSettingsAppearance.ui new file mode 100644 index 0000000000..fa9cf59e7b --- /dev/null +++ b/src/Mod/Sketcher/Gui/SketcherSettingsAppearance.ui @@ -0,0 +1,967 @@ + + + SketcherGui::SketcherSettingsAppearance + + + + 0 + 0 + 689 + 863 + + + + Appearance + + + + + + Working colors + + + + + + + + + 200 + 0 + + + + Creating line + + + + + + + Color used while new sketch elements are created + + + + 204 + 204 + 204 + + + + CreateLineColor + + + View + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 200 + 0 + + + + Coordinate text + + + + + + + Text color of the coordinates + + + + 0 + 0 + 255 + + + + CursorTextColor + + + View + + + + + + + Cursor crosshair + + + + + + + Color of crosshair cursor. +(The one you get when creating a new sketch element.) + + + + 255 + 255 + 255 + + + + CursorCrosshairColor + + + View + + + + + + + + + + + + Geometric element colors + + + + + + + 95 + 0 + + + + Constrained + + + + + + + + 95 + 0 + + + + Unconstrained + + + + + + + Pattern + + + + + + + Width + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Vertex + + + + + + + + 0 + 0 + + + + Color of fully constrained vertex color in edit mode + + + + 255 + 149 + 128 + + + + FullyConstraintConstructionPointColor + + + View + + + + + + + + 0 + 0 + + + + Color of vertices being edited + + + + 255 + 38 + 0 + + + + EditedVertexColor + + + View + + + + + + + Edge + + + + + + + + 0 + 0 + + + + Color of fully constrained edge color in edit mode + + + + 128 + 208 + 160 + + + + FullyConstraintElementColor + + + View + + + + + + + + 0 + 0 + + + + Color of edges being edited + + + + 255 + 255 + 255 + + + + EditedEdgeColor + + + View + + + + + + + Line pattern of normal edges. + + + -1 + + + + + + + Width of normal edges. + + + mm + + + 1 + + + 99 + + + 2 + + + EdgeWidth + + + Mod/Sketcher/View + + + + + + + Construction geometry + + + + + + + + 0 + 0 + + + + Color of fully constrained construction edge color in edit mode + + + + 143 + 169 + 253 + + + + FullyConstraintConstructionElementColor + + + View + + + + + + + + 0 + 0 + + + + Color of construction geometry in edit mode + + + + 0 + 0 + 220 + + + + ConstructionColor + + + View + + + + + + + Line pattern of construction edges. + + + -1 + + + + + + + Width of construction edges. + + + mm + + + 1 + + + 99 + + + 1 + + + ConstructionWidth + + + Mod/Sketcher/View + + + + + + + Internal alignment edge + + + + + + + + 0 + 0 + + + + Color of fully constrained internal alignment edge color in edit mode + + + + 222 + 222 + 200 + + + + FullyConstraintInternalAlignmentColor + + + View + + + + + + + + 0 + 0 + + + + Color of edges of internal alignment geometry + + + + 178 + 178 + 127 + + + + InternalAlignedGeoColor + + + View + + + + + + + Line pattern of internal aligned edges. + + + -1 + + + + + + + Width of internal aligned edges. + + + mm + + + 1 + + + 99 + + + 1 + + + InternalWidth + + + Mod/Sketcher/View + + + + + + + External geometry + + + + + + + + 0 + 0 + + + + Color of external geometry in edit mode + + + + 204 + 51 + 115 + + + + ExternalColor + + + View + + + + + + + Line pattern of external edges. + + + -1 + + + + + + + Width of external edges. + + + mm + + + 1 + + + 99 + + + 1 + + + ExternalWidth + + + Mod/Sketcher/View + + + + + + + + 200 + 0 + + + + Fully constrained Sketch + + + + + + + + 0 + 0 + + + + Color of fully constrained geometry in edit mode + + + + 0 + 255 + 0 + + + + FullyConstrainedColor + + + View + + + + + + + Invalid Sketch + + + + + + + + 0 + 0 + + + + Color of geometry indicating an invalid sketch + + + + 255 + 109 + 0 + + + + InvalidSketchColor + + + View + + + + + + + + + + Constraint colors + + + + + + + + + 200 + 0 + + + + Constraint symbols + + + + + + + Color of driving constraints in edit mode + + + + 255 + 38 + 0 + + + + ConstrainedIcoColor + + + View + + + + + + + Dimensional constraint + + + + + + + Color of dimensional driving constraints + + + + 255 + 38 + 0 + + + + ConstrainedDimColor + + + View + + + + + + + Reference constraint + + + + + + + Color of reference constraints in edit mode + + + + 0 + 38 + 255 + + + + NonDrivingConstrDimColor + + + View + + + + + + + Expression dependent constraint + + + + + + + Color of expression dependent constraints in edit mode + + + + 255 + 127 + 38 + + + + ExprBasedConstrDimColor + + + View + + + + + + + Deactivated constraint + + + + + + + Color of deactivated constraints in edit mode + + + + 127 + 127 + 127 + + + + DeactivatedConstrDimColor + + + View + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Colors outside Sketcher + + + + + + + + + 200 + 0 + + + + Edge + + + + + + + Color of edges + + + + 255 + 255 + 255 + + + + SketchEdgeColor + + + View + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Vertex + + + + + + + Color of vertices + + + + 255 + 255 + 255 + + + + SketchVertexColor + + + View + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Gui::PrefSpinBox + QSpinBox +
Gui/PrefWidgets.h
+
+ + Gui::ColorButton + QPushButton +
Gui/Widgets.h
+
+ + Gui::PrefColorButton + Gui::ColorButton +
Gui/PrefWidgets.h
+
+
+ + +
diff --git a/src/Mod/Sketcher/Gui/SketcherSettingsColors.ui b/src/Mod/Sketcher/Gui/SketcherSettingsColors.ui deleted file mode 100644 index ddf7005ffc..0000000000 --- a/src/Mod/Sketcher/Gui/SketcherSettingsColors.ui +++ /dev/null @@ -1,835 +0,0 @@ - - - SketcherGui::SketcherSettingsColors - - - - 0 - 0 - 689 - 863 - - - - Colors - - - - - - Working colors - - - - - - - - - 240 - 0 - - - - Creating line - - - - - - - Color used while new sketch elements are created - - - - 204 - 204 - 204 - - - - CreateLineColor - - - View - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 240 - 0 - - - - Coordinate text - - - - - - - Text color of the coordinates - - - - 0 - 0 - 255 - - - - CursorTextColor - - - View - - - - - - - Cursor crosshair - - - - - - - Color of crosshair cursor. -(The one you get when creating a new sketch element.) - - - - 255 - 255 - 255 - - - - CursorCrosshairColor - - - View - - - - - - - - - - - - Geometric element colors - - - - - - - - - 120 - 0 - - - - Constrained - - - - - - - - 120 - 0 - - - - Unconstrained - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Edge - - - - - - - - 0 - 0 - - - - Color of fully constrained edge color in edit mode - - - - 128 - 208 - 160 - - - - FullyConstraintElementColor - - - View - - - - - - - - 0 - 0 - - - - Color of edges being edited - - - - 255 - 255 - 255 - - - - EditedEdgeColor - - - View - - - - - - - Vertex - - - - - - - - 0 - 0 - - - - Color of fully constrained vertex color in edit mode - - - - 255 - 149 - 128 - - - - FullyConstraintConstructionPointColor - - - View - - - - - - - - 0 - 0 - - - - Color of vertices being edited - - - - 255 - 38 - 0 - - - - EditedVertexColor - - - View - - - - - - - Construction geometry - - - - - - - - 0 - 0 - - - - Color of fully constrained construction edge color in edit mode - - - - 143 - 169 - 253 - - - - FullyConstraintConstructionElementColor - - - View - - - - - - - - 0 - 0 - - - - Color of construction geometry in edit mode - - - - 0 - 0 - 220 - - - - ConstructionColor - - - View - - - - - - - - 240 - 0 - - - - Internal alignment edge - - - - - - - - 0 - 0 - - - - Color of fully constrained internal alignment edge color in edit mode - - - - 222 - 222 - 200 - - - - FullyConstraintInternalAlignmentColor - - - View - - - - - - - - 0 - 0 - - - - Color of edges of internal alignment geometry - - - - 178 - 178 - 127 - - - - InternalAlignedGeoColor - - - View - - - - - - - External geometry - - - - - - - - 0 - 0 - - - - Color of external geometry in edit mode - - - - 204 - 51 - 115 - - - - ExternalColor - - - View - - - - - - - - - - - - 240 - 0 - - - - Fully constrained Sketch - - - - - - - - 0 - 0 - - - - Color of fully constrained geometry in edit mode - - - - 0 - 255 - 0 - - - - FullyConstrainedColor - - - View - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Invalid Sketch - - - - - - - - 0 - 0 - - - - Color of geometry indicating an invalid sketch - - - - 255 - 109 - 0 - - - - InvalidSketchColor - - - View - - - - - - - - - - - - Constraint colors - - - - - - - - - 240 - 0 - - - - Constraint symbols - - - - - - - Color of driving constraints in edit mode - - - - 255 - 38 - 0 - - - - ConstrainedIcoColor - - - View - - - - - - - Dimensional constraint - - - - - - - Color of dimensional driving constraints - - - - 255 - 38 - 0 - - - - ConstrainedDimColor - - - View - - - - - - - Reference constraint - - - - - - - Color of reference constraints in edit mode - - - - 0 - 38 - 255 - - - - NonDrivingConstrDimColor - - - View - - - - - - - Expression dependent constraint - - - - - - - Color of expression dependent constraints in edit mode - - - - 255 - 127 - 38 - - - - ExprBasedConstrDimColor - - - View - - - - - - - Deactivated constraint - - - - - - - Color of deactivated constraints in edit mode - - - - 127 - 127 - 127 - - - - DeactivatedConstrDimColor - - - View - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - Colors outside Sketcher - - - - - - - - - 240 - 0 - - - - Edge - - - - - - - Color of edges - - - - 255 - 255 - 255 - - - - SketchEdgeColor - - - View - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Vertex - - - - - - - Color of vertices - - - - 255 - 255 - 255 - - - - SketchVertexColor - - - View - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - Gui::ColorButton - QPushButton -
Gui/Widgets.h
-
- - Gui::PrefColorButton - Gui::ColorButton -
Gui/PrefWidgets.h
-
-
- - -
From 049bd9c209ef3bb3bf06124b03056593e9abea74 Mon Sep 17 00:00:00 2001 From: mosfet80 Date: Tue, 16 Jan 2024 08:50:13 +0100 Subject: [PATCH 091/135] Update action.yml Updated ccache https://ccache.dev/releasenotes.html#_ccache_4_9 --- .github/workflows/actions/windows/getCcache/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/actions/windows/getCcache/action.yml b/.github/workflows/actions/windows/getCcache/action.yml index 7d0ceff711..be56349f46 100644 --- a/.github/workflows/actions/windows/getCcache/action.yml +++ b/.github/workflows/actions/windows/getCcache/action.yml @@ -41,11 +41,11 @@ inputs: ccachedownloadpath: description: "Path where to download ccache" required: false - default: https://github.com/ccache/ccache/releases/download/v4.8.2/ + default: https://github.com/ccache/ccache/releases/download/v4.9/ ccacheversion: description: "Ccache version to be downloaded" required: false - default: ccache-4.8.2-windows-x86_64 + default: ccache-4.9-windows-x86_64 runs: using: "composite" From 1bd3b531aa2e1270015ebd98c487b38e4bf12be9 Mon Sep 17 00:00:00 2001 From: Vincenzo Calligaro Date: Mon, 22 Jan 2024 18:19:46 +0100 Subject: [PATCH 092/135] [Core] [Preferences] Modernize for loop in DlgSettingsEditor.cpp (#12011) * Preferences: modernize for loop in DlgSettingsEditor.cpp Signed-off-by: CalligaroV * Preferences: modernize for loop in DlgSettingsEditor.cpp added textType and textColor variables to unpack range-for loops iterators Signed-off-by: CalligaroV * Update src/Gui/PreferencePages/DlgSettingsEditor.cpp Minor linter updates, as suggested Co-authored-by: Chris Hennes * Update src/Gui/PreferencePages/DlgSettingsEditor.cpp Minor linter updates, as suggested Co-authored-by: Chris Hennes --------- Signed-off-by: CalligaroV Co-authored-by: Chris Hennes --- src/Gui/PreferencePages/DlgSettingsEditor.cpp | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/Gui/PreferencePages/DlgSettingsEditor.cpp b/src/Gui/PreferencePages/DlgSettingsEditor.cpp index a8a4bb35a8..65b44ced5f 100644 --- a/src/Gui/PreferencePages/DlgSettingsEditor.cpp +++ b/src/Gui/PreferencePages/DlgSettingsEditor.cpp @@ -163,9 +163,10 @@ DlgSettingsEditor::DlgSettingsEditor( QWidget* parent ) QStringList labels; labels << tr("Items"); ui->displayItems->setHeaderLabels(labels); ui->displayItems->header()->hide(); - for (QVector >::Iterator it = d->colormap.begin(); it != d->colormap.end(); ++it) { + for (const auto &[textType, textColor]: d->colormap) + { auto item = new QTreeWidgetItem(ui->displayItems); - item->setText(0, tr((*it).first.toLatin1())); + item->setText(0, tr(textType.toLatin1())); } pythonSyntax = new PythonSyntaxHighlighter(ui->textEdit1); pythonSyntax->setDocument(ui->textEdit1->document()); @@ -243,11 +244,11 @@ void DlgSettingsEditor::saveSettings() // Saves the color map ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("Editor"); - for (QVector >::Iterator it = d->colormap.begin(); it != d->colormap.end(); ++it) { - auto col = static_cast((*it).second); - hGrp->SetUnsigned((*it).first.toLatin1(), col); + for (const auto &[textType, textColor] : d->colormap) + { + auto col = static_cast(textColor); + hGrp->SetUnsigned(textType.toLatin1(), col); } - hGrp->SetInt( "FontSize", ui->fontSize->value() ); hGrp->SetASCII( "Font", ui->fontFamily->currentText().toLatin1() ); @@ -280,13 +281,13 @@ void DlgSettingsEditor::loadSettings() // Restores the color map ParameterGrp::handle hGrp = WindowParameter::getDefaultParameter()->GetGroup("Editor"); - for (QVector>::Iterator it = d->colormap.begin(); - it != d->colormap.end(); ++it) { - auto col = static_cast((*it).second); - col = hGrp->GetUnsigned((*it).first.toLatin1(), col); - (*it).second = static_cast(col); + for (auto &[textType, textColor] : d->colormap) + { + auto col = static_cast(textColor); + col = hGrp->GetUnsigned(textType.toLatin1(), col); + textColor = static_cast(col); QColor color = App::Color::fromPackedRGB(col); - pythonSyntax->setColor((*it).first, color); + pythonSyntax->setColor(textType, color); } // fill up font styles @@ -332,8 +333,9 @@ void DlgSettingsEditor::resetSettingsToDefaults() ParameterGrp::handle hGrp; hGrp = WindowParameter::getDefaultParameter()->GetGroup("Editor"); //reset the parameters in the "Editor" group - for (QVector >::Iterator it = d->colormap.begin(); it != d->colormap.end(); ++it) { - hGrp->RemoveUnsigned((*it).first.toLatin1()); + for (const auto &[textType, textColor] : d->colormap) + { + hGrp->RemoveUnsigned(textType.toLatin1()); } //reset "FontSize" parameter hGrp->RemoveInt("FontSize"); @@ -351,8 +353,8 @@ void DlgSettingsEditor::changeEvent(QEvent *e) { if (e->type() == QEvent::LanguageChange) { int index = 0; - for (QVector >::Iterator it = d->colormap.begin(); it != d->colormap.end(); ++it) - ui->displayItems->topLevelItem(index++)->setText(0, tr((*it).first.toLatin1())); + for (const auto &[textType, textColor]: d->colormap) + ui->displayItems->topLevelItem(index++)->setText(0, tr(textType.toLatin1())); ui->retranslateUi(this); } else { QWidget::changeEvent(e); From 05f425eab96f07a0ed7de6a8936f0e56f6fe0984 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Fri, 19 Jan 2024 18:21:55 -0700 Subject: [PATCH 093/135] Gui: Add missing override --- src/Gui/propertyeditor/PropertyEditor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Gui/propertyeditor/PropertyEditor.h b/src/Gui/propertyeditor/PropertyEditor.h index bd01c2f99e..c50fedf0d9 100644 --- a/src/Gui/propertyeditor/PropertyEditor.h +++ b/src/Gui/propertyeditor/PropertyEditor.h @@ -100,7 +100,7 @@ protected Q_SLOTS: void onRowsRemoved(const QModelIndex &parent, int start, int end); protected: - bool eventFilter(QObject* object, QEvent* event); + bool eventFilter(QObject* object, QEvent* event) override; void closeEditor (QWidget * editor, QAbstractItemDelegate::EndEditHint hint) override; void commitData (QWidget * editor) override; void editorDestroyed (QObject * editor) override; From 7a85dd07e1721b5f1314f8e3e04490ea81bf1c0f Mon Sep 17 00:00:00 2001 From: Roy-043 Date: Sat, 20 Jan 2024 11:17:34 +0100 Subject: [PATCH 094/135] Draft: Improve layer functions The current make_layer function has a `None` default for the shape color and the line color. With that value the current preference is used. This, and how the function is called, results in some confusing behaviors: * Newly created layers will only use 2 values from the preferences when they might use 5. The latter makes more sense for the end-user IMO. * Layers created during DXF import (for example) will have a different shape color depending on the current preferences. * The make_layer function may reapply colors that have already been set by the view provider. To solve this all view property related function parameter have been changed to a not None value. If a None value is supplied the view property as set by the view provider is not changed. The Layer Manager has been updated accordingly. I realize that calling a function with 6 None values is not very convenient, but think it is the solution that is least likely to break other exiting code. Additionally: * Removed the makeLayer function. Layers were introduced in V0.19 when the naming scheme was changed to "make_*". Maybe it was created by mistake, or before the actual renaming operation started, but it is safe to remove it now. * Removed overly verbose messages. * gui_layers.py had a missing import (result of a previous V0.22 PR): `from draftutils import utils`. --- src/Mod/Draft/Draft.py | 3 +- src/Mod/Draft/draftguitools/gui_layers.py | 21 ++- src/Mod/Draft/draftmake/make_layer.py | 160 ++++++++---------- .../Draft/drafttests/draft_test_objects.py | 1 + .../Draft/draftviewproviders/view_layer.py | 3 +- 5 files changed, 83 insertions(+), 105 deletions(-) diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index 50f32c4bc6..d54960f138 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -379,8 +379,7 @@ if App.GuiUp: from draftobjects.layer import (Layer, _VisGroup) -from draftmake.make_layer import (make_layer, - makeLayer) +from draftmake.make_layer import make_layer if App.GuiUp: from draftviewproviders.view_layer import (ViewProviderLayer, diff --git a/src/Mod/Draft/draftguitools/gui_layers.py b/src/Mod/Draft/draftguitools/gui_layers.py index 21b709e33d..800c6c6091 100644 --- a/src/Mod/Draft/draftguitools/gui_layers.py +++ b/src/Mod/Draft/draftguitools/gui_layers.py @@ -37,6 +37,7 @@ import Draft import Draft_rc from draftguitools import gui_base from draftutils import params +from draftutils import utils from draftutils.translate import translate # The module is used to prevent complaints from code checkers (flake8) @@ -76,8 +77,8 @@ class Layer(gui_base.GuiCommandSimplest): self.doc.openTransaction("Create Layer") Gui.addModule("Draft") - Gui.doCommand('_layer_ = Draft.make_layer()') - Gui.doCommand('FreeCAD.ActiveDocument.recompute()') + Gui.doCommand("_layer_ = Draft.make_layer(name=None, line_color=None, shape_color=None, line_width=None, draw_style=None, transparency=None)") + Gui.doCommand("FreeCAD.ActiveDocument.recompute()") self.doc.commitTransaction() @@ -170,7 +171,8 @@ class LayerManager: if not changed: FreeCAD.ActiveDocument.openTransaction("Layers change") changed = True - obj = Draft.make_layer() + obj = Draft.make_layer(name=None, line_color=None, shape_color=None, + line_width=None, draw_style=None, transparency=None) # visibility checked = True if self.model.item(row,0).checkState() == QtCore.Qt.Checked else False @@ -303,7 +305,7 @@ class LayerManager: nameItem = QtGui.QStandardItem(translate("Draft", "New Layer")) widthItem = QtGui.QStandardItem() widthItem.setData(params.get_param_view("DefaultShapeLineWidth"), QtCore.Qt.DisplayRole) - styleItem = QtGui.QStandardItem("Solid") + styleItem = QtGui.QStandardItem(utils.DRAW_STYLES[params.get_param("DefaultDrawStyle")]) lineColorItem = QtGui.QStandardItem() lineColorItem.setData( utils.get_rgba_tuple(params.get_param_view("DefaultShapeLineColor"))[:3], @@ -315,7 +317,10 @@ class LayerManager: QtCore.Qt.UserRole ) transparencyItem = QtGui.QStandardItem() - transparencyItem.setData(0, QtCore.Qt.DisplayRole) + transparencyItem.setData( + params.get_param_view("DefaultShapeTransparency"), + QtCore.Qt.DisplayRole + ) linePrintColorItem = QtGui.QStandardItem() linePrintColorItem.setData( utils.get_rgba_tuple(params.get_param("DefaultPrintColor"))[:3], @@ -428,7 +433,7 @@ if FreeCAD.GuiUp: editor.setMaximum(99) elif index.column() == 3: # Line style editor = QtGui.QComboBox(parent) - editor.addItems(["Solid","Dashed","Dotted","Dashdot"]) + editor.addItems(utils.DRAW_STYLES) elif index.column() == 4: # Line color editor = QtGui.QLineEdit(parent) self.first = True @@ -452,7 +457,7 @@ if FreeCAD.GuiUp: elif index.column() == 2: # Line width editor.setValue(index.data()) elif index.column() == 3: # Line style - editor.setCurrentIndex(["Solid","Dashed","Dotted","Dashdot"].index(index.data())) + editor.setCurrentIndex(utils.DRAW_STYLES.index(index.data())) elif index.column() == 4: # Line color editor.setText(str(index.data(QtCore.Qt.UserRole))) if self.first: @@ -486,7 +491,7 @@ if FreeCAD.GuiUp: elif index.column() == 2: # Line width model.setData(index,editor.value()) elif index.column() == 3: # Line style - model.setData(index,["Solid","Dashed","Dotted","Dashdot"][editor.currentIndex()]) + model.setData(index,utils.DRAW_STYLES[editor.currentIndex()]) elif index.column() == 4: # Line color model.setData(index,eval(editor.text()),QtCore.Qt.UserRole) model.itemFromIndex(index).setIcon(getColorIcon(eval(editor.text()))) diff --git a/src/Mod/Draft/draftmake/make_layer.py b/src/Mod/Draft/draftmake/make_layer.py index d1287d11ed..59020fd912 100644 --- a/src/Mod/Draft/draftmake/make_layer.py +++ b/src/Mod/Draft/draftmake/make_layer.py @@ -30,7 +30,6 @@ # @{ import FreeCAD as App from draftobjects.layer import Layer, LayerContainer -from draftutils import params from draftutils import utils from draftutils.messages import _msg, _err from draftutils.translate import translate @@ -79,53 +78,52 @@ def getLayerContainer(): def make_layer(name=None, - line_color=None, shape_color=None, + line_color=(0.0, 0.0, 0.0), # does not match default DefaultShapeLineColor + shape_color=(0.8, 0.8, 0.8), # matches default DefaultShapeColor line_width=2.0, - draw_style="Solid", transparency=0): + draw_style="Solid", + transparency=0): """Create a Layer object in the active document. - If a layer container named `'LayerContainer'` does not exist, - it is created with this name. + If a layer container named `'LayerContainer'` does not exist, it is created. - A layer controls the view properties of the objects inside the layer, - so all parameters except for `name` only apply if the graphical interface + A layer controls the view properties of the objects inside the layer. + All parameters except for `name` only apply if the graphical interface is up. + All parameters that control view properties can be set to `None`. Their + value, as set by the view provider (matching the current preferences), is + then not changed. + Parameters ---------- - name: str, optional - It is used to set the layer's `Label` (user editable). - It defaults to `None`, in which case the `Label` - is set to `'Layer'` or to its translation in the current language. + name: str or `None`, optional + It defaults to `None`. + It is used to set the layer's `Label`. If it is `None` the `Label` is + set to `'Layer'` or to its translation in the current language. - line_color: tuple, optional - It defaults to `None`, in which case it uses the value of the parameter - `User parameter:BaseApp/Preferences/View/DefaultShapeLineColor`. - If it is given, it should be a tuple of three - floating point values from 0.0 to 1.0. + line_color: tuple or `None`, optional + It defaults to `(0.0, 0.0, 0.0)`. + If it is given, it should be a tuple of three floating point values + from 0.0 to 1.0. - shape_color: tuple, optional - It defaults to `None`, in which case it uses the value of the parameter - `User parameter:BaseApp/Preferences/View/DefaultShapeColor`. - If it is given, it should be a tuple of three - floating point values from 0.0 to 1.0. + shape_color: tuple or `None`, optional + It defaults to `(0.8, 0.8, 0.8)`. + If it is given, it should be a tuple of three floating point values + from 0.0 to 1.0. - line_width: float, optional + line_width: float or `None`, optional It defaults to 2.0. - It determines the width of the edges of the objects contained - in the layer. + It determines the width of the edges of the objects contained in the layer. - draw_style: str, optional + draw_style: str or `None`, optional It defaults to `'Solid'`. - It determines the style of the edges of the objects contained - in the layer. - If it is given, it should be 'Solid', 'Dashed', 'Dotted', - or 'Dashdot'. + It determines the style of the edges of the objects contained in the layer. + If it is given, it should be 'Solid', 'Dashed', 'Dotted' or 'Dashdot'. - transparency: int, optional + transparency: int or `None`, optional It defaults to 0. - It should be an integer value from 0 (completely opaque) - to 100 (completely transparent). + It should be an integer from 0 to 100. Return ------ @@ -139,15 +137,13 @@ def make_layer(name=None, If there is a problem it will return `None`. """ _name = "make_layer" - utils.print_header(_name, translate("draft","Layer")) found, doc = utils.find_doc(App.activeDocument()) if not found: _err(translate("draft","No active document. Aborting.")) return None - if name: - _msg("name: {}".format(name)) + if name is not None: try: utils.type_check([(name, str)], name=_name) except TypeError: @@ -156,8 +152,7 @@ def make_layer(name=None, else: name = translate("draft", "Layer") - if line_color: - _msg("line_color: {}".format(line_color)) + if line_color is not None: try: utils.type_check([(line_color, tuple)], name=_name) except TypeError: @@ -167,14 +162,8 @@ def make_layer(name=None, if not all(isinstance(color, (int, float)) for color in line_color): _err(translate("draft","Wrong input: must be a tuple of three floats 0.0 to 1.0.")) return None - else: - c = params.get_param_view("DefaultShapeLineColor") - line_color = (((c >> 24) & 0xFF) / 255, - ((c >> 16) & 0xFF) / 255, - ((c >> 8) & 0xFF) / 255) - if shape_color: - _msg("shape_color: {}".format(shape_color)) + if shape_color is not None: try: utils.type_check([(shape_color, tuple)], name=_name) except TypeError: @@ -184,38 +173,33 @@ def make_layer(name=None, if not all(isinstance(color, (int, float)) for color in shape_color): _err(translate("draft","Wrong input: must be a tuple of three floats 0.0 to 1.0.")) return None - else: - c = params.get_param_view("DefaultShapeColor") - shape_color = (((c >> 24) & 0xFF) / 255, - ((c >> 16) & 0xFF) / 255, - ((c >> 8) & 0xFF) / 255) - _msg("line_width: {}".format(line_width)) - try: - utils.type_check([(line_width, (int, float))], name=_name) - line_width = float(abs(line_width)) - except TypeError: - _err(translate("draft","Wrong input: must be a number.")) - return None + if line_width is not None: + try: + utils.type_check([(line_width, (int, float))], name=_name) + line_width = float(abs(line_width)) + except TypeError: + _err(translate("draft","Wrong input: must be a number.")) + return None - _msg("draw_style: {}".format(draw_style)) - try: - utils.type_check([(draw_style, str)], name=_name) - except TypeError: - _err(translate("draft","Wrong input: must be 'Solid', 'Dashed', 'Dotted', or 'Dashdot'.")) - return None + if draw_style is not None: + try: + utils.type_check([(draw_style, str)], name=_name) + except TypeError: + _err(translate("draft","Wrong input: must be 'Solid', 'Dashed', 'Dotted', or 'Dashdot'.")) + return None - if draw_style not in ('Solid', 'Dashed', 'Dotted', 'Dashdot'): - _err(translate("draft","Wrong input: must be 'Solid', 'Dashed', 'Dotted', or 'Dashdot'.")) - return None + if draw_style not in ('Solid', 'Dashed', 'Dotted', 'Dashdot'): + _err(translate("draft","Wrong input: must be 'Solid', 'Dashed', 'Dotted', or 'Dashdot'.")) + return None - _msg("transparency: {}".format(transparency)) - try: - utils.type_check([(transparency, (int, float))], name=_name) - transparency = int(abs(transparency)) - except TypeError: - _err(translate("draft","Wrong input: must be a number between 0 and 100.")) - return None + if transparency is not None: + try: + utils.type_check([(transparency, (int, float))], name=_name) + transparency = int(abs(transparency)) + except TypeError: + _err(translate("draft","Wrong input: must be a number between 0 and 100.")) + return None new_obj = doc.addObject("App::FeaturePython", "Layer") Layer(new_obj) @@ -224,32 +208,20 @@ def make_layer(name=None, if App.GuiUp: ViewProviderLayer(new_obj.ViewObject) - - new_obj.ViewObject.LineColor = line_color - new_obj.ViewObject.ShapeColor = shape_color - new_obj.ViewObject.LineWidth = line_width - new_obj.ViewObject.DrawStyle = draw_style - new_obj.ViewObject.Transparency = transparency + if line_color is not None: + new_obj.ViewObject.LineColor = line_color + if shape_color is not None: + new_obj.ViewObject.ShapeColor = shape_color + if line_width is not None: + new_obj.ViewObject.LineWidth = line_width + if draw_style is not None: + new_obj.ViewObject.DrawStyle = draw_style + if transparency is not None: + new_obj.ViewObject.Transparency = transparency container = get_layer_container() container.addObject(new_obj) return new_obj - -def makeLayer(name=None, linecolor=None, drawstyle=None, - shapecolor=None, transparency=None): - """Create a Layer. DEPRECATED. Use 'make_layer'.""" - utils.use_instead("make_layer") - - if not drawstyle: - drawstyle = "Solid" - - if not transparency: - transparency = 0 - - return make_layer(name, - line_color=linecolor, shape_color=shapecolor, - draw_style=drawstyle, transparency=transparency) - ## @} diff --git a/src/Mod/Draft/drafttests/draft_test_objects.py b/src/Mod/Draft/drafttests/draft_test_objects.py index 1544b7089a..e81c7730e1 100644 --- a/src/Mod/Draft/drafttests/draft_test_objects.py +++ b/src/Mod/Draft/drafttests/draft_test_objects.py @@ -586,6 +586,7 @@ def _create_objects(doc=None, line_color=(0.33, 0.0, 0.49), shape_color=(0.56, 0.89, 0.56), line_width=4, + draw_style="Solid", transparency=50) box = doc.addObject("Part::Box", "Box") box.Length = 200 diff --git a/src/Mod/Draft/draftviewproviders/view_layer.py b/src/Mod/Draft/draftviewproviders/view_layer.py index 2160d02475..65aa23f661 100644 --- a/src/Mod/Draft/draftviewproviders/view_layer.py +++ b/src/Mod/Draft/draftviewproviders/view_layer.py @@ -557,7 +557,8 @@ class ViewProviderLayerContainer: doc = App.ActiveDocument doc.openTransaction(translate("draft", "Add new layer")) - Draft.make_layer() + Draft.make_layer(name=None, line_color=None, shape_color=None, + line_width=None, draw_style=None, transparency=None) doc.recompute() doc.commitTransaction() From 6e0112d5f851bf4f9b85a01887772f51f3490f5d Mon Sep 17 00:00:00 2001 From: Roy-043 Date: Sun, 21 Jan 2024 12:32:31 +0100 Subject: [PATCH 095/135] Draft: Inform user that objects without a Shape cannot be cloned Fixes #11923. --- src/Mod/Draft/draftguitools/gui_clone.py | 64 ++++++++++++------------ 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/src/Mod/Draft/draftguitools/gui_clone.py b/src/Mod/Draft/draftguitools/gui_clone.py index b50262c6f7..262948dfb7 100644 --- a/src/Mod/Draft/draftguitools/gui_clone.py +++ b/src/Mod/Draft/draftguitools/gui_clone.py @@ -1,7 +1,8 @@ # *************************************************************************** -# * (c) 2009, 2010 Yorik van Havre * -# * (c) 2009, 2010 Ken Cline * -# * (c) 2020 Eliud Cabrera Castillo * +# * Copyright (c) 2009, 2010 Yorik van Havre * +# * Copyright (c) 2009, 2010 Ken Cline * +# * Copyright (c) 2020 Eliud Cabrera Castillo * +# * Copyright (c) 2023 FreeCAD Project Association * # * * # * This file is part of the FreeCAD CAx development system. * # * * @@ -48,7 +49,7 @@ import Draft_rc import draftguitools.gui_base_original as gui_base_original import draftguitools.gui_tool_utils as gui_tool_utils import draftutils.todo as todo -from draftutils.messages import _msg +from draftutils.messages import _msg, _wrn from draftutils.translate import translate # The module is used to prevent complaints from code checkers (flake8) @@ -65,10 +66,10 @@ class Clone(gui_base_original.Modifier): def GetResources(self): """Set icon, menu and tooltip.""" - return {'Pixmap': 'Draft_Clone', - 'Accel': "C,L", - 'MenuText': QT_TRANSLATE_NOOP("Draft_Clone", "Clone"), - 'ToolTip': QT_TRANSLATE_NOOP("Draft_Clone", "Creates a clone of the selected objects.\nThe resulting clone can be scaled in each of its three directions.")} + return {"Pixmap": "Draft_Clone", + "Accel": "C, L", + "MenuText": QT_TRANSLATE_NOOP("Draft_Clone", "Clone"), + "ToolTip": QT_TRANSLATE_NOOP("Draft_Clone", "Creates a clone of the selected objects.\nThe resulting clone can be scaled in each of its three directions.")} def Activated(self): """Execute when the command is called.""" @@ -84,30 +85,31 @@ class Clone(gui_base_original.Modifier): self.proceed() def proceed(self): - """Proceed with the command if one object was selected.""" - if Gui.Selection.getSelection(): - sels = len(Gui.Selection.getSelection()) - Gui.addModule("Draft") - App.ActiveDocument.openTransaction(translate("Draft", "Clone")) - nonRepeatList = [] - n = 0 - for obj in Gui.Selection.getSelection(): - if obj not in nonRepeatList: - _cmd = "Draft.make_clone" - _cmd += "(" - _cmd += "FreeCAD.ActiveDocument." - _cmd += 'getObject("' + obj.Name + '")' - _cmd += ")" - Gui.doCommand("c" + str(n) + " = " + _cmd) - nonRepeatList.append(obj) - n += 1 - App.ActiveDocument.commitTransaction() - App.ActiveDocument.recompute() - Gui.Selection.clearSelection() + """Proceed with the command if objects were selected.""" + objs = Gui.Selection.getSelection() + if not objs: + self.finish() + return + objs_shape = [obj for obj in objs if hasattr(obj, "Shape")] + if not objs_shape: + _wrn(translate("draft", "Cannot clone object(s) without a Shape, aborting")) + self.finish() + return + elif len(objs_shape) < len(objs): + _wrn(translate("draft", "Cannot clone object(s) without a Shape, skipping them")) - objects = App.ActiveDocument.Objects - for i in range(sels): - Gui.Selection.addSelection(objects[-(1 + i)]) + Gui.addModule("Draft") + App.ActiveDocument.openTransaction(translate("Draft", "Clone")) + for idx, obj in enumerate(objs_shape): + cmd = "Draft.make_clone(FreeCAD.ActiveDocument." + obj.Name + ")" + Gui.doCommand("clone" + str(idx) + " = " + cmd) + App.ActiveDocument.commitTransaction() + App.ActiveDocument.recompute() + + Gui.Selection.clearSelection() + objs = App.ActiveDocument.Objects + for i in range(len(objs_shape)): + Gui.Selection.addSelection(objs[-(1 + i)]) self.finish() def finish(self): From 427e75707e3c0cf64223a153b4f1c81793218827 Mon Sep 17 00:00:00 2001 From: Roy-043 Date: Sun, 21 Jan 2024 15:11:07 +0100 Subject: [PATCH 096/135] Draft: ShapeString double escape backslash in string Fixes 12058. --- .../Draft/drafttaskpanels/task_shapestring.py | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/Mod/Draft/drafttaskpanels/task_shapestring.py b/src/Mod/Draft/drafttaskpanels/task_shapestring.py index 2a18768b62..b98e3846b3 100644 --- a/src/Mod/Draft/drafttaskpanels/task_shapestring.py +++ b/src/Mod/Draft/drafttaskpanels/task_shapestring.py @@ -149,10 +149,9 @@ class ShapeStringTaskPanelCmd(ShapeStringTaskPanel): def createObject(self): """Create object in the current document.""" - dquote = '"' - String = self.form.leString.text() - String = dquote + String.replace(dquote, '\\"') + dquote - FFile = dquote + str(self.fileSpec) + dquote + String = self.form.leString.text().replace('\\', '\\\\').replace('"', '\\"') + String = '"' + String + '"' + FFile = '"' + str(self.fileSpec) + '"' Size = str(App.Units.Quantity(self.form.sbHeight.text()).Value) Tracking = str(0.0) @@ -163,13 +162,13 @@ class ShapeStringTaskPanelCmd(ShapeStringTaskPanel): try: qr, sup, points, fil = self.sourceCmd.getStrings() Gui.addModule("Draft") - self.sourceCmd.commit(translate("draft", "Create ShapeString"), - ['ss=Draft.make_shapestring(String=' + String + ', FontFile=' + FFile + ', Size=' + Size + ', Tracking=' + Tracking + ')', - 'plm=FreeCAD.Placement()', - 'plm.Base=' + toString(ssBase), - 'plm.Rotation.Q=' + qr, - 'ss.Placement=plm', - 'ss.Support=' + sup, + self.sourceCmd.commit(translate('draft', 'Create ShapeString'), + ['ss = Draft.make_shapestring(String=' + String + ', FontFile=' + FFile + ', Size=' + Size + ', Tracking=' + Tracking + ')', + 'plm = FreeCAD.Placement()', + 'plm.Base = ' + toString(ssBase), + 'plm.Rotation.Q = ' + qr, + 'ss.Placement = plm', + 'ss.Support = ' + sup, 'Draft.autogroup(ss)', 'FreeCAD.ActiveDocument.recompute()']) except Exception: From e72efde5eb6d5db6613bd0989e716a7303c40998 Mon Sep 17 00:00:00 2001 From: WandererFan Date: Mon, 22 Jan 2024 12:37:33 -0500 Subject: [PATCH 097/135] [Import]fix linkage warning on linux/gcc (#12071) * [Import]fix linkage warning on linux/gcc https://stackoverflow.com/questions/41167119/how-to-fix-a-wsubobject-linkage-warning --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- src/Mod/Import/App/dxf/dxf.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Mod/Import/App/dxf/dxf.h b/src/Mod/Import/App/dxf/dxf.h index d82119df08..45ac21fdec 100644 --- a/src/Mod/Import/App/dxf/dxf.h +++ b/src/Mod/Import/App/dxf/dxf.h @@ -173,7 +173,12 @@ struct LWPolyDataOut std::vector Bulge; point3D Extr; }; -using eDXFGroupCode_t = enum { + + +// "using" for enums is not supported by all platforms +// https://stackoverflow.com/questions/41167119/how-to-fix-a-wsubobject-linkage-warning +enum eDXFGroupCode_t +{ eObjectType = 0, ePrimaryText = 1, eName = 2, @@ -211,7 +216,8 @@ using eDXFGroupCode_t = enum { eYOffset = 10, eZOffset = 20 }; -using eDXFVersion_t = enum { +enum eDXFVersion_t +{ RUnknown, ROlder, R10, @@ -226,7 +232,8 @@ using eDXFVersion_t = enum { R2018, RNewer, }; -using eDimensionType_t = enum { +enum eDimensionType_t +{ eLinear = 0, // Rotated, Horizontal, or Vertical eAligned = 1, eAngular = 2, @@ -431,6 +438,7 @@ class ImportExport CDxfRead private: // Low-level reader members std::ifstream* m_ifs; // TODO: gsl::owner + // https://stackoverflow.com/questions/41167119/how-to-fix-a-wsubobject-linkage-warning eDXFGroupCode_t m_record_type = eObjectType; std::string m_record_data; bool m_not_eof = true; From b57a6b504cf4cbfe2bbd6b9925349587d8da60b7 Mon Sep 17 00:00:00 2001 From: Max Wilfinger Date: Sat, 13 Jan 2024 17:33:02 +0100 Subject: [PATCH 098/135] [Sketcher] enable BSpline commands in contextual right click menu --- .../splines/Sketcher_BSplineApproximate.svg | 31 +++++- .../splines/Sketcher_BSplineInsertKnot.svg | 6 +- .../icons/splines/Sketcher_JoinCurves.svg | 28 ++++- src/Mod/Sketcher/Gui/ViewProviderSketch.cpp | 101 ++++++++++++++---- 4 files changed, 134 insertions(+), 32 deletions(-) diff --git a/src/Mod/Sketcher/Gui/Resources/icons/splines/Sketcher_BSplineApproximate.svg b/src/Mod/Sketcher/Gui/Resources/icons/splines/Sketcher_BSplineApproximate.svg index 66e92f507b..e0c8e5ac32 100644 --- a/src/Mod/Sketcher/Gui/Resources/icons/splines/Sketcher_BSplineApproximate.svg +++ b/src/Mod/Sketcher/Gui/Resources/icons/splines/Sketcher_BSplineApproximate.svg @@ -15,6 +15,17 @@ xmlns:dc="http://purl.org/dc/elements/1.1/"> + + + + + @@ -464,7 +484,8 @@ transform="matrix(0.1460346,0,0,0.1460346,-220.10298,-55.131225)"> + id="g4428-3-2" + style="display:inline"> diff --git a/src/Mod/Sketcher/Gui/Resources/icons/splines/Sketcher_BSplineInsertKnot.svg b/src/Mod/Sketcher/Gui/Resources/icons/splines/Sketcher_BSplineInsertKnot.svg index fa676a240d..569e11ffd3 100644 --- a/src/Mod/Sketcher/Gui/Resources/icons/splines/Sketcher_BSplineInsertKnot.svg +++ b/src/Mod/Sketcher/Gui/Resources/icons/splines/Sketcher_BSplineInsertKnot.svg @@ -380,7 +380,7 @@ id="path3826" /> + diff --git a/src/Mod/Sketcher/Gui/Resources/icons/splines/Sketcher_JoinCurves.svg b/src/Mod/Sketcher/Gui/Resources/icons/splines/Sketcher_JoinCurves.svg index 0ac32bc5e1..3cf03594f5 100644 --- a/src/Mod/Sketcher/Gui/Resources/icons/splines/Sketcher_JoinCurves.svg +++ b/src/Mod/Sketcher/Gui/Resources/icons/splines/Sketcher_JoinCurves.svg @@ -261,11 +261,11 @@ @@ -316,6 +316,15 @@ y1="35.978416" x2="25.988253" y2="29.916241" /> + @@ -368,7 +377,7 @@ + transform="matrix(-1,0,0,1,47.987644,0)"> + + + + diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index dd3b963bc0..05f7db40c7 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -3886,6 +3886,11 @@ void ViewProviderSketch::generateContextMenu() int selectedConics = 0; int selectedPoints = 0; int selectedConstraints = 0; + int selectedBsplines = 0; + int selectedBsplineKnots = 0; + int selectedOrigin = 0; + int selectedEndPoints = 0; + bool onlyOrigin = false; Gui::MenuItem menu; menu.setCommand("Sketcher context"); @@ -3896,31 +3901,70 @@ void ViewProviderSketch::generateContextMenu() // if something is selected, count different elements in the current selection if (selection.size() > 0) { const std::vector SubNames = selection[0].getSubNames(); - - for (auto& name : SubNames) { - if (name.substr(0, 4) == "Edge") { - ++selectedEdges; - + const Sketcher::SketchObject* obj; + if (selection[0].getObject()->isDerivedFrom()) { + obj = static_cast(selection[0].getObject()); + for (auto& name : SubNames) { int geoId = std::atoi(name.substr(4, 4000).c_str()) - 1; - if (geoId >= 0) { - const Part::Geometry* geo = getSketchObject()->getGeometry(geoId); - if (isLineSegment(*geo)) { - ++selectedLines; - } - else { - ++selectedConics; + const Part::Geometry* geo = getSketchObject()->getGeometry(geoId); + if (name.substr(0, 4) == "Edge") { + ++selectedEdges; + + if (geoId >= 0) { + if (isLineSegment(*geo)) { + ++selectedLines; + } + else if (geo->is()) { + ++selectedBsplines; + } + else { + ++selectedConics; + } } } - } - else if (name.substr(0, 4) == "Vert") { - ++selectedPoints; - } - else if (name.substr(0, 4) == "Cons") { - ++selectedConstraints; + else if (name.substr(0, 4) == "Vert") { + ++selectedPoints; + Sketcher::PointPos posId; + getIdsFromName(name, obj, geoId, posId); + if (isBsplineKnotOrEndPoint(obj, geoId, posId)) { + ++selectedBsplineKnots; + } + if (Sketcher::PointPos::start != posId || Sketcher::PointPos::end != posId) { + ++selectedEndPoints; + } + } + else if (name.substr(0, 4) == "Cons") { + ++selectedConstraints; + } + else if (name.substr(2, 5) == "Axis") { + ++selectedEdges; + ++selectedLines; + ++selectedOrigin; + } + else if (name.substr(0, 4) == "Root") { + ++selectedPoints; + ++selectedOrigin; + } } } + if (selectedPoints + selectedEdges == selectedOrigin) { + onlyOrigin = true; + } // build context menu items depending on the selection - if (selectedEdges >= 1 && selectedPoints == 0) { + if (selectedBsplines > 0 && selectedBsplines == selectedEdges && selectedPoints == 0 + && !onlyOrigin) { + menu << "Sketcher_BSplineInsertKnot" + << "Sketcher_BSplineIncreaseDegree" + << "Sketcher_BSplineDecreaseDegree"; + } + else if (selectedBsplineKnots > 0 && selectedBsplineKnots == selectedPoints + && selectedEdges == 0 && !onlyOrigin) { + if (selectedBsplineKnots == 1) { + menu << "Sketcher_BSplineIncreaseKnotMultiplicity" + << "Sketcher_BSplineDecreaseKnotMultiplicity"; + } + } + if (selectedEdges >= 1 && selectedPoints == 0 && selectedBsplines == 0 && !onlyOrigin) { menu << "Sketcher_Dimension"; if (selectedConics == 0) { menu << "Sketcher_ConstrainHorVer" @@ -3948,9 +3992,9 @@ void ViewProviderSketch::generateContextMenu() menu << "Sketcher_ConstrainTangent"; } } - else if (selectedEdges == 1 && selectedPoints >= 1) { + else if (selectedEdges == 1 && selectedPoints >= 1 && !onlyOrigin) { menu << "Sketcher_Dimension"; - if (selectedConics == 0) { + if (selectedConics == 0 && selectedBsplines == 0) { menu << "Sketcher_ConstrainCoincidentUnified" << "Sketcher_ConstrainHorVer" << "Sketcher_ConstrainVertical" @@ -3958,6 +4002,10 @@ void ViewProviderSketch::generateContextMenu() if (selectedPoints == 2) { menu << "Sketcher_ConstrainSymmetric"; } + if (selectedPoints == 1) { + menu << "Sketcher_ConstrainPerpendicular" + << "Sketcher_ConstrainTangent"; + } } else { menu << "Sketcher_ConstrainCoincidentUnified" @@ -3965,7 +4013,7 @@ void ViewProviderSketch::generateContextMenu() << "Sketcher_ConstrainTangent"; } } - else if (selectedEdges == 0 && selectedPoints >= 1) { + else if (selectedEdges == 0 && selectedPoints >= 1 && !onlyOrigin) { menu << "Sketcher_Dimension"; if (selectedPoints > 1) { @@ -3974,8 +4022,15 @@ void ViewProviderSketch::generateContextMenu() << "Sketcher_ConstrainVertical" << "Sketcher_ConstrainHorizontal"; } + if (selectedPoints == 2) { + menu << "Sketcher_ConstrainPerpendicular" + << "Sketcher_ConstrainTangent"; + if (selectedEndPoints == 2) { + menu << "Sketcher_JoinCurves"; + } + } } - else if (selectedLines >= 1 && selectedPoints >= 1) { + else if (selectedLines >= 1 && selectedPoints >= 1 && !onlyOrigin) { menu << "Sketcher_Dimension" << "Sketcher_ConstrainHorVer" << "Sketcher_ConstrainVertical" From f0eb40f155ae58eed7f2546ba46071e678797568 Mon Sep 17 00:00:00 2001 From: bdieterm <119257544+bdieterm@users.noreply.github.com> Date: Mon, 22 Jan 2024 19:23:55 +0100 Subject: [PATCH 099/135] Part: add color transparency unit tests --- src/Mod/Part/CMakeLists.txt | 1 + src/Mod/Part/TestPartGui.py | 1 + .../Part/parttests/ColorTransparencyTest.py | 58 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 src/Mod/Part/parttests/ColorTransparencyTest.py diff --git a/src/Mod/Part/CMakeLists.txt b/src/Mod/Part/CMakeLists.txt index b7502d6e77..767b1a43d9 100644 --- a/src/Mod/Part/CMakeLists.txt +++ b/src/Mod/Part/CMakeLists.txt @@ -69,6 +69,7 @@ set(Part_tests parttests/regression_tests.py parttests/TopoShapeListTest.py parttests/ColorPerFaceTest.py + parttests/ColorTransparencyTest.py ) add_custom_target(PartScripts ALL SOURCES diff --git a/src/Mod/Part/TestPartGui.py b/src/Mod/Part/TestPartGui.py index 9527da2fbe..dc207cc831 100644 --- a/src/Mod/Part/TestPartGui.py +++ b/src/Mod/Part/TestPartGui.py @@ -44,6 +44,7 @@ def findDockWidget(name): #--------------------------------------------------------------------------- """ from parttests.ColorPerFaceTest import ColorPerFaceTest +from parttests.ColorTransparencyTest import ColorTransparencyTest #class PartGuiTestCases(unittest.TestCase): diff --git a/src/Mod/Part/parttests/ColorTransparencyTest.py b/src/Mod/Part/parttests/ColorTransparencyTest.py new file mode 100644 index 0000000000..f948bdac8a --- /dev/null +++ b/src/Mod/Part/parttests/ColorTransparencyTest.py @@ -0,0 +1,58 @@ +import unittest + +import FreeCAD as App + + +class ColorTransparencyTest(unittest.TestCase): + + def setUp(self): + self._doc = App.newDocument() + self._pg = App.ParamGet('User parameter:BaseApp/Preferences/View') + self._backup_default_transparency = self._pg.GetInt('DefaultShapeTransparency') + self._backup_default_shapecolor = self._pg.GetUnsigned('DefaultShapeColor') + + + def tearDown(self): + App.closeDocument(self._doc.Name) + self._pg.SetInt('DefaultShapeTransparency', self._backup_default_transparency) + self._pg.SetUnsigned('DefaultShapeColor', self._backup_default_shapecolor) + + + def test_default_shape_transparency(self): + """ + related: https://github.com/FreeCAD/FreeCAD/pull/11866 + related: https://github.com/FreeCAD/FreeCAD/pull/11586 + """ + transparency = 70 + self._pg.SetInt('DefaultShapeTransparency', transparency) + obj = self._doc.addObject('Part::Box') + assert obj.ViewObject.Transparency == transparency + obj.ViewObject.ShapeColor = (0.5, 0.0, 0.0) + + self.assertEqual(obj.ViewObject.Transparency, transparency, + 'transparency was unexpectedly changed to {} when changing the color.'.format( + obj.ViewObject.Transparency)) + + + def test_default_shape_color(self): + """ + related: https://github.com/FreeCAD/FreeCAD/pull/11866 + """ + self._pg.SetUnsigned('DefaultShapeColor', 0xff000000) # red + obj = self._doc.addObject('Part::Box') + + self.assertEqual(obj.ViewObject.ShapeColor, (1.0, 0.0, 0.0, 0.0), + 'default shape color was not set correctly') + + + def test_app_plane_transparency(self): + """ + related: https://github.com/FreeCAD/FreeCAD/pull/12064 + """ + self._pg.SetInt('DefaultShapeTransparency', 70) + obj = self._doc.addObject('App::Origin') + t = self._doc.findObjects('App::Plane')[0].ViewObject.Transparency + + self.assertEqual(t, 0, + 'transparency of App::Plane object is {} instead of 0'.format(t)) + From 5d40864811798c7b4adfdc67c0009b28a6935992 Mon Sep 17 00:00:00 2001 From: bgbsww Date: Mon, 22 Jan 2024 16:13:53 -0500 Subject: [PATCH 100/135] Small cleanups --- src/Mod/Part/App/FaceMaker.cpp | 20 +++++++++++--------- src/Mod/Part/App/TopoShape.h | 14 +++++++------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/Mod/Part/App/FaceMaker.cpp b/src/Mod/Part/App/FaceMaker.cpp index 5db6f6c640..44fa2ce2cd 100644 --- a/src/Mod/Part/App/FaceMaker.cpp +++ b/src/Mod/Part/App/FaceMaker.cpp @@ -181,28 +181,30 @@ void Part::FaceMaker::postBuild() { this->myTopoShape.setShape(this->myShape); this->myTopoShape.Hasher = this->MyHasher; this->myTopoShape.mapSubElement(this->mySourceShapes); - int i = 0; + int index = 0; const char *op = this->MyOp; if(!op) op = Part::OpCodes::Face; const auto &faces = this->myTopoShape.getSubTopoShapes(TopAbs_FACE); // name the face using the edges of its outer wire for(auto &face : faces) { - ++i; + ++index; TopoShape wire = face.splitWires(); wire.mapSubElement(face); std::set edgeNames; int count = wire.countSubShapes(TopAbs_EDGE); - for(int i=1;i<=count;++i) { + for (int index2 = 1; index2 <= count; ++index2) { Data::ElementIDRefs sids; - Data::MappedName name = face.getMappedName( - Data::IndexedName::fromConst("Edge",i), false, &sids); - if(!name) + Data::MappedName name = + face.getMappedName(Data::IndexedName::fromConst("Edge", index2), false, &sids); + if (!name) { continue; - edgeNames.emplace(wire.getElementHistory(name),name,sids); + } + edgeNames.emplace(wire.getElementHistory(name), name, sids); } - if(edgeNames.empty()) + if (edgeNames.empty()) { continue; + } std::vector names; Data::ElementIDRefs sids; @@ -211,7 +213,7 @@ void Part::FaceMaker::postBuild() { names.push_back(edgeNames.begin()->name); sids = edgeNames.begin()->sids; this->myTopoShape.setElementComboName( - Data::IndexedName::fromConst("Face",i),names,op,nullptr,&sids); + Data::IndexedName::fromConst("Face",index),names,op,nullptr,&sids); } this->myTopoShape.initCache(true); this->Done(); diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 28cc8d0e7f..06439746a9 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -257,7 +257,7 @@ public: bool analyze(bool runBopCheck, std::ostream&) const; bool isClosed() const; bool isCoplanar(const TopoShape& other, double tol = -1) const; - bool findPlane(gp_Pln& pln, double tol = -1) const; + bool findPlane(gp_Pln& plane, double tol = -1) const; /// Returns true if the expansion of the shape is infinite, false otherwise bool isInfinite() const; /// Checks whether the shape is a planar face @@ -679,7 +679,7 @@ public: TopoShape& makeElementFace(const std::vector& shapes, const char* op = nullptr, const char* maker = nullptr, - const gp_Pln* pln = nullptr); + const gp_Pln* plane = nullptr); /** Make a planar face with the input wire or edge * * @param shape: input shape. Can be either edge, wire, or compound of @@ -688,7 +688,7 @@ public: * the operation * @param maker: optional type name of the face maker. If not given, * default to "Part::FaceMakerBullseye" - * @param pln: optional plane of the face. + * @param plane: optional plane of the face. * * @return The function creates a planar face. The original content of this * TopoShape is discarded and replaced with the new shape. The @@ -699,23 +699,23 @@ public: TopoShape& makeElementFace(const TopoShape& shape, const char* op = nullptr, const char* maker = nullptr, - const gp_Pln* pln = nullptr); + const gp_Pln* plane = nullptr); /** Make a planar face using this shape * * @param op: optional string to be encoded into topo naming for indicating * the operation * @param maker: optional type name of the face maker. If not given, * default to "Part::FaceMakerBullseye" - * @param pln: optional plane of the face. + * @param plane: optional plane of the face. * * @return The function returns a new planar face made using the wire or edge * inside this shape. The shape itself is not modified. */ TopoShape makeElementFace(const char* op = nullptr, const char* maker = nullptr, - const gp_Pln* pln = nullptr) const + const gp_Pln* plane = nullptr) const { - return TopoShape(0, Hasher).makeElementFace(*this, op, maker, pln); + return TopoShape(0, Hasher).makeElementFace(*this, op, maker, plane); } /// Filling style when making a BSpline face From 852c6ae7d30112594cddfb4f59e990f8c7cf57d7 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Mon, 22 Jan 2024 12:44:28 -0600 Subject: [PATCH 101/135] Part/TopoShapeMapper: Add missing PreCompiled include --- src/Mod/Part/App/TopoShapeMapper.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Mod/Part/App/TopoShapeMapper.cpp b/src/Mod/Part/App/TopoShapeMapper.cpp index 69f9653d18..4268a433d2 100644 --- a/src/Mod/Part/App/TopoShapeMapper.cpp +++ b/src/Mod/Part/App/TopoShapeMapper.cpp @@ -1,3 +1,5 @@ +#include "PreCompiled.h" + #include "TopoShapeMapper.h" namespace Part From 57d747a83c09a6b163dd521e0894da337b423863 Mon Sep 17 00:00:00 2001 From: wandererfan Date: Mon, 22 Jan 2024 09:46:34 -0500 Subject: [PATCH 102/135] [TD]fix line number not saved for cosmetic edge --- src/Mod/TechDraw/App/Cosmetic.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/Mod/TechDraw/App/Cosmetic.cpp b/src/Mod/TechDraw/App/Cosmetic.cpp index aa47615f21..5b65996ff1 100644 --- a/src/Mod/TechDraw/App/Cosmetic.cpp +++ b/src/Mod/TechDraw/App/Cosmetic.cpp @@ -257,6 +257,8 @@ unsigned int CosmeticEdge::getMemSize () const void CosmeticEdge::Save(Base::Writer &writer) const { + // TODO: this should be using m_format->Save(writer) instead of saving the individual + // fields. writer.Stream() << writer.ind() << "