1 // Copyright (C) 2020 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>
59 #include <GeomDataAPI_Point2D.h>
60 #include <GeomDataAPI_Point2DArray.h>
64 SketchPlugin_Offset::SketchPlugin_Offset()
68 void SketchPlugin_Offset::initAttributes()
70 data()->addAttribute(EDGES_ID(), ModelAPI_AttributeRefList::typeId());
71 data()->addAttribute(VALUE_ID(), ModelAPI_AttributeDouble::typeId());
72 data()->addAttribute(REVERSED_ID(), ModelAPI_AttributeBoolean::typeId());
74 // store original entities
75 data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefList::typeId());
76 // store offset entities
77 data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefList::typeId());
78 // store mapping between original entity and index of the corresponding offset entity
79 data()->addAttribute(SketchPlugin_Constraint::ENTITY_C(), ModelAPI_AttributeIntArray::typeId());
81 ModelAPI_Session::get()->validators()->
82 registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_A());
83 ModelAPI_Session::get()->validators()->
84 registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_B());
85 ModelAPI_Session::get()->validators()->
86 registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_C());
89 void SketchPlugin_Offset::execute()
91 SketchPlugin_Sketch* aSketch = sketch();
95 std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
98 AttributeDoublePtr aValueAttr = real(VALUE_ID());
99 if (!aValueAttr->isInitialized()) return;
100 double aValue = aValueAttr->value();
101 const double tolerance = 1.e-7;
102 if (aValue < tolerance) return;
105 AttributeBooleanPtr aReversedAttr = boolean(REVERSED_ID());
106 if (!aReversedAttr->isInitialized()) return;
107 if (aReversedAttr->value()) aValue = -aValue; // reverse offset direction
109 // 3. List of all selected edges
110 AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
111 std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
113 // 4. Put all selected edges in a set to pass them into findWireOneWay() below
114 std::set<FeaturePtr> anEdgesSet;
115 std::list<ObjectPtr>::const_iterator anEdgesIt = anEdgesList.begin();
116 for (; anEdgesIt != anEdgesList.end(); anEdgesIt++) {
117 FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
119 anEdgesSet.insert(aFeature);
123 // Wait all objects being created, then send update events
124 static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
125 bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
127 Events_Loop::loop()->setFlushed(anUpdateEvent, false);
129 // 5. Gather wires and make offset for each wire
130 ListOfMakeShape anOffsetAlgos;
131 for (anEdgesIt = anEdgesList.begin(); anEdgesIt != anEdgesList.end(); anEdgesIt++) {
132 FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
133 if (aFeature.get()) {
134 if (anEdgesSet.find(aFeature) == anEdgesSet.end())
137 // 5.a. End points (if any)
138 std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
139 SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
141 // 5.b. Find a chain of edges
142 std::list<FeaturePtr> aChain;
143 aChain.push_back(aFeature);
144 if (aStartPoint && anEndPoint) { // not closed edge
145 bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet, aChain, true);
147 findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aChain, false);
149 std::set<FeaturePtr>::iterator aPos = anEdgesSet.find(aFeature);
150 if (aPos != anEdgesSet.end())
151 anEdgesSet.erase(aPos);
154 ListOfShape aTopoChain;
155 std::list<FeaturePtr>::iterator aChainIt = aChain.begin();
156 for (; aChainIt != aChain.end(); ++aChainIt) {
157 FeaturePtr aChainFeature = (*aChainIt);
158 GeomShapePtr aTopoEdge = aChainFeature->lastResult()->shape();
159 if (aTopoEdge->shapeType() == GeomAPI_Shape::EDGE) {
160 aTopoChain.push_back(aTopoEdge);
163 std::shared_ptr<GeomAlgoAPI_WireBuilder> aWireBuilder(
164 new GeomAlgoAPI_WireBuilder(aTopoChain));
166 // 5.d. Make offset for each wire
167 std::shared_ptr<GeomAlgoAPI_Offset> anOffsetShape(
168 new GeomAlgoAPI_Offset(aPlane, aWireBuilder->shape(), aValue));
170 std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeList(new GeomAlgoAPI_MakeShapeList);
171 aMakeList->appendAlgo(aWireBuilder);
172 aMakeList->appendAlgo(anOffsetShape);
173 anOffsetAlgos.push_back(aMakeList);
177 // 6. Store offset results.
178 // Create sketch feature for each edge of anOffsetShape, and also store
179 // created features in CREATED_ID() to remove them on next execute()
180 addToSketch(anOffsetAlgos);
182 // send events to update the sub-features by the solver
184 Events_Loop::loop()->setFlushed(anUpdateEvent, true);
187 bool SketchPlugin_Offset::findWireOneWay (const FeaturePtr& theFirstEdge,
188 const FeaturePtr& theEdge,
189 const std::shared_ptr<GeomDataAPI_Point2D>& theEndPoint,
190 std::set<FeaturePtr>& theEdgesSet,
191 std::list<FeaturePtr>& theChain,
192 const bool isPrepend)
194 // 1. Find a single edge, coincident to theEndPoint by one of its ends
195 if (!theEndPoint) return false;
197 std::shared_ptr<GeomAPI_Pnt2d> aP2d = theEndPoint->pnt();
199 FeaturePtr aNextEdgeFeature;
202 std::set<AttributePoint2DPtr> aCoincPoints;
203 std::map<AttributePoint2DArrayPtr, int> aCoincPointsInArray;
204 SketchPlugin_Tools::findPointsCoincidentToPoint(theEndPoint, aCoincPoints, aCoincPointsInArray);
206 // store all found attributes to a single array
207 std::set<AttributePtr> anAllCoincPoints;
208 anAllCoincPoints.insert(aCoincPoints.begin(), aCoincPoints.end());
209 for (auto it = aCoincPointsInArray.begin(); it != aCoincPointsInArray.end(); ++it)
210 anAllCoincPoints.insert(it->first);
212 std::set<AttributePtr>::iterator aPointsIt = anAllCoincPoints.begin();
213 for (; aPointsIt != anAllCoincPoints.end(); aPointsIt++) {
214 AttributePtr aP = (*aPointsIt);
215 FeaturePtr aCoincFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aP->owner());
217 // Condition 0: not auxiliary
218 if (aCoincFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) continue;
220 // Condition 1: not a point feature
221 if (aCoincFeature->getKind() != SketchPlugin_Point::ID()) {
222 // Condition 2: it is not the current edge
223 if (aCoincFeature != theEdge) {
224 // Condition 3: it is in the set of interest.
225 // Empty set means all sketch edges.
227 if (theEdgesSet.size()) {
228 isInSet = (theEdgesSet.find(aCoincFeature) != theEdgesSet.end());
231 // Condition 4: consider only features with two end points
232 std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
233 SketchPlugin_SegmentationTools::getFeaturePoints(aCoincFeature, aP1, aP2);
235 // Condition 5: consider only features, that have aP as one of they ends.
236 // For example, we do not need an arc, coincident to aP by its center.
237 if (theEndPoint->pnt()->isEqual(aP1->pnt()) ||
238 theEndPoint->pnt()->isEqual(aP2->pnt())) {
239 // Condition 6: only one edge can prolongate the chain. If several, we stop here.
245 aNextEdgeFeature = aCoincFeature;
253 // Only one edge can prolongate the chain. If several or none, we stop here.
257 // 2. So, we have the single edge, that prolongate the chain
259 // Condition 7: if we reached the very first edge of the chain
260 if (aNextEdgeFeature == theFirstEdge)
261 // Closed chain found
264 // 3. Add the found edge to the chain
266 theChain.push_front(aNextEdgeFeature);
268 theChain.push_back(aNextEdgeFeature);
269 // remove from the set, if the set is used
270 if (theEdgesSet.size()) {
271 std::set<FeaturePtr>::iterator aPos = theEdgesSet.find(aNextEdgeFeature);
272 if (aPos != theEdgesSet.end())
273 theEdgesSet.erase(aPos);
276 // 4. Which end of aNextEdgeFeature we need to proceed
277 std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
278 SketchPlugin_SegmentationTools::getFeaturePoints(aNextEdgeFeature, aP1, aP2);
279 if (aP2->pnt()->isEqual(theEndPoint->pnt())) {
284 // 5. Continue gathering the chain (recursive)
285 return findWireOneWay (theFirstEdge, aNextEdgeFeature, aP2, theEdgesSet, theChain, isPrepend);
288 static void setRefListValue(AttributeRefListPtr theList, int theListSize,
289 ObjectPtr theValue, int theIndex)
291 if (theIndex < theListSize) {
292 ObjectPtr aCur = theList->object(theIndex);
293 if (aCur != theValue)
294 theList->substitute(aCur, theValue);
297 theList->append(theValue);
300 static void removeLastFromIndex(AttributeRefListPtr theList, int theListSize, int& theLastIndex)
302 if (theLastIndex < theListSize) {
303 std::set<int> anIndicesToRemove;
304 for (; theLastIndex < theListSize; ++theLastIndex)
305 anIndicesToRemove.insert(theLastIndex);
306 theList->remove(anIndicesToRemove);
310 void SketchPlugin_Offset::addToSketch(const ListOfMakeShape& theOffsetAlgos)
312 AttributeRefListPtr aSelectedRefList = reflist(EDGES_ID());
313 AttributeRefListPtr aBaseRefList = reflist(ENTITY_A());
314 AttributeRefListPtr anOffsetRefList = reflist(ENTITY_B());
315 AttributeIntArrayPtr anOffsetToBaseMap = intArray(ENTITY_C());
317 // compare the list of selected edges and the previously stored,
318 // and store maping between them
319 std::map<ObjectPtr, std::list<ObjectPtr> > aMapExistent;
320 std::list<ObjectPtr> anObjectsToRemove;
321 std::list<ObjectPtr> aSelectedList = aSelectedRefList->list();
322 for (std::list<ObjectPtr>::iterator aSIt = aSelectedList.begin();
323 aSIt != aSelectedList.end(); ++aSIt) {
324 aMapExistent[*aSIt] = std::list<ObjectPtr>();
326 for (int anIndex = 0, aSize = anOffsetRefList->size(); anIndex < aSize; ++anIndex) {
327 ObjectPtr aCurrent = anOffsetRefList->object(anIndex);
328 int aBaseIndex = anOffsetToBaseMap->value(anIndex);
329 if (aBaseIndex >= 0) {
330 ObjectPtr aBaseObj = aBaseRefList->object(aBaseIndex);
331 std::map<ObjectPtr, std::list<ObjectPtr> >::iterator aFound = aMapExistent.find(aBaseObj);
332 if (aFound != aMapExistent.end())
333 aFound->second.push_back(aCurrent);
335 anObjectsToRemove.push_back(aCurrent);
338 anObjectsToRemove.push_back(aCurrent);
341 // update lists of base shapes and of offset shapes
342 int aBaseListSize = aBaseRefList->size();
343 int anOffsetListSize = anOffsetRefList->size();
344 int aBaseListIndex = 0, anOffsetListIndex = 0;
345 std::list<int> anOffsetBaseBackRefs;
346 std::set<GeomShapePtr, GeomAPI_Shape::ComparatorWithOri> aProcessedOffsets;
347 for (std::list<ObjectPtr>::iterator aSIt = aSelectedList.begin();
348 aSIt != aSelectedList.end(); ++aSIt) {
349 // find an offseted edge
350 FeaturePtr aBaseFeature = ModelAPI_Feature::feature(*aSIt);
351 GeomShapePtr aBaseShape = aBaseFeature->lastResult()->shape();
352 ListOfShape aNewShapes;
353 for (ListOfMakeShape::const_iterator anAlgoIt = theOffsetAlgos.begin();
354 anAlgoIt != theOffsetAlgos.end() && aNewShapes.empty(); ++anAlgoIt) {
355 (*anAlgoIt)->generated(aBaseShape, aNewShapes);
358 // store base feature
359 setRefListValue(aBaseRefList, aBaseListSize, *aSIt, aBaseListIndex);
361 // create or update an offseted feature
362 const std::list<ObjectPtr>& anImages = aMapExistent[*aSIt];
363 std::list<ObjectPtr>::const_iterator anImgIt = anImages.begin();
364 for (ListOfShape::iterator aNewIt = aNewShapes.begin(); aNewIt != aNewShapes.end(); ++aNewIt) {
365 FeaturePtr aNewFeature;
366 if (anImgIt != anImages.end())
367 aNewFeature = ModelAPI_Feature::feature(*anImgIt++);
368 updateExistentOrCreateNew(*aNewIt, aNewFeature, anObjectsToRemove);
369 aProcessedOffsets.insert(*aNewIt);
371 // store an offseted feature
372 setRefListValue(anOffsetRefList, anOffsetListSize, aNewFeature, anOffsetListIndex);
374 anOffsetBaseBackRefs.push_back(aBaseListIndex);
378 anObjectsToRemove.insert(anObjectsToRemove.end(), anImgIt, anImages.end());
380 // create arcs generated from vertices
381 for (ListOfMakeShape::const_iterator anAlgoIt = theOffsetAlgos.begin();
382 anAlgoIt != theOffsetAlgos.end(); ++anAlgoIt) {
383 GeomShapePtr aCurWire = (*anAlgoIt)->shape();
384 GeomAPI_ShapeExplorer anExp(aCurWire, GeomAPI_Shape::EDGE);
385 for (; anExp.more(); anExp.next()) {
386 GeomShapePtr aCurEdge = anExp.current();
387 if (aProcessedOffsets.find(aCurEdge) == aProcessedOffsets.end()) {
388 FeaturePtr aNewFeature;
389 updateExistentOrCreateNew(aCurEdge, aNewFeature, anObjectsToRemove);
390 aProcessedOffsets.insert(aCurEdge);
392 // store an offseted feature
393 setRefListValue(anOffsetRefList, anOffsetListSize, aNewFeature, anOffsetListIndex);
395 anOffsetBaseBackRefs.push_back(-1);
401 removeLastFromIndex(aBaseRefList, aBaseListSize, aBaseListIndex);
402 removeLastFromIndex(anOffsetRefList, anOffsetListSize, anOffsetListIndex);
404 anOffsetToBaseMap->setSize((int)anOffsetBaseBackRefs.size(), false);
406 for (std::list<int>::iterator anIt = anOffsetBaseBackRefs.begin();
407 anIt != anOffsetBaseBackRefs.end(); ++anIt) {
408 anOffsetToBaseMap->setValue(anIndex++, *anIt, false);
411 // remove unused objects
412 std::set<FeaturePtr> aSet;
413 for (std::list<ObjectPtr>::iterator anIt = anObjectsToRemove.begin();
414 anIt != anObjectsToRemove.end(); ++anIt) {
415 FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
417 aSet.insert(aFeature);
419 ModelAPI_Tools::removeFeaturesAndReferences(aSet);
422 static void findOrCreateFeatureByKind(SketchPlugin_Sketch* theSketch,
423 const std::string& theFeatureKind,
424 FeaturePtr& theFeature,
425 std::list<ObjectPtr>& thePoolOfFeatures)
428 // try to find appropriate feature in the pool
429 for (std::list<ObjectPtr>::iterator it = thePoolOfFeatures.begin();
430 it != thePoolOfFeatures.end(); ++it) {
431 FeaturePtr aCurFeature = ModelAPI_Feature::feature(*it);
432 if (aCurFeature->getKind() == theFeatureKind) {
433 theFeature = aCurFeature;
434 thePoolOfFeatures.erase(it);
438 // feature not found, create new
440 theFeature = theSketch->addFeature(theFeatureKind);
444 void SketchPlugin_Offset::updateExistentOrCreateNew(const GeomShapePtr& theShape,
445 FeaturePtr& theFeature,
446 std::list<ObjectPtr>& thePoolOfFeatures)
448 if (theShape->shapeType() != GeomAPI_Shape::EDGE)
451 std::shared_ptr<GeomAPI_Edge> aResEdge(new GeomAPI_Edge(theShape));
453 std::shared_ptr<GeomAPI_Pnt2d> aFP, aLP;
454 std::shared_ptr<GeomAPI_Pnt> aFP3d = aResEdge->firstPoint();
455 std::shared_ptr<GeomAPI_Pnt> aLP3d = aResEdge->lastPoint();
456 if (aFP3d && aLP3d) {
457 aFP = sketch()->to2D(aFP3d);
458 aLP = sketch()->to2D(aLP3d);
461 if (aResEdge->isLine()) {
462 findOrCreateFeatureByKind(sketch(), SketchPlugin_Line::ID(), theFeature, thePoolOfFeatures);
464 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
465 (theFeature->attribute(SketchPlugin_Line::START_ID()))->setValue(aFP);
466 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
467 (theFeature->attribute(SketchPlugin_Line::END_ID()))->setValue(aLP);
469 else if (aResEdge->isArc()) {
470 std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
471 std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
472 std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
474 findOrCreateFeatureByKind(sketch(), SketchPlugin_Arc::ID(), theFeature, thePoolOfFeatures);
476 bool aWasBlocked = theFeature->data()->blockSendAttributeUpdated(true);
477 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
478 (theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(aCP);
479 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
480 (theFeature->attribute(SketchPlugin_Arc::START_ID()))->setValue(aFP);
481 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
482 (theFeature->attribute(SketchPlugin_Arc::END_ID()))->setValue(aLP);
483 theFeature->data()->blockSendAttributeUpdated(aWasBlocked);
485 else if (aResEdge->isCircle()) {
486 std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
487 std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
488 std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
490 findOrCreateFeatureByKind(sketch(), SketchPlugin_Circle::ID(), theFeature, thePoolOfFeatures);
492 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
493 (theFeature->attribute(SketchPlugin_Circle::CENTER_ID()))->setValue(aCP);
494 theFeature->real(SketchPlugin_Circle::RADIUS_ID())->setValue(aCircEdge->radius());
496 else if (aResEdge->isEllipse()) {
497 std::shared_ptr<GeomAPI_Ellipse> anEllipseEdge = aResEdge->ellipse();
499 GeomPointPtr aCP3d = anEllipseEdge->center();
500 GeomPnt2dPtr aCP = sketch()->to2D(aCP3d);
502 GeomPointPtr aFocus3d = anEllipseEdge->firstFocus();
503 GeomPnt2dPtr aFocus = sketch()->to2D(aFocus3d);
505 if (aFP3d && aLP3d) {
507 findOrCreateFeatureByKind(sketch(), SketchPlugin_EllipticArc::ID(),
508 theFeature, thePoolOfFeatures);
510 bool aWasBlocked = theFeature->data()->blockSendAttributeUpdated(true);
511 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
512 (theFeature->attribute(SketchPlugin_EllipticArc::CENTER_ID()))->setValue(aCP);
513 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
514 (theFeature->attribute(SketchPlugin_EllipticArc::FIRST_FOCUS_ID()))->setValue(aFocus);
515 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
516 (theFeature->attribute(SketchPlugin_EllipticArc::START_POINT_ID()))->setValue(aFP);
517 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
518 (theFeature->attribute(SketchPlugin_EllipticArc::END_POINT_ID()))->setValue(aLP);
519 theFeature->data()->blockSendAttributeUpdated(aWasBlocked);
523 findOrCreateFeatureByKind(sketch(), SketchPlugin_Ellipse::ID(),
524 theFeature, thePoolOfFeatures);
526 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
527 (theFeature->attribute(SketchPlugin_Ellipse::CENTER_ID()))->setValue(aCP);
528 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
529 (theFeature->attribute(SketchPlugin_Ellipse::FIRST_FOCUS_ID()))->setValue(aFocus);
530 theFeature->real(SketchPlugin_Ellipse::MINOR_RADIUS_ID())->setValue(anEllipseEdge->minorRadius());
534 // convert to b-spline
535 mkBSpline(theFeature, aResEdge, thePoolOfFeatures);
538 if (theFeature.get()) {
539 theFeature->boolean(SketchPlugin_SketchEntity::COPY_ID())->setValue(true);
540 theFeature->execute();
542 static Events_ID aRedisplayEvent = Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY);
543 ModelAPI_EventCreator::get()->sendUpdated(theFeature, aRedisplayEvent);
544 const std::list<ResultPtr>& aResults = theFeature->results();
545 for (std::list<ResultPtr>::const_iterator anIt = aResults.begin();
546 anIt != aResults.end(); ++anIt)
547 ModelAPI_EventCreator::get()->sendUpdated(*anIt, aRedisplayEvent);
551 void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult,
552 const GeomEdgePtr& theEdge,
553 std::list<ObjectPtr>& thePoolOfFeatures)
555 GeomCurvePtr aCurve (new GeomAPI_Curve (theEdge));
556 // Forced conversion to b-spline, if aCurve is not b-spline
557 GeomAPI_BSpline aBSpline (aCurve, /*isForced*/true);
559 const std::string& aBSplineKind = aBSpline.isPeriodic() ? SketchPlugin_BSplinePeriodic::ID()
560 : SketchPlugin_BSpline::ID();
561 findOrCreateFeatureByKind(sketch(), aBSplineKind, theResult, thePoolOfFeatures);
563 theResult->integer(SketchPlugin_BSpline::DEGREE_ID())->setValue(aBSpline.degree());
565 AttributePoint2DArrayPtr aPolesAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>
566 (theResult->attribute(SketchPlugin_BSpline::POLES_ID()));
567 std::list<GeomPointPtr> aPoles = aBSpline.poles();
568 aPolesAttr->setSize((int)aPoles.size());
569 std::list<GeomPointPtr>::iterator anIt = aPoles.begin();
570 for (int anIndex = 0; anIt != aPoles.end(); ++anIt, ++anIndex) {
571 GeomPnt2dPtr aPoleInSketch = sketch()->to2D(*anIt);
572 aPolesAttr->setPnt(anIndex, aPoleInSketch);
575 AttributeDoubleArrayPtr aWeightsAttr =
576 theResult->data()->realArray(SketchPlugin_BSpline::WEIGHTS_ID());
577 std::list<double> aWeights = aBSpline.weights();
578 if (aWeights.empty()) { // rational B-spline
579 int aSize = (int)aPoles.size();
580 aWeightsAttr->setSize(aSize);
581 for (int anIndex = 0; anIndex < aSize; ++anIndex)
582 aWeightsAttr->setValue(anIndex, 1.0);
584 else { // non-rational B-spline
585 aWeightsAttr->setSize((int)aWeights.size());
586 std::list<double>::iterator anIt = aWeights.begin();
587 for (int anIndex = 0; anIt != aWeights.end(); ++anIt, ++anIndex)
588 aWeightsAttr->setValue(anIndex, *anIt);
591 AttributeDoubleArrayPtr aKnotsAttr =
592 theResult->data()->realArray(SketchPlugin_BSpline::KNOTS_ID());
593 std::list<double> aKnots = aBSpline.knots();
594 int aSize = (int)aKnots.size();
595 aKnotsAttr->setSize(aSize);
596 std::list<double>::iterator aKIt = aKnots.begin();
597 for (int index = 0; index < aSize; ++index, ++aKIt)
598 aKnotsAttr->setValue(index, *aKIt);
600 AttributeIntArrayPtr aMultsAttr =
601 theResult->data()->intArray(SketchPlugin_BSpline::MULTS_ID());
602 std::list<int> aMultiplicities = aBSpline.mults();
603 aSize = (int)aMultiplicities.size();
604 aMultsAttr->setSize(aSize);
605 std::list<int>::iterator aMIt = aMultiplicities.begin();
606 for (int index = 0; index < aSize; ++index, ++aMIt)
607 aMultsAttr->setValue(index, *aMIt);
610 void SketchPlugin_Offset::attributeChanged(const std::string& theID)
612 //// if (theID == EDGES_ID())
613 //// removeCreated();
616 bool SketchPlugin_Offset::customAction(const std::string& theActionId)
619 if (theActionId == ADD_WIRE_ACTION_ID()) {
623 std::string aMsg = "Error: Feature \"%1\" does not support action \"%2\".";
624 Events_InfoMessage("SketchPlugin_Offset", aMsg).arg(getKind()).arg(theActionId).send();
629 bool SketchPlugin_Offset::findWires()
631 AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
632 std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
635 std::set<FeaturePtr> anEdgesSet;
638 std::set<FeaturePtr> aProcessedSet;
640 // Put all selected edges in a set to avoid adding them in reflist(EDGES_ID())
641 std::set<FeaturePtr> aSelectedSet;
642 std::list<ObjectPtr>::const_iterator anEdgesIt = anEdgesList.begin();
643 for (; anEdgesIt != anEdgesList.end(); anEdgesIt++) {
644 FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
646 aSelectedSet.insert(aFeature);
650 // Gather chains of edges
651 for (anEdgesIt = anEdgesList.begin(); anEdgesIt != anEdgesList.end(); anEdgesIt++) {
652 FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
653 if (aFeature.get()) {
654 if (aProcessedSet.find(aFeature) != aProcessedSet.end())
656 aProcessedSet.insert(aFeature);
658 // End points (if any)
659 std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
660 SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
662 std::list<FeaturePtr> aChain;
663 aChain.push_back(aFeature);
664 bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet, aChain, true);
666 findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aChain, false);
668 std::list<FeaturePtr>::iterator aChainIt = aChain.begin();
669 for (; aChainIt != aChain.end(); ++aChainIt) {
670 FeaturePtr aChainFeature = (*aChainIt);
671 aProcessedSet.insert(aChainFeature);
672 if (aSelectedSet.find(aChainFeature) == aSelectedSet.end()) {
673 aSelectedEdges->append(aChainFeature->lastResult());
678 // TODO: hilight selection in the viewer
684 AISObjectPtr SketchPlugin_Offset::getAISObject(AISObjectPtr thePrevious)
689 AISObjectPtr anAIS = SketcherPrs_Factory::offsetObject(this, sketch(),