]> SALOME platform Git repositories - modules/shaper.git/commitdiff
Salome HOME
[bos #29476] [EDF] (2022-T1) SHAPER: Offset in sketch
authorjfa <jfa@opencascade.com>
Wed, 11 May 2022 10:57:50 +0000 (13:57 +0300)
committerjfa <jfa@opencascade.com>
Wed, 11 May 2022 10:57:50 +0000 (13:57 +0300)
17 files changed:
src/GeomAlgoAPI/GeomAlgoAPI_Offset.cpp
src/GeomAlgoAPI/GeomAlgoAPI_Offset.h
src/SketchAPI/SketchAPI_Offset.cpp
src/SketchAPI/SketchAPI_Offset.h
src/SketchAPI/SketchAPI_Sketch.cpp
src/SketchAPI/SketchAPI_Sketch.h
src/SketchPlugin/SketchPlugin_Offset.cpp
src/SketchPlugin/SketchPlugin_Offset.h
src/SketchPlugin/Test/TestOffset3.py [new file with mode: 0644]
src/SketchPlugin/doc/examples/offset.py
src/SketchPlugin/doc/images/Offset_panel.png
src/SketchPlugin/doc/offsetFeature.rst
src/SketchPlugin/icons/offset_arcs_32x32.png [new file with mode: 0644]
src/SketchPlugin/icons/offset_keep_distance_32x32.png [new file with mode: 0644]
src/SketchPlugin/icons/offset_lines_32x32.png [new file with mode: 0644]
src/SketchPlugin/plugin-Sketch.xml
src/SketchPlugin/tests.set

index 176c569ba2614e788cf19e452d90b3af99fcb109..c9c0e48c129ae65aa3fe5ef4800bec4ce0253128 100644 (file)
@@ -64,7 +64,8 @@ void GeomAlgoAPI_Offset::generated(const GeomShapePtr theOldShape,
 
 GeomAlgoAPI_Offset::GeomAlgoAPI_Offset(const GeomPlanePtr& thePlane,
                                        const GeomShapePtr& theEdgeOrWire,
-                                       const double theOffsetValue)
+                                       const double theOffsetValue,
+                                       const GeomAlgoAPI_OffsetJoint theJoint)
 {
   // 1. Make wire from edge, if need
   TopoDS_Wire aWire;
@@ -92,11 +93,23 @@ GeomAlgoAPI_Offset::GeomAlgoAPI_Offset(const GeomPlanePtr& thePlane,
   setImpl(aParal);
   setBuilderType(OCCT_BRepBuilderAPI_MakeShape);
 
+  // Joint type
+  GeomAbs_JoinType aJoin = GeomAbs_Arc; // default mode, corresponding to KeepDistance
+  if (theJoint == GeomAlgoAPI_OffsetJoint::Lines)
+    aJoin = GeomAbs_Intersection;
+  // for GeomAlgoAPI_OffsetJoint::Arcs do the same as for KeepDistance
+
   Standard_Boolean isOpenResult = !aWire.Closed();
-  aParal->Init(aFace, GeomAbs_Arc, isOpenResult);
+  aParal->Init(aFace, aJoin, isOpenResult);
   aParal->Perform(theOffsetValue, 0.);
   if (aParal->IsDone()) {
     TopoDS_Shape anOffset = aParal->Shape();
+
+    if (theJoint == GeomAlgoAPI_OffsetJoint::Arcs) {
+      // TODO: process Arcs case - make fillets on straight
+      //       edges intersections, except too short edges
+    }
+
     GeomShapePtr aResult(new GeomAPI_Shape());
     aResult->setImpl(new TopoDS_Shape(anOffset));
     setShape(aResult);
index 1afa7ddcfbb876decae4b0cca4ec56e35f9d9afa..d175237fe65874865bb27c0530239254248e52f7 100644 (file)
 
 class GeomAPI_Pln;
 
+/// \enum GeomAlgoAPI_OffsetJoint
+/// \brief Type of joint of straight edges in 2D offset
+/// \a KeepDistance Keep the distance from initial contour to the resulting one,
+///    creating contour from lines and arcs where it is needed
+/// \a Arcs On connection of straight edges, tangent arcs are created,
+///    except the case of too short edges. Radius of arcs equals to the offset value.
+/// \a Lines No arcs is created on the connection of the resulting lines,
+///    adjacent lines are prolonged to the point of their intersection.
+enum class GeomAlgoAPI_OffsetJoint { KeepDistance, Arcs, Lines };
+
 /// \class GeomAlgoAPI_Offset
 /// \ingroup DataAlgo
 /// \brief Perform 3D offset for the shape
@@ -39,9 +49,12 @@ public:
   /// \param[in] thePlane base plane for all offsets
   /// \param[in] theEdgesOrWire base shapes
   /// \param[in] theOffsetValue offset distance, it can be negative
-  GEOMALGOAPI_EXPORT GeomAlgoAPI_Offset(const std::shared_ptr<GeomAPI_Pln>& thePlane,
-                                        const GeomShapePtr& theEdgeOrWire,
-                                        const double theOffsetValue);
+  /// \param[in] theJointType type of joint of straight edges
+  GEOMALGOAPI_EXPORT GeomAlgoAPI_Offset
+    (const std::shared_ptr<GeomAPI_Pln>& thePlane,
+     const GeomShapePtr& theEdgeOrWire,
+     const double theOffsetValue,
+     const GeomAlgoAPI_OffsetJoint theJoint = GeomAlgoAPI_OffsetJoint::KeepDistance);
 
   /// \return the list of shapes generated from the shape \a theShape.
   /// \param[in] theOldShape base shape.
index 3aa00e729f565c00c31c371385856b8cd407dbeb..e7d6d071518f62e8bb4602fe68d43900b002b445 100644 (file)
@@ -34,13 +34,15 @@ SketchAPI_Offset::SketchAPI_Offset (const std::shared_ptr<ModelAPI_Feature> & th
 SketchAPI_Offset::SketchAPI_Offset (const std::shared_ptr<ModelAPI_Feature> & theFeature,
                                     const std::list<std::shared_ptr<ModelAPI_Object> > & theObjects,
                                     const ModelHighAPI_Double & theOffsetValue,
-                                    bool theIsReversed)
+                                    const bool theIsReversed,
+                                    const std::string & theJointType)
   : ModelHighAPI_Interface(theFeature)
 {
   if (initialize()) {
     fillAttribute(theObjects, edgesList());
     fillAttribute(theOffsetValue, value());
     fillAttribute(theIsReversed, reversed());
+    fillAttribute(theJointType, joint());
 
     execute();
   }
@@ -72,6 +74,7 @@ void SketchAPI_Offset::dump (ModelHighAPI_Dumper& theDumper) const
   AttributeRefListPtr aOffsetObjects = edgesList();
   AttributeDoublePtr aValue = value();
   AttributeBooleanPtr aReversed = reversed();
+  AttributeStringPtr aJoint = joint();
 
   // Check all attributes are already dumped. If not, store the feature as postponed.
   if (!theDumper.isDumped(aOffsetObjects)) {
@@ -80,7 +83,7 @@ void SketchAPI_Offset::dump (ModelHighAPI_Dumper& theDumper) const
   }
 
   theDumper << aBase << " = " << aSketchName << ".addOffset(" << aOffsetObjects << ", "
-            << aValue << ", " << aReversed << ")" << std::endl;
+            << aValue << ", " << aReversed  << ", " << aJoint << ")" << std::endl;
 
   // Dump variables for a list of created features
   theDumper << "[";
index 13e105686325b2f69cc9714af4767cc3dd166a4f..a6d8c29068913a38ea6553305e9474ee68299757 100644 (file)
@@ -50,12 +50,13 @@ public:
   SketchAPI_Offset(const std::shared_ptr<ModelAPI_Feature> & theFeature,
                    const std::list<std::shared_ptr<ModelAPI_Object> > & theObjects,
                    const ModelHighAPI_Double & theOffsetValue,
-                   bool theIsReversed);
+                   const bool theIsReversed = false,
+                   const std::string & theJointType = SketchPlugin_Offset::JOINT_KEEP_DISTANCE());
   /// Destructor
   SKETCHAPI_EXPORT
   virtual ~SketchAPI_Offset();
 
-  INTERFACE_3(SketchPlugin_Offset::ID(),
+  INTERFACE_4(SketchPlugin_Offset::ID(),
 
               edgesList, SketchPlugin_Offset::EDGES_ID(),
               ModelAPI_AttributeRefList, /** Offset edges list */,
@@ -64,7 +65,10 @@ public:
               ModelAPI_AttributeDouble, /** Value */,
 
               reversed, SketchPlugin_Offset::REVERSED_ID(),
-              ModelAPI_AttributeBoolean, /** Negative value */
+              ModelAPI_AttributeBoolean, /** Negative value */,
+
+              joint, SketchPlugin_Offset::JOINT_ID(),
+              ModelAPI_AttributeString, /** Joint type */
               )
 
   /// List of created objects
index 838a558bb5d7af59874c3ff94c70c14ba59681b9..fbb5814af45f163b6a41d6f0cfd0d395795c557f 100644 (file)
@@ -930,11 +930,12 @@ std::shared_ptr<SketchAPI_Mirror> SketchAPI_Sketch::addMirror(
 std::shared_ptr<SketchAPI_Offset> SketchAPI_Sketch::addOffset(
     const std::list<std::shared_ptr<ModelAPI_Object> > & theObjects,
     const ModelHighAPI_Double & theValue,
-    const bool theReversed)
+    const bool theReversed,
+    const std::string & theJointType)
 {
   std::shared_ptr<ModelAPI_Feature> aFeature =
     compositeFeature()->addFeature(SketchPlugin_Offset::ID());
-  return OffsetPtr(new SketchAPI_Offset(aFeature, theObjects, theValue, theReversed));
+  return OffsetPtr(new SketchAPI_Offset(aFeature, theObjects, theValue, theReversed, theJointType));
 }
 
 //--------------------------------------------------------------------------------------
index bb83b18871ca5d7a065a5f9ef2fff127f61539cf..7b84cebfd9a9a95034dc053916b98b381a70bf6b 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <SketchPlugin_Sketch.h>
 #include <SketchPlugin_SketchEntity.h>
+#include <SketchPlugin_Offset.h>
 
 #include <ModelHighAPI_Double.h>
 #include <ModelHighAPI_Interface.h>
@@ -378,7 +379,8 @@ public:
   std::shared_ptr<SketchAPI_Offset> addOffset(
       const std::list<std::shared_ptr<ModelAPI_Object> > & theObjects,
       const ModelHighAPI_Double & theValue,
-      const bool theReversed);
+      const bool theReversed = false,
+      const std::string & theJointType = SketchPlugin_Offset::JOINT_KEEP_DISTANCE());
 
   /// Add translation
   SKETCHAPI_EXPORT
index 258fdbb8031f3e5b62d261fb5d454930323bfbb7..84a77e9649f5f7db0501958aa424bea19c0d33aa 100644 (file)
 #include <ModelAPI_AttributeInteger.h>
 #include <ModelAPI_AttributeIntArray.h>
 #include <ModelAPI_AttributeRefList.h>
+#include <ModelAPI_AttributeString.h>
 #include <ModelAPI_Events.h>
 #include <ModelAPI_ResultConstruction.h>
 #include <ModelAPI_Tools.h>
 #include <ModelAPI_Validator.h>
 
 #include <GeomAlgoAPI_MakeShapeList.h>
+#include <GeomAlgoAPI_MapShapesAndAncestors.h>
 #include <GeomAlgoAPI_Offset.h>
 #include <GeomAlgoAPI_ShapeTools.h>
 #include <GeomAlgoAPI_WireBuilder.h>
+#include <GeomAlgoAPI_Fillet1D.h>
+#include <GeomAlgoAPI_Tools.h>
 
 #include <GeomAPI_BSpline.h>
 #include <GeomAPI_Circ.h>
@@ -61,6 +65,8 @@
 #include <GeomDataAPI_Point2D.h>
 #include <GeomDataAPI_Point2DArray.h>
 
+#include <math.h>
+
 static const double tolerance = 1.e-7;
 
 SketchPlugin_Offset::SketchPlugin_Offset()
@@ -86,6 +92,11 @@ void SketchPlugin_Offset::initAttributes()
       registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_B());
   ModelAPI_Session::get()->validators()->
       registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_C());
+
+  AttributeStringPtr aJointAttr = std::dynamic_pointer_cast<ModelAPI_AttributeString>
+    (data()->addAttribute(JOINT_ID(), ModelAPI_AttributeString::typeId()));
+  if (!aJointAttr->isInitialized())
+    aJointAttr->setValue(JOINT_KEEP_DISTANCE());
 }
 
 void SketchPlugin_Offset::execute()
@@ -93,6 +104,20 @@ void SketchPlugin_Offset::execute()
   SketchPlugin_Sketch* aSketch = sketch();
   if (!aSketch) return;
 
+  // 0. Joint type
+  AttributeStringPtr aJointAttr = string(JOINT_ID());
+  std::string aType = JOINT_KEEP_DISTANCE();
+  if (aJointAttr->isInitialized())
+    aType = aJointAttr->value();
+
+  GeomAlgoAPI_OffsetJoint aJoint;
+  if (aType == JOINT_ARCS())
+    aJoint = GeomAlgoAPI_OffsetJoint::Arcs;
+  else if (aType == JOINT_LINES())
+    aJoint = GeomAlgoAPI_OffsetJoint::Lines;
+  else // Default mode
+    aJoint = GeomAlgoAPI_OffsetJoint::KeepDistance;
+
   // 1. Sketch plane
   std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
 
@@ -189,14 +214,21 @@ void SketchPlugin_Offset::execute()
       }
 
       // 5.d. Make offset for the wire
