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