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_Ellipse.h>
28 #include <SketchPlugin_EllipticArc.h>
29 #include <SketchPlugin_BSpline.h>
30 #include <SketchPlugin_BSplinePeriodic.h>
31 #include <SketchPlugin_Tools.h>
33 #include <Events_InfoMessage.h>
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>
43 #include <GeomAlgoAPI_Offset.h>
44 #include <GeomAlgoAPI_ShapeTools.h>
45 #include <GeomAlgoAPI_WireBuilder.h>
47 #include <GeomAPI_Edge.h>
48 #include <GeomAPI_Circ.h>
49 #include <GeomAPI_Ellipse.h>
50 #include <GeomAPI_BSpline.h>
52 #include <GeomDataAPI_Point2D.h>
53 #include <GeomDataAPI_Point2DArray.h>
57 SketchPlugin_Offset::SketchPlugin_Offset()
58 : SketchPlugin_SketchEntity()
62 void SketchPlugin_Offset::initDerivedClassAttributes()
64 data()->addAttribute(EDGES_ID(), ModelAPI_AttributeRefList::typeId());
65 data()->addAttribute(VALUE_ID(), ModelAPI_AttributeDouble::typeId());
66 data()->addAttribute(REVERSED_ID(), ModelAPI_AttributeBoolean::typeId());
69 void SketchPlugin_Offset::execute()
71 ModelAPI_Tools::removeFeaturesAndReferences(myCreatedFeatures);
72 myCreatedFeatures.clear();
74 SketchPlugin_Sketch* aSketch = sketch();
78 std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
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;
88 AttributeBooleanPtr aReversedAttr = boolean(REVERSED_ID());
89 if (!aReversedAttr->isInitialized()) return;
90 if (aReversedAttr->value()) aValue = -aValue; // reverse offset direction
92 // 3. List of all selected edges
93 AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
94 std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
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);
102 anEdgesSet.insert(aFeature);
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())
113 // 5.a. End points (if any)
114 std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
115 SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
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);
123 findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aChain);
125 std::set<FeaturePtr>::iterator aPos = anEdgesSet.find(aFeature);
126 if (aPos != anEdgesSet.end())
127 anEdgesSet.erase(aPos);
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);
139 GeomShapePtr anEdgeOrWire = GeomAlgoAPI_WireBuilder::wire(aTopoChain);
141 // 5.d. Make offset for each wire
142 std::shared_ptr<GeomAPI_Shape> anOffsetShape =
143 GeomAlgoAPI_Offset::OffsetInPlane(aPlane, anEdgeOrWire, aValue);
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);
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)
159 // 1. Find a single edge, coincident to theEndPoint by one of its ends
160 if (!theEndPoint) return false;
162 std::shared_ptr<GeomAPI_Pnt2d> aP2d = theEndPoint->pnt();
164 FeaturePtr aNextEdgeFeature;
167 std::set<AttributePoint2DPtr> aCoincPoints;
168 std::map<AttributePoint2DArrayPtr, int> aCoincPointsInArray;
169 SketchPlugin_Tools::findPointsCoincidentToPoint(theEndPoint, aCoincPoints, aCoincPointsInArray);
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);
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());
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.
189 if (theEdgesSet.size()) {
190 isInSet = (theEdgesSet.find(aCoincFeature) != theEdgesSet.end());
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);
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.
207 aNextEdgeFeature = aCoincFeature;
215 // Only one edge can prolongate the chain. If several or none, we stop here.
219 // 2. So, we have the single edge, that prolongate the chain
221 // Condition 7: if we reached the very first edge of the chain
222 if (aNextEdgeFeature == theFirstEdge)
223 // Closed chain found
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);
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())) {
243 // 5. Continue gathering the chain (recursive)
244 return findWireOneWay (theFirstEdge, aNextEdgeFeature, aP2, theEdgesSet, theChain);
247 void SketchPlugin_Offset::addToSketch(const std::shared_ptr<GeomAPI_Shape>& anOffsetShape)
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) {
256 FeaturePtr aResFeature;
257 std::shared_ptr<GeomAPI_Edge> aResEdge (new GeomAPI_Edge(aResShape));
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);
268 if (aResEdge->isLine()) {
269 aResFeature = sketch()->addFeature(SketchPlugin_Line::ID());
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);
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);
281 aResFeature = sketch()->addFeature(SketchPlugin_Arc::ID());
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);
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);
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());
302 else if (aResEdge->isEllipse()) {
303 std::shared_ptr<GeomAPI_Ellipse> anEllipseEdge = aResEdge->ellipse();
305 GeomPointPtr aCP3d = anEllipseEdge->center();
306 GeomPnt2dPtr aCP = sketch()->to2D(aCP3d);
308 GeomPointPtr aFocus3d = anEllipseEdge->firstFocus();
309 GeomPnt2dPtr aFocus = sketch()->to2D(aFocus3d);
311 if (aFP3d && aLP3d) {
313 aResFeature = sketch()->addFeature(SketchPlugin_EllipticArc::ID());
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);
328 aResFeature = sketch()->addFeature(SketchPlugin_Ellipse::ID());
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());
337 else if (aResEdge->isBSpline()) {
338 mkBSpline(aResFeature, aResEdge);
343 if (aResFeature.get()) {
344 myCreatedFeatures.insert(aResFeature);
346 aResFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue
347 (boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value());
348 aResFeature->execute();
354 void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult,
355 const GeomEdgePtr& theEdge)
357 if (!theEdge->isBSpline())
360 GeomCurvePtr aCurve (new GeomAPI_Curve (theEdge));
361 GeomAPI_BSpline aBSpline (aCurve);
363 if (aBSpline.isPeriodic())
364 theResult = sketch()->addFeature(SketchPlugin_BSplinePeriodic::ID());
366 theResult = sketch()->addFeature(SketchPlugin_BSpline::ID());
368 theResult->integer(SketchPlugin_BSpline::DEGREE_ID())->setValue(aBSpline.degree());
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);
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);
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);
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);
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);
415 void SketchPlugin_Offset::attributeChanged(const std::string& theID)
417 ModelAPI_Tools::removeFeaturesAndReferences(myCreatedFeatures);
418 myCreatedFeatures.clear();
421 bool SketchPlugin_Offset::customAction(const std::string& theActionId)
424 if (theActionId == ADD_WIRE_ACTION_ID()) {
428 std::string aMsg = "Error: Feature \"%1\" does not support action \"%2\".";
429 Events_InfoMessage("SketchPlugin_Offset", aMsg).arg(getKind()).arg(theActionId).send();
434 bool SketchPlugin_Offset::findWires()
436 AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
437 std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
440 std::set<FeaturePtr> anEdgesSet;
443 std::set<FeaturePtr> aProcessedSet;
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);
451 aSelectedSet.insert(aFeature);
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())
461 aProcessedSet.insert(aFeature);
463 // End points (if any)
464 std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
465 SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
467 std::list<FeaturePtr> aChain;
468 aChain.push_back(aFeature);
469 bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet, aChain);
471 findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aChain);
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());
483 // TODO: hilight selection in the viewer