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_Tools.h>
29 #include <Events_InfoMessage.h>
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>
37 #include <GeomAlgoAPI_Offset.h>
38 #include <GeomAlgoAPI_ShapeTools.h>
39 #include <GeomAlgoAPI_WireBuilder.h>
41 #include <GeomAPI_Edge.h>
42 #include <GeomAPI_Circ.h>
44 #include <GeomDataAPI_Point2D.h>
48 SketchPlugin_Offset::SketchPlugin_Offset()
49 : SketchPlugin_SketchEntity()
53 void SketchPlugin_Offset::initDerivedClassAttributes()
55 data()->addAttribute(EDGES_ID(), ModelAPI_AttributeRefList::typeId());
56 data()->addAttribute(VALUE_ID(), ModelAPI_AttributeDouble::typeId());
57 data()->addAttribute(REVERSED_ID(), ModelAPI_AttributeBoolean::typeId());
60 void SketchPlugin_Offset::execute()
62 ModelAPI_Tools::removeFeaturesAndReferences(myCreatedFeatures);
63 myCreatedFeatures.clear();
65 SketchPlugin_Sketch* aSketch = sketch();
69 std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
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;
79 AttributeBooleanPtr aReversedAttr = boolean(REVERSED_ID());
80 if (!aReversedAttr->isInitialized()) return;
81 if (aReversedAttr->value()) aValue = -aValue; // reverse offset direction
83 // 3. List of all selected edges
84 AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
85 std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
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);
93 anEdgesSet.insert(aFeature);
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())
104 // 5.a. End points (if any)
105 std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
106 SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
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);
114 findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aChain);
116 std::set<FeaturePtr>::iterator aPos = anEdgesSet.find(aFeature);
117 if (aPos != anEdgesSet.end())
118 anEdgesSet.erase(aPos);
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);
130 GeomShapePtr anEdgeOrWire = GeomAlgoAPI_WireBuilder::wire(aTopoChain);
132 // 5.d. Make offset for each wire
133 std::shared_ptr<GeomAPI_Shape> anOffsetShape =
134 GeomAlgoAPI_Offset::OffsetInPlane(aPlane, anEdgeOrWire, aValue);
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);
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)
150 // 1. Find a single edge, coincident to theEndPoint by one of its ends
151 if (!theEndPoint) return false;
153 std::shared_ptr<GeomAPI_Pnt2d> aP2d = theEndPoint->pnt();
155 FeaturePtr aNextEdgeFeature;
158 std::set<AttributePoint2DPtr> aCoincPoints =
159 SketchPlugin_Tools::findPointsCoincidentToPoint(theEndPoint);
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());
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.
173 if (theEdgesSet.size()) {
174 isInSet = (theEdgesSet.find(aCoincFeature) != theEdgesSet.end());
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);
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.
191 aNextEdgeFeature = aCoincFeature;
199 // Only one edge can prolongate the chain. If several or none, we stop here.
203 // 2. So, we have the single edge, that prolongate the chain
205 // Condition 7: if we reached the very first edge of the chain
206 if (aNextEdgeFeature == theFirstEdge)
207 // Closed chain found
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);
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())) {
227 // 5. Continue gathering the chain (recursive)
228 return findWireOneWay (theFirstEdge, aNextEdgeFeature, aP2, theEdgesSet, theChain);
231 void SketchPlugin_Offset::addToSketch(const std::shared_ptr<GeomAPI_Shape>& anOffsetShape)
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) {
240 FeaturePtr aResFeature;
241 std::shared_ptr<GeomAPI_Edge> aResEdge (new GeomAPI_Edge(aResShape));
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);
251 if (aResEdge->isLine()) {
252 aResFeature = sketch()->addFeature(SketchPlugin_Line::ID());
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);
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);
264 aResFeature = sketch()->addFeature(SketchPlugin_Arc::ID());
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);
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);
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());
288 if (aResFeature.get()) {
289 myCreatedFeatures.insert(aResFeature);
291 aResFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue
292 (boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value());
293 aResFeature->execute();
299 void SketchPlugin_Offset::attributeChanged(const std::string& theID)
301 ModelAPI_Tools::removeFeaturesAndReferences(myCreatedFeatures);
302 myCreatedFeatures.clear();
305 bool SketchPlugin_Offset::customAction(const std::string& theActionId)
308 if (theActionId == ADD_WIRE_ACTION_ID()) {
312 std::string aMsg = "Error: Feature \"%1\" does not support action \"%2\".";
313 Events_InfoMessage("SketchPlugin_Offset", aMsg).arg(getKind()).arg(theActionId).send();
318 bool SketchPlugin_Offset::findWires()
320 AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
321 std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
324 std::set<FeaturePtr> anEdgesSet;
327 std::set<FeaturePtr> aProcessedSet;
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);
335 aSelectedSet.insert(aFeature);
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())
345 aProcessedSet.insert(aFeature);
347 // End points (if any)
348 std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
349 SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
351 std::list<FeaturePtr> aChain;
352 aChain.push_back(aFeature);
353 bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet, aChain);
355 findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aChain);
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());
367 // TODO: hilight selection in the viewer