]> SALOME platform Git repositories - modules/shaper.git/blob - src/SketchPlugin/SketchPlugin_Offset.cpp
Salome HOME
Issue #3231: Provide presentation for offset operation
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_Offset.cpp
1 // Copyright (C) 2020  CEA/DEN, EDF R&D
2 //
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.
7 //
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.
12 //
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
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include <SketchPlugin_Offset.h>
21
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>
32
33 #include <SketcherPrs_Factory.h>
34
35 #include <Events_InfoMessage.h>
36
37 #include <ModelAPI_AttributeBoolean.h>
38 #include <ModelAPI_AttributeDouble.h>
39 #include <ModelAPI_AttributeDoubleArray.h>
40 #include <ModelAPI_AttributeInteger.h>
41 #include <ModelAPI_AttributeRefList.h>
42 #include <ModelAPI_ResultConstruction.h>
43 #include <ModelAPI_Tools.h>
44
45 #include <GeomAlgoAPI_Offset.h>
46 #include <GeomAlgoAPI_ShapeTools.h>
47 #include <GeomAlgoAPI_WireBuilder.h>
48
49 #include <GeomAPI_Edge.h>
50 #include <GeomAPI_Circ.h>
51 #include <GeomAPI_Ellipse.h>
52 #include <GeomAPI_BSpline.h>
53
54 #include <GeomDataAPI_Point2D.h>
55 #include <GeomDataAPI_Point2DArray.h>
56
57 #include <iostream>
58
59 SketchPlugin_Offset::SketchPlugin_Offset()
60   : SketchPlugin_SketchEntity()
61 {
62 }
63
64 void SketchPlugin_Offset::initDerivedClassAttributes()
65 {
66   data()->addAttribute(EDGES_ID(), ModelAPI_AttributeRefList::typeId());
67   data()->addAttribute(VALUE_ID(), ModelAPI_AttributeDouble::typeId());
68   data()->addAttribute(REVERSED_ID(), ModelAPI_AttributeBoolean::typeId());
69 }
70
71 void SketchPlugin_Offset::execute()
72 {
73   ModelAPI_Tools::removeFeaturesAndReferences(myCreatedFeatures);
74   myCreatedFeatures.clear();
75
76   SketchPlugin_Sketch* aSketch = sketch();
77   if (!aSketch) return;
78
79   // 1. Sketch plane
80   std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
81
82   // 2. Offset value
83   AttributeDoublePtr aValueAttr = real(VALUE_ID());
84   if (!aValueAttr->isInitialized()) return;
85   double aValue = aValueAttr->value();
86   const double tolerance = 1.e-7;
87   if (aValue < tolerance) return;
88
89   // 2.a. Reversed?
90   AttributeBooleanPtr aReversedAttr = boolean(REVERSED_ID());
91   if (!aReversedAttr->isInitialized()) return;
92   if (aReversedAttr->value()) aValue = -aValue; // reverse offset direction
93
94   // 3. List of all selected edges
95   AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
96   std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
97
98   // 4. Put all selected edges in a set to pass them into findWireOneWay() below
99   std::set<FeaturePtr> anEdgesSet;
100   std::list<ObjectPtr>::const_iterator anEdgesIt = anEdgesList.begin();
101   for (; anEdgesIt != anEdgesList.end(); anEdgesIt++) {
102     FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
103     if (aFeature) {
104       anEdgesSet.insert(aFeature);
105     }
106   }
107
108   // 5. Gather wires and make offset for each wire
109   for (anEdgesIt = anEdgesList.begin(); anEdgesIt != anEdgesList.end(); anEdgesIt++) {
110     FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
111     if (aFeature.get()) {
112       if (anEdgesSet.find(aFeature) == anEdgesSet.end())
113         continue;
114
115       // 5.a. End points (if any)
116       std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
117       SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
118
119       // 5.b. Find a chain of edges
120       std::list<FeaturePtr> aChain;
121       aChain.push_back(aFeature);
122       if (aStartPoint && anEndPoint) { // not closed edge
123         bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet, aChain);
124         if (!isClosed)
125           findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aChain);
126       }
127       std::set<FeaturePtr>::iterator aPos = anEdgesSet.find(aFeature);
128       if (aPos != anEdgesSet.end())
129         anEdgesSet.erase(aPos);
130
131       // 5.c. Make wire
132       ListOfShape aTopoChain;
133       std::list<FeaturePtr>::iterator aChainIt = aChain.begin();
134       for (; aChainIt != aChain.end(); ++aChainIt) {
135         FeaturePtr aChainFeature = (*aChainIt);
136         GeomShapePtr aTopoEdge = aChainFeature->lastResult()->shape();
137         if (aTopoEdge->shapeType() == GeomAPI_Shape::EDGE) {
138           aTopoChain.push_back(aTopoEdge);
139         }
140       }
141       GeomShapePtr anEdgeOrWire = GeomAlgoAPI_WireBuilder::wire(aTopoChain);
142
143       // 5.d. Make offset for each wire
144       std::shared_ptr<GeomAPI_Shape> anOffsetShape =
145         GeomAlgoAPI_Offset::OffsetInPlane(aPlane, anEdgeOrWire, aValue);
146
147       // 5.e. Store offset results.
148       //      Create sketch feature for each edge of anOffsetShape, and also store
149       //      created features in myCreatedFeatures to remove them on next execute()
150       addToSketch(anOffsetShape);
151     }
152   }
153 }
154
155 bool SketchPlugin_Offset::findWireOneWay (const FeaturePtr& theFirstEdge,
156                                           const FeaturePtr& theEdge,
157                                           const std::shared_ptr<GeomDataAPI_Point2D>& theEndPoint,
158                                           std::set<FeaturePtr>& theEdgesSet,
159                                           std::list<FeaturePtr>& theChain)
160 {
161   // 1. Find a single edge, coincident to theEndPoint by one of its ends
162   if (!theEndPoint) return false;
163
164   std::shared_ptr<GeomAPI_Pnt2d> aP2d = theEndPoint->pnt();
165
166   FeaturePtr aNextEdgeFeature;
167   int nbFound = 0;
168
169   std::set<AttributePoint2DPtr> aCoincPoints;
170   std::map<AttributePoint2DArrayPtr, int> aCoincPointsInArray;
171   SketchPlugin_Tools::findPointsCoincidentToPoint(theEndPoint, aCoincPoints, aCoincPointsInArray);
172
173   // store all found attributes to a single array
174   std::set<AttributePtr> anAllCoincPoints;
175   anAllCoincPoints.insert(aCoincPoints.begin(), aCoincPoints.end());
176   for (auto it = aCoincPointsInArray.begin(); it != aCoincPointsInArray.end(); ++it)
177     anAllCoincPoints.insert(it->first);
178
179   std::set<AttributePtr>::iterator aPointsIt = anAllCoincPoints.begin();
180   for (; aPointsIt != anAllCoincPoints.end(); aPointsIt++) {
181     AttributePtr aP = (*aPointsIt);
182     FeaturePtr aCoincFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aP->owner());
183
184     // Condition 1: not a point feature
185     if (aCoincFeature->getKind() != SketchPlugin_Point::ID()) {
186       // Condition 2: it is not the current edge
187       if (aCoincFeature != theEdge) {
188         // Condition 3: it is in the set of interest.
189         //              Empty set means all sketch edges.
190         bool isInSet = true;
191         if (theEdgesSet.size()) {
192           isInSet = (theEdgesSet.find(aCoincFeature) != theEdgesSet.end());
193         }
194         if (isInSet) {
195           // Condition 4: consider only features with two end points
196           std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
197           SketchPlugin_SegmentationTools::getFeaturePoints(aCoincFeature, aP1, aP2);
198           if (aP1 && aP2) {
199             // Condition 5: consider only features, that have aP as one of they ends.
200             //              For example, we do not need an arc, coincident to aP by its center.
201             if (theEndPoint->pnt()->isEqual(aP1->pnt()) ||
202                 theEndPoint->pnt()->isEqual(aP2->pnt())) {
203               // Condition 6: only one edge can prolongate the chain. If several, we stop here.
204               nbFound++;
205               if (nbFound > 1)
206                 return false;
207
208               // One found
209               aNextEdgeFeature = aCoincFeature;
210             }
211           }
212         }
213       }
214     }
215   }
216
217   // Only one edge can prolongate the chain. If several or none, we stop here.
218   if (nbFound != 1)
219     return false;
220
221   // 2. So, we have the single edge, that prolongate the chain
222
223   // Condition 7: if we reached the very first edge of the chain
224   if (aNextEdgeFeature == theFirstEdge)
225     // Closed chain found
226     return true;
227
228   // 3. Add the found edge to the chain
229   theChain.push_back(aNextEdgeFeature);
230   // remove from the set, if the set is used
231   if (theEdgesSet.size()) {
232     std::set<FeaturePtr>::iterator aPos = theEdgesSet.find(aNextEdgeFeature);
233     if (aPos != theEdgesSet.end())
234       theEdgesSet.erase(aPos);
235   }
236
237   // 4. Which end of aNextEdgeFeature we need to proceed
238   std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
239   SketchPlugin_SegmentationTools::getFeaturePoints(aNextEdgeFeature, aP1, aP2);
240   if (aP2->pnt()->isEqual(theEndPoint->pnt())) {
241     // reversed
242     aP2 = aP1;
243   }
244
245   // 5. Continue gathering the chain (recursive)
246   return findWireOneWay (theFirstEdge, aNextEdgeFeature, aP2, theEdgesSet, theChain);
247 }
248
249 void SketchPlugin_Offset::addToSketch(const std::shared_ptr<GeomAPI_Shape>& anOffsetShape)
250 {
251   //GeomAPI_ShapeExplorer::GeomAPI_ShapeExplorer
252   ListOfShape aResEdges = GeomAlgoAPI_ShapeTools::getLowLevelSubShapes(anOffsetShape);
253   std::list<GeomShapePtr>::const_iterator aResEdgesIt = aResEdges.begin();
254   for (; aResEdgesIt != aResEdges.end(); aResEdgesIt++) {
255     GeomShapePtr aResShape = (*aResEdgesIt);
256     if (aResShape->shapeType() == GeomAPI_Shape::EDGE) {
257       // Add new feature
258       FeaturePtr aResFeature;
259       std::shared_ptr<GeomAPI_Edge> aResEdge (new GeomAPI_Edge(aResShape));
260
261       std::shared_ptr<GeomAPI_Pnt2d> aFP, aLP;
262       std::shared_ptr<GeomAPI_Pnt> aFP3d = aResEdge->firstPoint();
263       std::shared_ptr<GeomAPI_Pnt> aLP3d = aResEdge->lastPoint();
264       //if (aFP3d.get() && aLP3d.get()) {
265       if (aFP3d && aLP3d) {
266         aFP = sketch()->to2D(aFP3d);
267         aLP = sketch()->to2D(aLP3d);
268       }
269
270       if (aResEdge->isLine()) {
271         aResFeature = sketch()->addFeature(SketchPlugin_Line::ID());
272
273         std::dynamic_pointer_cast<GeomDataAPI_Point2D>
274           (aResFeature->attribute(SketchPlugin_Line::START_ID()))->setValue(aFP);
275         std::dynamic_pointer_cast<GeomDataAPI_Point2D>
276           (aResFeature->attribute(SketchPlugin_Line::END_ID()))->setValue(aLP);
277       }
278       else if (aResEdge->isArc()) {
279         std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
280         std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
281         std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
282
283         aResFeature = sketch()->addFeature(SketchPlugin_Arc::ID());
284
285         bool aWasBlocked = aResFeature->data()->blockSendAttributeUpdated(true);
286         std::dynamic_pointer_cast<GeomDataAPI_Point2D>
287           (aResFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(aCP);
288         std::dynamic_pointer_cast<GeomDataAPI_Point2D>
289           (aResFeature->attribute(SketchPlugin_Arc::START_ID()))->setValue(aFP);
290         std::dynamic_pointer_cast<GeomDataAPI_Point2D>
291           (aResFeature->attribute(SketchPlugin_Arc::END_ID()))->setValue(aLP);
292         aResFeature->data()->blockSendAttributeUpdated(aWasBlocked);
293       }
294       else if (aResEdge->isCircle()) {
295         std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
296         std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
297         std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
298
299         aResFeature = sketch()->addFeature(SketchPlugin_Circle::ID());
300         std::dynamic_pointer_cast<GeomDataAPI_Point2D>
301           (aResFeature->attribute(SketchPlugin_Circle::CENTER_ID()))->setValue(aCP);
302         aResFeature->real(SketchPlugin_Circle::RADIUS_ID())->setValue(aCircEdge->radius());
303       }
304       else if (aResEdge->isEllipse()) {
305         std::shared_ptr<GeomAPI_Ellipse> anEllipseEdge = aResEdge->ellipse();
306
307         GeomPointPtr aCP3d = anEllipseEdge->center();
308         GeomPnt2dPtr aCP = sketch()->to2D(aCP3d);
309
310         GeomPointPtr aFocus3d = anEllipseEdge->firstFocus();
311         GeomPnt2dPtr aFocus = sketch()->to2D(aFocus3d);
312
313         if (aFP3d && aLP3d) {
314           // Elliptic arc
315           aResFeature = sketch()->addFeature(SketchPlugin_EllipticArc::ID());
316
317           bool aWasBlocked = aResFeature->data()->blockSendAttributeUpdated(true);
318           std::dynamic_pointer_cast<GeomDataAPI_Point2D>
319             (aResFeature->attribute(SketchPlugin_EllipticArc::CENTER_ID()))->setValue(aCP);
320           std::dynamic_pointer_cast<GeomDataAPI_Point2D>
321             (aResFeature->attribute(SketchPlugin_EllipticArc::FIRST_FOCUS_ID()))->setValue(aFocus);
322           std::dynamic_pointer_cast<GeomDataAPI_Point2D>
323             (aResFeature->attribute(SketchPlugin_EllipticArc::START_POINT_ID()))->setValue(aFP);
324           std::dynamic_pointer_cast<GeomDataAPI_Point2D>
325             (aResFeature->attribute(SketchPlugin_EllipticArc::END_POINT_ID()))->setValue(aLP);
326           aResFeature->data()->blockSendAttributeUpdated(aWasBlocked);
327         }
328         else {
329           // Ellipse
330           aResFeature = sketch()->addFeature(SketchPlugin_Ellipse::ID());
331
332           std::dynamic_pointer_cast<GeomDataAPI_Point2D>
333             (aResFeature->attribute(SketchPlugin_Ellipse::CENTER_ID()))->setValue(aCP);
334           std::dynamic_pointer_cast<GeomDataAPI_Point2D>
335             (aResFeature->attribute(SketchPlugin_Ellipse::FIRST_FOCUS_ID()))->setValue(aFocus);
336           aResFeature->real(SketchPlugin_Ellipse::MINOR_RADIUS_ID())->setValue(anEllipseEdge->minorRadius());
337         }
338       }
339       else if (aResEdge->isBSpline()) {
340         mkBSpline(aResFeature, aResEdge);
341       }
342       else {
343       }
344
345       if (aResFeature.get()) {
346         myCreatedFeatures.insert(aResFeature);
347
348         aResFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue
349           (boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value());
350         aResFeature->execute();
351       }
352     }
353   }
354 }
355
356 void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult,
357                                      const GeomEdgePtr& theEdge)
358 {
359   if (!theEdge->isBSpline())
360     return;
361
362   GeomCurvePtr aCurve (new GeomAPI_Curve (theEdge));
363   GeomAPI_BSpline aBSpline (aCurve);
364
365   if (aBSpline.isPeriodic())
366     theResult = sketch()->addFeature(SketchPlugin_BSplinePeriodic::ID());
367   else
368     theResult = sketch()->addFeature(SketchPlugin_BSpline::ID());
369
370   theResult->integer(SketchPlugin_BSpline::DEGREE_ID())->setValue(aBSpline.degree());
371
372   AttributePoint2DArrayPtr aPolesAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>
373     (theResult->attribute(SketchPlugin_BSpline::POLES_ID()));
374   std::list<GeomPointPtr> aPoles = aBSpline.poles();
375   aPolesAttr->setSize((int)aPoles.size());
376   std::list<GeomPointPtr>::iterator anIt = aPoles.begin();
377   for (int anIndex = 0; anIt != aPoles.end(); ++anIt, ++anIndex) {
378     GeomPnt2dPtr aPoleInSketch = sketch()->to2D(*anIt);
379     aPolesAttr->setPnt(anIndex, aPoleInSketch);
380   }
381
382   AttributeDoubleArrayPtr aWeightsAttr =
383       theResult->data()->realArray(SketchPlugin_BSpline::WEIGHTS_ID());
384   std::list<double> aWeights = aBSpline.weights();
385   if (aWeights.empty()) { // rational B-spline
386     int aSize = (int)aPoles.size();
387     aWeightsAttr->setSize(aSize);
388     for (int anIndex = 0; anIndex < aSize; ++anIndex)
389       aWeightsAttr->setValue(anIndex, 1.0);
390   }
391   else { // non-rational B-spline
392     aWeightsAttr->setSize((int)aWeights.size());
393     std::list<double>::iterator anIt = aWeights.begin();
394     for (int anIndex = 0; anIt != aWeights.end(); ++anIt, ++anIndex)
395       aWeightsAttr->setValue(anIndex, *anIt);
396   }
397
398   AttributeDoubleArrayPtr aKnotsAttr =
399       theResult->data()->realArray(SketchPlugin_BSpline::KNOTS_ID());
400   std::list<double> aKnots = aBSpline.knots();
401   int aSize = (int)aKnots.size();
402   aKnotsAttr->setSize(aSize);
403   std::list<double>::iterator aKIt = aKnots.begin();
404   for (int index = 0; index < aSize; ++index, ++aKIt)
405     aKnotsAttr->setValue(index, *aKIt);
406
407   AttributeIntArrayPtr aMultsAttr =
408       theResult->data()->intArray(SketchPlugin_BSpline::MULTS_ID());
409   std::list<int> aMultiplicities = aBSpline.mults();
410   aSize = (int)aMultiplicities.size();
411   aMultsAttr->setSize(aSize);
412   std::list<int>::iterator aMIt = aMultiplicities.begin();
413   for (int index = 0; index < aSize; ++index, ++aMIt)
414     aMultsAttr->setValue(index, *aMIt);
415 }
416
417 void SketchPlugin_Offset::attributeChanged(const std::string& theID)
418 {
419   ModelAPI_Tools::removeFeaturesAndReferences(myCreatedFeatures);
420   myCreatedFeatures.clear();
421 }
422
423 bool SketchPlugin_Offset::customAction(const std::string& theActionId)
424 {
425   bool isOk = false;
426   if (theActionId == ADD_WIRE_ACTION_ID()) {
427     isOk = findWires();
428   }
429   else {
430     std::string aMsg = "Error: Feature \"%1\" does not support action \"%2\".";
431     Events_InfoMessage("SketchPlugin_Offset", aMsg).arg(getKind()).arg(theActionId).send();
432   }
433   return isOk;
434 }
435
436 bool SketchPlugin_Offset::findWires()
437 {
438   AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
439   std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
440
441   // Empty set
442   std::set<FeaturePtr> anEdgesSet;
443
444   // Processed set
445   std::set<FeaturePtr> aProcessedSet;
446
447   // Put all selected edges in a set to avoid adding them in reflist(EDGES_ID())
448   std::set<FeaturePtr> aSelectedSet;
449   std::list<ObjectPtr>::const_iterator anEdgesIt = anEdgesList.begin();
450   for (; anEdgesIt != anEdgesList.end(); anEdgesIt++) {
451     FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
452     if (aFeature) {
453       aSelectedSet.insert(aFeature);
454     }
455   }
456
457   // Gather chains of edges
458   for (anEdgesIt = anEdgesList.begin(); anEdgesIt != anEdgesList.end(); anEdgesIt++) {
459     FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
460     if (aFeature.get()) {
461       if (aProcessedSet.find(aFeature) != aProcessedSet.end())
462         continue;
463       aProcessedSet.insert(aFeature);
464
465       // End points (if any)
466       std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
467       SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
468
469       std::list<FeaturePtr> aChain;
470       aChain.push_back(aFeature);
471       bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet, aChain);
472       if (!isClosed)
473         findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aChain);
474
475       std::list<FeaturePtr>::iterator aChainIt = aChain.begin();
476       for (; aChainIt != aChain.end(); ++aChainIt) {
477         FeaturePtr aChainFeature = (*aChainIt);
478         aProcessedSet.insert(aChainFeature);
479         if (aSelectedSet.find(aChainFeature) == aSelectedSet.end()) {
480           aSelectedEdges->append(aChainFeature->lastResult());
481         }
482       }
483     }
484   }
485   // TODO: hilight selection in the viewer
486
487   return true;
488 }
489
490
491 AISObjectPtr SketchPlugin_Offset::getAISObject(AISObjectPtr thePrevious)
492 {
493   if (!sketch())
494     return thePrevious;
495
496   AISObjectPtr anAIS = SketcherPrs_Factory::offsetObject(this, sketch(),
497     thePrevious);
498   return anAIS;
499 }