Salome HOME
[bos #35151][EDF](2023-T1) Centered rectangle.
authordish <Dmitrii.SHVYDKOI@opencascade.com>
Thu, 7 Dec 2023 10:03:46 +0000 (10:03 +0000)
committerdish <dmitrii.shvydkoi@opencascade.com>
Thu, 9 May 2024 11:52:19 +0000 (11:52 +0000)
Central point is added to a sketch along with edges rectangle.

src/PythonAddons/Test/TestRectangleCentered.py
src/PythonAddons/macros/rectangle/feature.py
src/SketchAPI/SketchAPI_Rectangle.cpp
src/SketchAPI/SketchAPI_Rectangle.h
src/SketchAPI/SketchAPI_Sketch.cpp
src/SketchAPI/SketchAPI_Sketch.h
src/SketchPlugin/Test/TestRectangleCentered1.py [new file with mode: 0644]
src/SketchPlugin/doc/examples/rectangle.py
src/SketchPlugin/tests.set

index e806f8be923c0f9a97e30e1770aed9615985bbee..ae1fdbb26c7977fd49575c2ac454dd5d4814f111 100755 (executable)
@@ -38,13 +38,62 @@ def checkRectangle(lines, center, corner, tolerance = 1.e-7):
     ep_ref = points[i]
     assert(ep.distance(ep_ref) <= tolerance)
 
-def checkRectangleL(lines, valref, tolerance = 1.e-5):
-  for i in range(0, 4):
-    line = SketchAPI_Line(lines[i])
-    #print (line.defaultResult().shape().edge().length())
-    #print (valref[i%2])
-    #print (abs(line.defaultResult().shape().edge().length()-valref[i%2]))
-    assert(abs(line.defaultResult().shape().edge().length()-valref[i%2]) <= tolerance)
+def areCounterDirectedAndOfEqualLength(theLineA, theLineB):
+  tolerance = 1.e-5
+
+  aStartA = theLineA.startPoint().pnt()
+  aEndA   = theLineA.endPoint().pnt()
+  aDirA_X = aEndA.x() - aStartA.x()
+  aDirA_Y = aEndA.y() - aStartA.y()
+
+  aStartB = theLineB.startPoint().pnt()
+  aEndB   = theLineB.endPoint().pnt()
+  aDirB_X = aEndB.x() - aStartB.x()
+  aDirB_Y = aEndB.y() - aStartB.y()
+
+  return abs(aDirA_X + aDirB_X) < tolerance and abs(aDirA_Y + aDirB_Y) < tolerance
+
+def arePerpendicular(theLineA, theLineB):
+  tolerance = 1.e-5
+
+  aStartA = theLineA.startPoint().pnt()
+  aEndA   = theLineA.endPoint().pnt()
+  aDirA_X = aEndA.x() - aStartA.x()
+  aDirA_Y = aEndA.y() - aStartA.y()
+  aLengthA = theLineA.defaultResult().shape().edge().length()
+
+  aStartB = theLineB.startPoint().pnt()
+  aEndB   = theLineB.startPoint().pnt()
+  aDirB_X = aEndB.x() - aStartB.x()
+  aDirB_Y = aEndB.y() - aStartB.y()
+  aLengthB = theLineB.defaultResult().shape().edge().length()
+
+  if (aLengthA < tolerance or aLengthB < tolerance):
+    return True
+
+  return (aDirA_X * aDirB_X + aDirA_Y * aDirB_Y) / (aLengthA * aLengthB) < tolerance
+
+def areConnected(theLineA, theLineB):
+  aEndA   = theLineA.endPoint().pnt()
+  aStartB = theLineB.startPoint().pnt()
+  return aEndA.x() == aStartB.x() and aEndA.y() == aStartB.y()
+
+def checkIfArbitraryAlignedRectangle(theEdgeLines):
+  aLine0 = SketchAPI_Line(theEdgeLines[0])
+  aLine1 = SketchAPI_Line(theEdgeLines[1])
+  aLine2 = SketchAPI_Line(theEdgeLines[2])
+  aLine3 = SketchAPI_Line(theEdgeLines[3])
+
+  assert (areCounterDirectedAndOfEqualLength(aLine0, aLine2))
+  assert (areCounterDirectedAndOfEqualLength(aLine1, aLine3))
+  assert (arePerpendicular(aLine0, aLine1))
+  assert (arePerpendicular(aLine2, aLine3))
+  assert (areConnected(aLine0, aLine1))
+  assert (areConnected(aLine1, aLine2))
+  assert (areConnected(aLine2, aLine3))
+  assert (areConnected(aLine3, aLine0))       
+
+
 
 model.begin()
 partSet = model.moduleDocument()
@@ -80,34 +129,22 @@ checkRectangle(lines_3, SketchAPI_Line(lines_1[0]).startPoint().pnt(), SketchAPI
 # move center of rectangle
 SHIFT = 1.0
 center = SketchAPI_Line(lines_1[0]).startPoint().pnt()
-valref = [ \
-400.86931 , 200.78509 , \
-401.73886 , 201.57021 , \
-402.60865 , 202.35537 , \
-403.47866 , 203.14056 , \
-404.34890 , 203.92580 ]
 for i in range(0, 5):
   center.setX(center.x() + SHIFT)
   center.setY(center.y() + SHIFT)
   model.begin()
   sketch.move(SketchAPI_Line(lines_1[0]).startPoint(), center)
   model.end()
-  checkRectangleL(lines_3, valref[2*i:])
+  checkIfArbitraryAlignedRectangle(lines_3)
 
 # move corner of rectangle
 corner = SketchAPI_Line(lines_2[0]).endPoint().pnt()
-valref = [ \
-403.11209 , 202.95437 , \
-401.87551 , 201.98300 , \
-400.63915 , 201.01169 , \
-399.40301 , 200.04043 , \
-398.16710 , 199.06922 ]
 for i in range(0, 5):
   corner.setX(corner.x() + SHIFT)
   corner.setY(corner.y() + SHIFT)
   model.begin()
   sketch.move(SketchAPI_Line(lines_2[0]).endPoint(), corner)
   model.end()
-  checkRectangleL(lines_3, valref[2*i:])
+  checkIfArbitraryAlignedRectangle(lines_3)
 
 assert(model.checkPythonDump())
index 991c55a04e3cb33821a2815ea3e75fcdeb696cc2..d2df5435642ab693c5be44bba2a62f0647b692f5 100755 (executable)
@@ -106,15 +106,15 @@ class SketchPlugin_Rectangle(model.Feature):
         """Override Feature.initAttributes()"""
         # Flag whether the rectangle is accessory
         self.data().addAttribute(self.AUXILIARY_ID(), ModelAPI.ModelAPI_AttributeBoolean.typeId())
-        # Creating corners of the rectangle
+        # Corners of the rectangle (being defined by opposite corners)
         self.data().addAttribute(self.START_ID(), GeomDataAPI.GeomDataAPI_Point2D.typeId())
         self.data().addAttribute(self.END_ID(), GeomDataAPI.GeomDataAPI_Point2D.typeId())
-        # Creating list to store lines
+        # List with both contour and diagonal lines
         self.data().addAttribute(self.LINES_LIST_ID(), ModelAPI.ModelAPI_AttributeRefList.typeId())
         ModelAPI.ModelAPI_Session.get().validators().registerNotObligatory(self.getKind(), self.LINES_LIST_ID())
         # Type of rectangle
         self.data().addAttribute(self.RECTANGLE_TYPE_ID(), ModelAPI.ModelAPI_AttributeString.typeId())
-        # Center and corner of the rectangle
+        # Center and corner of centered rectangle (being defined by center and corner)
         self.data().addAttribute(self.CENTER_ID(), GeomDataAPI.GeomDataAPI_Point2D.typeId())
         self.data().addAttribute(self.CORNER_ID(), GeomDataAPI.GeomDataAPI_Point2D.typeId())
 
@@ -128,144 +128,191 @@ class SketchPlugin_Rectangle(model.Feature):
         """
         return True
 
-# Edition of the rectangle
+
+# Editing of a rectangle. It is called on select of the first generative point
+# and on hover of the second generative point. Generative point is either corner or center,
+# depending on rectangle type. And also it is called on call of Sketch.addRectangleCentered(...) in TUI.
 
     def execute(self):
-        # Retrieving list of already created lines
-        aLinesList = self.reflist(self.LINES_LIST_ID())
-        aNbLines = aLinesList.size()
-        if aNbLines == 1:
-            # Create 1-4 lines to compose the rectangle
-            for i in range (0, 3):
+        # Retrieve list of already created lines
+        aLinesList = self.reflist(self.LINES_LIST_ID())        
+        if aLinesList.size() == 1:
+            # Create remaining rectangle contour lines
+            for i in range (1, 4):
                 aLine = self.__sketch.addFeature("SketchLine")
                 aLinesList.append(aLine)
-            self.updateLines()
-            aNbLines = aLinesList.size()
+            self.__updateLines()
+
+            # Connect rectangle contour lines
             aStartPoints = []
-            # Create constraints to keep the rectangle
-            for i in range (0, aNbLines):
+            for i in range (0, 4):
                 aLine = ModelAPI.objectToFeature(aLinesList.object(i))
-                # connect neighbor lines by coincidence
-                iPrev = i - 1
-                if iPrev < 0:
-                    iPrev = aNbLines - 1
+                iPrev = i - 1 if i != 0 else 3
                 aPrevLine = ModelAPI.objectToFeature(aLinesList.object(iPrev))
                 aCoincidence = self.__sketch.addFeature("SketchConstraintCoincidence")
-                aRefAttrA = aCoincidence.refattr("ConstraintEntityA")
-                aRefAttrB = aCoincidence.refattr("ConstraintEntityB")
-                aRefAttrA.setAttr(aPrevLine.attribute("EndPoint"))
-                aRefAttrB.setAttr(aLine.attribute("StartPoint"))
+                aCoincidence.refattr("ConstraintEntityA").setAttr(aPrevLine.attribute("EndPoint"))
+                aCoincidence.refattr("ConstraintEntityB").setAttr(aLine.attribute("StartPoint"))
                 aStartPoints.append(aLine.attribute("StartPoint"))
-            # Flags which show perpendicular constraint is build for correponding line
-            self.__isPERP = [False, False, False]
+
             # Update coordinates of created lines
-            self.updateLines()
+            self.__updateLines()
+
+            # Flags, indicating perpendicular constraint is imposed on contour lines i and i+1.
+            self.__isPERP = [False, False, False]
+
             # Create auxiliary diagonals in case of centered rectangle
             if self.string(self.RECTANGLE_TYPE_ID()).value() == self.RECTANGLE_CENTERED_ID():
                 aDiag1 = self.__sketch.addFeature("SketchLine")
                 aLinesList.append(aDiag1)
                 aDiag2 = self.__sketch.addFeature("SketchLine")
                 aLinesList.append(aDiag2)
-                # coincidences in corners
+
+                # Add coincidences between diagonals' endpoints and rectangle vertices
                 aPoints = [aDiag1.attribute("StartPoint"), aDiag2.attribute("StartPoint"),
                            aDiag1.attribute("EndPoint"), aDiag2.attribute("EndPoint")]
+                
                 for i in range (0, len(aPoints)):
                     aCoincidence = self.__sketch.addFeature("SketchConstraintCoincidence")
-                    aRefAttrA = aCoincidence.refattr("ConstraintEntityA")
-                    aRefAttrB = aCoincidence.refattr("ConstraintEntityB")
-                    aRefAttrA.setAttr(aStartPoints[i])
-                    aRefAttrB.setAttr(aPoints[i])
+                    aCoincidence.refattr("ConstraintEntityA").setAttr(aStartPoints[i])
+                    aCoincidence.refattr("ConstraintEntityB").setAttr(aPoints[i])
+
                 # Update coordinates of created lines
-                self.updateLines()
+                self.__updateLines()
                 aDiag1.execute()
                 aDiag2.execute()
+
                 # coincidences between center point and diagonals
-                refPnt = self.getReferencePoint(self.refattr(self.CENTER_REF_ID()))
-                if refPnt is not None:
+                attr = self.__getPoint2DAttrOfSketchPoint(self.refattr(self.CENTER_REF_ID()))
+                if attr is not None:
                     for line in [aDiag1.lastResult(), aDiag2.lastResult()]:
                         aCoincidence = self.__sketch.addFeature("SketchConstraintCoincidence")
-                        aCoincidence.refattr("ConstraintEntityA").setAttr(refPnt)
+                        aCoincidence.refattr("ConstraintEntityA").setAttr(attr)
                         aCoincidence.refattr("ConstraintEntityB").setObject(line)
-        # Perpendicular for the lines which already have result
+
+        # Add perpendicular constraints to the contour lines, which already have result
         for i in range (0, 3):
             if self.__isPERP[i]:
                 continue
+            
             aLine_A = ModelAPI.objectToFeature(aLinesList.object(i))
             aLineResult_A = aLine_A.lastResult()
             if aLineResult_A is None:
                 continue
+
             aLine_B = ModelAPI.objectToFeature(aLinesList.object(i+1))
             aLineResult_B = aLine_B.lastResult()
             if aLineResult_B is None:
                 continue
-            aHVConstraint = self.__sketch.addFeature("SketchConstraintPerpendicular")
-            refattrA = aHVConstraint.refattr("ConstraintEntityA")
-            refattrA.setObject(aLine_A.lastResult())
-            refattrB = aHVConstraint.refattr("ConstraintEntityB")
-            refattrB.setObject(aLine_B.lastResult())
+
+            aConstraintPerp = self.__sketch.addFeature("SketchConstraintPerpendicular")
+            aConstraintPerp.refattr("ConstraintEntityA").setObject(aLine_A.lastResult())
+            aConstraintPerp.refattr("ConstraintEntityB").setObject(aLine_B.lastResult())
             self.__isPERP[i] = True
 
+
     def attributeChanged(self, theID):
         if theID == self.START_ID() or theID == self.END_ID() or theID == self.CENTER_ID() or theID == self.CENTER_REF_ID() or theID == self.CORNER_ID():
-            # Search the sketch containing this rectangle
+            # Find the sketch containing this rectangle
             self.__sketch = None
-            aRefs = self.data().refsToMe();
-            for iter in aRefs:
-                aFeature = ModelAPI.objectToFeature(iter.owner())
+            aRefsToMe = self.data().refsToMe()
+            for aRefToMe in aRefsToMe:
+                aFeature = ModelAPI.objectToFeature(aRefToMe.owner())
                 if aFeature.getKind() == "Sketch":
                     self.__sketch = ModelAPI.featureToCompositeFeature(aFeature)
                     break
 
+            if theID == self.CENTER_ID():
+                aCenter = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.CENTER_ID())) # shared_ptr to Point2D
+                aCenterSketchPointAttr = self.refattr(self.CENTER_REF_ID())
+                if (not aCenterSketchPointAttr.isInitialized()):
+                    # Create SketchPoint. In .execute() it will be constrained to keep center.
+                    aCenterSketchPoint = self.__sketch.addFeature("SketchPoint")
+                    aCenterSketchPoint.data().boolean("Auxiliary").setValue(True)
+                    aCenterSketchPointCoords = GeomDataAPI.geomDataAPI_Point2D(aCenterSketchPoint.attribute("PointCoordinates")) # shared_ptr to Point2D                    
+                    aCenterSketchPointCoords.setValue(aCenter.x(), aCenter.y())
+                    aCenterSketchPointAttr.setObject(aCenterSketchPoint)
+                else:
+                    # Maintain consistency between center SketchPoint and center Point2D.
+                    aCenterSketchPointCoordsAttr = self.__getPoint2DAttrOfSketchPoint(aCenterSketchPointAttr)
+                    if (aCenterSketchPointCoordsAttr == None):
+                        Warning("Faulty logic")
+                    else:
+                        aCenterSketchPointCoords = GeomDataAPI.geomDataAPI_Point2D(aCenterSketchPointCoordsAttr) # shared_ptr to Point2D
+                        aCenterSketchPointCoords.setValue(aCenter.x(), aCenter.y())
+            elif theID == self.CENTER_REF_ID():
+                aCenterSketchPointAttr = self.refattr(self.CENTER_REF_ID())
+                aCenterSketchPointCoordsAttr = self.__getPoint2DAttrOfSketchPoint(aCenterSketchPointAttr) # shared_ptr to Point2D
+                if (aCenterSketchPointCoordsAttr == None):
+                    Warning("Faulty logic. Attempt to set rectangle's attribute " + self.CENTER_REF_ID() + " not with refattr to SketchPoint.")
+                    return
+                
+                # Maintain consistency between center SketchPoint and center Point2D.
+                aCenterSketchPointCoords = GeomDataAPI.geomDataAPI_Point2D(aCenterSketchPointCoordsAttr) # shared_ptr to Point2D
+                aCenter = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.CENTER_ID())) # shared_ptr to Point2D
+                aCenter.setValue(aCenterSketchPointCoords.x(), aCenterSketchPointCoords.y())
+
             aLinesList = self.reflist(self.LINES_LIST_ID())
             aNbLines = aLinesList.size()
             if aNbLines == 0:
-                # Create first line to be able to create a coincidence with selected point/feature
-                for i in range (0, 1):
-                    aLine = self.__sketch.addFeature("SketchLine")
-                    aLinesList.append(aLine)
+                # If only one generative point is iniialized,
+                # do not create the full set of contour lines to not clutter
+                # UI with icons of constraints near the mouse pointer.
+                aLine = self.__sketch.addFeature("SketchLine")
+                aLinesList.append(aLine)                    
 
             aStartPoint = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.START_ID()))
             aEndPoint = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.END_ID()))
-            aCenter = self.getPointByRef(self.attribute(self.CENTER_ID()), self.refattr(self.CENTER_REF_ID()))
+            aCenter = self.__getPoint2D(self.attribute(self.CENTER_ID()), self.refattr(self.CENTER_REF_ID()))
             aCorner = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.CORNER_ID()))
+
             if (aStartPoint.isInitialized() and aEndPoint.isInitialized()) or (aCenter is not None and aCorner.isInitialized()):
-              self.updateLines()
+              self.__updateLines()
             else:
-              self.updateStartPoint()
-        if theID == self.AUXILIARY_ID():
+              self.__updateLinesWithOnlyGenerativePoint()
+        elif theID == self.AUXILIARY_ID():
+            # Change aux attribute of contour lines
             anAuxiliary = self.data().boolean(self.AUXILIARY_ID()).value()
-            aLinesList = self.reflist(self.LINES_LIST_ID())
-            aNbLines = aLinesList.size()
-            # Update coordinates of rectangle lines
-            for i in range (0, aNbLines):
+            aLinesList = self.reflist(self.LINES_LIST_ID())            
+            for i in range (0, min(aLinesList.size(), 4)):
                 aLine = ModelAPI.objectToFeature(aLinesList.object(i))
                 aLine.data().boolean("Auxiliary").setValue(anAuxiliary)
-
-    def getReferencePoint(self, theRef):
-        if theRef.isObject() and theRef.object() is not None:
-            feature = ModelAPI.ModelAPI_Feature.feature(theRef.object())
+        elif theID == self.RECTANGLE_TYPE_ID():
+            # TODO Find a way to distinguish "attribute changed" events on hover and on click.
+            # Now, if both generative points are selected, but the rectangle is not applied,
+            # and then rectangle type is changed, the unapplied rectangle is erased. 
+            # It should be applied instead.
+            aLinesList = self.reflist(self.LINES_LIST_ID()).clear()
+            aCenterSketchPointAttr = self.refattr(self.CENTER_REF_ID())
+            aCenterSketchPointAttr.reset()
+            self.attribute(self.START_ID()).reset()
+            self.attribute(self.END_ID()).reset()
+            self.attribute(self.CENTER_ID()).reset()
+            self.attribute(self.CORNER_ID()).reset()
+
+
+    def __getPoint2DAttrOfSketchPoint(self, theSketchPointRefAttr):
+        if theSketchPointRefAttr.isObject() and theSketchPointRefAttr.object() is not None:
+            feature = ModelAPI.ModelAPI_Feature.feature(theSketchPointRefAttr.object())
             if feature.getKind() == "SketchPoint":
                 return feature.attribute("PointCoordinates")
         else:
-            return theRef.attr()
-        return None
-
-    def getPointByRef(self, thePoint, theRef):
-        attr = thePoint
-        if theRef.isInitialized():
-            refPnt = self.getReferencePoint(theRef)
-            if refPnt is not None:
-                attr = refPnt
+            return theSketchPointRefAttr.attr()
+
+
+    def __getPoint2D(self, thePoint2DAttr, theSketchPointRefAttr):
+        attr = thePoint2DAttr
+        if theSketchPointRefAttr.isInitialized():
+            aPoint2DAttr = self.__getPoint2DAttrOfSketchPoint(theSketchPointRefAttr)
+            if aPoint2DAttr is not None:
+                attr = aPoint2DAttr
         if attr is None or not attr.isInitialized():
             return None
         return GeomDataAPI.geomDataAPI_Point2D(attr).pnt()
 
-    def updateLines(self):
-        # Retrieving list of already created lines
-        aLinesList = self.reflist(self.LINES_LIST_ID())
-        aNbLines = min(aLinesList.size(), 4)
+
+    def __updateLines(self):
         if self.string(self.RECTANGLE_TYPE_ID()).value() == self.RECTANGLE_CENTERED_ID():
-            aCenter = self.getPointByRef(self.attribute(self.CENTER_ID()), self.refattr(self.CENTER_REF_ID()))
+            aCenter = self.__getPoint2D(self.attribute(self.CENTER_ID()), self.refattr(self.CENTER_REF_ID()))
             aCorner = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.CORNER_ID()))
             aStartX = 2.0 * aCenter.x() - aCorner.x()
             aStartY = 2.0 * aCenter.y() - aCorner.y()
@@ -275,16 +322,20 @@ class SketchPlugin_Rectangle(model.Feature):
             aStartPoint = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.START_ID()))
             aEndPoint = GeomDataAPI.geomDataAPI_Point2D(self.attribute(self.END_ID()))
             aX = [aStartPoint.x(), aStartPoint.x(), aEndPoint.x(), aEndPoint.x()]
-            aY = [aStartPoint.y(), aEndPoint.y(), aEndPoint.y(), aStartPoint.y()]
-        anAuxiliary = self.data().boolean(self.AUXILIARY_ID()).value()
+            aY = [aStartPoint.y(), aEndPoint.y(), aEndPoint.y(), aStartPoint.y()]        
+
+        # Retrieve list of already created lines
+        aLinesList = self.reflist(self.LINES_LIST_ID())
+        aNumOfContourLines = min(aLinesList.size(), 4)
 
-        # do not recalculate the rectrangle after each update
+        # Do not update lines during update of coordinates
         wasBlocked = []
         for i in range (0, aLinesList.size()):
             wasBlocked.append(aLinesList.object(i).data().blockSendAttributeUpdated(True))
 
         # Update coordinates of rectangle lines
-        for i in range (0, aNbLines):
+        anAuxiliary = self.data().boolean(self.AUXILIARY_ID()).value()
+        for i in range (0, aNumOfContourLines):
             aLine = ModelAPI.objectToFeature(aLinesList.object(i))
             aLineStart = GeomDataAPI.geomDataAPI_Point2D(aLine.attribute("StartPoint"))
             aLineEnd = GeomDataAPI.geomDataAPI_Point2D(aLine.attribute("EndPoint"))
@@ -294,20 +345,21 @@ class SketchPlugin_Rectangle(model.Feature):
 
         # Update auxiliary diagonals
         if self.string(self.RECTANGLE_TYPE_ID()).value() == self.RECTANGLE_CENTERED_ID():
-            for i in range (aNbLines, aLinesList.size()):
+            for i in range (aNumOfContourLines, aLinesList.size()):
                 aLine = ModelAPI.objectToFeature(aLinesList.object(i))
                 aLineStart = GeomDataAPI.geomDataAPI_Point2D(aLine.attribute("StartPoint"))
                 aLineEnd = GeomDataAPI.geomDataAPI_Point2D(aLine.attribute("EndPoint"))
-                aLineStart.setValue(aX[i-aNbLines-1], aY[i-aNbLines-1])
-                aLineEnd.setValue(aX[i-aNbLines+1], aY[i-aNbLines+1])
+                aLineStart.setValue(aX[i-aNumOfContourLines-1], aY[i-aNumOfContourLines-1])
+                aLineEnd.setValue(aX[i-aNumOfContourLines+1], aY[i-aNumOfContourLines+1])
                 aLine.data().boolean("Auxiliary").setValue(True)
 
-        # update the rectangle
+        # Update the rectangle
         for i in range (0, aLinesList.size()):
             aLinesList.object(i).data().blockSendAttributeUpdated(wasBlocked[i], True)
 
-    def updateStartPoint(self):
-        # Retrieving list of already created lines
+
+    def __updateLinesWithOnlyGenerativePoint(self):
+        # Retrieve list of already created lines
         aLinesList = self.reflist(self.LINES_LIST_ID())
         aNbLines = aLinesList.size()
 
@@ -316,7 +368,7 @@ class SketchPlugin_Rectangle(model.Feature):
             aX = aStartPoint.x()
             aY = aStartPoint.y()
         else:
-            aCenter = self.getPointByRef(self.attribute(self.CENTER_ID()), self.refattr(self.CENTER_REF_ID()))
+            aCenter = self.__getPoint2D(self.attribute(self.CENTER_ID()), self.refattr(self.CENTER_REF_ID()))
             aX = aCenter.x()
             aY = aCenter.y()
 
index 303a3bcf01f6dae931144f3ad1be31402d8abeb7..cd1e2c233caf59c6fc5518b0daebb79e3ae5d544 100644 (file)
@@ -34,21 +34,24 @@ SketchAPI_Rectangle::SketchAPI_Rectangle(
 }
 
 SketchAPI_Rectangle::SketchAPI_Rectangle(const std::shared_ptr<ModelAPI_Feature> & theFeature,
-                                         double theX1, double theY1, double theX2, double theY2)
+                                         double theX1, double theY1, 
+                                         double theX2, double theY2,
+                                         bool theCreateByCenterAndCorner)
   : SketchAPI_SketchEntity(theFeature)
 {
   if (initialize()) {
-    setByCoordinates(theX1, theY1, theX2, theY2);
+    theCreateByCenterAndCorner ? setByCenterAndCornerCoords(theX1, theY1, theX2, theY2) : setByCoordinates(theX1, theY1, theX2, theY2);
   }
 }
 
 SketchAPI_Rectangle::SketchAPI_Rectangle(const std::shared_ptr<ModelAPI_Feature> & theFeature,
-                                         const std::shared_ptr<GeomAPI_Pnt2d> & theFirstPoint,
-                                         const std::shared_ptr<GeomAPI_Pnt2d> & theEndPoint)
+                                         const std::shared_ptr<GeomAPI_Pnt2d> & thePoint1,
+                                         const std::shared_ptr<GeomAPI_Pnt2d> & thePoint2,
+                                         bool theCreateByCenterAndCorner)
   : SketchAPI_SketchEntity(theFeature)
 {
   if (initialize()) {
-    setByPoints(theFirstPoint, theEndPoint);
+    theCreateByCenterAndCorner ? setByCenterAndCornerPoints(thePoint1, thePoint2) : setByPoints(thePoint1, thePoint2);
   }
 }
 
@@ -75,6 +78,26 @@ void SketchAPI_Rectangle::setByPoints(const std::shared_ptr<GeomAPI_Pnt2d> & the
   execute();
 }
 
+void SketchAPI_Rectangle::setByCenterAndCornerCoords(
+  double theCenterX, double theCenterY, 
+  double theCornerX, double theCornerY
+) {
+  fillAttribute("RectangleTypeCentered", type());
+  fillAttribute(centerPoint(), theCenterX, theCenterY);
+  fillAttribute(cornerPoint(), theCornerX, theCornerY);
+  execute();
+}
+
+void SketchAPI_Rectangle::setByCenterAndCornerPoints(
+  const std::shared_ptr<GeomAPI_Pnt2d> & theCenterPoint,
+  const std::shared_ptr<GeomAPI_Pnt2d> & theCornerPoint
+) {
+  fillAttribute("RectangleTypeCentered", type());
+  fillAttribute(theCenterPoint, centerPoint());
+  fillAttribute(theCornerPoint, cornerPoint());
+  execute();
+}
+
 //--------------------------------------------------------------------------------------
 
 std::list<std::shared_ptr<SketchAPI_SketchEntity> > SketchAPI_Rectangle::lines() const
index b504729ea97eb5fbabcf908a0dd9b798a007a669..c570ddeb2f4db559320251cd6c624435bc66bcad 100644 (file)
@@ -41,12 +41,18 @@ public:
   /// Constructor with values
   SKETCHAPI_EXPORT
   SketchAPI_Rectangle(const std::shared_ptr<ModelAPI_Feature> & theFeature,
-                      double theX1, double theY1, double theX2, double theY2);
+                      double theX1, double theY1, 
+                      double theX2, double theY2, 
+                      bool theCreateByCenterAndCorner = false);
   /// Constructor with values
   SKETCHAPI_EXPORT
   SketchAPI_Rectangle(const std::shared_ptr<ModelAPI_Feature> & theFeature,
-                      const std::shared_ptr<GeomAPI_Pnt2d> & theFirstPoint,
-                      const std::shared_ptr<GeomAPI_Pnt2d> & theEndPoint);
+                      const std::shared_ptr<GeomAPI_Pnt2d> & thePoint1,
+                      const std::shared_ptr<GeomAPI_Pnt2d> & thePoint2,
+                      bool theCreateByCenterAndCorner = false);
+
+  
+
   /// Destructor
   SKETCHAPI_EXPORT
   virtual ~SketchAPI_Rectangle();
@@ -77,6 +83,20 @@ public:
   void setByPoints(const std::shared_ptr<GeomAPI_Pnt2d> & theFirstPoint,
                    const std::shared_ptr<GeomAPI_Pnt2d> & theSecondPoint);
 
+  /// Set by coordinates
+  SKETCHAPI_EXPORT
+  void setByCenterAndCornerCoords(
+    double theCenterX, double theCenterY, 
+    double theCornerX, double theCornerY
+  );
+
+  /// Set by points
+  SKETCHAPI_EXPORT
+  void setByCenterAndCornerPoints(
+    const std::shared_ptr<GeomAPI_Pnt2d> & theCenterPoint,
+    const std::shared_ptr<GeomAPI_Pnt2d> & theCornerPoint
+  );
+
   /// List of lines composing rectangle
   SKETCHAPI_EXPORT std::list<std::shared_ptr<SketchAPI_SketchEntity> > lines() const;
 };
index 028b1b2d365bfe0a5ad760a34e673efcc04f807f..b3b5b2251304056ae406e5565d0a9b0694281d57 100644 (file)
@@ -533,18 +533,9 @@ std::shared_ptr<SketchAPI_Line> SketchAPI_Sketch::addLine(const std::wstring & t
 std::shared_ptr<SketchAPI_Rectangle> SketchAPI_Sketch::addRectangle(double theX1, double theY1,
                                                                     double theX2, double theY2)
 {
-  std::shared_ptr<ModelAPI_Feature> aFeature =
-    compositeFeature()->addFeature(SketchAPI_Rectangle::ID());
+  std::shared_ptr<ModelAPI_Feature> aFeature = compositeFeature()->addFeature(SketchAPI_Rectangle::ID());
   return RectanglePtr(new SketchAPI_Rectangle(aFeature, theX1, theY1, theX2, theY2));
 }
-std::shared_ptr<SketchAPI_Rectangle> SketchAPI_Sketch::addRectangle(
-    const std::shared_ptr<GeomAPI_Pnt2d> & theStartPoint,
-    const std::shared_ptr<GeomAPI_Pnt2d> & theEndPoint)
-{
-  std::shared_ptr<ModelAPI_Feature> aFeature =
-    compositeFeature()->addFeature(SketchAPI_Rectangle::ID());
-  return RectanglePtr(new SketchAPI_Rectangle(aFeature, theStartPoint, theEndPoint));
-}
 
 static std::shared_ptr<GeomAPI_Pnt2d> pointCoordinates(
     const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr> & thePoint)
@@ -559,29 +550,57 @@ static std::shared_ptr<GeomAPI_Pnt2d> pointCoordinates(
       anAttr = aFeature->attribute(SketchPlugin_Point::COORD_ID());
   }
 
-  std::shared_ptr<GeomDataAPI_Point2D> aPntAttr =
-      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttr);
+  std::shared_ptr<GeomDataAPI_Point2D> aPntAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttr);
   if (aPntAttr)
     return aPntAttr->pnt();
   return std::shared_ptr<GeomAPI_Pnt2d>();
 }
 
+std::shared_ptr<SketchAPI_Rectangle> SketchAPI_Sketch::addRectangle(
+      const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr> & theStartPoint,
+      const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr> & theEndPoint) 
+{
+  std::shared_ptr<ModelAPI_Feature> aFeature = compositeFeature()->addFeature(SketchAPI_Rectangle::ID());
+  RectanglePtr aRect(new SketchAPI_Rectangle(aFeature));
+  fillAttribute("RectangleTypeByCorners", aRect->type());
+  fillAttribute(pointCoordinates(theStartPoint), aRect->startPoint());
+  fillAttribute(pointCoordinates(theEndPoint), aRect->endPoint());
+  aRect->execute();
+
+  if (!theStartPoint.second.isEmpty() && aRect->linesList()->size() >= 1) {
+    // Get end point of the first line in rectangle and apply coincidence constraint
+    FeaturePtr aLine = ModelAPI_Feature::feature(aRect->linesList()->object(0));
+    AttributePtr aLinePnt = aLine->attribute(SketchPlugin_Line::END_ID());
+    setCoincident(ModelHighAPI_RefAttr(aLinePnt), theStartPoint.second);
+  }
+
+  if (!theEndPoint.second.isEmpty() && aRect->linesList()->size() >= 4) {
+    // Get start point of the last line in rectangle and apply coincidence constraint
+    FeaturePtr aLine = ModelAPI_Feature::feature(aRect->linesList()->object(3));
+    AttributePtr aLinePnt = aLine->attribute(SketchPlugin_Line::START_ID());
+    setCoincident(ModelHighAPI_RefAttr(aLinePnt), theEndPoint.second);
+  }
+  return aRect;
+}
+
 std::shared_ptr<SketchAPI_Rectangle> SketchAPI_Sketch::addRectangleCentered(
     const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr> & theCenter,
     const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr> & theCorner)
 {
-  std::shared_ptr<ModelAPI_Feature> aFeature =
-    compositeFeature()->addFeature(SketchAPI_Rectangle::ID());
+  std::shared_ptr<ModelAPI_Feature> aFeature = compositeFeature()->addFeature(SketchAPI_Rectangle::ID());
   RectanglePtr aRect(new SketchAPI_Rectangle(aFeature));
   fillAttribute("RectangleTypeCentered", aRect->type());
+
   if (!theCenter.second.isEmpty())
     fillAttribute(theCenter.second, aRect->centerPointRef());
-  fillAttribute(pointCoordinates(theCenter), aRect->centerPoint());
+  else
+    fillAttribute(pointCoordinates(theCenter), aRect->centerPoint());
+  
   fillAttribute(pointCoordinates(theCorner), aRect->cornerPoint());
   aRect->execute();
 
-  if (!theCorner.second.isEmpty() && aRect->linesList()->size() > 1) {
-    // get start point of the last line in rectangle and apply coindidence constraint
+  if (!theCorner.second.isEmpty() && aRect->linesList()->size() >= 4) {
+    // get start point of the last line in rectangle and apply coincidence constraint
     FeaturePtr aLine = ModelAPI_Feature::feature(aRect->linesList()->object(3));
     AttributePtr aEndPnt = aLine->attribute(SketchPlugin_Line::START_ID());
     setCoincident(ModelHighAPI_RefAttr(aEndPnt), theCorner.second);
@@ -589,6 +608,14 @@ std::shared_ptr<SketchAPI_Rectangle> SketchAPI_Sketch::addRectangleCentered(
   return aRect;
 }
 
+std::shared_ptr<SketchAPI_Rectangle> SketchAPI_Sketch::addRectangleCentered(
+      double theCenterX, double theCenterY, 
+      double theCornerX, double theCornerY
+) {
+  std::shared_ptr<ModelAPI_Feature> aFeature = compositeFeature()->addFeature(SketchAPI_Rectangle::ID());
+  return RectanglePtr(new SketchAPI_Rectangle(aFeature, theCenterX, theCenterY, theCornerX, theCornerY, true));
+}
+
 //--------------------------------------------------------------------------------------
 std::shared_ptr<SketchAPI_Circle> SketchAPI_Sketch::addCircle(double theCenterX,
                                                               double theCenterY,
index f9eea73cbfd7f0e38784669436389f12f78d7308..fffa685caa26c138c01cee3e12bb6ee0d29e3d17 100644 (file)
@@ -179,13 +179,19 @@ public:
   /// Add rectangle
   SKETCHAPI_EXPORT
   std::shared_ptr<SketchAPI_Rectangle> addRectangle(
-      const std::shared_ptr<GeomAPI_Pnt2d> & theStartPoint,
-      const std::shared_ptr<GeomAPI_Pnt2d> & theEndPoint);
+      const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr> & theStartPoint,
+      const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr> & theEndPoint);    
   /// Add rectangle
   SKETCHAPI_EXPORT
   std::shared_ptr<SketchAPI_Rectangle> addRectangleCentered(
       const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr> & theCenter,
       const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr> & theCorner);
+  /// Add rectangle
+  SKETCHAPI_EXPORT
+  std::shared_ptr<SketchAPI_Rectangle> addRectangleCentered(
+      double theCenterX, double theCenterY, 
+      double theCornerX, double theCornerY
+  );
 
   /// Add circle
   SKETCHAPI_EXPORT
diff --git a/src/SketchPlugin/Test/TestRectangleCentered1.py b/src/SketchPlugin/Test/TestRectangleCentered1.py
new file mode 100644 (file)
index 0000000..86c3b9e
--- /dev/null
@@ -0,0 +1,174 @@
+# Copyright (C) 2014-2023  CEA, EDF
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+"""
+    TestRectangle.py
+    Unit test of SketchPlugin_Rectangle class (centered rectangle case)
+
+"""
+from GeomDataAPI import *
+from ModelAPI import *
+import math
+from salome.shaper import model
+
+#=========================================================================
+# Initialization of the test
+#=========================================================================
+
+__updated__ = "2023-08-07"
+
+
+#=========================================================================
+# Auxiliary functions
+#=========================================================================
+def isHorizontal(line):
+    aStart = geomDataAPI_Point2D(line.attribute("StartPoint"))
+    aEnd   = geomDataAPI_Point2D(line.attribute("EndPoint"))
+    return aStart.y() == aEnd.y()
+
+def isVertical(line):
+    aStart = geomDataAPI_Point2D(line.attribute("StartPoint"))
+    aEnd   = geomDataAPI_Point2D(line.attribute("EndPoint"))
+    return aStart.x() == aEnd.x()
+
+def areCounterDirectedAndOfEqualLength(theLineA, theLineB):
+    tolerance = 1.e-5
+
+    aStartA = geomDataAPI_Point2D(theLineA.attribute("StartPoint"))
+    aEndA   = geomDataAPI_Point2D(theLineA.attribute("EndPoint"))
+    aDirA_X = aEndA.x() - aStartA.x()
+    aDirA_Y = aEndA.y() - aStartA.y()
+
+    aStartB = geomDataAPI_Point2D(theLineB.attribute("StartPoint"))
+    aEndB   = geomDataAPI_Point2D(theLineB.attribute("EndPoint"))
+    aDirB_X = aEndB.x() - aStartB.x()
+    aDirB_Y = aEndB.y() - aStartB.y()
+
+    return abs(aDirA_X + aDirB_X) < tolerance and abs(aDirA_Y + aDirB_Y) < tolerance
+
+def arePerpendicular(theLineA, theLineB):
+    tolerance = 1.e-5
+
+    aStartA = geomDataAPI_Point2D(theLineA.attribute("StartPoint"))
+    aEndA   = geomDataAPI_Point2D(theLineA.attribute("EndPoint"))
+    aDirA_X = aEndA.x() - aStartA.x()
+    aDirA_Y = aEndA.y() - aStartA.y()
+    aLengthA = theLineA.lastResult().shape().edge().length()
+
+    aStartB = geomDataAPI_Point2D(theLineB.attribute("StartPoint"))
+    aEndB   = geomDataAPI_Point2D(theLineB.attribute("EndPoint"))
+    aDirB_X = aEndB.x() - aStartB.x()
+    aDirB_Y = aEndB.y() - aStartB.y()
+    aLengthB = theLineB.lastResult().shape().edge().length()
+
+    if (aLengthA < tolerance or aLengthB < tolerance):
+        return True
+
+    return (aDirA_X * aDirB_X + aDirA_Y * aDirB_Y) / (aLengthA * aLengthB) < tolerance
+
+def areConnected(theLineA, theLineB): 
+    aEndA = geomDataAPI_Point2D(theLineA.attribute("EndPoint"))
+    aStartB = geomDataAPI_Point2D(theLineB.attribute("StartPoint"))
+    return aEndA.x() == aStartB.x() and aEndA.y() == aStartB.y()
+
+
+#=========================================================================
+# Start of test
+#=========================================================================
+aSession = ModelAPI_Session.get()
+aDocument = aSession.moduleDocument()
+#=========================================================================
+# Creation of a sketch
+#=========================================================================
+aSession.startOperation()
+aSketchFeature = featureToCompositeFeature(aDocument.addFeature("Sketch"))
+origin = geomDataAPI_Point(aSketchFeature.attribute("Origin"))
+origin.setValue(0, 0, 0)
+dirx = geomDataAPI_Dir(aSketchFeature.attribute("DirX"))
+dirx.setValue(1, 0, 0)
+norm = geomDataAPI_Dir(aSketchFeature.attribute("Norm"))
+norm.setValue(0, 0, 1)
+aSession.finishOperation()
+#=========================================================================
+# Create a rectangle
+#=========================================================================
+aSession.startOperation()
+aRectangle = aSketchFeature.addFeature("SketchRectangle")
+aRectangle.string("RectangleType").setValue("RectangleTypeCentered")
+aCenter = geomDataAPI_Point2D(aRectangle.attribute("RectCenterPoint"))
+aCorner = geomDataAPI_Point2D(aRectangle.attribute("RectCornerPoint"))
+aCenter.setValue(10., 10.)
+aCorner.setValue(40., 30.)
+aSession.finishOperation()
+#=========================================================================
+# Check the lines of rectangle are parallel to the axes
+#=========================================================================
+aNbSubs = aSketchFeature.numberOfSubs()
+assert (aNbSubs >= 5) # The first feature on the sketch is center SketchPoint.
+aNbLines = 0
+for i in range (1, 5):
+    aFeature = objectToFeature(aSketchFeature.subFeature(i))
+    if aFeature.getKind() == "SketchLine":
+        aLastLine = aFeature
+        assert (isHorizontal(aLastLine) or isVertical(aLastLine))
+        aNbLines = aNbLines + 1
+assert (aNbLines == 4)
+assert (model.dof(aSketchFeature) == 5)
+#=========================================================================
+# Move one of lines
+#=========================================================================
+aSession.startOperation()
+aLineEnd = geomDataAPI_Point2D(aLastLine.attribute("EndPoint"))
+aLineEnd.setValue(41., 30.)
+aSession.finishOperation()
+
+#=========================================================================
+# Check the opposites lines of rectangle are parallel, and neighboring
+# ones are perpendicular
+#=========================================================================
+aLine0 = objectToFeature(aSketchFeature.subFeature(1))
+assert (aLine0.getKind() == "SketchLine")
+
+aLine1 = objectToFeature(aSketchFeature.subFeature(2))
+assert (aLine1.getKind() == "SketchLine")
+
+aLine2 = objectToFeature(aSketchFeature.subFeature(3))
+assert (aLine2.getKind() == "SketchLine")
+
+aLine3 = objectToFeature(aSketchFeature.subFeature(4))
+assert (aLine3.getKind() == "SketchLine")
+
+assert (areCounterDirectedAndOfEqualLength(aLine0, aLine2))
+assert (areCounterDirectedAndOfEqualLength(aLine1, aLine3))
+assert (arePerpendicular(aLine0, aLine1))
+assert (arePerpendicular(aLine2, aLine3))
+
+#=========================================================================
+# Check the contour is closed
+#=========================================================================
+assert (areConnected(aLine0, aLine1))
+assert (areConnected(aLine1, aLine2))
+assert (areConnected(aLine2, aLine3))
+assert (areConnected(aLine3, aLine0))
+
+#=========================================================================
+# End of test
+#=========================================================================
+
+assert(model.checkPythonDump())
index 81d8910ff176c1e3e56c41d9aa796dad6d18bd22..596e076b6a0cf5b0c450928e760b4da623e32e17 100644 (file)
@@ -1,16 +1,63 @@
+#!/usr/bin/env python
+"""Un exemple de création d'un rectangle"""
+
 from salome.shaper import model
 from salome.shaper import geom
 
+
 model.begin()
 partSet = model.moduleDocument()
+
+### Create Part
 Part_1 = model.addPart(partSet)
 Part_1_doc = Part_1.document()
+
+### Create Sketch
 Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
-Rectangle_1 = Sketch_1.addRectangle(5.5, 8.5, 31.3, 78.9)
-# rectangle from center and end points
-center = geom.Pnt2d(10, 5)
-corner = geom.Pnt2d(25, 75)
-rectangle_2 = sketch.addRectangleCentered(center, corner)
+
+
+### Rectangle by corners defined with doubles.
+rectWithFloats = Sketch_1.addRectangle(-8, -8, -5, -3)
+############################################################
+
+
+### Rectangle by corners defined with SketchPoints.
+SP_corner1 = Sketch_1.addPoint(-8, 8)
+SP_corner2 = Sketch_1.addPoint(-5, 3)
+
+rectWithSPs = Sketch_1.addRectangle(SP_corner1, SP_corner2)
+############################################################
+
+
+### Rectangle by corners defined with Pnt2Ds.
+Pnt2D_corner1 = geom.Pnt2d(-8 - 5, 8)
+Pnt2D_corner2 = geom.Pnt2d(-5 - 5, 3)
+
+rectWithPnt2Ds = Sketch_1.addRectangle(Pnt2D_corner1, Pnt2D_corner2)
+############################################################
+
+
+############################################################
+############################################################
+### Rectangle by center and corner defined with Pnt2Ds.
+Pnt2D_center = geom.Pnt2d(6.5 + 5, 5.5)
+Pnt2D_corner = geom.Pnt2d(8.0 + 5, 8.0)
+
+rectCenteredWithPnt2Ds = Sketch_1.addRectangleCentered(Pnt2D_center, Pnt2D_corner)
+############################################################
+
+
+### Rectangle by center and corner defined with SketchPoints.
+SP_center = Sketch_1.addPoint(6.5, 5.5)
+SP_corner = Sketch_1.addPoint(8.0, 8.0)
+
+rectCenteredWithPnt2Ds = Sketch_1.addRectangleCentered(SP_center, SP_corner)
+############################################################
+
+
+### Rectangle by center and corner defined with doubles.
+rectCenteredWithDoubles = Sketch_1.addRectangleCentered(6.5, -5.5, 8.0, -8.0)
+############################################################
 
 model.do()
-model.end()
+model.end()
\ No newline at end of file
index e737708c0d4e81d8875a45d0a406a08630d647d5..a6c4c87438ad120763ef6f3c699f8188436cc820 100644 (file)
@@ -182,6 +182,7 @@ SET(TEST_NAMES_PARA
   TestProjectionUpdate.py
   TestProjectionWithoutReference.py
   TestRectangle1.py
+  TestRectangleCentered1.py
   TestRemainingDoF.py
   TestRemoveBSpline.py
   TestRemoveBSplinePeriodic.py