-      std::shared_ptr<GeomAlgoAPI_Offset> anOffsetShape(
-          new GeomAlgoAPI_Offset(aPlane, aWireShape, aValue*aSign));
+      std::shared_ptr<GeomAlgoAPI_Offset> anOffsetShape
+        (new GeomAlgoAPI_Offset(aPlane, aWireShape, aValue*aSign, aJoint));
 
       if (anOffsetShape->isDone()) {
-        std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeList(new GeomAlgoAPI_MakeShapeList);
-        aMakeList->appendAlgo(aWireBuilder);
-        aMakeList->appendAlgo(anOffsetShape);
-        anOffsetAlgos.push_back(aMakeList);
+        if (aJoint == GeomAlgoAPI_OffsetJoint::Arcs) {
+          // For Arcs joint make fillet at all straight edges intersections
+          // of the wire, resulting from GeomAlgoAPI_Offset algorithm
+          makeFillet(fabs(aValue), aWireBuilder, anOffsetShape, anOffsetAlgos);
+        }
+        else {
+          std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeList (new GeomAlgoAPI_MakeShapeList);
+          aMakeList->appendAlgo(aWireBuilder);
+          aMakeList->appendAlgo(anOffsetShape);
+          anOffsetAlgos.push_back(aMakeList);
+        }
       }
       else {
         setError("Offset algorithm failed");
@@ -753,7 +785,6 @@ bool SketchPlugin_Offset::findWires()
       }
     }
   }
