diff --git a/src/Mod/Draft/draftguitools/gui_arcs.py b/src/Mod/Draft/draftguitools/gui_arcs.py index bb202aa1eada..4a46256fb33a 100644 --- a/src/Mod/Draft/draftguitools/gui_arcs.py +++ b/src/Mod/Draft/draftguitools/gui_arcs.py @@ -68,7 +68,7 @@ def GetResources(self): def Activated(self): """Execute when the command is called.""" - super(Arc, self).Activated(name=translate("draft","Arc") + super(Arc, self).Activated(name=translate("draft","Arc")) if self.ui: self.step = 0 self.center = None diff --git a/src/Mod/Draft/draftguitools/gui_beziers.py b/src/Mod/Draft/draftguitools/gui_beziers.py index 044bb036156a..6dd84d1ec27a 100644 --- a/src/Mod/Draft/draftguitools/gui_beziers.py +++ b/src/Mod/Draft/draftguitools/gui_beziers.py @@ -235,7 +235,7 @@ def __init__(self): def GetResources(self): """Set icon, menu and tooltip.""" - _menu = + _menu = "" _tip = () return {'Pixmap': 'Draft_CubicBezCurve', diff --git a/src/Mod/Draft/draftguitools/gui_groups.py b/src/Mod/Draft/draftguitools/gui_groups.py index 7e1039e450c0..e8e95ab582c0 100644 --- a/src/Mod/Draft/draftguitools/gui_groups.py +++ b/src/Mod/Draft/draftguitools/gui_groups.py @@ -163,7 +163,10 @@ def GetResources(self): d = {'Pixmap': 'Draft_SelectGroup', 'MenuText': QT_TRANSLATE_NOOP("Draft_SelectGroup","Select group"), - 'ToolTip': QT_TRANSLATE_NOOP("Draft_SelectGroup","If the selection is a group, it selects all objects that are inside this group, including those in nested sub-groups.\n\nIf the selection is a simple object inside a group, it will select the "brother" objects, that is,\nthose that are at the same level as this object, including the upper group that contains them all.")} + 'ToolTip': QT_TRANSLATE_NOOP("Draft_SelectGroup","If the selection is a group, it selects all objects that are inside this group, " + "including those in nested sub-groups.\n\nIf the selection is a simple object " + "inside a group, it will select the \"brother\" objects, that is,\nthose that are " + "at the same level as this object, including the upper group that contains them all.")} return d def Activated(self): diff --git a/src/Mod/Draft/draftguitools/gui_shape2dview.py b/src/Mod/Draft/draftguitools/gui_shape2dview.py index 80cacece0a8e..292e0d96141a 100644 --- a/src/Mod/Draft/draftguitools/gui_shape2dview.py +++ b/src/Mod/Draft/draftguitools/gui_shape2dview.py @@ -54,7 +54,7 @@ class Shape2DView(gui_base_original.Modifier): def GetResources(self): """Set icon, menu and tooltip.""" - _menu = + _menu = "" _tip = () return {'Pixmap': 'Draft_2DShapeView', diff --git a/src/Mod/Draft/draftguitools/gui_shapestrings.py b/src/Mod/Draft/draftguitools/gui_shapestrings.py index e1bf33abede8..813bc85920dc 100644 --- a/src/Mod/Draft/draftguitools/gui_shapestrings.py +++ b/src/Mod/Draft/draftguitools/gui_shapestrings.py @@ -62,7 +62,7 @@ class ShapeString(gui_base_original.Creator): def GetResources(self): """Set icon, menu and tooltip.""" - _menu = + _menu = "" _tip = () d = {'Pixmap': 'Draft_ShapeString', diff --git a/src/Mod/Draft/draftguitools/gui_snaps.py b/src/Mod/Draft/draftguitools/gui_snaps.py index 362a2a1e9c5d..ccffaef8a635 100644 --- a/src/Mod/Draft/draftguitools/gui_snaps.py +++ b/src/Mod/Draft/draftguitools/gui_snaps.py @@ -567,7 +567,7 @@ def __init__(self): def GetResources(self): """Set icon, menu and tooltip.""" - _tip = + _tip = "" return {'Pixmap': 'Draft_Snap', 'MenuText': QT_TRANSLATE_NOOP("Draft_ShowSnapBar","Show snap toolbar"), diff --git a/src/Mod/Draft/draftguitools/gui_subelements.py b/src/Mod/Draft/draftguitools/gui_subelements.py index 4abcc9770d1d..711de64753ad 100644 --- a/src/Mod/Draft/draftguitools/gui_subelements.py +++ b/src/Mod/Draft/draftguitools/gui_subelements.py @@ -54,7 +54,7 @@ def __init__(self): def GetResources(self): """Set icon, menu and tooltip.""" - _menu = + _menu = "" _tip = () return {'Pixmap': 'Draft_SubelementHighlight', diff --git a/src/Mod/Draft/drafttaskpanels/task_circulararray.py b/src/Mod/Draft/drafttaskpanels/task_circulararray.py index a815c4e56ca1..6b0d86abf9bc 100644 --- a/src/Mod/Draft/drafttaskpanels/task_circulararray.py +++ b/src/Mod/Draft/drafttaskpanels/task_circulararray.py @@ -297,7 +297,7 @@ def create_object(self): "App.ActiveDocument.recompute()"] # We commit the command list through the parent command - self.source_command.commit(translate("draft","Circular array", _cmd_list) + self.source_command.commit(translate("draft","Circular array", _cmd_list)) def get_distances(self): """Get the distance parameters from the widgets.""" diff --git a/src/Mod/Draft/drafttaskpanels/task_polararray.py b/src/Mod/Draft/drafttaskpanels/task_polararray.py index 0258a9dbf9ef..66ad102f9e87 100644 --- a/src/Mod/Draft/drafttaskpanels/task_polararray.py +++ b/src/Mod/Draft/drafttaskpanels/task_polararray.py @@ -92,7 +92,7 @@ def __init__(self): pix = QtGui.QPixmap(svg) icon = QtGui.QIcon.fromTheme(icon_name, QtGui.QIcon(svg)) self.form.setWindowIcon(icon) - self.form.setWindowTitle(translate("draft","Polar array") + self.form.setWindowTitle(translate("draft","Polar array")) self.form.label_icon.setPixmap(pix.scaled(32, 32)) diff --git a/src/Mod/Sketcher/App/Sketch.cpp b/src/Mod/Sketcher/App/Sketch.cpp index 86a21cb61cf7..f783918c27c3 100644 --- a/src/Mod/Sketcher/App/Sketch.cpp +++ b/src/Mod/Sketcher/App/Sketch.cpp @@ -138,7 +138,9 @@ bool Sketch::analyseBlockedGeometry( const std::vector &intern std::vector &onlyblockedGeometry, std::vector &blockedGeoIds) const { - bool isSomethingBlocked = false; + // To understand this function read the documentation in Sketch.h + // It is important that "onlyblockedGeometry" ONLY identifies blocked geometry + // that is not affected by any other driving constraint bool doesBlockAffectOtherConstraints = false; int geoindex = 0; @@ -152,7 +154,7 @@ bool Sketch::analyseBlockedGeometry( const std::vector &intern // is block driving if( c->Type == Sketcher::Block && c->isDriving && c->First == geoindex) blockisDriving = true; - + // We have another driving constraint (which may be InternalAlignment) if( c->Type != Sketcher::Block && c->isDriving && (c->First == geoindex || c->Second == geoindex || c->Third == geoindex) ) blockOnly = false; @@ -161,12 +163,10 @@ bool Sketch::analyseBlockedGeometry( const std::vector &intern if(blockisDriving) { if(blockOnly) { onlyblockedGeometry[geoindex] = true; // we pre-fix this geometry - isSomethingBlocked = true; } else { // we will have to pos-analyse the first diagnose result for these geometries // in order to avoid redundant constraints - isSomethingBlocked = true; doesBlockAffectOtherConstraints = true; blockedGeoIds.push_back(geoindex); } @@ -176,39 +176,6 @@ bool Sketch::analyseBlockedGeometry( const std::vector &intern geoindex++; } - if(isSomethingBlocked) { - - // look for internal geometry linked IAs - for(auto c : constraintList) { - if(c->Type == InternalAlignment) { - - auto geoit = std::find(blockedGeoIds.begin(),blockedGeoIds.end(),c->Second); - - if(geoit != blockedGeoIds.end() || onlyblockedGeometry[c->Second]) { // internal alignment geometry found, add to list - // check if pre-fix or post-analyses - bool blockAffectedOnly = true; - - for(auto ic : constraintList) { - // there is another driving constraint - if( ic->Type != Sketcher::Block && ic->isDriving && - (ic->First == c->First || ic->Second == c->First || ic->Third == c->First)) - blockAffectedOnly = false; - } - - if(blockAffectedOnly) { - onlyblockedGeometry[c->Second] = true; // we pre-fix this geometry - } - else { - // we will have to post-analyse the first diagnose result for these geometries - // in order to avoid redundant constraints - doesBlockAffectOtherConstraints = true; - blockedGeoIds.push_back(*geoit); - } - } - } - } - } - return doesBlockAffectOtherConstraints; } @@ -247,19 +214,23 @@ int Sketch::setUpSketch(const std::vector &GeoList, Base::Console().Log("\nOnlyBlocked GeoIds:"); size_t i = 0; + bool found = false; for(; i < onlyBlockedGeometry.size(); i++) { - if(onlyBlockedGeometry[i]) + if(onlyBlockedGeometry[i]) { Base::Console().Log("\n GeoId=%d", i); + found = true; + } } - if( i == 0) + if(found) Base::Console().Log("\n None"); Base::Console().Log("\nNotOnlyBlocked GeoIds:"); i = 0; for(; i < blockedGeoIds.size(); i++) Base::Console().Log("\n GeoId=%d", blockedGeoIds[i]); - if( i == 0) + if(i == 0) Base::Console().Log("\n None"); + Base::Console().Log("\n"); #endif //DEBUG_BLOCK_CONSTRAINT addGeometry(intGeoList,onlyBlockedGeometry); diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp index 81ff77e0c524..ed8e0afda8e1 100644 --- a/src/Mod/Sketcher/App/SketchObject.cpp +++ b/src/Mod/Sketcher/App/SketchObject.cpp @@ -6591,86 +6591,86 @@ void SketchObject::rebuildExternalGeometry(void) gp_Dir origAxisMinorDir = elipsOrig.YAxis().Direction(); gp_Vec origAxisMinor = elipsOrig.MinorRadius() * gp_Vec(origAxisMinorDir); - if (sketchPlane.Position().Direction().IsParallel(elipsOrig.Position().Direction(), Precision::Angular())) { - elipsDest = elipsOrig.Translated(origCenter, destCenter); - Handle(Geom_Ellipse) curve = new Geom_Ellipse(elipsDest); - Part::GeomEllipse* ellipse = new Part::GeomEllipse(); - ellipse->setHandle(curve); - GeometryFacade::setConstruction(ellipse, true); - - ExternalGeo.push_back(ellipse); + // Here, it used to be a test for parallel direction between the sketchplane and the elipsOrig, in which + // the original ellipse would be copied and translated to the new position. + // The problem with that approach is that for the sketcher the normal vector is always (0,0,1). If the original + // ellipse was not on the XY plane, the copy will not be either. Then, the dimensions would be wrong because of the + // different major axis direction (which is not projected on the XY plane). So here, we default to the more general + // ellipse construction algorithm. + // + // Doing that solves: + // https://forum.freecadweb.org/viewtopic.php?f=3&t=55284#p477522 + + // GENERAL ELLIPSE CONSTRUCTION ALGORITHM + // + // look for major axis of projected ellipse + // + // t is the parameter along the origin ellipse + // OM(t) = origCenter + // + majorRadius * cos(t) * origAxisMajorDir + // + minorRadius * sin(t) * origAxisMinorDir + gp_Vec2d PA = ProjVecOnPlane_UV(origAxisMajor, sketchPlane); + gp_Vec2d PB = ProjVecOnPlane_UV(origAxisMinor, sketchPlane); + double t_max = 2.0 * PA.Dot(PB) / (PA.SquareMagnitude() - PB.SquareMagnitude()); + t_max = 0.5 * atan(t_max); // gives new major axis is most cases, but not all + double t_min = t_max + 0.5 * M_PI; + + // ON_max = OM(t_max) gives the point, which projected on the sketch plane, + // becomes the apoapse of the pojected ellipse. + gp_Vec ON_max = origAxisMajor * cos(t_max) + origAxisMinor * sin(t_max); + gp_Vec ON_min = origAxisMajor * cos(t_min) + origAxisMinor * sin(t_min); + gp_Vec destAxisMajor = ProjVecOnPlane_UVN(ON_max, sketchPlane); + gp_Vec destAxisMinor = ProjVecOnPlane_UVN(ON_min, sketchPlane); + + double RDest = destAxisMajor.Magnitude(); + double rDest = destAxisMinor.Magnitude(); + + if (RDest < rDest) { + double rTmp = rDest; + rDest = RDest; + RDest = rTmp; + gp_Vec axisTmp = destAxisMajor; + destAxisMajor = destAxisMinor; + destAxisMinor = axisTmp; } - else { - // look for major axis of projected ellipse - // - // t is the parameter along the origin ellipse - // OM(t) = origCenter - // + majorRadius * cos(t) * origAxisMajorDir - // + minorRadius * sin(t) * origAxisMinorDir - gp_Vec2d PA = ProjVecOnPlane_UV(origAxisMajor, sketchPlane); - gp_Vec2d PB = ProjVecOnPlane_UV(origAxisMinor, sketchPlane); - double t_max = 2.0 * PA.Dot(PB) / (PA.SquareMagnitude() - PB.SquareMagnitude()); - t_max = 0.5 * atan(t_max); // gives new major axis is most cases, but not all - double t_min = t_max + 0.5 * M_PI; - - // ON_max = OM(t_max) gives the point, which projected on the sketch plane, - // becomes the apoapse of the pojected ellipse. - gp_Vec ON_max = origAxisMajor * cos(t_max) + origAxisMinor * sin(t_max); - gp_Vec ON_min = origAxisMajor * cos(t_min) + origAxisMinor * sin(t_min); - gp_Vec destAxisMajor = ProjVecOnPlane_UVN(ON_max, sketchPlane); - gp_Vec destAxisMinor = ProjVecOnPlane_UVN(ON_min, sketchPlane); - - double RDest = destAxisMajor.Magnitude(); - double rDest = destAxisMinor.Magnitude(); - - if (RDest < rDest) { - double rTmp = rDest; - rDest = RDest; - RDest = rTmp; - gp_Vec axisTmp = destAxisMajor; - destAxisMajor = destAxisMinor; - destAxisMinor = axisTmp; - } + double sens = sketchAx3.Direction().Dot(elipsOrig.Position().Direction()); + gp_Ax2 destCurveAx2(destCenter, + gp_Dir(0, 0, sens > 0.0 ? 1.0 : -1.0), + gp_Dir(destAxisMajor)); - double sens = sketchAx3.Direction().Dot(elipsOrig.Position().Direction()); - gp_Ax2 destCurveAx2(destCenter, - gp_Dir(0, 0, sens > 0.0 ? 1.0 : -1.0), - gp_Dir(destAxisMajor)); + if ((RDest - rDest) < (double) Precision::Confusion()) { // projection is a circle + Handle(Geom_Circle) curve = new Geom_Circle(destCurveAx2, 0.5 * (rDest + RDest)); + Part::GeomCircle* circle = new Part::GeomCircle(); + circle->setHandle(curve); + GeometryFacade::setConstruction(circle, true); - if ((RDest - rDest) < (double) Precision::Confusion()) { // projection is a circle - Handle(Geom_Circle) curve = new Geom_Circle(destCurveAx2, 0.5 * (rDest + RDest)); - Part::GeomCircle* circle = new Part::GeomCircle(); - circle->setHandle(curve); - GeometryFacade::setConstruction(circle, true); + ExternalGeo.push_back(circle); + } + else { + if (sketchPlane.Position().Direction().IsNormal(elipsOrig.Position().Direction(), Precision::Angular())) { + gp_Vec start = gp_Vec(destCenter.XYZ()) + destAxisMajor; + gp_Vec end = gp_Vec(destCenter.XYZ()) - destAxisMajor; - ExternalGeo.push_back(circle); + Part::GeomLineSegment * projectedSegment = new Part::GeomLineSegment(); + projectedSegment->setPoints(Base::Vector3d(start.X(), start.Y(), start.Z()), + Base::Vector3d(end.X(), end.Y(), end.Z())); + GeometryFacade::setConstruction(projectedSegment, true); + ExternalGeo.push_back(projectedSegment); } else { - if (sketchPlane.Position().Direction().IsNormal(elipsOrig.Position().Direction(), Precision::Angular())) { - gp_Vec start = gp_Vec(destCenter.XYZ()) + destAxisMajor; - gp_Vec end = gp_Vec(destCenter.XYZ()) - destAxisMajor; - - Part::GeomLineSegment * projectedSegment = new Part::GeomLineSegment(); - projectedSegment->setPoints(Base::Vector3d(start.X(), start.Y(), start.Z()), - Base::Vector3d(end.X(), end.Y(), end.Z())); - GeometryFacade::setConstruction(projectedSegment, true); - ExternalGeo.push_back(projectedSegment); - } - else { - elipsDest.SetPosition(destCurveAx2); - elipsDest.SetMajorRadius(destAxisMajor.Magnitude()); - elipsDest.SetMinorRadius(destAxisMinor.Magnitude()); + elipsDest.SetPosition(destCurveAx2); + elipsDest.SetMajorRadius(destAxisMajor.Magnitude()); + elipsDest.SetMinorRadius(destAxisMinor.Magnitude()); - Handle(Geom_Ellipse) curve = new Geom_Ellipse(elipsDest); - Part::GeomEllipse* ellipse = new Part::GeomEllipse(); - ellipse->setHandle(curve); - GeometryFacade::setConstruction(ellipse, true); + Handle(Geom_Ellipse) curve = new Geom_Ellipse(elipsDest); + Part::GeomEllipse* ellipse = new Part::GeomEllipse(); + ellipse->setHandle(curve); + GeometryFacade::setConstruction(ellipse, true); - ExternalGeo.push_back(ellipse); - } + ExternalGeo.push_back(ellipse); } } } diff --git a/src/Mod/Sketcher/SketcherTests/TestSketcherSolver.py b/src/Mod/Sketcher/SketcherTests/TestSketcherSolver.py index 90a0e42cf0c4..9b99266dbb92 100644 --- a/src/Mod/Sketcher/SketcherTests/TestSketcherSolver.py +++ b/src/Mod/Sketcher/SketcherTests/TestSketcherSolver.py @@ -35,92 +35,90 @@ def CreateRectangleSketch(SketchFeature, corner, lengths): SketchFeature.addGeometry(Part.LineSegment(FreeCAD.Vector(hmin,vmin,0),FreeCAD.Vector(hmin,vmax,0))) # add the rectangular constraints - SketchFeature.addConstraint(Sketcher.Constraint('Coincident',i+0,2,i+1,1)) - SketchFeature.addConstraint(Sketcher.Constraint('Coincident',i+1,2,i+2,1)) - SketchFeature.addConstraint(Sketcher.Constraint('Coincident',i+2,2,i+3,1)) - SketchFeature.addConstraint(Sketcher.Constraint('Coincident',i+3,2,i+0,1)) - SketchFeature.addConstraint(Sketcher.Constraint('Horizontal',i+0)) - SketchFeature.addConstraint(Sketcher.Constraint('Horizontal',i+2)) - SketchFeature.addConstraint(Sketcher.Constraint('Vertical',i+1)) - SketchFeature.addConstraint(Sketcher.Constraint('Vertical',i+3)) + SketchFeature.addConstraint(Sketcher.Constraint('Coincident',i+0,2,i+1,1)) + SketchFeature.addConstraint(Sketcher.Constraint('Coincident',i+1,2,i+2,1)) + SketchFeature.addConstraint(Sketcher.Constraint('Coincident',i+2,2,i+3,1)) + SketchFeature.addConstraint(Sketcher.Constraint('Coincident',i+3,2,i+0,1)) + SketchFeature.addConstraint(Sketcher.Constraint('Horizontal',i+0)) + SketchFeature.addConstraint(Sketcher.Constraint('Horizontal',i+2)) + SketchFeature.addConstraint(Sketcher.Constraint('Vertical',i+1)) + SketchFeature.addConstraint(Sketcher.Constraint('Vertical',i+3)) # Fix the bottom left corner of the rectangle - SketchFeature.addConstraint(Sketcher.Constraint('DistanceX',i+2,2,corner[0])) - SketchFeature.addConstraint(Sketcher.Constraint('DistanceY',i+2,2,corner[1])) + SketchFeature.addConstraint(Sketcher.Constraint('DistanceX',i+2,2,corner[0])) + SketchFeature.addConstraint(Sketcher.Constraint('DistanceY',i+2,2,corner[1])) # add dimensions if lengths[0] == lengths[1]: - SketchFeature.addConstraint(Sketcher.Constraint('Equal',i+2,i+3)) - SketchFeature.addConstraint(Sketcher.Constraint('Distance',i+0,hmax-hmin)) + SketchFeature.addConstraint(Sketcher.Constraint('Equal',i+2,i+3)) + SketchFeature.addConstraint(Sketcher.Constraint('Distance',i+0,hmax-hmin)) else: - SketchFeature.addConstraint(Sketcher.Constraint('Distance',i+1,vmax-vmin)) - SketchFeature.addConstraint(Sketcher.Constraint('Distance',i+0,hmax-hmin)) + SketchFeature.addConstraint(Sketcher.Constraint('Distance',i+1,vmax-vmin)) + SketchFeature.addConstraint(Sketcher.Constraint('Distance',i+0,hmax-hmin)) def CreateCircleSketch(SketchFeature, center, radius): i = int(SketchFeature.GeometryCount) SketchFeature.addGeometry(Part.Circle(App.Vector(*center), App.Vector(0,0,1), radius),False) - SketchFeature.addConstraint(Sketcher.Constraint('Radius',i,radius)) - SketchFeature.addConstraint(Sketcher.Constraint('DistanceX',i,3,center[0])) - SketchFeature.addConstraint(Sketcher.Constraint('DistanceY',i,3,center[1])) + SketchFeature.addConstraint(Sketcher.Constraint('Radius',i,radius)) + SketchFeature.addConstraint(Sketcher.Constraint('DistanceX',i,3,center[0])) + SketchFeature.addConstraint(Sketcher.Constraint('DistanceY',i,3,center[1])) def CreateBoxSketchSet(SketchFeature): - SketchFeature.addGeometry(Part.LineSegment(FreeCAD.Vector(-99.230339,36.960674,0),FreeCAD.Vector(69.432587,36.960674,0))) - SketchFeature.addGeometry(Part.LineSegment(FreeCAD.Vector(69.432587,36.960674,0),FreeCAD.Vector(69.432587,-53.196629,0))) - SketchFeature.addGeometry(Part.LineSegment(FreeCAD.Vector(69.432587,-53.196629,0),FreeCAD.Vector(-99.230339,-53.196629,0))) - SketchFeature.addGeometry(Part.LineSegment(FreeCAD.Vector(-99.230339,-53.196629,0),FreeCAD.Vector(-99.230339,36.960674,0))) - # add the constraints - SketchFeature.addConstraint(Sketcher.Constraint('Coincident',0,2,1,1)) - SketchFeature.addConstraint(Sketcher.Constraint('Coincident',1,2,2,1)) - SketchFeature.addConstraint(Sketcher.Constraint('Coincident',2,2,3,1)) - SketchFeature.addConstraint(Sketcher.Constraint('Coincident',3,2,0,1)) - SketchFeature.addConstraint(Sketcher.Constraint('Horizontal',0)) - SketchFeature.addConstraint(Sketcher.Constraint('Horizontal',2)) - SketchFeature.addConstraint(Sketcher.Constraint('Vertical',1)) - SketchFeature.addConstraint(Sketcher.Constraint('Vertical',3)) - # add dimensions - SketchFeature.addConstraint(Sketcher.Constraint('Distance',1,81.370787)) - SketchFeature.addConstraint(Sketcher.Constraint('Distance',0,187.573036)) + SketchFeature.addGeometry(Part.LineSegment(FreeCAD.Vector(-99.230339,36.960674,0),FreeCAD.Vector(69.432587,36.960674,0))) + SketchFeature.addGeometry(Part.LineSegment(FreeCAD.Vector(69.432587,36.960674,0),FreeCAD.Vector(69.432587,-53.196629,0))) + SketchFeature.addGeometry(Part.LineSegment(FreeCAD.Vector(69.432587,-53.196629,0),FreeCAD.Vector(-99.230339,-53.196629,0))) + SketchFeature.addGeometry(Part.LineSegment(FreeCAD.Vector(-99.230339,-53.196629,0),FreeCAD.Vector(-99.230339,36.960674,0))) + # add the constraints + SketchFeature.addConstraint(Sketcher.Constraint('Coincident',0,2,1,1)) + SketchFeature.addConstraint(Sketcher.Constraint('Coincident',1,2,2,1)) + SketchFeature.addConstraint(Sketcher.Constraint('Coincident',2,2,3,1)) + SketchFeature.addConstraint(Sketcher.Constraint('Coincident',3,2,0,1)) + SketchFeature.addConstraint(Sketcher.Constraint('Horizontal',0)) + SketchFeature.addConstraint(Sketcher.Constraint('Horizontal',2)) + SketchFeature.addConstraint(Sketcher.Constraint('Vertical',1)) + SketchFeature.addConstraint(Sketcher.Constraint('Vertical',3)) + # add dimensions + SketchFeature.addConstraint(Sketcher.Constraint('Distance',1,81.370787)) + SketchFeature.addConstraint(Sketcher.Constraint('Distance',0,187.573036)) def CreateSlotPlateSet(SketchFeature): - SketchFeature.addGeometry(Part.LineSegment(App.Vector(60.029362,-30.279360,0),App.Vector(-120.376335,-30.279360,0))) - SketchFeature.addConstraint(Sketcher.Constraint('Horizontal',0)) - SketchFeature.addGeometry(Part.LineSegment(App.Vector(-120.376335,-30.279360,0),App.Vector(-70.193062,38.113884,0))) - SketchFeature.addConstraint(Sketcher.Constraint('Coincident',0,2,1,1)) - SketchFeature.addGeometry(Part.LineSegment(App.Vector(-70.193062,38.113884,0),App.Vector(60.241116,37.478645,0))) - SketchFeature.addConstraint(Sketcher.Constraint('Coincident',1,2,2,1)) - SketchFeature.addConstraint(Sketcher.Constraint('Horizontal',2)) - SketchFeature.addGeometry(Part.ArcOfCircle(Part.Circle(App.Vector(60.039921,3.811391,0),App.Vector(0,0,1),35.127132),-1.403763,1.419522)) - SketchFeature.addConstraint(Sketcher.Constraint('Tangent',2,2,3,2)) - SketchFeature.addConstraint(Sketcher.Constraint('Tangent',0,1,3,1)) - SketchFeature.addConstraint(Sketcher.Constraint('Angle',0,2,1,1,0.947837)) - SketchFeature.addConstraint(Sketcher.Constraint('Distance',0,184.127425)) - SketchFeature.setDatum(7,200.000000) - SketchFeature.addConstraint(Sketcher.Constraint('Radius',3,38.424808)) - SketchFeature.setDatum(8,40.000000) - SketchFeature.setDatum(6,0.872665) - SketchFeature.addConstraint(Sketcher.Constraint('DistanceX',0,2,0.0)) - SketchFeature.setDatum(9,0.000000) - SketchFeature.movePoint(0,2,App.Vector(-0.007829,-33.376450,0)) - SketchFeature.movePoint(0,2,App.Vector(-0.738149,-10.493386,0)) - SketchFeature.movePoint(0,2,App.Vector(-0.007829,2.165328,0)) - SketchFeature.addConstraint(Sketcher.Constraint('DistanceY',0,2,2.165328)) - SketchFeature.setDatum(10,0.000000) + SketchFeature.addGeometry(Part.LineSegment(App.Vector(60.029362,-30.279360,0),App.Vector(-120.376335,-30.279360,0))) + SketchFeature.addConstraint(Sketcher.Constraint('Horizontal',0)) + SketchFeature.addGeometry(Part.LineSegment(App.Vector(-120.376335,-30.279360,0),App.Vector(-70.193062,38.113884,0))) + SketchFeature.addConstraint(Sketcher.Constraint('Coincident',0,2,1,1)) + SketchFeature.addGeometry(Part.LineSegment(App.Vector(-70.193062,38.113884,0),App.Vector(60.241116,37.478645,0))) + SketchFeature.addConstraint(Sketcher.Constraint('Coincident',1,2,2,1)) + SketchFeature.addConstraint(Sketcher.Constraint('Horizontal',2)) + SketchFeature.addGeometry(Part.ArcOfCircle(Part.Circle(App.Vector(60.039921,3.811391,0),App.Vector(0,0,1),35.127132),-1.403763,1.419522)) + SketchFeature.addConstraint(Sketcher.Constraint('Tangent',2,2,3,2)) + SketchFeature.addConstraint(Sketcher.Constraint('Tangent',0,1,3,1)) + SketchFeature.addConstraint(Sketcher.Constraint('Angle',0,2,1,1,0.947837)) + SketchFeature.addConstraint(Sketcher.Constraint('Distance',0,184.127425)) + SketchFeature.setDatum(7,200.000000) + SketchFeature.addConstraint(Sketcher.Constraint('Radius',3,38.424808)) + SketchFeature.setDatum(8,40.000000) + SketchFeature.setDatum(6,0.872665) + SketchFeature.addConstraint(Sketcher.Constraint('DistanceX',0,2,0.0)) + SketchFeature.setDatum(9,0.000000) + SketchFeature.movePoint(0,2,App.Vector(-0.007829,-33.376450,0)) + SketchFeature.movePoint(0,2,App.Vector(-0.738149,-10.493386,0)) + SketchFeature.movePoint(0,2,App.Vector(-0.007829,2.165328,0)) + SketchFeature.addConstraint(Sketcher.Constraint('DistanceY',0,2,2.165328)) + SketchFeature.setDatum(10,0.000000) def CreateSlotPlateInnerSet(SketchFeature): - SketchFeature.addGeometry(Part.Circle(App.Vector(195.055893,39.562252,0),App.Vector(0,0,1),29.846098)) - SketchFeature.addGeometry(Part.LineSegment(App.Vector(150.319031,13.449363,0),App.Vector(36.700474,13.139774,0))) - SketchFeature.addConstraint(Sketcher.Constraint('Horizontal',5)) - SketchFeature.addGeometry(Part.LineSegment(App.Vector(36.700474,13.139774,0),App.Vector(77.566010,63.292927,0))) - SketchFeature.addConstraint(Sketcher.Constraint('Coincident',5,2,6,1)) - SketchFeature.addGeometry(Part.LineSegment(App.Vector(77.566010,63.292927,0),App.Vector(148.151917,63.602505,0))) - SketchFeature.addConstraint(Sketcher.Constraint('Coincident',6,2,7,1)) - SketchFeature.addConstraint(Sketcher.Constraint('Horizontal',7)) - SketchFeature.addConstraint(Sketcher.Constraint('Parallel',1,6)) - SketchFeature.addGeometry(Part.ArcOfCircle(Part.Circle(App.Vector(192.422913,38.216347,0),App.Vector(0,0,1),45.315174),2.635158,3.602228)) - SketchFeature.addConstraint(Sketcher.Constraint('Coincident',7,2,8,1)) - SketchFeature.addConstraint(Sketcher.Constraint('Coincident',8,2,5,1)) - - + SketchFeature.addGeometry(Part.Circle(App.Vector(195.055893,39.562252,0),App.Vector(0,0,1),29.846098)) + SketchFeature.addGeometry(Part.LineSegment(App.Vector(150.319031,13.449363,0),App.Vector(36.700474,13.139774,0))) + SketchFeature.addConstraint(Sketcher.Constraint('Horizontal',5)) + SketchFeature.addGeometry(Part.LineSegment(App.Vector(36.700474,13.139774,0),App.Vector(77.566010,63.292927,0))) + SketchFeature.addConstraint(Sketcher.Constraint('Coincident',5,2,6,1)) + SketchFeature.addGeometry(Part.LineSegment(App.Vector(77.566010,63.292927,0),App.Vector(148.151917,63.602505,0))) + SketchFeature.addConstraint(Sketcher.Constraint('Coincident',6,2,7,1)) + SketchFeature.addConstraint(Sketcher.Constraint('Horizontal',7)) + SketchFeature.addConstraint(Sketcher.Constraint('Parallel',1,6)) + SketchFeature.addGeometry(Part.ArcOfCircle(Part.Circle(App.Vector(192.422913,38.216347,0),App.Vector(0,0,1),45.315174),2.635158,3.602228)) + SketchFeature.addConstraint(Sketcher.Constraint('Coincident',7,2,8,1)) + SketchFeature.addConstraint(Sketcher.Constraint('Coincident',8,2,5,1)) #--------------------------------------------------------------------------- # define the test cases to test the FreeCAD Sketcher module @@ -128,89 +126,126 @@ def CreateSlotPlateInnerSet(SketchFeature): class TestSketcherSolver(unittest.TestCase): - def setUp(self): - self.Doc = FreeCAD.newDocument("SketchSolverTest") - - def testBoxCase(self): - self.Box = self.Doc.addObject('Sketcher::SketchObject','SketchBox') - CreateBoxSketchSet(self.Box) - self.Doc.recompute() - # moving a point of the sketch - self.Box.movePoint(0,2,App.Vector(88.342697,28.174158,0)) - # fully constrain - self.Box.addConstraint(Sketcher.Constraint('DistanceX',1,2,90.0)) - self.Box.addConstraint(Sketcher.Constraint('DistanceY',1,2,-50.0)) - self.Doc.recompute() - - def testSlotCase(self): - self.Slot = self.Doc.addObject('Sketcher::SketchObject','SketchSlot') - CreateSlotPlateSet(self.Slot) - self.Doc.recompute() - # test if all edges created - self.failUnless(len(self.Slot.Shape.Edges) == 4) - CreateSlotPlateInnerSet(self.Slot) - self.Doc.recompute() - self.failUnless(len(self.Slot.Shape.Edges) == 9) - - def testIssue3245(self): - self.Doc2 = FreeCAD.newDocument("Issue3245") - self.Doc2.addObject('Sketcher::SketchObject','Sketch') - self.Doc2.Sketch.Placement = App.Placement(App.Vector(0.000000,0.000000,0.000000),App.Rotation(0.000000,0.000000,0.000000,1.000000)) - self.Doc2.Sketch.MapMode = "Deactivated" - self.Doc2.Sketch.addGeometry(Part.LineSegment(App.Vector(-1.195999,56.041161,0),App.Vector(60.654316,56.382877,0)),False) - self.Doc2.Sketch.addConstraint(Sketcher.Constraint('PointOnObject',0,1,-2)) - self.Doc2.Sketch.addConstraint(Sketcher.Constraint('Horizontal',0)) - self.Doc2.Sketch.addGeometry(Part.LineSegment(App.Vector(0.512583,32.121155,0),App.Vector(60.654316,31.779440,0)),False) - self.Doc2.Sketch.addConstraint(Sketcher.Constraint('Horizontal',1)) - self.Doc2.Sketch.addGeometry(Part.LineSegment(App.Vector(0.170867,13.326859,0),App.Vector(61.679455,13.326859,0)),False) - self.Doc2.Sketch.addConstraint(Sketcher.Constraint('PointOnObject',2,1,-2)) - self.Doc2.Sketch.addConstraint(Sketcher.Constraint('Horizontal',2)) - self.Doc2.Sketch.addConstraint(Sketcher.Constraint('PointOnObject',1,1,-2)) - self.Doc2.Sketch.addConstraint(Sketcher.Constraint('DistanceX',0,1,0,2,60.654316)) - self.Doc2.Sketch.setExpression('Constraints[6]', u'60') - self.Doc2.Sketch.addConstraint(Sketcher.Constraint('DistanceX',1,1,1,2,60.654316)) - self.Doc2.Sketch.setExpression('Constraints[7]', u'65') - self.Doc2.Sketch.addConstraint(Sketcher.Constraint('DistanceX',2,1,2,2,61.679455)) - self.Doc2.Sketch.setExpression('Constraints[8]', u'70') - self.Doc2.recompute() - self.Doc2.Sketch.delGeometry(2) - values = d = {key: value for (key, value) in self.Doc2.Sketch.ExpressionEngine} - self.failUnless(values['Constraints[4]'] == u'60') - self.failUnless(values['Constraints[5]'] == u'65') - FreeCAD.closeDocument("Issue3245") - - def testIssue3245_2(self): - self.Doc2 = FreeCAD.newDocument("Issue3245") - ActiveSketch = self.Doc2.addObject('Sketcher::SketchObject','Sketch') - ActiveSketch.Placement = App.Placement(App.Vector(0.000000,0.000000,0.000000),App.Rotation(0.000000,0.000000,0.000000,1.000000)) - ActiveSketch.MapMode = "Deactivated" - geoList = [] - geoList.append(Part.LineSegment(App.Vector(-23.574591,42.399727,0),App.Vector(81.949776,42.399727,0))) - geoList.append(Part.LineSegment(App.Vector(81.949776,42.399727,0),App.Vector(81.949776,-19.256901,0))) - geoList.append(Part.LineSegment(App.Vector(81.949776,-19.256901,0),App.Vector(-23.574591,-19.256901,0))) - geoList.append(Part.LineSegment(App.Vector(-23.574591,-19.256901,0),App.Vector(-23.574591,42.399727,0))) - ActiveSketch.addGeometry(geoList,False) - conList = [] - conList.append(Sketcher.Constraint('Coincident',0,2,1,1)) - conList.append(Sketcher.Constraint('Coincident',1,2,2,1)) - conList.append(Sketcher.Constraint('Coincident',2,2,3,1)) - conList.append(Sketcher.Constraint('Coincident',3,2,0,1)) - conList.append(Sketcher.Constraint('Horizontal',0)) - conList.append(Sketcher.Constraint('Horizontal',2)) - conList.append(Sketcher.Constraint('Vertical',1)) - conList.append(Sketcher.Constraint('Vertical',3)) - ActiveSketch.addConstraint(conList) - ActiveSketch.addConstraint(Sketcher.Constraint('DistanceX',0,1,0,2,105.524367)) - ActiveSketch.setExpression('Constraints[8]', u'10 + 10') - ActiveSketch.addConstraint(Sketcher.Constraint('DistanceY',3,1,3,2,61.656628)) - ActiveSketch.setDatum(9,App.Units.Quantity('5.000000 mm')) - ActiveSketch.delConstraint(8) - values = d = {key: value for (key, value) in self.Doc2.Sketch.ExpressionEngine} - self.Doc2.recompute() - self.failUnless(len(values) == 0) - FreeCAD.closeDocument("Issue3245") - - def tearDown(self): - #closing doc - FreeCAD.closeDocument("SketchSolverTest") - #print ("omit closing document for debugging") + def setUp(self): + self.Doc = FreeCAD.newDocument("SketchSolverTest") + + def testBoxCase(self): + self.Box = self.Doc.addObject('Sketcher::SketchObject','SketchBox') + CreateBoxSketchSet(self.Box) + self.Doc.recompute() + # moving a point of the sketch + self.Box.movePoint(0,2,App.Vector(88.342697,28.174158,0)) + # fully constrain + self.Box.addConstraint(Sketcher.Constraint('DistanceX',1,2,90.0)) + self.Box.addConstraint(Sketcher.Constraint('DistanceY',1,2,-50.0)) + self.Doc.recompute() + + def testSlotCase(self): + self.Slot = self.Doc.addObject('Sketcher::SketchObject','SketchSlot') + CreateSlotPlateSet(self.Slot) + self.Doc.recompute() + # test if all edges created + self.failUnless(len(self.Slot.Shape.Edges) == 4) + CreateSlotPlateInnerSet(self.Slot) + self.Doc.recompute() + self.failUnless(len(self.Slot.Shape.Edges) == 9) + + def testIssue3245(self): + self.Doc2 = FreeCAD.newDocument("Issue3245") + self.Doc2.addObject('Sketcher::SketchObject','Sketch') + self.Doc2.Sketch.Placement = App.Placement(App.Vector(0.000000,0.000000,0.000000),App.Rotation(0.000000,0.000000,0.000000,1.000000)) + self.Doc2.Sketch.MapMode = "Deactivated" + self.Doc2.Sketch.addGeometry(Part.LineSegment(App.Vector(-1.195999,56.041161,0),App.Vector(60.654316,56.382877,0)),False) + self.Doc2.Sketch.addConstraint(Sketcher.Constraint('PointOnObject',0,1,-2)) + self.Doc2.Sketch.addConstraint(Sketcher.Constraint('Horizontal',0)) + self.Doc2.Sketch.addGeometry(Part.LineSegment(App.Vector(0.512583,32.121155,0),App.Vector(60.654316,31.779440,0)),False) + self.Doc2.Sketch.addConstraint(Sketcher.Constraint('Horizontal',1)) + self.Doc2.Sketch.addGeometry(Part.LineSegment(App.Vector(0.170867,13.326859,0),App.Vector(61.679455,13.326859,0)),False) + self.Doc2.Sketch.addConstraint(Sketcher.Constraint('PointOnObject',2,1,-2)) + self.Doc2.Sketch.addConstraint(Sketcher.Constraint('Horizontal',2)) + self.Doc2.Sketch.addConstraint(Sketcher.Constraint('PointOnObject',1,1,-2)) + self.Doc2.Sketch.addConstraint(Sketcher.Constraint('DistanceX',0,1,0,2,60.654316)) + self.Doc2.Sketch.setExpression('Constraints[6]', u'60') + self.Doc2.Sketch.addConstraint(Sketcher.Constraint('DistanceX',1,1,1,2,60.654316)) + self.Doc2.Sketch.setExpression('Constraints[7]', u'65') + self.Doc2.Sketch.addConstraint(Sketcher.Constraint('DistanceX',2,1,2,2,61.679455)) + self.Doc2.Sketch.setExpression('Constraints[8]', u'70') + self.Doc2.recompute() + self.Doc2.Sketch.delGeometry(2) + values = d = {key: value for (key, value) in self.Doc2.Sketch.ExpressionEngine} + self.failUnless(values['Constraints[4]'] == u'60') + self.failUnless(values['Constraints[5]'] == u'65') + FreeCAD.closeDocument("Issue3245") + + def testIssue3245_2(self): + self.Doc2 = FreeCAD.newDocument("Issue3245") + ActiveSketch = self.Doc2.addObject('Sketcher::SketchObject','Sketch') + ActiveSketch.Placement = App.Placement(App.Vector(0.000000,0.000000,0.000000),App.Rotation(0.000000,0.000000,0.000000,1.000000)) + ActiveSketch.MapMode = "Deactivated" + geoList = [] + geoList.append(Part.LineSegment(App.Vector(-23.574591,42.399727,0),App.Vector(81.949776,42.399727,0))) + geoList.append(Part.LineSegment(App.Vector(81.949776,42.399727,0),App.Vector(81.949776,-19.256901,0))) + geoList.append(Part.LineSegment(App.Vector(81.949776,-19.256901,0),App.Vector(-23.574591,-19.256901,0))) + geoList.append(Part.LineSegment(App.Vector(-23.574591,-19.256901,0),App.Vector(-23.574591,42.399727,0))) + ActiveSketch.addGeometry(geoList,False) + conList = [] + conList.append(Sketcher.Constraint('Coincident',0,2,1,1)) + conList.append(Sketcher.Constraint('Coincident',1,2,2,1)) + conList.append(Sketcher.Constraint('Coincident',2,2,3,1)) + conList.append(Sketcher.Constraint('Coincident',3,2,0,1)) + conList.append(Sketcher.Constraint('Horizontal',0)) + conList.append(Sketcher.Constraint('Horizontal',2)) + conList.append(Sketcher.Constraint('Vertical',1)) + conList.append(Sketcher.Constraint('Vertical',3)) + ActiveSketch.addConstraint(conList) + ActiveSketch.addConstraint(Sketcher.Constraint('DistanceX',0,1,0,2,105.524367)) + ActiveSketch.setExpression('Constraints[8]', u'10 + 10') + ActiveSketch.addConstraint(Sketcher.Constraint('DistanceY',3,1,3,2,61.656628)) + ActiveSketch.setDatum(9,App.Units.Quantity('5.000000 mm')) + ActiveSketch.delConstraint(8) + values = d = {key: value for (key, value) in self.Doc2.Sketch.ExpressionEngine} + self.Doc2.recompute() + self.failUnless(len(values) == 0) + FreeCAD.closeDocument("Issue3245") + + def testBlockConstraintEllipse(self): + self.Doc3 = FreeCAD.newDocument("BlockConstraintTests") + ActiveSketch = self.Doc3.addObject('Sketcher::SketchObject','Sketch') + ActiveSketch.Placement = App.Placement(App.Vector(0.000000,0.000000,0.000000),App.Rotation(0.000000,0.000000,0.000000,1.000000)) + ActiveSketch.MapMode = "Deactivated" + ActiveSketch.addGeometry(Part.Ellipse(App.Vector(-19.129438,14.345055,0),App.Vector(-33.806261,12.085921,0),App.Vector(-30.689360,7.107538,0)),False) + ActiveSketch.solve() + ActiveSketch.exposeInternalGeometry(0) + ActiveSketch.solve() + ActiveSketch.movePoint(0,0,App.Vector(-26.266434,14.345055,0),0) + ActiveSketch.solve() + ActiveSketch.addConstraint(Sketcher.Constraint('Block',0)) # Block the Ellipse in place + ActiveSketch.addConstraint(Sketcher.Constraint('Block',1)) # Block the major axis in place (on purpose) + status = ActiveSketch.solve() + self.failUnless(status == 0) # no redundants/conflicts/convergence issues + ActiveSketch.addConstraint(Sketcher.Constraint('Distance',1,27.277350)) # Length of major axis + ActiveSketch.setDriving(6,True) # ensure length is driving (because pre-existing block constraint on major axis) + ActiveSketch.setDatum(6,App.Units.Quantity('28.000000 mm')) + status = ActiveSketch.solve() + self.failUnless(status == 0) # no redundants/conflicts/convergence issues + ActiveSketch.addConstraint(Sketcher.Constraint('Distance',2,11.747233)) # Length of minor axis + ActiveSketch.setDatum(7,App.Units.Quantity('15.000000 mm')) + ActiveSketch.solve() + self.failUnless(status == 0) # no redundants/conflicts/convergence issues + ActiveSketch.addConstraint(Sketcher.Constraint('Block',2)) + ActiveSketch.solve() + self.failUnless(status == 0) # no redundants/conflicts/convergence issues + ActiveSketch.addConstraint(Sketcher.Constraint('Horizontal',1)) # Make major axis horizontal (together with horizontal and length driving constraints) + ActiveSketch.solve() + self.failUnless(status == 0) # no redundants/conflicts/convergence issues + ActiveSketch.addConstraint(Sketcher.Constraint('DistanceX',0,3,-1,1,27.655024)) # Locate Ellipse center + ActiveSketch.addConstraint(Sketcher.Constraint('DistanceY',0,3,-1,1,-20.877021)) + ActiveSketch.setDatum(10,App.Units.Quantity('25.000000 mm')) + ActiveSketch.setDatum(11,App.Units.Quantity('-20.000000 mm')) + ActiveSketch.solve() + self.failUnless(status == 0) # no redundants/conflicts/convergence issues + + def tearDown(self): + #closing doc + FreeCAD.closeDocument("SketchSolverTest") + #print ("omit closing document for debugging")