]> SALOME platform Git repositories - modules/shaper.git/blob - src/SketchPlugin/SketchPlugin_Offset.cpp
Salome HOME
Sketcher Offset: Debug.
[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_Tools.h>
28
29 #include <Events_InfoMessage.h>
30
31 #include <ModelAPI_AttributeBoolean.h>
32 #include <ModelAPI_AttributeDouble.h>
33 #include <ModelAPI_AttributeRefList.h>
34 #include <ModelAPI_ResultConstruction.h>
35 #include <ModelAPI_Tools.h>
36
37 #include <GeomAlgoAPI_Offset.h>
38 #include <GeomAlgoAPI_ShapeTools.h>
39 #include <GeomAlgoAPI_WireBuilder.h>
40
41 #include <GeomAPI_Edge.h>
42 #include <GeomAPI_Circ.h>
43
44 #include <GeomDataAPI_Point2D.h>
45
46 #include <iostream>
47
48 SketchPlugin_Offset::SketchPlugin_Offset()
49   : SketchPlugin_SketchEntity()
50 {
51 }
52
53 void SketchPlugin_Offset::initDerivedClassAttributes()
54 {
55   data()->addAttribute(EDGES_ID(), ModelAPI_AttributeRefList::typeId());
56   data()->addAttribute(VALUE_ID(), ModelAPI_AttributeDouble::typeId());
57   data()->addAttribute(REVERSED_ID(), ModelAPI_AttributeBoolean::typeId());
58 }
59
60 void SketchPlugin_Offset::execute()
61 {
62   ModelAPI_Tools::removeFeaturesAndReferences(myCreatedFeatures);
63   myCreatedFeatures.clear();
64
65   SketchPlugin_Sketch* aSketch = sketch();
66   if (!aSketch) return;
67
68   // 1. Sketch plane
69   std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
70
71   // 2. Offset value
72   AttributeDoublePtr aValueAttr = real(VALUE_ID());
73   if (!aValueAttr->isInitialized()) return;
74   double aValue = aValueAttr->value();
75   const double tolerance = 1.e-7;
76   if (aValue < tolerance) return;
77
78   // 2.a. Reversed?
79   AttributeBooleanPtr aReversedAttr = boolean(REVERSED_ID());
80   if (!aReversedAttr->isInitialized()) return;
81   if (aReversedAttr->value()) aValue = -aValue; // reverse offset direction
82
83   // 3. List of all selected edges
84   AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
85   std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
86
87   // 4. Put all selected edges in a set to pass them into findWireOneWay() below
88   std::set<FeaturePtr> anEdgesSet;
89   std::list<ObjectPtr>::const_iterator anEdgesIt = anEdgesList.begin();
90   for (; anEdgesIt != anEdgesList.end(); anEdgesIt++) {
91     FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
92     if (aFeature) {
93       anEdgesSet.insert(aFeature);
94     }
95   }
96
97   // 5. Gather wires and make offset for each wire
98   for (anEdgesIt = anEdgesList.begin(); anEdgesIt != anEdgesList.end(); anEdgesIt++) {
99     FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
100     if (aFeature.get()) {
101       if (anEdgesSet.find(aFeature) == anEdgesSet.end())
102         continue;
103
104       // 5.a. End points (if any)
105       std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
106       SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
107
108       // 5.b. Find a chain of edges
109       std::list<FeaturePtr> aChain;
110       aChain.push_back(aFeature);
111       if (aStartPoint && anEndPoint) { // not closed edge
112         bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet, aChain);
113         if (!isClosed)
114           findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aChain);
115       }
116       std::set<FeaturePtr>::iterator aPos = anEdgesSet.find(aFeature);
117       if (aPos != anEdgesSet.end())
118         anEdgesSet.erase(aPos);
119
120       // 5.c. Make wire
121       ListOfShape aTopoChain;
122       std::list<FeaturePtr>::iterator aChainIt = aChain.begin();
123       for (; aChainIt != aChain.end(); ++aChainIt) {
124         FeaturePtr aChainFeature = (*aChainIt);
125         GeomShapePtr aTopoEdge = aChainFeature->lastResult()->shape();
126         if (aTopoEdge->shapeType() == GeomAPI_Shape::EDGE) {
127           aTopoChain.push_back(aTopoEdge);
128         }
129       }
130       GeomShapePtr anEdgeOrWire = GeomAlgoAPI_WireBuilder::wire(aTopoChain);
131
132       // 5.d. Make offset for each wire
133       std::shared_ptr<GeomAPI_Shape> anOffsetShape =
134         GeomAlgoAPI_Offset::OffsetInPlane(aPlane, anEdgeOrWire, aValue);
135
136       // 5.e. Store offset results.
137       //      Create sketch feature for each edge of anOffsetShape, and also store
138       //      created features in myCreatedFeatures to remove them on next execute()
139       addToSketch(anOffsetShape);
140     }
141   }
142 }
143
144 bool SketchPlugin_Offset::findWireOneWay (const FeaturePtr& theFirstEdge,
145                                           const FeaturePtr& theEdge,
146                                           const std::shared_ptr<GeomDataAPI_Point2D>& theEndPoint,
147                                           std::set<FeaturePtr>& theEdgesSet,
148                                           std::list<FeaturePtr>& theChain)
149 {
150   // 1. Find a single edge, coincident to theEndPoint by one of its ends
151   if (!theEndPoint) return false;
152
153   std::shared_ptr<GeomAPI_Pnt2d> aP2d = theEndPoint->pnt();
154
155   FeaturePtr aNextEdgeFeature;
156   int nbFound = 0;
157
158   std::set<AttributePoint2DPtr> aCoincPoints =
159     SketchPlugin_Tools::findPointsCoincidentToPoint(theEndPoint);
160
161   std::set<AttributePoint2DPtr>::iterator aPointsIt = aCoincPoints.begin();
162   for (; aPointsIt != aCoincPoints.end(); aPointsIt++) {
163     AttributePoint2DPtr aP = (*aPointsIt);
164     FeaturePtr aCoincFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aP->owner());
165
166     // Condition 1: not a point feature
167     if (aCoincFeature->getKind() != SketchPlugin_Point::ID()) {
168       // Condition 2: it is not the current edge
169       if (aCoincFeature != theEdge) {
170         // Condition 3: it is in the set of interest.
171         //              Empty set means all sketch edges.
172         bool isInSet = true;
173         if (theEdgesSet.size()) {
174           isInSet = (theEdgesSet.find(aCoincFeature) != theEdgesSet.end());
175         }
176         if (isInSet) {
177           // Condition 4: consider only features with two end points
178           std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
179           SketchPlugin_SegmentationTools::getFeaturePoints(aCoincFeature, aP1, aP2);
180           if (aP1 && aP2) {
181             // Condition 5: consider only features, that have aP as one of they ends.
182             //              For example, we do not need an arc, coincident to aP by its center.
183             if (theEndPoint->pnt()->isEqual(aP1->pnt()) ||
184                 theEndPoint->pnt()->isEqual(aP2->pnt())) {
185               // Condition 6: only one edge can prolongate the chain. If several, we stop here.
186               nbFound++;
187               if (nbFound > 1)
188                 return false;
189
190               // One found
191               aNextEdgeFeature = aCoincFeature;
192             }
193           }
194         }
195       }
196     }
197   }
198
199   // Only one edge can prolongate the chain. If several or none, we stop here.
200   if (nbFound != 1)
201     return false;
202
203   // 2. So, we have the single edge, that prolongate the chain
204
205   // Condition 7: if we reached the very first edge of the chain
206   if (aNextEdgeFeature == theFirstEdge)
207     // Closed chain found
208     return true;
209
210   // 3. Add the found edge to the chain
211   theChain.push_back(aNextEdgeFeature);
212   // remove from the set, if the set is used
213   if (theEdgesSet.size()) {
214     std::set<FeaturePtr>::iterator aPos = theEdgesSet.find(aNextEdgeFeature);
215     if (aPos != theEdgesSet.end())
216       theEdgesSet.erase(aPos);
217   }
218
219   // 4. Which end of aNextEdgeFeature we need to proceed
220   std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
221   SketchPlugin_SegmentationTools::getFeaturePoints(aNextEdgeFeature, aP1, aP2);
222   if (aP2->pnt()->isEqual(theEndPoint->pnt())) {
223     // reversed
224     aP2 = aP1;
225   }
226
227   // 5. Continue gathering the chain (recursive)
228   return findWireOneWay (theFirstEdge, aNextEdgeFeature, aP2, theEdgesSet, theChain);
229 }
230
231 void SketchPlugin_Offset::addToSketch(const std::shared_ptr<GeomAPI_Shape>& anOffsetShape)
232 {
233   //GeomAPI_ShapeExplorer::GeomAPI_ShapeExplorer
234   ListOfShape aResEdges = GeomAlgoAPI_ShapeTools::getLowLevelSubShapes(anOffsetShape);
235   std::list<GeomShapePtr>::const_iterator aResEdgesIt = aResEdges.begin();
236   for (; aResEdgesIt != aResEdges.end(); aResEdgesIt++) {
237     GeomShapePtr aResShape = (*aResEdgesIt);
238     if (aResShape->shapeType() == GeomAPI_Shape::EDGE) {
239       // Add new feature
240       FeaturePtr aResFeature;
241       std::shared_ptr<GeomAPI_Edge> aResEdge (new GeomAPI_Edge(aResShape));
242
243       std::shared_ptr<GeomAPI_Pnt2d> aFP, aLP;
244       std::shared_ptr<GeomAPI_Pnt> aFP3d = aResEdge->firstPoint();
245       std::shared_ptr<GeomAPI_Pnt> aLP3d = aResEdge->lastPoint();
246       if (aFP3d.get() && aLP3d.get()) {
247         aFP = sketch()->to2D(aFP3d);
248         aLP = sketch()->to2D(aLP3d);
249       }
250
251       if (aResEdge->isLine()) {
252         aResFeature = sketch()->addFeature(SketchPlugin_Line::ID());
253
254         std::dynamic_pointer_cast<GeomDataAPI_Point2D>
255           (aResFeature->attribute(SketchPlugin_Line::START_ID()))->setValue(aFP);
256         std::dynamic_pointer_cast<GeomDataAPI_Point2D>
257           (aResFeature->attribute(SketchPlugin_Line::END_ID()))->setValue(aLP);
258       }
259       else if (aResEdge->isArc()) {
260         std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
261         std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
262         std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
263
264         aResFeature = sketch()->addFeature(SketchPlugin_Arc::ID());
265
266         bool aWasBlocked = aResFeature->data()->blockSendAttributeUpdated(true);
267         std::dynamic_pointer_cast<GeomDataAPI_Point2D>
268           (aResFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(aCP);
269        std::dynamic_pointer_cast<GeomDataAPI_Point2D>
270           (aResFeature->attribute(SketchPlugin_Arc::START_ID()))->setValue(aFP);
271         std::dynamic_pointer_cast<GeomDataAPI_Point2D>
272           (aResFeature->attribute(SketchPlugin_Arc::END_ID()))->setValue(aLP);
273         aResFeature->data()->blockSendAttributeUpdated(aWasBlocked);
274       }
275       else if (aResEdge->isCircle()) {
276         std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
277         std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
278         std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
279
280         aResFeature = sketch()->addFeature(SketchPlugin_Circle::ID());
281         std::dynamic_pointer_cast<GeomDataAPI_Point2D>
282           (aResFeature->attribute(SketchPlugin_Circle::CENTER_ID()))->setValue(aCP);
283         aResFeature->real(SketchPlugin_Circle::RADIUS_ID())->setValue(aCircEdge->radius());
284       }
285       else {
286       }
287
288       if (aResFeature.get()) {
289         myCreatedFeatures.insert(aResFeature);
290
291         aResFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue
292           (boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value());
293         aResFeature->execute();
294       }
295     }
296   }
297 }
298
299 void SketchPlugin_Offset::attributeChanged(const std::string& theID)
300 {
301   ModelAPI_Tools::removeFeaturesAndReferences(myCreatedFeatures);
302   myCreatedFeatures.clear();
303 }
304
305 bool SketchPlugin_Offset::customAction(const std::string& theActionId)
306 {
307   bool isOk = false;
308   if (theActionId == ADD_WIRE_ACTION_ID()) {
309     isOk = findWires();
310   }
311   else {
312     std::string aMsg = "Error: Feature \"%1\" does not support action \"%2\".";
313     Events_InfoMessage("SketchPlugin_Offset", aMsg).arg(getKind()).arg(theActionId).send();
314   }
315   return isOk;
316 }
317
318 bool SketchPlugin_Offset::findWires()
319 {
320   AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
321   std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
322
323   // Empty set
324   std::set<FeaturePtr> anEdgesSet;
325
326   // Processed set
327   std::set<FeaturePtr> aProcessedSet;
328
329   // Put all selected edges in a set to avoid adding them in reflist(EDGES_ID())
330   std::set<FeaturePtr> aSelectedSet;
331   std::list<ObjectPtr>::const_iterator anEdgesIt = anEdgesList.begin();
332   for (; anEdgesIt != anEdgesList.end(); anEdgesIt++) {
333     FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
334     if (aFeature) {
335       aSelectedSet.insert(aFeature);
336     }
337   }
338
339   // Gather chains of edges
340   for (anEdgesIt = anEdgesList.begin(); anEdgesIt != anEdgesList.end(); anEdgesIt++) {
341     FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
342     if (aFeature.get()) {
343       if (aProcessedSet.find(aFeature) != aProcessedSet.end())
344         continue;
345       aProcessedSet.insert(aFeature);
346
347       // End points (if any)
348       std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
349       SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
350
351       std::list<FeaturePtr> aChain;
352       aChain.push_back(aFeature);
353       bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet, aChain);
354       if (!isClosed)
355         findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aChain);
356
357       std::list<FeaturePtr>::iterator aChainIt = aChain.begin();
358       for (; aChainIt != aChain.end(); ++aChainIt) {
359         FeaturePtr aChainFeature = (*aChainIt);
360         aProcessedSet.insert(aChainFeature);
361         if (aSelectedSet.find(aChainFeature) == aSelectedSet.end()) {
362           aSelectedEdges->append(aChainFeature->lastResult());
363         }
364       }
365     }
366   }
367   // TODO: hilight selection in the viewer
368
369   return true;
370 }