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_Events.h>
44 #include <ModelAPI_ResultConstruction.h>
45 #include <ModelAPI_Tools.h>
46 #include <ModelAPI_Validator.h>
48 #include <GeomAlgoAPI_MakeShapeList.h>
49 #include <GeomAlgoAPI_Offset.h>
50 #include <GeomAlgoAPI_ShapeTools.h>
51 #include <GeomAlgoAPI_WireBuilder.h>
53 #include <GeomAPI_BSpline.h>
54 #include <GeomAPI_Circ.h>
55 #include <GeomAPI_Edge.h>
56 #include <GeomAPI_Ellipse.h>
57 #include <GeomAPI_ShapeExplorer.h>
58 #include <GeomAPI_Wire.h>
59 #include <GeomAPI_WireExplorer.h>
61 #include <GeomDataAPI_Point2D.h>
62 #include <GeomDataAPI_Point2DArray.h>
64 static const double tolerance = 1.e-7;
66 SketchPlugin_Offset::SketchPlugin_Offset()
70 void SketchPlugin_Offset::initAttributes()
72 data()->addAttribute(EDGES_ID(), ModelAPI_AttributeRefList::typeId());
73 data()->addAttribute(VALUE_ID(), ModelAPI_AttributeDouble::typeId());
74 data()->addAttribute(REVERSED_ID(), ModelAPI_AttributeBoolean::typeId());
76 // store original entities
77 data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefList::typeId());
78 // store offset entities
79 data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefList::typeId());
80 // store mapping between original entity and index of the corresponding offset entity
81 data()->addAttribute(SketchPlugin_Constraint::ENTITY_C(), ModelAPI_AttributeIntArray::typeId());
83 ModelAPI_Session::get()->validators()->
84 registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_A());
85 ModelAPI_Session::get()->validators()->
86 registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_B());
87 ModelAPI_Session::get()->validators()->
88 registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_C());
91 void SketchPlugin_Offset::execute()
93 SketchPlugin_Sketch* aSketch = sketch();
97 std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
100 AttributeDoublePtr aValueAttr = real(VALUE_ID());
101 if (!aValueAttr->isInitialized()) return;
102 double aValue = aValueAttr->value();
103 if (aValue < tolerance) return;
106 AttributeBooleanPtr aReversedAttr = boolean(REVERSED_ID());
107 if (!aReversedAttr->isInitialized()) return;
108 if (aReversedAttr->value()) aValue = -aValue; // reverse offset direction
110 // 3. List of all selected edges
111 AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
112 std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
114 // 4. Put all selected edges in a set to pass them into findWireOneWay() below
115 std::set<FeaturePtr> anEdgesSet;
116 std::list<ObjectPtr>::const_iterator anEdgesIt = anEdgesList.begin();
117 for (; anEdgesIt != anEdgesList.end(); anEdgesIt++) {
118 FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
120 anEdgesSet.insert(aFeature);
124 // Wait all objects being created, then send update events
125 static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
126 bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
128 Events_Loop::loop()->setFlushed(anUpdateEvent, false);
130 // Save the current feature of the document, because new features may appear while executing.
131 // In this case, they will become current. But if the number of copies is updated from outside
132 // of sketch (e.g. by parameter change), the history line should not hold in sketch.
133 keepCurrentFeature();
135 // 5. Gather wires and make offset for each wire
136 ListOfMakeShape anOffsetAlgos;
137 std::set<FeaturePtr> aProcessedEdgesSet;
138 for (anEdgesIt = anEdgesList.begin(); anEdgesIt != anEdgesList.end(); anEdgesIt++) {
139 FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
140 if (aFeature.get()) {
141 if (aProcessedEdgesSet.find(aFeature) != aProcessedEdgesSet.end())
144 // 5.a. End points (if any)
145 std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
146 SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
148 // 5.b. Find a chain of edges
149 std::list<FeaturePtr> aChain;
150 aChain.push_back(aFeature);
151 bool isClosed = !(aStartPoint && anEndPoint); // not closed edge
153 isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet,
154 aProcessedEdgesSet, aChain, true);
156 isClosed = findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet,
157 aProcessedEdgesSet, aChain, false);
160 aProcessedEdgesSet.insert(aFeature);
163 ListOfShape aTopoChain;
164 std::list<FeaturePtr>::iterator aChainIt = aChain.begin();
165 for (; aChainIt != aChain.end(); ++aChainIt) {
166 FeaturePtr aChainFeature = (*aChainIt);
167 GeomShapePtr aTopoEdge = aChainFeature->lastResult()->shape();
168 if (aTopoEdge->shapeType() == GeomAPI_Shape::EDGE) {
169 aTopoChain.push_back(aTopoEdge);
172 std::shared_ptr<GeomAlgoAPI_WireBuilder> aWireBuilder(
173 new GeomAlgoAPI_WireBuilder(aTopoChain, !isClosed));
175 GeomShapePtr aWireShape = aWireBuilder->shape();
176 GeomWirePtr aWire (new GeomAPI_Wire (aWireShape));
178 // Fix for a problem of offset side change with selection change.
179 // Wire direction is defined by the first selected edge of this wire.
181 if (!aWire->isClosed()) {
182 ListOfShape aModified;
183 // First selected edge of current chain
184 GeomShapePtr aFirstSel = aFeature->lastResult()->shape();
185 aWireBuilder->modified(aFirstSel, aModified);
186 GeomShapePtr aModFS = aModified.front();
187 if (aModFS->orientation() != aFirstSel->orientation())
191 // 5.d. Make offset for the wire
192 std::shared_ptr<GeomAlgoAPI_Offset> anOffsetShape(
193 new GeomAlgoAPI_Offset(aPlane, aWireShape, aValue*aSign));
195 if (anOffsetShape->isDone()) {
196 std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeList(new GeomAlgoAPI_MakeShapeList);
197 aMakeList->appendAlgo(aWireBuilder);
198 aMakeList->appendAlgo(anOffsetShape);
199 anOffsetAlgos.push_back(aMakeList);
202 setError("Offset algorithm failed");
207 // 6. Store offset results.
208 // Create sketch feature for each edge of anOffsetShape, and also store
209 // created features in CREATED_ID() to remove them on next execute()
210 addToSketch(anOffsetAlgos);
212 restoreCurrentFeature();
214 // send events to update the sub-features by the solver
216 Events_Loop::loop()->setFlushed(anUpdateEvent, true);
219 bool SketchPlugin_Offset::findWireOneWay (const FeaturePtr& theFirstEdge,
220 const FeaturePtr& theEdge,
221 const std::shared_ptr<GeomDataAPI_Point2D>& theEndPoint,
222 std::set<FeaturePtr>& theEdgesSet,
223 std::set<FeaturePtr>& theProcessedEdgesSet,
224 std::list<FeaturePtr>& theChain,
225 const bool isPrepend)
227 // 1. Find a single edge, coincident to theEndPoint by one of its ends
228 if (!theEndPoint) return false;
230 FeaturePtr aNextEdgeFeature;
233 std::set<AttributePoint2DPtr> aCoincPoints =
234 SketchPlugin_Tools::findPointsCoincidentToPoint(theEndPoint);
236 std::set<AttributePoint2DPtr>::iterator aPointsIt = aCoincPoints.begin();
237 for (; aPointsIt != aCoincPoints.end(); aPointsIt++) {
238 AttributePoint2DPtr aP = (*aPointsIt);
239 FeaturePtr aCoincFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aP->owner());
240 bool isInSet = (theEdgesSet.find(aCoincFeature) != theEdgesSet.end());
242 // Condition 0: not auxiliary
243 if (!isInSet && aCoincFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value())
246 // Condition 1: not a point feature
247 if (aCoincFeature->getKind() != SketchPlugin_Point::ID()) {
248 // Condition 2: it is not the current edge
249 if (aCoincFeature != theEdge) {
250 // Condition 3: it is in the set of interest.
251 // Empty set means all sketch edges.
252 if (isInSet || theEdgesSet.empty()) {
253 // Condition 4: consider only features with two end points
254 std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
255 SketchPlugin_SegmentationTools::getFeaturePoints(aCoincFeature, aP1, aP2);
257 // Condition 5: consider only features, that have aP as one of they ends.
258 // For example, we do not need an arc, coincident to aP by its center.
259 if (theEndPoint->pnt()->isEqual(aP1->pnt()) ||
260 theEndPoint->pnt()->isEqual(aP2->pnt())) {
261 // Condition 6: only one edge can prolongate the chain. If several, we stop here.
267 aNextEdgeFeature = aCoincFeature;
275 // Only one edge can prolongate the chain. If several or none, we stop here.
279 // 2. So, we have the single edge, that prolongate the chain
281 // Condition 7: if we reached the very first edge of the chain
282 if (aNextEdgeFeature == theFirstEdge)
283 // Closed chain found
286 // 3. Add the found edge to the chain
288 theChain.push_front(aNextEdgeFeature);
290 theChain.push_back(aNextEdgeFeature);
291 theProcessedEdgesSet.insert(aNextEdgeFeature);
293 // 4. Which end of aNextEdgeFeature we need to proceed
294 std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
295 SketchPlugin_SegmentationTools::getFeaturePoints(aNextEdgeFeature, aP1, aP2);
296 if (aP2->pnt()->isEqual(theEndPoint->pnt())) {
301 // 5. Continue gathering the chain (recursive)
302 return findWireOneWay (theFirstEdge, aNextEdgeFeature, aP2, theEdgesSet,
303 theProcessedEdgesSet, theChain, isPrepend);
306 static void setRefListValue(AttributeRefListPtr theList, int theListSize,
307 ObjectPtr theValue, int theIndex)
309 if (theIndex < theListSize) {
310 ObjectPtr aCur = theList->object(theIndex);
311 if (aCur != theValue)
312 theList->substitute(aCur, theValue);
315 theList->append(theValue);
318 // Reorder shapes according to the wire's order
319 static void reorderShapes(ListOfShape& theShapes, GeomShapePtr theWire)
321 std::set<GeomShapePtr, GeomAPI_Shape::Comparator> aShapes;
322 aShapes.insert(theShapes.begin(), theShapes.end());
325 GeomWirePtr aWire(new GeomAPI_Wire(theWire));
326 GeomAPI_WireExplorer anExp(aWire);
327 for (; anExp.more(); anExp.next()) {
328 GeomShapePtr aCurEdge = anExp.current();
329 auto aFound = aShapes.find(aCurEdge);
330 if (aFound != aShapes.end()) {
331 theShapes.push_back(aCurEdge);
332 aShapes.erase(aFound);
337 static void removeLastFromIndex(AttributeRefListPtr theList, int theListSize, int& theLastIndex)
339 if (theLastIndex < theListSize) {
340 std::set<int> anIndicesToRemove;
341 for (; theLastIndex < theListSize; ++theLastIndex)
342 anIndicesToRemove.insert(theLastIndex);
343 theList->remove(anIndicesToRemove);
347 void SketchPlugin_Offset::addToSketch(const ListOfMakeShape& theOffsetAlgos)
349 AttributeRefListPtr aSelectedRefList = reflist(EDGES_ID());
350 AttributeRefListPtr aBaseRefList = reflist(ENTITY_A());
351 AttributeRefListPtr anOffsetRefList = reflist(ENTITY_B());
352 AttributeIntArrayPtr anOffsetToBaseMap = intArray(ENTITY_C());
354 // compare the list of selected edges and the previously stored,
355 // and store maping between them
356 std::map<ObjectPtr, std::list<ObjectPtr> > aMapExistent;
357 std::list<ObjectPtr> anObjectsToRemove;
358 std::list<ObjectPtr> aSelectedList = aSelectedRefList->list();
359 for (std::list<ObjectPtr>::iterator aSIt = aSelectedList.begin();
360 aSIt != aSelectedList.end(); ++aSIt) {
361 aMapExistent[*aSIt] = std::list<ObjectPtr>();
363 for (int anIndex = 0, aSize = anOffsetRefList->size(); anIndex < aSize; ++anIndex) {
364 ObjectPtr aCurrent = anOffsetRefList->object(anIndex);
365 int aBaseIndex = anOffsetToBaseMap->value(anIndex);
366 if (aBaseIndex >= 0) {
367 ObjectPtr aBaseObj = aBaseRefList->object(aBaseIndex);
368 std::map<ObjectPtr, std::list<ObjectPtr> >::iterator aFound = aMapExistent.find(aBaseObj);
369 if (aFound != aMapExistent.end())
370 aFound->second.push_back(aCurrent);
372 anObjectsToRemove.push_back(aCurrent);
375 anObjectsToRemove.push_back(aCurrent);
378 // update lists of base shapes and of offset shapes
379 int aBaseListSize = aBaseRefList->size();
380 int anOffsetListSize = anOffsetRefList->size();
381 int aBaseListIndex = 0, anOffsetListIndex = 0;
382 std::list<int> anOffsetBaseBackRefs;
383 std::set<GeomShapePtr, GeomAPI_Shape::ComparatorWithOri> aProcessedOffsets;
384 for (std::list<ObjectPtr>::iterator aSIt = aSelectedList.begin();
385 aSIt != aSelectedList.end(); ++aSIt) {
386 // find an offseted edge
387 FeaturePtr aBaseFeature = ModelAPI_Feature::feature(*aSIt);
388 GeomShapePtr aBaseShape = aBaseFeature->lastResult()->shape();
389 ListOfShape aNewShapes;
390 for (ListOfMakeShape::const_iterator anAlgoIt = theOffsetAlgos.begin();
391 anAlgoIt != theOffsetAlgos.end(); ++anAlgoIt) {
392 (*anAlgoIt)->generated(aBaseShape, aNewShapes);
393 if (!aNewShapes.empty()) {
394 reorderShapes(aNewShapes, (*anAlgoIt)->shape());
399 // store base feature
400 setRefListValue(aBaseRefList, aBaseListSize, *aSIt, aBaseListIndex);
402 // create or update an offseted feature
403 const std::list<ObjectPtr>& anImages = aMapExistent[*aSIt];
404 std::list<ObjectPtr>::const_iterator anImgIt = anImages.begin();
405 for (ListOfShape::iterator aNewIt = aNewShapes.begin(); aNewIt != aNewShapes.end(); ++aNewIt) {
406 FeaturePtr aNewFeature;
407 if (anImgIt != anImages.end())
408 aNewFeature = ModelAPI_Feature::feature(*anImgIt++);
409 updateExistentOrCreateNew(*aNewIt, aNewFeature, anObjectsToRemove);
410 aProcessedOffsets.insert(*aNewIt);
412 // store an offseted feature
413 setRefListValue(anOffsetRefList, anOffsetListSize, aNewFeature, anOffsetListIndex);
415 anOffsetBaseBackRefs.push_back(aBaseListIndex);
419 anObjectsToRemove.insert(anObjectsToRemove.end(), anImgIt, anImages.end());
421 // create arcs generated from vertices
422 for (ListOfMakeShape::const_iterator anAlgoIt = theOffsetAlgos.begin();
423 anAlgoIt != theOffsetAlgos.end(); ++anAlgoIt) {
424 GeomShapePtr aCurWire = (*anAlgoIt)->shape();
425 GeomAPI_ShapeExplorer anExp(aCurWire, GeomAPI_Shape::EDGE);
426 for (; anExp.more(); anExp.next()) {
427 GeomShapePtr aCurEdge = anExp.current();
428 if (aProcessedOffsets.find(aCurEdge) == aProcessedOffsets.end()) {
429 FeaturePtr aNewFeature;
430 updateExistentOrCreateNew(aCurEdge, aNewFeature, anObjectsToRemove);
431 aProcessedOffsets.insert(aCurEdge);
433 // store an offseted feature
434 setRefListValue(anOffsetRefList, anOffsetListSize, aNewFeature, anOffsetListIndex);
436 anOffsetBaseBackRefs.push_back(-1);
442 removeLastFromIndex(aBaseRefList, aBaseListSize, aBaseListIndex);
443 removeLastFromIndex(anOffsetRefList, anOffsetListSize, anOffsetListIndex);
445 anOffsetToBaseMap->setSize((int)anOffsetBaseBackRefs.size(), false);
447 for (std::list<int>::iterator anIt = anOffsetBaseBackRefs.begin();
448 anIt != anOffsetBaseBackRefs.end(); ++anIt) {
449 anOffsetToBaseMap->setValue(anIndex++, *anIt, false);
452 // remove unused objects
453 std::set<FeaturePtr> aSet;
454 for (std::list<ObjectPtr>::iterator anIt = anObjectsToRemove.begin();
455 anIt != anObjectsToRemove.end(); ++anIt) {
456 FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
458 aSet.insert(aFeature);
460 ModelAPI_Tools::removeFeaturesAndReferences(aSet);
463 static void findOrCreateFeatureByKind(SketchPlugin_Sketch* theSketch,
464 const std::string& theFeatureKind,
465 FeaturePtr& theFeature,
466 std::list<ObjectPtr>& thePoolOfFeatures)
469 // check the feature type is the same as required
470 if (theFeature->getKind() != theFeatureKind) {
471 // return feature to the pool and try to find the most appropriate
472 thePoolOfFeatures.push_back(theFeature);
473 theFeature = FeaturePtr();
477 // try to find appropriate feature in the pool
478 for (std::list<ObjectPtr>::iterator it = thePoolOfFeatures.begin();
479 it != thePoolOfFeatures.end(); ++it) {
480 FeaturePtr aCurFeature = ModelAPI_Feature::feature(*it);
481 if (aCurFeature->getKind() == theFeatureKind) {
482 theFeature = aCurFeature;
483 thePoolOfFeatures.erase(it);
487 // feature not found, create new
489 theFeature = theSketch->addFeature(theFeatureKind);
493 void SketchPlugin_Offset::updateExistentOrCreateNew(const GeomShapePtr& theShape,
494 FeaturePtr& theFeature,
495 std::list<ObjectPtr>& thePoolOfFeatures)
497 if (theShape->shapeType() != GeomAPI_Shape::EDGE)
500 std::shared_ptr<GeomAPI_Edge> aResEdge(new GeomAPI_Edge(theShape));
502 std::shared_ptr<GeomAPI_Pnt2d> aFP, aLP;
503 std::shared_ptr<GeomAPI_Pnt> aFP3d = aResEdge->firstPoint();
504 std::shared_ptr<GeomAPI_Pnt> aLP3d = aResEdge->lastPoint();
505 if (aFP3d && aLP3d) {
506 aFP = sketch()->to2D(aFP3d);
507 aLP = sketch()->to2D(aLP3d);
510 if (aResEdge->isLine()) {
511 findOrCreateFeatureByKind(sketch(), SketchPlugin_Line::ID(), theFeature, thePoolOfFeatures);
513 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
514 (theFeature->attribute(SketchPlugin_Line::START_ID()))->setValue(aFP);
515 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
516 (theFeature->attribute(SketchPlugin_Line::END_ID()))->setValue(aLP);
518 else if (aResEdge->isArc()) {
519 std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
520 std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
521 std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
523 findOrCreateFeatureByKind(sketch(), SketchPlugin_Arc::ID(), theFeature, thePoolOfFeatures);
525 GeomDirPtr aCircNormal = aCircEdge->normal();
526 GeomDirPtr aSketchNormal = sketch()->coordinatePlane()->normal();
527 if (aSketchNormal->dot(aCircNormal) < -tolerance)
530 bool aWasBlocked = theFeature->data()->blockSendAttributeUpdated(true);
531 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
532 (theFeature->attribute(SketchPlugin_Arc::END_ID()))->setValue(aLP);
533 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
534 (theFeature->attribute(SketchPlugin_Arc::START_ID()))->setValue(aFP);
535 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
536 (theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(aCP);
537 theFeature->data()->blockSendAttributeUpdated(aWasBlocked);
539 else if (aResEdge->isCircle()) {
540 std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
541 std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
542 std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
544 findOrCreateFeatureByKind(sketch(), SketchPlugin_Circle::ID(), theFeature, thePoolOfFeatures);
546 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
547 (theFeature->attribute(SketchPlugin_Circle::CENTER_ID()))->setValue(aCP);
548 theFeature->real(SketchPlugin_Circle::RADIUS_ID())->setValue(aCircEdge->radius());
550 else if (aResEdge->isEllipse()) {
551 std::shared_ptr<GeomAPI_Ellipse> anEllipseEdge = aResEdge->ellipse();
553 GeomPointPtr aCP3d = anEllipseEdge->center();
554 GeomPnt2dPtr aCP = sketch()->to2D(aCP3d);
556 GeomPointPtr aFocus3d = anEllipseEdge->firstFocus();
557 GeomPnt2dPtr aFocus = sketch()->to2D(aFocus3d);
559 if (aFP3d && aLP3d) {
561 findOrCreateFeatureByKind(sketch(), SketchPlugin_EllipticArc::ID(),
562 theFeature, thePoolOfFeatures);
564 bool aWasBlocked = theFeature->data()->blockSendAttributeUpdated(true);
565 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
566 (theFeature->attribute(SketchPlugin_EllipticArc::CENTER_ID()))->setValue(aCP);
567 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
568 (theFeature->attribute(SketchPlugin_EllipticArc::FIRST_FOCUS_ID()))->setValue(aFocus);
569 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
570 (theFeature->attribute(SketchPlugin_EllipticArc::START_POINT_ID()))->setValue(aFP);
571 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
572 (theFeature->attribute(SketchPlugin_EllipticArc::END_POINT_ID()))->setValue(aLP);
573 theFeature->data()->blockSendAttributeUpdated(aWasBlocked);
577 findOrCreateFeatureByKind(sketch(), SketchPlugin_Ellipse::ID(),
578 theFeature, thePoolOfFeatures);
580 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
581 (theFeature->attribute(SketchPlugin_Ellipse::CENTER_ID()))->setValue(aCP);
582 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
583 (theFeature->attribute(SketchPlugin_Ellipse::FIRST_FOCUS_ID()))->setValue(aFocus);
584 theFeature->real(SketchPlugin_Ellipse::MINOR_RADIUS_ID())->setValue(
585 anEllipseEdge->minorRadius());
589 // convert to b-spline
590 mkBSpline(theFeature, aResEdge, thePoolOfFeatures);
593 if (theFeature.get()) {
594 theFeature->boolean(SketchPlugin_SketchEntity::COPY_ID())->setValue(true);
595 theFeature->execute();
597 static Events_ID aRedisplayEvent = Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY);
598 ModelAPI_EventCreator::get()->sendUpdated(theFeature, aRedisplayEvent);
599 const std::list<ResultPtr>& aResults = theFeature->results();
600 for (std::list<ResultPtr>::const_iterator anIt = aResults.begin();
601 anIt != aResults.end(); ++anIt)
602 ModelAPI_EventCreator::get()->sendUpdated(*anIt, aRedisplayEvent);
606 void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult,
607 const GeomEdgePtr& theEdge,
608 std::list<ObjectPtr>& thePoolOfFeatures)
610 GeomCurvePtr aCurve (new GeomAPI_Curve (theEdge));
611 // Forced conversion to b-spline, if aCurve is not b-spline
612 GeomBSplinePtr aBSpline = GeomAPI_BSpline::convertToBSpline(aCurve);
614 const std::string& aBSplineKind = aBSpline->isPeriodic() ? SketchPlugin_BSplinePeriodic::ID()
615 : SketchPlugin_BSpline::ID();
616 findOrCreateFeatureByKind(sketch(), aBSplineKind, theResult, thePoolOfFeatures);
618 theResult->integer(SketchPlugin_BSpline::DEGREE_ID())->setValue(aBSpline->degree());
620 AttributePoint2DArrayPtr aPolesAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>
621 (theResult->attribute(SketchPlugin_BSpline::POLES_ID()));
622 std::list<GeomPointPtr> aPoles = aBSpline->poles();
623 aPolesAttr->setSize((int)aPoles.size());
624 std::list<GeomPointPtr>::iterator anIt = aPoles.begin();
625 for (int anIndex = 0; anIt != aPoles.end(); ++anIt, ++anIndex) {
626 GeomPnt2dPtr aPoleInSketch = sketch()->to2D(*anIt);
627 aPolesAttr->setPnt(anIndex, aPoleInSketch);
630 AttributeDoubleArrayPtr aWeightsAttr =
631 theResult->data()->realArray(SketchPlugin_BSpline::WEIGHTS_ID());
632 std::list<double> aWeights = aBSpline->weights();
633 if (aWeights.empty()) { // rational B-spline
634 int aSize = (int)aPoles.size();
635 aWeightsAttr->setSize(aSize);
636 for (int anIndex = 0; anIndex < aSize; ++anIndex)
637 aWeightsAttr->setValue(anIndex, 1.0);
639 else { // non-rational B-spline
640 aWeightsAttr->setSize((int)aWeights.size());
641 std::list<double>::iterator aWIt = aWeights.begin();
642 for (int anIndex = 0; aWIt != aWeights.end(); ++aWIt, ++anIndex)
643 aWeightsAttr->setValue(anIndex, *aWIt);
646 AttributeDoubleArrayPtr aKnotsAttr =
647 theResult->data()->realArray(SketchPlugin_BSpline::KNOTS_ID());
648 std::list<double> aKnots = aBSpline->knots();
649 int aSize = (int)aKnots.size();
650 aKnotsAttr->setSize(aSize);
651 std::list<double>::iterator aKIt = aKnots.begin();
652 for (int index = 0; index < aSize; ++index, ++aKIt)
653 aKnotsAttr->setValue(index, *aKIt);
655 AttributeIntArrayPtr aMultsAttr =
656 theResult->data()->intArray(SketchPlugin_BSpline::MULTS_ID());
657 std::list<int> aMultiplicities = aBSpline->mults();
658 aSize = (int)aMultiplicities.size();
659 aMultsAttr->setSize(aSize);
660 std::list<int>::iterator aMIt = aMultiplicities.begin();
661 for (int index = 0; index < aSize; ++index, ++aMIt)
662 aMultsAttr->setValue(index, *aMIt);
665 void SketchPlugin_Offset::attributeChanged(const std::string& theID)
667 if (theID == EDGES_ID()) {
668 AttributeRefListPtr aSelected = reflist(EDGES_ID());
669 if (aSelected->size() == 0) {
670 // Clear list of objects
671 AttributeRefListPtr anOffsetAttr = reflist(SketchPlugin_Constraint::ENTITY_B());
672 std::list<ObjectPtr> anOffsetList = anOffsetAttr->list();
673 std::set<FeaturePtr> aFeaturesToBeRemoved;
674 for (std::list<ObjectPtr>::iterator anIt = anOffsetList.begin();
675 anIt != anOffsetList.end(); ++anIt) {
676 FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
678 aFeaturesToBeRemoved.insert(aFeature);
681 reflist(SketchPlugin_Constraint::ENTITY_A())->clear();
682 anOffsetAttr->clear();
683 intArray(SketchPlugin_Constraint::ENTITY_C())->setSize(0);
685 ModelAPI_Tools::removeFeaturesAndReferences(aFeaturesToBeRemoved);
690 bool SketchPlugin_Offset::customAction(const std::string& theActionId)
693 if (theActionId == ADD_WIRE_ACTION_ID()) {
697 std::string aMsg = "Error: Feature \"%1\" does not support action \"%2\".";
698 Events_InfoMessage("SketchPlugin_Offset", aMsg).arg(getKind()).arg(theActionId).send();
703 bool SketchPlugin_Offset::findWires()
705 AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
706 std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
709 std::set<FeaturePtr> anEdgesSet;
712 std::set<FeaturePtr> aProcessedEdgesSet;
714 // Put all selected edges in a set to avoid adding them in reflist(EDGES_ID())
715 std::set<FeaturePtr> aSelectedSet;
716 std::list<ObjectPtr>::const_iterator anEdgesIt = anEdgesList.begin();
717 for (; anEdgesIt != anEdgesList.end(); anEdgesIt++) {
718 FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
720 aSelectedSet.insert(aFeature);
724 bool aWasBlocked = data()->blockSendAttributeUpdated(true);
726 // Gather chains of edges
727 for (anEdgesIt = anEdgesList.begin(); anEdgesIt != anEdgesList.end(); anEdgesIt++) {
728 FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
729 if (aFeature.get()) {
730 if (aProcessedEdgesSet.find(aFeature) != aProcessedEdgesSet.end())
732 aProcessedEdgesSet.insert(aFeature);
734 // End points (if any)
735 std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
736 SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
738 std::list<FeaturePtr> aChain;
739 aChain.push_back(aFeature);
740 bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet,
741 aProcessedEdgesSet, aChain, true);
743 findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet,
744 aProcessedEdgesSet, aChain, false);
747 std::list<FeaturePtr>::iterator aChainIt = aChain.begin();
748 for (; aChainIt != aChain.end(); ++aChainIt) {
749 FeaturePtr aChainFeature = (*aChainIt);
750 if (aSelectedSet.find(aChainFeature) == aSelectedSet.end()) {
751 aSelectedEdges->append(aChainFeature->lastResult());
756 // TODO: hilight selection in the viewer
758 data()->blockSendAttributeUpdated(aWasBlocked);
763 AISObjectPtr SketchPlugin_Offset::getAISObject(AISObjectPtr thePrevious)
768 AISObjectPtr anAIS = SketcherPrs_Factory::offsetObject(this, sketch(),