]> SALOME platform Git repositories - modules/shaper.git/blob - src/SketchPlugin/SketchPlugin_Offset.cpp
Salome HOME
d789c85a714c8c7002d8125bd5b47501ce94bdbb
[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     SketchPlugin_Tools::findPointsCoincidentToPoint(theEndPoint);
169
170   std::set<AttributePoint2DPtr>::iterator aPointsIt = aCoincPoints.begin();
171   for (; aPointsIt != aCoincPoints.end(); aPointsIt++) {
172     AttributePoint2DPtr aP = (*aPointsIt);
173     FeaturePtr aCoincFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aP->owner());
174
175     // Condition 1: not a point feature
176     if (aCoincFeature->getKind() != SketchPlugin_Point::ID()) {
177       // Condition 2: it is not the current edge
178       if (aCoincFeature != theEdge) {
179         // Condition 3: it is in the set of interest.
180         //              Empty set means all sketch edges.
181         bool isInSet = true;
182         if (theEdgesSet.size()) {
183           isInSet = (theEdgesSet.find(aCoincFeature) != theEdgesSet.end());
184         }
185         if (isInSet) {
186           // Condition 4: consider only features with two end points
187           std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
188           SketchPlugin_SegmentationTools::getFeaturePoints(aCoincFeature, aP1, aP2);
189           if (aP1 && aP2) {
190             // Condition 5: consider only features, that have aP as one of they ends.
191             //              For example, we do not need an arc, coincident to aP by its center.
192             if (theEndPoint->pnt()->isEqual(aP1->pnt()) ||
193                 theEndPoint->pnt()->isEqual(aP2->pnt())) {
194               // Condition 6: only one edge can prolongate the chain. If several, we stop here.
195               nbFound++;
196               if (nbFound > 1)
197                 return false;
198
199               // One found
200               aNextEdgeFeature = aCoincFeature;
201             }
202           }
203         }
204       }
205     }
206   }
207
208   // Only one edge can prolongate the chain. If several or none, we stop here.
209   if (nbFound != 1)
210     return false;
211
212   // 2. So, we have the single edge, that prolongate the chain
213
214   // Condition 7: if we reached the very first edge of the chain
215   if (aNextEdgeFeature == theFirstEdge)
216     // Closed chain found
217     return true;
218
219   // 3. Add the found edge to the chain
220   theChain.push_back(aNextEdgeFeature);
221   // remove from the set, if the set is used
222   if (theEdgesSet.size()) {
223     std::set<FeaturePtr>::iterator aPos = theEdgesSet.find(aNextEdgeFeature);
224     if (aPos != theEdgesSet.end())
225       theEdgesSet.erase(aPos);
226   }
227
228   // 4. Which end of aNextEdgeFeature we need to proceed
229   std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
230   SketchPlugin_SegmentationTools::getFeaturePoints(aNextEdgeFeature, aP1, aP2);
231   if (aP2->pnt()->isEqual(theEndPoint->pnt())) {
232     // reversed
233     aP2 = aP1;
234   }
235
236   // 5. Continue gathering the chain (recursive)
237   return findWireOneWay (theFirstEdge, aNextEdgeFeature, aP2, theEdgesSet, theChain);
238 }
239
240 void SketchPlugin_Offset::addToSketch(const std::shared_ptr<GeomAPI_Shape>& anOffsetShape)
241 {
242   //GeomAPI_ShapeExplorer::GeomAPI_ShapeExplorer
243   ListOfShape aResEdges = GeomAlgoAPI_ShapeTools::getLowLevelSubShapes(anOffsetShape);
244   std::list<GeomShapePtr>::const_iterator aResEdgesIt = aResEdges.begin();
245   for (; aResEdgesIt != aResEdges.end(); aResEdgesIt++) {
246     GeomShapePtr aResShape = (*aResEdgesIt);
247     if (aResShape->shapeType() == GeomAPI_Shape::EDGE) {
248       // Add new feature
249       FeaturePtr aResFeature;
250       std::shared_ptr<GeomAPI_Edge> aResEdge (new GeomAPI_Edge(aResShape));
251
252       std::shared_ptr<GeomAPI_Pnt2d> aFP, aLP;
253       std::shared_ptr<GeomAPI_Pnt> aFP3d = aResEdge->firstPoint();
254       std::shared_ptr<GeomAPI_Pnt> aLP3d = aResEdge->lastPoint();
255       //if (aFP3d.get() && aLP3d.get()) {
256       if (aFP3d && aLP3d) {
257         aFP = sketch()->to2D(aFP3d);
258         aLP = sketch()->to2D(aLP3d);
259       }
260
261       if (aResEdge->isLine()) {
262         aResFeature = sketch()->addFeature(SketchPlugin_Line::ID());
263
264         std::dynamic_pointer_cast<GeomDataAPI_Point2D>
265           (aResFeature->attribute(SketchPlugin_Line::START_ID()))->setValue(aFP);
266         std::dynamic_pointer_cast<GeomDataAPI_Point2D>
267           (aResFeature->attribute(SketchPlugin_Line::END_ID()))->setValue(aLP);
268       }
269       else if (aResEdge->isArc()) {
270         std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
271         std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
272         std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
273
274         aResFeature = sketch()->addFeature(SketchPlugin_Arc::ID());
275
276         bool aWasBlocked = aResFeature->data()->blockSendAttributeUpdated(true);
277         std::dynamic_pointer_cast<GeomDataAPI_Point2D>
278           (aResFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(aCP);
279         std::dynamic_pointer_cast<GeomDataAPI_Point2D>
280           (aResFeature->attribute(SketchPlugin_Arc::START_ID()))->setValue(aFP);
281         std::dynamic_pointer_cast<GeomDataAPI_Point2D>
282           (aResFeature->attribute(SketchPlugin_Arc::END_ID()))->setValue(aLP);
283         aResFeature->data()->blockSendAttributeUpdated(aWasBlocked);
284       }
285       else if (aResEdge->isCircle()) {
286         std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
287         std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
288         std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
289
290         aResFeature = sketch()->addFeature(SketchPlugin_Circle::ID());
291         std::dynamic_pointer_cast<GeomDataAPI_Point2D>
292           (aResFeature->attribute(SketchPlugin_Circle::CENTER_ID()))->setValue(aCP);
293         aResFeature->real(SketchPlugin_Circle::RADIUS_ID())->setValue(aCircEdge->radius());
294       }
295       else if (aResEdge->isEllipse()) {
296         std::shared_ptr<GeomAPI_Ellipse> anEllipseEdge = aResEdge->ellipse();
297
298         GeomPointPtr aCP3d = anEllipseEdge->center();
299         GeomPnt2dPtr aCP = sketch()->to2D(aCP3d);
300
301         GeomPointPtr aFocus3d = anEllipseEdge->firstFocus();
302         GeomPnt2dPtr aFocus = sketch()->to2D(aFocus3d);
303
304         if (aFP3d && aLP3d) {
305           // Elliptic arc
306           aResFeature = sketch()->addFeature(SketchPlugin_EllipticArc::ID());
307
308           bool aWasBlocked = aResFeature->data()->blockSendAttributeUpdated(true);
309           std::dynamic_pointer_cast<GeomDataAPI_Point2D>
310             (aResFeature->attribute(SketchPlugin_EllipticArc::CENTER_ID()))->setValue(aCP);
311           std::dynamic_pointer_cast<GeomDataAPI_Point2D>
312             (aResFeature->attribute(SketchPlugin_EllipticArc::FIRST_FOCUS_ID()))->setValue(aFocus);
313           std::dynamic_pointer_cast<GeomDataAPI_Point2D>
314             (aResFeature->attribute(SketchPlugin_EllipticArc::START_POINT_ID()))->setValue(aFP);
315           std::dynamic_pointer_cast<GeomDataAPI_Point2D>
316             (aResFeature->attribute(SketchPlugin_EllipticArc::END_POINT_ID()))->setValue(aLP);
317           aResFeature->data()->blockSendAttributeUpdated(aWasBlocked);
318         }
319         else {
320           // Ellipse
321           aResFeature = sketch()->addFeature(SketchPlugin_Ellipse::ID());
322
323           std::dynamic_pointer_cast<GeomDataAPI_Point2D>
324             (aResFeature->attribute(SketchPlugin_Ellipse::CENTER_ID()))->setValue(aCP);
325           std::dynamic_pointer_cast<GeomDataAPI_Point2D>
326             (aResFeature->attribute(SketchPlugin_Ellipse::FIRST_FOCUS_ID()))->setValue(aFocus);
327           aResFeature->real(SketchPlugin_Ellipse::MINOR_RADIUS_ID())->setValue(anEllipseEdge->minorRadius());
328         }
329       }
330       else if (aResEdge->isBSpline()) {
331         mkBSpline(aResFeature, aResEdge);
332       }
333       else {
334       }
335
336       if (aResFeature.get()) {
337         myCreatedFeatures.insert(aResFeature);
338
339         aResFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue
340           (boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value());
341         aResFeature->execute();
342       }
343     }
344   }
345 }
346
347 void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult,
348                                      const GeomEdgePtr& theEdge)
349 {
350   if (!theEdge->isBSpline())
351     return;
352
353   GeomCurvePtr aCurve (new GeomAPI_Curve (theEdge));
354   GeomAPI_BSpline aBSpline (aCurve);
355
356   if (aBSpline.isPeriodic())
357     theResult = sketch()->addFeature(SketchPlugin_BSplinePeriodic::ID());
358   else
359     theResult = sketch()->addFeature(SketchPlugin_BSpline::ID());
360
361   theResult->integer(SketchPlugin_BSpline::DEGREE_ID())->setValue(aBSpline.degree());
362
363   AttributePoint2DArrayPtr aPolesAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>
364     (theResult->attribute(SketchPlugin_BSpline::POLES_ID()));
365   std::list<GeomPointPtr> aPoles = aBSpline.poles();
366   aPolesAttr->setSize((int)aPoles.size());
367   std::list<GeomPointPtr>::iterator anIt = aPoles.begin();
368   for (int anIndex = 0; anIt != aPoles.end(); ++anIt, ++anIndex) {
369     GeomPnt2dPtr aPoleInSketch = sketch()->to2D(*anIt);
370     aPolesAttr->setPnt(anIndex, aPoleInSketch);
371   }
372
373   AttributeDoubleArrayPtr aWeightsAttr =
374       theResult->data()->realArray(SketchPlugin_BSpline::WEIGHTS_ID());
375   std::list<double> aWeights = aBSpline.weights();
376   if (aWeights.empty()) { // rational B-spline
377     int aSize = (int)aPoles.size();
378     aWeightsAttr->setSize(aSize);
379     for (int anIndex = 0; anIndex < aSize; ++anIndex)
380       aWeightsAttr->setValue(anIndex, 1.0);
381   }
382   else { // non-rational B-spline
383     aWeightsAttr->setSize((int)aWeights.size());
384     std::list<double>::iterator anIt = aWeights.begin();
385     for (int anIndex = 0; anIt != aWeights.end(); ++anIt, ++anIndex)
386       aWeightsAttr->setValue(anIndex, *anIt);
387   }
388
389   AttributeDoubleArrayPtr aKnotsAttr =
390       theResult->data()->realArray(SketchPlugin_BSpline::KNOTS_ID());
391   std::list<double> aKnots = aBSpline.knots();
392   int aSize = (int)aKnots.size();
393   aKnotsAttr->setSize(aSize);
394   std::list<double>::iterator aKIt = aKnots.begin();
395   for (int index = 0; index < aSize; ++index, ++aKIt)
396     aKnotsAttr->setValue(index, *aKIt);
397
398   AttributeIntArrayPtr aMultsAttr =
399       theResult->data()->intArray(SketchPlugin_BSpline::MULTS_ID());
400   std::list<int> aMultiplicities = aBSpline.mults();
401   aSize = (int)aMultiplicities.size();
402   aMultsAttr->setSize(aSize);
403   std::list<int>::iterator aMIt = aMultiplicities.begin();
404   for (int index = 0; index < aSize; ++index, ++aMIt)
405     aMultsAttr->setValue(index, *aMIt);
406 }
407
408 void SketchPlugin_Offset::attributeChanged(const std::string& theID)
409 {
410   ModelAPI_Tools::removeFeaturesAndReferences(myCreatedFeatures);
411   myCreatedFeatures.clear();
412 }
413
414 bool SketchPlugin_Offset::customAction(const std::string& theActionId)
415 {
416   bool isOk = false;
417   if (theActionId == ADD_WIRE_ACTION_ID()) {
418     isOk = findWires();
419   }
420   else {
421     std::string aMsg = "Error: Feature \"%1\" does not support action \"%2\".";
422     Events_InfoMessage("SketchPlugin_Offset", aMsg).arg(getKind()).arg(theActionId).send();
423   }
424   return isOk;
425 }
426
427 bool SketchPlugin_Offset::findWires()
428 {
429   AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
430   std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
431
432   // Empty set
433   std::set<FeaturePtr> anEdgesSet;
434
435   // Processed set
436   std::set<FeaturePtr> aProcessedSet;
437
438   // Put all selected edges in a set to avoid adding them in reflist(EDGES_ID())
439   std::set<FeaturePtr> aSelectedSet;
440   std::list<ObjectPtr>::const_iterator anEdgesIt = anEdgesList.begin();
441   for (; anEdgesIt != anEdgesList.end(); anEdgesIt++) {
442     FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
443     if (aFeature) {
444       aSelectedSet.insert(aFeature);
445     }
446   }
447
448   // Gather chains of edges
449   for (anEdgesIt = anEdgesList.begin(); anEdgesIt != anEdgesList.end(); anEdgesIt++) {
450     FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
451     if (aFeature.get()) {
452       if (aProcessedSet.find(aFeature) != aProcessedSet.end())
453         continue;
454       aProcessedSet.insert(aFeature);
455
456       // End points (if any)
457       std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
458       SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
459
460       std::list<FeaturePtr> aChain;
461       aChain.push_back(aFeature);
462       bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet, aChain);
463       if (!isClosed)
464         findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aChain);
465
466       std::list<FeaturePtr>::iterator aChainIt = aChain.begin();
467       for (; aChainIt != aChain.end(); ++aChainIt) {
468         FeaturePtr aChainFeature = (*aChainIt);
469         aProcessedSet.insert(aChainFeature);
470         if (aSelectedSet.find(aChainFeature) == aSelectedSet.end()) {
471           aSelectedEdges->append(aChainFeature->lastResult());
472         }
473       }
474     }
475   }
476   // TODO: hilight selection in the viewer
477
478   return true;
479 }