1 // Copyright (C) 2020-2022 CEA/DEN, EDF R&D
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #include <SketchPlugin_Offset.h>
22 #include <SketchPlugin_Sketch.h>
23 #include <SketchPlugin_Line.h>
24 #include <SketchPlugin_Point.h>
25 #include <SketchPlugin_Arc.h>
26 #include <SketchPlugin_Circle.h>
27 #include <SketchPlugin_Ellipse.h>
28 #include <SketchPlugin_EllipticArc.h>
29 #include <SketchPlugin_BSpline.h>
30 #include <SketchPlugin_BSplinePeriodic.h>
31 #include <SketchPlugin_Tools.h>
33 #include <SketcherPrs_Factory.h>
35 #include <Events_InfoMessage.h>
37 #include <ModelAPI_AttributeBoolean.h>
38 #include <ModelAPI_AttributeDouble.h>
39 #include <ModelAPI_AttributeDoubleArray.h>
40 #include <ModelAPI_AttributeInteger.h>
41 #include <ModelAPI_AttributeIntArray.h>
42 #include <ModelAPI_AttributeRefList.h>
43 #include <ModelAPI_AttributeString.h>
44 #include <ModelAPI_Events.h>
45 #include <ModelAPI_ResultConstruction.h>
46 #include <ModelAPI_Tools.h>
47 #include <ModelAPI_Validator.h>
49 #include <GeomAlgoAPI_MakeShapeList.h>
50 #include <GeomAlgoAPI_MapShapesAndAncestors.h>
51 #include <GeomAlgoAPI_Offset.h>
52 #include <GeomAlgoAPI_ShapeTools.h>
53 #include <GeomAlgoAPI_WireBuilder.h>
54 #include <GeomAlgoAPI_Fillet1D.h>
55 #include <GeomAlgoAPI_Tools.h>
57 #include <GeomAPI_BSpline.h>
58 #include <GeomAPI_Circ.h>
59 #include <GeomAPI_Edge.h>
60 #include <GeomAPI_Ellipse.h>
61 #include <GeomAPI_ShapeExplorer.h>
62 #include <GeomAPI_Wire.h>
63 #include <GeomAPI_WireExplorer.h>
65 #include <GeomDataAPI_Point2D.h>
66 #include <GeomDataAPI_Point2DArray.h>
70 static const double tolerance = 1.e-7;
72 SketchPlugin_Offset::SketchPlugin_Offset()
76 void SketchPlugin_Offset::initAttributes()
78 data()->addAttribute(EDGES_ID(), ModelAPI_AttributeRefList::typeId());
79 data()->addAttribute(VALUE_ID(), ModelAPI_AttributeDouble::typeId());
80 data()->addAttribute(REVERSED_ID(), ModelAPI_AttributeBoolean::typeId());
82 // store original entities
83 data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefList::typeId());
84 // store offset entities
85 data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefList::typeId());
86 // store mapping between original entity and index of the corresponding offset entity
87 data()->addAttribute(SketchPlugin_Constraint::ENTITY_C(), ModelAPI_AttributeIntArray::typeId());
89 ModelAPI_Session::get()->validators()->
90 registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_A());
91 ModelAPI_Session::get()->validators()->
92 registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_B());
93 ModelAPI_Session::get()->validators()->
94 registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_C());
96 AttributeStringPtr aJointAttr = std::dynamic_pointer_cast<ModelAPI_AttributeString>
97 (data()->addAttribute(JOINT_ID(), ModelAPI_AttributeString::typeId()));
98 if (!aJointAttr->isInitialized())
99 aJointAttr->setValue(JOINT_KEEP_DISTANCE());
102 void SketchPlugin_Offset::execute()
104 SketchPlugin_Sketch* aSketch = sketch();
105 if (!aSketch) return;
108 AttributeStringPtr aJointAttr = string(JOINT_ID());
109 std::string aType = JOINT_KEEP_DISTANCE();
110 if (aJointAttr->isInitialized())
111 aType = aJointAttr->value();
113 GeomAlgoAPI_OffsetJoint aJoint;
114 if (aType == JOINT_ARCS())
115 aJoint = GeomAlgoAPI_OffsetJoint::Arcs;
116 else if (aType == JOINT_LINES())
117 aJoint = GeomAlgoAPI_OffsetJoint::Lines;
119 aJoint = GeomAlgoAPI_OffsetJoint::KeepDistance;
122 std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
125 AttributeDoublePtr aValueAttr = real(VALUE_ID());
126 if (!aValueAttr->isInitialized()) return;
127 double aValue = aValueAttr->value();
128 if (aValue < tolerance) return;
131 AttributeBooleanPtr aReversedAttr = boolean(REVERSED_ID());
132 if (!aReversedAttr->isInitialized()) return;
133 if (aReversedAttr->value()) aValue = -aValue; // reverse offset direction
135 // 3. List of all selected edges
136 AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
137 std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
139 // 4. Put all selected edges in a set to pass them into findWireOneWay() below
140 std::set<FeaturePtr> anEdgesSet;
141 std::list<ObjectPtr>::const_iterator anEdgesIt = anEdgesList.begin();
142 for (; anEdgesIt != anEdgesList.end(); anEdgesIt++) {
143 FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
145 anEdgesSet.insert(aFeature);
149 // Wait all objects being created, then send update events
150 static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
151 bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
153 Events_Loop::loop()->setFlushed(anUpdateEvent, false);
155 // Save the current feature of the document, because new features may appear while executing.
156 // In this case, they will become current. But if the number of copies is updated from outside
157 // of sketch (e.g. by parameter change), the history line should not hold in sketch.
158 keepCurrentFeature();
160 // 5. Gather wires and make offset for each wire
161 ListOfMakeShape anOffsetAlgos;
162 std::set<FeaturePtr> aProcessedEdgesSet;
163 for (anEdgesIt = anEdgesList.begin(); anEdgesIt != anEdgesList.end(); anEdgesIt++) {
164 FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
165 if (aFeature.get()) {
166 if (aProcessedEdgesSet.find(aFeature) != aProcessedEdgesSet.end())
169 // 5.a. End points (if any)
170 std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
171 SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
173 // 5.b. Find a chain of edges
174 std::list<FeaturePtr> aChain;
175 aChain.push_back(aFeature);
176 bool isClosed = !(aStartPoint && anEndPoint); // not closed edge
178 isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet,
179 aProcessedEdgesSet, aChain, true);
181 isClosed = findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet,
182 aProcessedEdgesSet, aChain, false);
185 aProcessedEdgesSet.insert(aFeature);
188 ListOfShape aTopoChain;
189 std::list<FeaturePtr>::iterator aChainIt = aChain.begin();
190 for (; aChainIt != aChain.end(); ++aChainIt) {
191 FeaturePtr aChainFeature = (*aChainIt);
192 GeomShapePtr aTopoEdge = aChainFeature->lastResult()->shape();
193 if (aTopoEdge->shapeType() == GeomAPI_Shape::EDGE) {
194 aTopoChain.push_back(aTopoEdge);
197 std::shared_ptr<GeomAlgoAPI_WireBuilder> aWireBuilder(
198 new GeomAlgoAPI_WireBuilder(aTopoChain, !isClosed));
200 GeomShapePtr aWireShape = aWireBuilder->shape();
201 GeomWirePtr aWire (new GeomAPI_Wire (aWireShape));
203 // Fix for a problem of offset side change with selection change.
204 // Wire direction is defined by the first selected edge of this wire.
206 if (!aWire->isClosed()) {
207 ListOfShape aModified;
208 // First selected edge of current chain
209 GeomShapePtr aFirstSel = aFeature->lastResult()->shape();
210 aWireBuilder->modified(aFirstSel, aModified);
211 GeomShapePtr aModFS = aModified.front();
212 if (aModFS->orientation() != aFirstSel->orientation())
216 // 5.d. Make offset for the wire
217 std::shared_ptr<GeomAlgoAPI_Offset> anOffsetShape
218 (new GeomAlgoAPI_Offset(aPlane, aWireShape, aValue*aSign, aJoint));
220 if (anOffsetShape->isDone()) {
221 if (aJoint == GeomAlgoAPI_OffsetJoint::Arcs) {
222 // For Arcs joint make fillet at all straight edges intersections
223 // of the wire, resulting from GeomAlgoAPI_Offset algorithm
224 makeFillet(fabs(aValue), aWireBuilder, anOffsetShape, anOffsetAlgos);
227 std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeList (new GeomAlgoAPI_MakeShapeList);
228 aMakeList->appendAlgo(aWireBuilder);
229 aMakeList->appendAlgo(anOffsetShape);
230 anOffsetAlgos.push_back(aMakeList);
234 setError("Offset algorithm failed");
239 // 6. Store offset results.
240 // Create sketch feature for each edge of anOffsetShape, and also store
241 // created features in CREATED_ID() to remove them on next execute()
242 addToSketch(anOffsetAlgos);
244 restoreCurrentFeature();
246 // send events to update the sub-features by the solver
248 Events_Loop::loop()->setFlushed(anUpdateEvent, true);
251 bool SketchPlugin_Offset::findWireOneWay (const FeaturePtr& theFirstEdge,
252 const FeaturePtr& theEdge,
253 const std::shared_ptr<GeomDataAPI_Point2D>& theEndPoint,
254 std::set<FeaturePtr>& theEdgesSet,
255 std::set<FeaturePtr>& theProcessedEdgesSet,
256 std::list<FeaturePtr>& theChain,
257 const bool isPrepend)
259 // 1. Find a single edge, coincident to theEndPoint by one of its ends
260 if (!theEndPoint) return false;
262 FeaturePtr aNextEdgeFeature;
265 std::set<AttributePoint2DPtr> aCoincPoints =
266 SketchPlugin_Tools::findPointsCoincidentToPoint(theEndPoint);
268 std::set<AttributePoint2DPtr>::iterator aPointsIt = aCoincPoints.begin();
269 for (; aPointsIt != aCoincPoints.end(); aPointsIt++) {
270 AttributePoint2DPtr aP = (*aPointsIt);
271 FeaturePtr aCoincFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aP->owner());
272 bool isInSet = (theEdgesSet.find(aCoincFeature) != theEdgesSet.end());
274 // Condition 0: not auxiliary
275 if (!isInSet && aCoincFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value())
278 // Condition 1: not a point feature
279 if (aCoincFeature->getKind() != SketchPlugin_Point::ID()) {
280 // Condition 2: it is not the current edge
281 if (aCoincFeature != theEdge) {
282 // Condition 3: it is in the set of interest.
283 // Empty set means all sketch edges.
284 if (isInSet || theEdgesSet.empty()) {
285 // Condition 4: consider only features with two end points
286 std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
287 SketchPlugin_SegmentationTools::getFeaturePoints(aCoincFeature, aP1, aP2);
289 // Condition 5: consider only features, that have aP as one of they ends.
290 // For example, we do not need an arc, coincident to aP by its center.
291 if (theEndPoint->pnt()->isEqual(aP1->pnt()) ||
292 theEndPoint->pnt()->isEqual(aP2->pnt())) {
293 // Condition 6: only one edge can prolongate the chain. If several, we stop here.
299 aNextEdgeFeature = aCoincFeature;
307 // Only one edge can prolongate the chain. If several or none, we stop here.
311 // 2. So, we have the single edge, that prolongate the chain
313 // Condition 7: if we reached the very first edge of the chain
314 if (aNextEdgeFeature == theFirstEdge)
315 // Closed chain found
318 // 3. Add the found edge to the chain
320 theChain.push_front(aNextEdgeFeature);
322 theChain.push_back(aNextEdgeFeature);
323 theProcessedEdgesSet.insert(aNextEdgeFeature);
325 // 4. Which end of aNextEdgeFeature we need to proceed
326 std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
327 SketchPlugin_SegmentationTools::getFeaturePoints(aNextEdgeFeature, aP1, aP2);
328 if (aP2->pnt()->isEqual(theEndPoint->pnt())) {
333 // 5. Continue gathering the chain (recursive)
334 return findWireOneWay (theFirstEdge, aNextEdgeFeature, aP2, theEdgesSet,
335 theProcessedEdgesSet, theChain, isPrepend);
338 static void setRefListValue(AttributeRefListPtr theList, int theListSize,
339 ObjectPtr theValue, int theIndex)
341 if (theIndex < theListSize) {
342 ObjectPtr aCur = theList->object(theIndex);
343 if (aCur != theValue)
344 theList->substitute(aCur, theValue);
347 theList->append(theValue);
350 // Reorder shapes according to the wire's order
351 static void reorderShapes(ListOfShape& theShapes, GeomShapePtr theWire)
353 std::set<GeomShapePtr, GeomAPI_Shape::Comparator> aShapes;
354 aShapes.insert(theShapes.begin(), theShapes.end());
357 GeomWirePtr aWire(new GeomAPI_Wire(theWire));
358 GeomAPI_WireExplorer anExp(aWire);
359 for (; anExp.more(); anExp.next()) {
360 GeomShapePtr aCurEdge = anExp.current();
361 auto aFound = aShapes.find(aCurEdge);
362 if (aFound != aShapes.end()) {
363 theShapes.push_back(aCurEdge);
364 aShapes.erase(aFound);
369 static void removeLastFromIndex(AttributeRefListPtr theList, int theListSize, int& theLastIndex)
371 if (theLastIndex < theListSize) {
372 std::set<int> anIndicesToRemove;
373 for (; theLastIndex < theListSize; ++theLastIndex)
374 anIndicesToRemove.insert(theLastIndex);
375 theList->remove(anIndicesToRemove);
379 void SketchPlugin_Offset::addToSketch(const ListOfMakeShape& theOffsetAlgos)
381 AttributeRefListPtr aSelectedRefList = reflist(EDGES_ID());
382 AttributeRefListPtr aBaseRefList = reflist(ENTITY_A());
383 AttributeRefListPtr anOffsetRefList = reflist(ENTITY_B());
384 AttributeIntArrayPtr anOffsetToBaseMap = intArray(ENTITY_C());
386 // compare the list of selected edges and the previously stored,
387 // and store maping between them
388 std::map<ObjectPtr, std::list<ObjectPtr> > aMapExistent;
389 std::list<ObjectPtr> anObjectsToRemove;
390 std::list<ObjectPtr> aSelectedList = aSelectedRefList->list();
391 for (std::list<ObjectPtr>::iterator aSIt = aSelectedList.begin();
392 aSIt != aSelectedList.end(); ++aSIt) {
393 aMapExistent[*aSIt] = std::list<ObjectPtr>();
395 for (int anIndex = 0, aSize = anOffsetRefList->size(); anIndex < aSize; ++anIndex) {
396 ObjectPtr aCurrent = anOffsetRefList->object(anIndex);
397 int aBaseIndex = anOffsetToBaseMap->value(anIndex);
398 if (aBaseIndex >= 0) {
399 ObjectPtr aBaseObj = aBaseRefList->object(aBaseIndex);
400 std::map<ObjectPtr, std::list<ObjectPtr> >::iterator aFound = aMapExistent.find(aBaseObj);
401 if (aFound != aMapExistent.end())
402 aFound->second.push_back(aCurrent);
404 anObjectsToRemove.push_back(aCurrent);
407 anObjectsToRemove.push_back(aCurrent);
410 // update lists of base shapes and of offset shapes
411 int aBaseListSize = aBaseRefList->size();
412 int anOffsetListSize = anOffsetRefList->size();
413 int aBaseListIndex = 0, anOffsetListIndex = 0;
414 std::list<int> anOffsetBaseBackRefs;
415 std::set<GeomShapePtr, GeomAPI_Shape::ComparatorWithOri> aProcessedOffsets;
416 for (std::list<ObjectPtr>::iterator aSIt = aSelectedList.begin();
417 aSIt != aSelectedList.end(); ++aSIt) {
418 // find an offseted edge
419 FeaturePtr aBaseFeature = ModelAPI_Feature::feature(*aSIt);
420 GeomShapePtr aBaseShape = aBaseFeature->lastResult()->shape();
421 ListOfShape aNewShapes;
422 for (ListOfMakeShape::const_iterator anAlgoIt = theOffsetAlgos.begin();
423 anAlgoIt != theOffsetAlgos.end(); ++anAlgoIt) {
424 (*anAlgoIt)->generated(aBaseShape, aNewShapes);
425 if (!aNewShapes.empty()) {
426 reorderShapes(aNewShapes, (*anAlgoIt)->shape());
431 // store base feature
432 setRefListValue(aBaseRefList, aBaseListSize, *aSIt, aBaseListIndex);
434 // create or update an offseted feature
435 const std::list<ObjectPtr>& anImages = aMapExistent[*aSIt];
436 std::list<ObjectPtr>::const_iterator anImgIt = anImages.begin();
437 for (ListOfShape::iterator aNewIt = aNewShapes.begin(); aNewIt != aNewShapes.end(); ++aNewIt) {
438 FeaturePtr aNewFeature;
439 if (anImgIt != anImages.end())
440 aNewFeature = ModelAPI_Feature::feature(*anImgIt++);
441 updateExistentOrCreateNew(*aNewIt, aNewFeature, anObjectsToRemove);
442 aProcessedOffsets.insert(*aNewIt);
444 // store an offseted feature
445 setRefListValue(anOffsetRefList, anOffsetListSize, aNewFeature, anOffsetListIndex);
447 anOffsetBaseBackRefs.push_back(aBaseListIndex);
451 anObjectsToRemove.insert(anObjectsToRemove.end(), anImgIt, anImages.end());
453 // create arcs generated from vertices
454 for (ListOfMakeShape::const_iterator anAlgoIt = theOffsetAlgos.begin();
455 anAlgoIt != theOffsetAlgos.end(); ++anAlgoIt) {
456 GeomShapePtr aCurWire = (*anAlgoIt)->shape();
457 GeomAPI_ShapeExplorer anExp(aCurWire, GeomAPI_Shape::EDGE);
458 for (; anExp.more(); anExp.next()) {
459 GeomShapePtr aCurEdge = anExp.current();
460 if (aProcessedOffsets.find(aCurEdge) == aProcessedOffsets.end()) {
461 FeaturePtr aNewFeature;
462 updateExistentOrCreateNew(aCurEdge, aNewFeature, anObjectsToRemove);
463 aProcessedOffsets.insert(aCurEdge);
465 // store an offseted feature
466 setRefListValue(anOffsetRefList, anOffsetListSize, aNewFeature, anOffsetListIndex);
468 anOffsetBaseBackRefs.push_back(-1);
474 removeLastFromIndex(aBaseRefList, aBaseListSize, aBaseListIndex);
475 removeLastFromIndex(anOffsetRefList, anOffsetListSize, anOffsetListIndex);
477 anOffsetToBaseMap->setSize((int)anOffsetBaseBackRefs.size(), false);
479 for (std::list<int>::iterator anIt = anOffsetBaseBackRefs.begin();
480 anIt != anOffsetBaseBackRefs.end(); ++anIt) {
481 anOffsetToBaseMap->setValue(anIndex++, *anIt, false);
484 // remove unused objects
485 std::set<FeaturePtr> aSet;
486 for (std::list<ObjectPtr>::iterator anIt = anObjectsToRemove.begin();
487 anIt != anObjectsToRemove.end(); ++anIt) {
488 FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
490 aSet.insert(aFeature);
492 ModelAPI_Tools::removeFeaturesAndReferences(aSet);
495 static void findOrCreateFeatureByKind(SketchPlugin_Sketch* theSketch,
496 const std::string& theFeatureKind,
497 FeaturePtr& theFeature,
498 std::list<ObjectPtr>& thePoolOfFeatures)
501 // check the feature type is the same as required
502 if (theFeature->getKind() != theFeatureKind) {
503 // return feature to the pool and try to find the most appropriate
504 thePoolOfFeatures.push_back(theFeature);
505 theFeature = FeaturePtr();
509 // try to find appropriate feature in the pool
510 for (std::list<ObjectPtr>::iterator it = thePoolOfFeatures.begin();
511 it != thePoolOfFeatures.end(); ++it) {
512 FeaturePtr aCurFeature = ModelAPI_Feature::feature(*it);
513 if (aCurFeature->getKind() == theFeatureKind) {
514 theFeature = aCurFeature;
515 thePoolOfFeatures.erase(it);
519 // feature not found, create new
521 theFeature = theSketch->addFeature(theFeatureKind);
525 void SketchPlugin_Offset::updateExistentOrCreateNew(const GeomShapePtr& theShape,
526 FeaturePtr& theFeature,
527 std::list<ObjectPtr>& thePoolOfFeatures)
529 if (theShape->shapeType() != GeomAPI_Shape::EDGE)
532 std::shared_ptr<GeomAPI_Edge> aResEdge(new GeomAPI_Edge(theShape));
534 std::shared_ptr<GeomAPI_Pnt2d> aFP, aLP;
535 std::shared_ptr<GeomAPI_Pnt> aFP3d = aResEdge->firstPoint();
536 std::shared_ptr<GeomAPI_Pnt> aLP3d = aResEdge->lastPoint();
537 if (aFP3d && aLP3d) {
538 aFP = sketch()->to2D(aFP3d);
539 aLP = sketch()->to2D(aLP3d);
542 if (aResEdge->isLine()) {
543 findOrCreateFeatureByKind(sketch(), SketchPlugin_Line::ID(), theFeature, thePoolOfFeatures);
545 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
546 (theFeature->attribute(SketchPlugin_Line::START_ID()))->setValue(aFP);
547 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
548 (theFeature->attribute(SketchPlugin_Line::END_ID()))->setValue(aLP);
550 else if (aResEdge->isArc()) {
551 std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
552 std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
553 std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
555 findOrCreateFeatureByKind(sketch(), SketchPlugin_Arc::ID(), theFeature, thePoolOfFeatures);
557 GeomDirPtr aCircNormal = aCircEdge->normal();
558 GeomDirPtr aSketchNormal = sketch()->coordinatePlane()->normal();
559 if (aSketchNormal->dot(aCircNormal) < -tolerance)
562 bool aWasBlocked = theFeature->data()->blockSendAttributeUpdated(true);
563 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
564 (theFeature->attribute(SketchPlugin_Arc::END_ID()))->setValue(aLP);
565 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
566 (theFeature->attribute(SketchPlugin_Arc::START_ID()))->setValue(aFP);
567 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
568 (theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(aCP);
569 theFeature->data()->blockSendAttributeUpdated(aWasBlocked);
571 else if (aResEdge->isCircle()) {
572 std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
573 std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
574 std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
576 findOrCreateFeatureByKind(sketch(), SketchPlugin_Circle::ID(), theFeature, thePoolOfFeatures);
578 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
579 (theFeature->attribute(SketchPlugin_Circle::CENTER_ID()))->setValue(aCP);
580 theFeature->real(SketchPlugin_Circle::RADIUS_ID())->setValue(aCircEdge->radius());
582 else if (aResEdge->isEllipse()) {
583 std::shared_ptr<GeomAPI_Ellipse> anEllipseEdge = aResEdge->ellipse();
585 GeomPointPtr aCP3d = anEllipseEdge->center();
586 GeomPnt2dPtr aCP = sketch()->to2D(aCP3d);
588 GeomPointPtr aFocus3d = anEllipseEdge->firstFocus();
589 GeomPnt2dPtr aFocus = sketch()->to2D(aFocus3d);
591 if (aFP3d && aLP3d) {
593 findOrCreateFeatureByKind(sketch(), SketchPlugin_EllipticArc::ID(),
594 theFeature, thePoolOfFeatures);
596 bool aWasBlocked = theFeature->data()->blockSendAttributeUpdated(true);
597 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
598 (theFeature->attribute(SketchPlugin_EllipticArc::CENTER_ID()))->setValue(aCP);
599 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
600 (theFeature->attribute(SketchPlugin_EllipticArc::FIRST_FOCUS_ID()))->setValue(aFocus);
601 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
602 (theFeature->attribute(SketchPlugin_EllipticArc::START_POINT_ID()))->setValue(aFP);
603 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
604 (theFeature->attribute(SketchPlugin_EllipticArc::END_POINT_ID()))->setValue(aLP);
605 theFeature->data()->blockSendAttributeUpdated(aWasBlocked);
609 findOrCreateFeatureByKind(sketch(), SketchPlugin_Ellipse::ID(),
610 theFeature, thePoolOfFeatures);
612 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
613 (theFeature->attribute(SketchPlugin_Ellipse::CENTER_ID()))->setValue(aCP);
614 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
615 (theFeature->attribute(SketchPlugin_Ellipse::FIRST_FOCUS_ID()))->setValue(aFocus);
616 theFeature->real(SketchPlugin_Ellipse::MINOR_RADIUS_ID())->setValue(
617 anEllipseEdge->minorRadius());
621 // convert to b-spline
622 mkBSpline(theFeature, aResEdge, thePoolOfFeatures);
625 if (theFeature.get()) {
626 theFeature->boolean(SketchPlugin_SketchEntity::COPY_ID())->setValue(true);
627 theFeature->execute();
629 static Events_ID aRedisplayEvent = Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY);
630 ModelAPI_EventCreator::get()->sendUpdated(theFeature, aRedisplayEvent);
631 const std::list<ResultPtr>& aResults = theFeature->results();
632 for (std::list<ResultPtr>::const_iterator anIt = aResults.begin();
633 anIt != aResults.end(); ++anIt)
634 ModelAPI_EventCreator::get()->sendUpdated(*anIt, aRedisplayEvent);
638 void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult,
639 const GeomEdgePtr& theEdge,
640 std::list<ObjectPtr>& thePoolOfFeatures)
642 GeomCurvePtr aCurve (new GeomAPI_Curve (theEdge));
643 // Forced conversion to b-spline, if aCurve is not b-spline
644 GeomBSplinePtr aBSpline = GeomAPI_BSpline::convertToBSpline(aCurve);
646 const std::string& aBSplineKind = aBSpline->isPeriodic() ? SketchPlugin_BSplinePeriodic::ID()
647 : SketchPlugin_BSpline::ID();
648 findOrCreateFeatureByKind(sketch(), aBSplineKind, theResult, thePoolOfFeatures);
650 theResult->integer(SketchPlugin_BSpline::DEGREE_ID())->setValue(aBSpline->degree());
652 AttributePoint2DArrayPtr aPolesAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>
653 (theResult->attribute(SketchPlugin_BSpline::POLES_ID()));
654 std::list<GeomPointPtr> aPoles = aBSpline->poles();
655 aPolesAttr->setSize((int)aPoles.size());
656 std::list<GeomPointPtr>::iterator anIt = aPoles.begin();
657 for (int anIndex = 0; anIt != aPoles.end(); ++anIt, ++anIndex) {
658 GeomPnt2dPtr aPoleInSketch = sketch()->to2D(*anIt);
659 aPolesAttr->setPnt(anIndex, aPoleInSketch);
662 AttributeDoubleArrayPtr aWeightsAttr =
663 theResult->data()->realArray(SketchPlugin_BSpline::WEIGHTS_ID());
664 std::list<double> aWeights = aBSpline->weights();
665 if (aWeights.empty()) { // rational B-spline
666 int aSize = (int)aPoles.size();
667 aWeightsAttr->setSize(aSize);
668 for (int anIndex = 0; anIndex < aSize; ++anIndex)
669 aWeightsAttr->setValue(anIndex, 1.0);
671 else { // non-rational B-spline
672 aWeightsAttr->setSize((int)aWeights.size());
673 std::list<double>::iterator aWIt = aWeights.begin();
674 for (int anIndex = 0; aWIt != aWeights.end(); ++aWIt, ++anIndex)
675 aWeightsAttr->setValue(anIndex, *aWIt);
678 AttributeDoubleArrayPtr aKnotsAttr =
679 theResult->data()->realArray(SketchPlugin_BSpline::KNOTS_ID());
680 std::list<double> aKnots = aBSpline->knots();
681 int aSize = (int)aKnots.size();
682 aKnotsAttr->setSize(aSize);
683 std::list<double>::iterator aKIt = aKnots.begin();
684 for (int index = 0; index < aSize; ++index, ++aKIt)
685 aKnotsAttr->setValue(index, *aKIt);
687 AttributeIntArrayPtr aMultsAttr =
688 theResult->data()->intArray(SketchPlugin_BSpline::MULTS_ID());
689 std::list<int> aMultiplicities = aBSpline->mults();
690 aSize = (int)aMultiplicities.size();
691 aMultsAttr->setSize(aSize);
692 std::list<int>::iterator aMIt = aMultiplicities.begin();
693 for (int index = 0; index < aSize; ++index, ++aMIt)
694 aMultsAttr->setValue(index, *aMIt);
697 void SketchPlugin_Offset::attributeChanged(const std::string& theID)
699 if (theID == EDGES_ID()) {
700 AttributeRefListPtr aSelected = reflist(EDGES_ID());
701 if (aSelected->size() == 0) {
702 // Clear list of objects
703 AttributeRefListPtr anOffsetAttr = reflist(SketchPlugin_Constraint::ENTITY_B());
704 std::list<ObjectPtr> anOffsetList = anOffsetAttr->list();
705 std::set<FeaturePtr> aFeaturesToBeRemoved;
706 for (std::list<ObjectPtr>::iterator anIt = anOffsetList.begin();
707 anIt != anOffsetList.end(); ++anIt) {
708 FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
710 aFeaturesToBeRemoved.insert(aFeature);
713 reflist(SketchPlugin_Constraint::ENTITY_A())->clear();
714 anOffsetAttr->clear();
715 intArray(SketchPlugin_Constraint::ENTITY_C())->setSize(0);
717 ModelAPI_Tools::removeFeaturesAndReferences(aFeaturesToBeRemoved);
722 bool SketchPlugin_Offset::customAction(const std::string& theActionId)
725 if (theActionId == ADD_WIRE_ACTION_ID()) {
729 std::string aMsg = "Error: Feature \"%1\" does not support action \"%2\".";
730 Events_InfoMessage("SketchPlugin_Offset", aMsg).arg(getKind()).arg(theActionId).send();
735 bool SketchPlugin_Offset::findWires()
737 AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
738 std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
741 std::set<FeaturePtr> anEdgesSet;
744 std::set<FeaturePtr> aProcessedEdgesSet;
746 // Put all selected edges in a set to avoid adding them in reflist(EDGES_ID())
747 std::set<FeaturePtr> aSelectedSet;
748 std::list<ObjectPtr>::const_iterator anEdgesIt = anEdgesList.begin();
749 for (; anEdgesIt != anEdgesList.end(); anEdgesIt++) {
750 FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
752 aSelectedSet.insert(aFeature);
756 bool aWasBlocked = data()->blockSendAttributeUpdated(true);
758 // Gather chains of edges
759 for (anEdgesIt = anEdgesList.begin(); anEdgesIt != anEdgesList.end(); anEdgesIt++) {
760 FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
761 if (aFeature.get()) {
762 if (aProcessedEdgesSet.find(aFeature) != aProcessedEdgesSet.end())
764 aProcessedEdgesSet.insert(aFeature);
766 // End points (if any)
767 std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
768 SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
770 std::list<FeaturePtr> aChain;
771 aChain.push_back(aFeature);
772 bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet,
773 aProcessedEdgesSet, aChain, true);
775 findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet,
776 aProcessedEdgesSet, aChain, false);
779 std::list<FeaturePtr>::iterator aChainIt = aChain.begin();
780 for (; aChainIt != aChain.end(); ++aChainIt) {
781 FeaturePtr aChainFeature = (*aChainIt);
782 if (aSelectedSet.find(aChainFeature) == aSelectedSet.end()) {
783 aSelectedEdges->append(aChainFeature->lastResult());
789 data()->blockSendAttributeUpdated(aWasBlocked);
794 AISObjectPtr SketchPlugin_Offset::getAISObject(AISObjectPtr thePrevious)
799 AISObjectPtr anAIS = SketcherPrs_Factory::offsetObject(this, sketch(),
805 void SketchPlugin_Offset::makeFillet
806 (const double theValue,
807 const std::shared_ptr<GeomAlgoAPI_WireBuilder>& theWireBuilder,
808 const std::shared_ptr<GeomAlgoAPI_Offset>& theOffsetShape,
809 ListOfMakeShape& theOffsetAlgos)
811 std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeList (new GeomAlgoAPI_MakeShapeList);
812 aMakeList->appendAlgo(theWireBuilder);
813 aMakeList->appendAlgo(theOffsetShape);
817 GeomShapePtr aResWire = theOffsetShape->shape();
818 GeomAlgoAPI_MapShapesAndAncestors aMapVE
819 (aResWire, GeomAPI_Shape::VERTEX, GeomAPI_Shape::EDGE);
820 const MapShapeToShapes& aSubshapes = aMapVE.map();
822 // find vertices for fillet
823 std::set<GeomShapePtr, GeomAPI_Shape::Comparator> aFilletVertices;
824 for (MapShapeToShapes::const_iterator anIt = aSubshapes.begin();
825 anIt != aSubshapes.end(); ++anIt) {
826 // vertex should have 2 adjacent edges
827 if (anIt->second.size() != 2)
830 // both edges should be linear
832 anEdges.insert(anEdges.end(), anIt->second.begin(), anIt->second.end());
833 GeomEdgePtr anEdge1 (new GeomAPI_Edge(anEdges.front()));
834 GeomEdgePtr anEdge2 (new GeomAPI_Edge(anEdges.back()));
835 if (!anEdge1->isLine() || !anEdge2->isLine())
838 // skip vertices, which smoothly connect adjacent edges
839 GeomVertexPtr aSharedVertex(new GeomAPI_Vertex(anIt->first));
840 if (GeomAlgoAPI_ShapeTools::isTangent(anEdge1, anEdge2, aSharedVertex))
843 aFilletVertices.insert(anIt->first);
846 if (!aFilletVertices.empty()) {
847 isOK = false; // the wire needs correction
848 ListOfShape aVerticesList (aFilletVertices.begin(), aFilletVertices.end());
850 // Fillet1D on all linear edges intersections
851 std::shared_ptr<GeomAlgoAPI_Fillet1D> aFilletBuilder
852 (new GeomAlgoAPI_Fillet1D(aResWire, aVerticesList, theValue));
855 if (!GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed
856 (aFilletBuilder, getKind(), anError)) {
857 aMakeList->appendAlgo(aFilletBuilder);
861 ListOfShape aFailedVertices = aFilletBuilder->failedVertices();
862 if (aFailedVertices.size() != 0) {
863 // Exclude failed vertices and also vertices, joined
864 // with failed by one edge, and run algorithm once again
865 ListOfShape::iterator itVertices = aFailedVertices.begin();
866 for (; itVertices != aFailedVertices.end(); itVertices++) {
867 GeomShapePtr aFailedVertex = *itVertices;
868 aFilletVertices.erase(aFailedVertex);
869 // remove also neighbour vertices
870 MapShapeToShapes::const_iterator anIt = aSubshapes.find(aFailedVertex);
871 if (anIt != aSubshapes.end()) { // should be always true
873 anEdges.insert(anEdges.end(), anIt->second.begin(), anIt->second.end());
874 GeomEdgePtr anEdge1 (new GeomAPI_Edge(anEdges.front()));
875 GeomEdgePtr anEdge2 (new GeomAPI_Edge(anEdges.back()));
876 GeomVertexPtr V1, V2;
877 anEdge1->vertices(V1, V2);
878 if (V1->isEqual(aFailedVertex)) V1 = V2;
879 aFilletVertices.erase(V1);
880 anEdge2->vertices(V1, V2);
881 if (V1->isEqual(aFailedVertex)) V1 = V2;
882 aFilletVertices.erase(V1);
885 if (aFilletVertices.size() == 0) {
886 // there are no suitable vertices for fillet
890 // Fillet1D one more try
891 ListOfShape aVerticesList1 (aFilletVertices.begin(), aFilletVertices.end());
893 std::shared_ptr<GeomAlgoAPI_Fillet1D> aFilletBuilder1
894 (new GeomAlgoAPI_Fillet1D(aResWire, aVerticesList1, theValue));
896 if (!GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed
897 (aFilletBuilder1, getKind(), anError)) {
898 aMakeList->appendAlgo(aFilletBuilder1);
907 theOffsetAlgos.push_back(aMakeList);
909 setError("Offset algorithm failed");