-  // TODO: hilight selection in the viewer
 
   data()->blockSendAttributeUpdated(aWasBlocked);
   return true;
@@ -769,3 +800,125 @@ AISObjectPtr SketchPlugin_Offset::getAISObject(AISObjectPtr thePrevious)
     thePrevious);
   return anAIS;
 }
+
+
+void SketchPlugin_Offset::makeFillet
+     (const double theValue,
+      const std::shared_ptr<GeomAlgoAPI_WireBuilder>& theWireBuilder,
+      const std::shared_ptr<GeomAlgoAPI_Offset>& theOffsetShape,
+      ListOfMakeShape& theOffsetAlgos)
+{
+  std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeList (new GeomAlgoAPI_MakeShapeList);
+  aMakeList->appendAlgo(theWireBuilder);
+  aMakeList->appendAlgo(theOffsetShape);
+
+  bool isOK = true;
+
+  GeomShapePtr aResWire = theOffsetShape->shape();
+  GeomAlgoAPI_MapShapesAndAncestors aMapVE
+    (aResWire, GeomAPI_Shape::VERTEX, GeomAPI_Shape::EDGE);
+  const MapShapeToShapes& aSubshapes = aMapVE.map();
+
+  // find vertices for fillet
+  std::set<GeomShapePtr, GeomAPI_Shape::Comparator> aFilletVertices;
+  // ??? TODO: if aResWire is a compound of wires - process each wire ???
+  if (aResWire->isWire()) {
+    for (MapShapeToShapes::const_iterator anIt = aSubshapes.begin();
+         anIt != aSubshapes.end(); ++anIt) {
+      // vertex should have 2 adjacent edges
+      if (anIt->second.size() != 2)
+        continue;
+
+      // both edges should be linear
+      ListOfShape anEdges;
+      anEdges.insert(anEdges.end(), anIt->second.begin(), anIt->second.end());
+      GeomEdgePtr anEdge1 (new GeomAPI_Edge(anEdges.front()));
+      GeomEdgePtr anEdge2 (new GeomAPI_Edge(anEdges.back()));
+      if (!anEdge1->isLine() || !anEdge2->isLine())
+        continue;
+
+      // skip vertices, which smoothly connect adjacent edges
+      GeomVertexPtr aSharedVertex(new GeomAPI_Vertex(anIt->first));
+      if (GeomAlgoAPI_ShapeTools::isTangent(anEdge1, anEdge2, aSharedVertex))
+        continue;
+
+      aFilletVertices.insert(anIt->first);
+    }
+  }
+
+  if (!aFilletVertices.empty()) {
+    isOK = false; // the wire needs correction
+    ListOfShape aVerticesList;
+    for (GeomAPI_WireExplorer anExp (aResWire->wire()); anExp.more(); anExp.next()) {
+      GeomShapePtr aVertex = anExp.currentVertex();
+      if (aFilletVertices.find(aVertex) != aFilletVertices.end())
+        aVerticesList.push_back(aVertex);
+    }
+
+    // Fillet1D on all linear edges intersections
+    std::shared_ptr<GeomAlgoAPI_Fillet1D> aFilletBuilder
+      (new GeomAlgoAPI_Fillet1D(aResWire, aVerticesList, theValue));
+
+    std::string anError;
+    if (!GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed
+        (aFilletBuilder, getKind(), anError)) {
+      aMakeList->appendAlgo(aFilletBuilder);
+      isOK = true;
+    }
+    else {
+      ListOfShape aFailedVertices = aFilletBuilder->failedVertices();
+      if (aFailedVertices.size() != 0 &&
+          aFailedVertices.size() != aVerticesList.size()) {
+        // Exclude failed vertices and also vertices, joined
+        // with failed by one edge, and run algorithm once again
+        ListOfShape::iterator itVertices = aFailedVertices.begin();
+        for (; itVertices != aFailedVertices.end(); itVertices++) {
+          GeomShapePtr aFailedVertex = *itVertices;
+          aFilletVertices.erase(aFailedVertex);
+          // remove also neighbour vertices
+          MapShapeToShapes::const_iterator anIt = aSubshapes.find(aFailedVertex);
+          if (anIt != aSubshapes.end()) { // should be always true
+            ListOfShape anEdges;
+            anEdges.insert(anEdges.end(), anIt->second.begin(), anIt->second.end());
+            GeomEdgePtr anEdge1 (new GeomAPI_Edge(anEdges.front()));
+            GeomEdgePtr anEdge2 (new GeomAPI_Edge(anEdges.back()));
+            GeomVertexPtr V1, V2;
+            anEdge1->vertices(V1, V2);
+            if (V1->isEqual(aFailedVertex)) V1 = V2;
+            aFilletVertices.erase(V1);
+            anEdge2->vertices(V1, V2);
+            if (V1->isEqual(aFailedVertex)) V1 = V2;
+            aFilletVertices.erase(V1);
+          }
+        }
+        if (aFilletVertices.size() == 0) {
+          // there are no suitable vertices for fillet
+          isOK = true;
+        }
+        else {
+          // Fillet1D one more try
+          ListOfShape aVerticesList1;
+          for (GeomAPI_WireExplorer anExp (aResWire->wire()); anExp.more(); anExp.next()) {
+            GeomShapePtr aVertex = anExp.currentVertex();
+            if (aFilletVertices.find(aVertex) != aFilletVertices.end())
+              aVerticesList1.push_back(aVertex);
+          }
+
+          std::shared_ptr<GeomAlgoAPI_Fillet1D> aFilletBuilder1
+            (new GeomAlgoAPI_Fillet1D(aResWire, aVerticesList1, theValue));
+
+          if (!GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed
+              (aFilletBuilder1, getKind(), anError)) {
+            aMakeList->appendAlgo(aFilletBuilder1);
+            isOK = true;
+          }
+        }
+      }
+    }
+  }
+
+  if (isOK)
+    theOffsetAlgos.push_back(aMakeList);
+  else
+    setError("Offset algorithm failed");
+}
index c18efdae3c213c1d1bb5f6af865e816322e90298..09c388440c6dbe2cceead0a8abf55fcc71b4eb3d 100644 (file)
@@ -28,6 +28,8 @@
 #include <GeomAPI_Edge.h>
 
 class GeomAlgoAPI_MakeShape;
