#include <GeomAlgoAPI_Copy.h>
#include <GeomAlgoAPI_MapShapesAndAncestors.h>
#include <GeomAlgoAPI_ShapeTools.h>
+#include <GeomAlgoAPI_CompoundBuilder.h>
#include <GeomAPI_Edge.h>
#include <GeomAPI_Pln.h>
#include <GeomAPI_Pnt.h>
#include <GeomAPI_Wire.h>
#include <GeomAPI_WireExplorer.h>
+#include <GeomAPI_ShapeIterator.h>
#include <GEOMImpl_Fillet1d.hxx>
build(theBaseWire, theFilletVertices, theFilletRadius);
}
-void GeomAlgoAPI_Fillet1D::build(const GeomShapePtr& theBaseWire,
+void GeomAlgoAPI_Fillet1D::build(const GeomShapePtr& theBaseShape,
const ListOfShape& theFilletVertices,
const double theRadius)
{
- if (!theBaseWire || theFilletVertices.empty() || theRadius < 0.)
+ if (!theBaseShape.get() || theFilletVertices.empty() || theRadius < 0.)
return;
myFailedVertices.clear();
+ GeomShapePtr aShape;
+
+ if (theBaseShape->isWire())
+ aShape = buildWire(theBaseShape, theFilletVertices, theRadius);
+ else if (theBaseShape->isCompound()) {
+ std::list<GeomShapePtr> aShapes;
+ for (GeomAPI_ShapeIterator it (theBaseShape); it.more(); it.next()) {
+ GeomShapePtr aSubShape = it.current();
+ if (aSubShape->isWire()) {
+ // get only fillet vertices of current wire
+ ListOfShape aFilletVertices;
+ std::set<GeomShapePtr, GeomAPI_Shape::Comparator> aFilletVerticesSet
+ (theFilletVertices.begin(), theFilletVertices.end());
+ for (GeomAPI_WireExplorer anExp(aSubShape->wire()); anExp.more(); anExp.next()) {
+ if (aFilletVerticesSet.find(anExp.currentVertex()) != aFilletVerticesSet.end())
+ aFilletVertices.push_back(anExp.currentVertex());
+ }
+
+ GeomShapePtr aShape_i = buildWire(aSubShape, aFilletVertices, theRadius);
+ if (aShape_i.get() != NULL)
+ aShapes.push_back(aShape_i);
+ else
+ aShapes.push_back(aSubShape);
+ }
+ else {
+ setDone(false);
+ myError = "Input shape for fillet is neither a wire nor a compound of wires";
+ return;
+ }
+ }
+ aShape = GeomAlgoAPI_CompoundBuilder::compound(aShapes);
+ myModified[theBaseShape].push_back(aShape);
+ } else {
+ setDone(false);
+ myError = "Input shape for fillet is neither a wire nor a compound";
+ return;
+ }
+
+ setShape(aShape);
+ setDone(myFailedVertices.empty());
+}
+
+GeomShapePtr GeomAlgoAPI_Fillet1D::buildWire(const GeomShapePtr& theBaseWire,
+ const ListOfShape& theFilletVertices,
+ const double theRadius)
+{
+ std::shared_ptr<GeomAPI_Shape> aShape;
+ if (!theBaseWire.get() || theFilletVertices.empty() || theRadius < 0.)
+ return aShape;
+
// store all edges of a base wire as modified, because they will be rebuild by ShapeFix
for (GeomAPI_WireExplorer aWExp(theBaseWire->wire()); aWExp.more(); aWExp.next()) {
GeomShapePtr aCurrent = aWExp.current();
GeomPlanePtr aPlane = GeomAlgoAPI_ShapeTools::findPlane(anEdges);
if (!aPlane)
- return; // non-planar edges
+ return aShape; // non-planar edges
TopoDS_Edge anEdge1 = TopoDS::Edge(anEdges.front()->impl<TopoDS_Shape>());
TopoDS_Edge anEdge2 = TopoDS::Edge(anEdges.back()->impl<TopoDS_Shape>());
aNewEdges.push_back(aWExp.current());
for (ListOfShape::iterator anIt = aNewEdges.begin(); anIt != aNewEdges.end(); ++anIt)
aBuilder.Add(aNewWire, TopoDS::Edge((*anIt)->impl<TopoDS_Shape>()));
- }
- for (MapModified::iterator aGenIt = myGenerated.begin(); aGenIt != myGenerated.end(); ++aGenIt) {
- for (ListOfShape::iterator anIt = aGenIt->second.begin(); anIt != aGenIt->second.end(); ++anIt)
+
+ ListOfShape aNewEdges1;
+ generated(aWExp.currentVertex(), aNewEdges1);
+ for (ListOfShape::iterator anIt = aNewEdges1.begin(); anIt != aNewEdges1.end(); ++anIt)
aBuilder.Add(aNewWire, TopoDS::Edge((*anIt)->impl<TopoDS_Shape>()));
}
// fix the wire connectivity
aNewWire = aFixWire.WireAPIMake();
if (aNewWire.IsNull()) {
myFailedVertices = theFilletVertices;
- return;
+ return aShape;
}
// update the map of modified shapes, because the edges are changed by ShapeFix
aNewWire = aReorderedWire;
}
- std::shared_ptr<GeomAPI_Shape> aShape(new GeomAPI_Shape());
- aShape->setImpl(new TopoDS_Shape(aNewWire));
- myModified[theBaseWire].push_back(aShape);
-
- setShape(aShape);
- setDone(myFailedVertices.empty());
+ std::shared_ptr<GeomAPI_Shape> aResShape(new GeomAPI_Shape);
+ aResShape->setImpl(new TopoDS_Shape(aNewWire));
+ myModified[theBaseWire].push_back(aResShape);
+ return aResShape;
}
void GeomAlgoAPI_Fillet1D::generated(const GeomShapePtr theOldShape,
public:
/// Run fillet operation on a set of vertices with fixed radius.
- /// \param theBaseWire a changing Wire
- /// \param theFilletVertices list of edges the fillet is performed on
+ /// \param theBaseWire a wire or a compound of wires
+ /// \param theFilletVertices list of vertices the fillet is performed on
/// \param theFilletRadius radius of the fillet
GEOMALGOAPI_EXPORT GeomAlgoAPI_Fillet1D(const GeomShapePtr& theBaseWire,
const ListOfShape& theFilletVertices,
const ListOfShape& failedVertices() const { return myFailedVertices; }
private:
- /// Perform 1d-fillet on wire
- /// \param theBaseWire a changing wire
- /// \param theFilletVertices list of vertices of filler
+ /// Perform 1d-fillet on a wire or a compound of wires
+ /// \param theBaseShape the base wire or a compound of wires for fillet
+ /// \param theFilletVertices list of vertices of fillet
/// \param theRadius fillet radius
- void build(const GeomShapePtr& theBaseWire,
+ void build(const GeomShapePtr& theBaseShape,
const ListOfShape& theFilletVertices,
const double theRadius);
+ /// Perform 1d-fillet on wire
+ /// \param theBaseWire the base wire for fillet
+ /// \param theFilletVertices list of vertices of fillet
+ /// \param theRadius fillet radius
+ GeomShapePtr buildWire(const GeomShapePtr& theBaseWire,
+ const ListOfShape& theFilletVertices,
+ const double theRadius);
+
private:
MapModified myGenerated;
MapModified myModified;
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;
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();
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
/// \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.
} else { // reset the old array
if (theSize) {
if (theSize != myArray->Length()) { // old data is not kept, a new array is created
- Handle(TColStd_HArray1OfInteger) aNewArray = new TColStd_HArray1OfInteger(0, theSize - 1);
+ Handle(TColStd_HArray1OfInteger) aNewArray =
+ new TColStd_HArray1OfInteger(0, theSize - 1, 0);
myArray->ChangeArray(aNewArray);
if (sendUpdated)
owner()->data()->sendAttributeUpdated(this);
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();
}
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)) {
}
theDumper << aBase << " = " << aSketchName << ".addOffset(" << aOffsetObjects << ", "
- << aValue << ", " << aReversed << ")" << std::endl;
+ << aValue << ", " << aReversed << ", " << aJoint << ")" << std::endl;
// Dump variables for a list of created features
theDumper << "[";
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 */,
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
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));
}
//--------------------------------------------------------------------------------------
#include <SketchPlugin_Sketch.h>
#include <SketchPlugin_SketchEntity.h>
+#include <SketchPlugin_Offset.h>
#include <ModelHighAPI_Double.h>
#include <ModelHighAPI_Interface.h>
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
#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>
#include <GeomDataAPI_Point2D.h>
#include <GeomDataAPI_Point2DArray.h>
+#include <math.h>
+
static const double tolerance = 1.e-7;
SketchPlugin_Offset::SketchPlugin_Offset()
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()
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();
}
// 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");
}
}
}
- // TODO: hilight selection in the viewer
data()->blockSendAttributeUpdated(aWasBlocked);
return true;
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;
+ 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 (aFilletVertices.begin(), aFilletVertices.end());
+
+ // 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) {
+ // 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 (aFilletVertices.begin(), aFilletVertices.end());
+
+ 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");
+}
#include <GeomAPI_Edge.h>
class GeomAlgoAPI_MakeShape;
+class GeomAlgoAPI_Offset;
+class GeomAlgoAPI_WireBuilder;
/**\class SketchPlugin_Offset
* \ingroup Plugins
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()
{
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
--- /dev/null
+# 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())
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()
======
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
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).
**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
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"
TestMultiTranslation.py
TestOffset1.py
TestOffset2.py
+ TestOffset3.py
TestPresentation.py
TestProjection.py
TestProjectionBSpline.py