+class GeomAlgoAPI_Offset;
+class GeomAlgoAPI_WireBuilder;
 
 /**\class SketchPlugin_Offset
  * \ingroup Plugins
@@ -50,6 +52,34 @@ public:
     return MY_KIND;
   }
 
+  /// Type of joint
+  inline static const std::string& JOINT_ID()
+  {
+    static const std::string ID("offset_joint");
+    return ID;
+  }
+
+  /// Keep distance joint (add arcs where needed)
+  inline static const std::string& JOINT_KEEP_DISTANCE()
+  {
+    static const std::string ID("KeepDistance");
+    return ID;
+  }
+
+  /// Arcs joint (make fillets on all straight lines intersections)
+  inline static const std::string& JOINT_ARCS()
+  {
+    static const std::string ID("Arcs");
+    return ID;
+  }
+
+  /// Lines joint (do not add new arcs, prolongate and intersect adjacent lines)
+  inline static const std::string& JOINT_LINES()
+  {
+    static const std::string ID("Lines");
+    return ID;
+  }
+
   /// list of offset edges
   inline static const std::string& EDGES_ID()
   {
@@ -140,6 +170,11 @@ private:
                        std::set<FeaturePtr>& theProcessedEdgesSet,
                        std::list<FeaturePtr>& theChain,
                        const bool isPrepend = false);
+
+  void makeFillet (const double theValue,
+                   const std::shared_ptr<GeomAlgoAPI_WireBuilder>&,
+                   const std::shared_ptr<GeomAlgoAPI_Offset>&,
+                   std::list< std::shared_ptr<GeomAlgoAPI_MakeShape> >& theOffsetAlgos);
 };
 
 #endif
diff --git a/src/SketchPlugin/Test/TestOffset3.py b/src/SketchPlugin/Test/TestOffset3.py
new file mode 100644 (file)
index 0000000..f5e1f42
--- /dev/null
@@ -0,0 +1,106 @@
+# Copyright (C) 2020-2021  CEA/DEN, EDF R&D
+#
+# 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
+#
+
+from salome.shaper import model
+from SketchAPI import *
+
+model.begin()
+partSet = model.moduleDocument()
+
+### Create Sketch
+Sketch_1 = model.addSketch(partSet, model.defaultPlane("XOY"))
+
+### Create SketchLine
+SketchLine_1 = Sketch_1.addLine(0, 0, 0, 130)
+
+### Create SketchProjection
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchAPI_Point(SketchPoint_1).coordinates())
+Sketch_1.setVertical(SketchLine_1.result())
+Sketch_1.setLength(SketchLine_1.result(), 130)
+
+### Create SketchLine
+SketchLine_2 = Sketch_1.addLine(0, 130, 52.63824701112046, 101.2039073554021)
+Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+Sketch_1.setLength(SketchLine_2.result(), 60)
+
+### Create SketchLine
+SketchLine_3 = Sketch_1.addLine(52.63824701112046, 101.2039073554021, 199.7207674554361, 130.6442229064876)
+Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+Sketch_1.setLength(SketchLine_3.result(), 150)
+
+### Create SketchConstraintAngle
+Sketch_1.setAngle(SketchLine_3.result(), SketchLine_2.result(), 140, type = "Direct")
+
+### Create SketchLine
+SketchLine_4 = Sketch_1.addLine(158.0815949372134, 7.493180231252017, 199.7207674554361, 130.6442229064876)
+Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.endPoint())
+Sketch_1.setLength(SketchLine_4.result(), 130)
+
+### Create SketchConstraintAngle
+Sketch_1.setAngle(SketchLine_3.result(), SketchLine_4.result(), 60, type = "Direct")
+
+### Create SketchLine
+SketchLine_5 = Sketch_1.addLine(158.0815949372134, 7.493180231252017, 106.6596776583922, 54.98830022657437)
+Sketch_1.setCoincident(SketchLine_4.startPoint(), SketchLine_5.startPoint())
+Sketch_1.setLength(SketchLine_5.result(), 70)
+
+### Create SketchLine
+SketchLine_6 = Sketch_1.addLine(106.6596776583922, 54.98830022657437, 0, 0)
+Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_1.startPoint())
+Sketch_1.setLength(SketchLine_6.result(), 120)
+
+### Create SketchConstraintAngle
+Sketch_1.setAngle(SketchLine_6.result(), SketchLine_5.result(), 110, type = "Direct")
+
+### SketchOffset objects
+SketchOffset_objects = [SketchLine_1.result(), SketchLine_2.result(), SketchLine_3.result(), SketchLine_4.result(), SketchLine_5.result(), SketchLine_6.result()]
+
+### 1. "KeepDistance" mode
+
+SketchOffset_1 = Sketch_1.addOffset(SketchOffset_objects, 8, False, "KeepDistance") # outside
+SketchOffset_2 = Sketch_1.addOffset(SketchOffset_objects, 8, True, "KeepDistance") # inside
+model.do()
+
+assert(len(SketchOffset_1.offset()) == 10)
+assert(len(SketchOffset_2.offset()) == 8)
+
+### 2. "Arcs" mode
+
+SketchOffset_3 = Sketch_1.addOffset(SketchOffset_objects, 16, False, "Arcs") # outside
+SketchOffset_4 = Sketch_1.addOffset(SketchOffset_objects, 16, True, "Arcs") # inside
+model.do()
+
+assert(len(SketchOffset_3.offset()) == 12)
+assert(len(SketchOffset_4.offset()) == 12)
+
+### 3. "Lines" mode
+
+SketchOffset_5 = Sketch_1.addOffset(SketchOffset_objects, 24, False, "Lines") # outside
+SketchOffset_6 = Sketch_1.addOffset(SketchOffset_objects, 24, True, "Lines") # inside
+model.do()
+
+assert(len(SketchOffset_5.offset()) == 6)
+assert(len(SketchOffset_6.offset()) == 6)
+
+model.end()
+
+assert(model.checkPythonDump())
index 881db9a4004433342f6a097a7e6dec3e0be16acf..79e8ae9e1b97c9ae1eed660f64700364e1f8a73b 100644 (file)
@@ -16,7 +16,18 @@ Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.endPoint())
 Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_4.startPoint())
 
 SketchOffset_1_objects = [SketchLine_1.result(), SketchLine_2.result(), SketchLine_3.result(), SketchLine_4.result()]
+
+### KeepDistance (default) mode, not reversed (outside)
 SketchOffset_1 = Sketch_1.addOffset(SketchOffset_1_objects, 10.0, False)
 
+### KeepDistance mode, reversed (inside)
+SketchOffset_2 = Sketch_1.addOffset(SketchOffset_1_objects, 25.0, True, "KeepDistance")
+
+### Arcs mode, reversed (inside)
+SketchOffset_3 = Sketch_1.addOffset(SketchOffset_1_objects, 15.0, True, "Arcs")
+
+### Lines mode, not reversed (outside)
+SketchOffset_4 = Sketch_1.addOffset(SketchOffset_1_objects, 20.0, False, "Lines")
+
 model.do()
 model.end()
index d34ad6c3c35e1f7692de854f1b8ceb0c8bffabee..de9741e548eaf8e20bb5d2bb24bb127a467e044a 100644 (file)
Binary files a/src/SketchPlugin/doc/images/Offset_panel.png and b/src/SketchPlugin/doc/images/Offset_panel.png differ
index ce42254dfbce3f71bb7495628d8f1f5266866c51..65af062c925362fd25d512e558383fab38f9de48 100644 (file)
@@ -4,10 +4,24 @@ Offset
 ======
 
 Offset operation offsets sketch entities on a given distance.
-Gaps are filled by arcs.
 Offset is performed outside a closed contour or to the right
 of an open one, unless the **Reversed** flag is not set.
 
+Edges junctions are filled in accordance with selected offset mode.
+The following modes are implemented:
+  - **Keep distance** mode, enabled by default. It keeps the distance from initial
+    contour to the resulting one, creating additional arcs where it is needed.
+  - **Arcs mode**, where on connection of straight segments, tangent arcs are created.
+    Only connection of straight lines is supported; with other types of curves,
+    offset will work as in **Keep distance** mode. Radius of arcs equals to the offset value.
+    These arcs cut off the initially connected lines, so, if in result at least one
+    such line becomes invalid (removed from the result), the arc is not created
+    in this connection, the line is created in this point as in the **Lines** mode.
+  - **Lines** mode, where no arcs is created on the connection of the resulting lines.
+    Adjacent lines are prolonged to the point of their intersection. If the offset
+    arguments are not straigh lines (arcs, bspline, etc), the offset will work
+    as in the **Keep distance** mode in these locations.
+
 To create an Offset in the active Sketch:
 
 #. select in the Main Menu *Sketch - > Offset* item  or
@@ -23,6 +37,7 @@ Property panel:
 
 Input fields:
 
+- Offset mode can be **Keep distance**, **Arcs** or **Lines**
 - **Edges** is the list of segments (lines, circles, arcs) selected in the view.
 - **Offset value** is the offset distance.
 - **Reversed** sets the reversed offset side (inside a closed contour or to the left of an open one).
@@ -35,11 +50,12 @@ Button:
 
 **TUI Command**:
 
-.. py:function:: Sketch_1.addOffset(Objects, Distance, isReversed)
+.. py:function:: Sketch_1.addOffset(Objects, Distance, isReversed, Mode)
 
     :param list: A list of objects.
     :param real: An offset distance.
     :param boolean: Reversed flag.
+    :param string: Offset mode. Can be "KeepDistance", "Arcs" or "Lines".
     :return: Result object.
 
 Result
diff --git a/src/SketchPlugin/icons/offset_arcs_32x32.png b/src/SketchPlugin/icons/offset_arcs_32x32.png
new file mode 100644 (file)
index 0000000..046c60a
Binary files /dev/null and b/src/SketchPlugin/icons/offset_arcs_32x32.png differ
diff --git a/src/SketchPlugin/icons/offset_keep_distance_32x32.png b/src/SketchPlugin/icons/offset_keep_distance_32x32.png
new file mode 100644 (file)
index 0000000..ae7dd5b
Binary files /dev/null and b/src/SketchPlugin/icons/offset_keep_distance_32x32.png differ
diff --git a/src/SketchPlugin/icons/offset_lines_32x32.png b/src/SketchPlugin/icons/offset_lines_32x32.png
new file mode 100644 (file)
index 0000000..486f6e9
Binary files /dev/null and b/src/SketchPlugin/icons/offset_lines_32x32.png differ
index 824e6281b2c9b60f4145dc74efe6a0fd50fbb68b..86b234c51909ff957f29cf0a18559ff0a77ab21a 100644 (file)
                tooltip="Offset a curve to a distance"
                icon="icons/Sketch/offset.png"
                helpfile="offsetFeature.html">
+        <toolbox id="offset_joint">
+           <box id="KeepDistance"
+               icon="icons/Sketch/offset_keep_distance_32x32.png"
+               title="Keep distance"/>
+           <box id="Arcs"
+               icon="icons/Sketch/offset_arcs_32x32.png"
+               title="Arcs"/>
+           <box id="Lines"
+               icon="icons/Sketch/offset_lines_32x32.png"
+               title="Lines"/>
+        </toolbox>
         <sketch_multi_selector id="segments"
                                label="Edges"
                                tooltip="Select edges to offset"
index 4ba5a73c844f9b5d9b292102ca7f992ddb82b781..25bbd10269593a46b0f7770a08936fee7d70b4bc 100644 (file)
@@ -171,6 +171,7 @@ SET(TEST_NAMES
   TestMultiTranslation.py
   TestOffset1.py
   TestOffset2.py
+  TestOffset3.py
   TestPresentation.py
   TestProjection.py
   TestProjectionBSpline.py