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 SketchPlugin_Tools::findPointsCoincidentToPoint(theEndPoint);
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());
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.
182 if (theEdgesSet.size()) {
183 isInSet = (theEdgesSet.find(aCoincFeature) != theEdgesSet.end());
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);
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.
200 aNextEdgeFeature = aCoincFeature;
208 // Only one edge can prolongate the chain. If several or none, we stop here.
212 // 2. So, we have the single edge, that prolongate the chain
214 // Condition 7: if we reached the very first edge of the chain
215 if (aNextEdgeFeature == theFirstEdge)
216 // Closed chain found
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);
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())) {
236 // 5. Continue gathering the chain (recursive)
237 return findWireOneWay (theFirstEdge, aNextEdgeFeature, aP2, theEdgesSet, theChain);
240 void SketchPlugin_Offset::addToSketch(const std::shared_ptr<GeomAPI_Shape>& anOffsetShape)
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) {
249 FeaturePtr aResFeature;
250 std::shared_ptr<GeomAPI_Edge> aResEdge (new GeomAPI_Edge(aResShape));
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);
261 if (aResEdge->isLine()) {
262 aResFeature = sketch()->addFeature(SketchPlugin_Line::ID());
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);
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);
274 aResFeature = sketch()->addFeature(SketchPlugin_Arc::ID());
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);
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);
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());
295 else if (aResEdge->isEllipse()) {
296 std::shared_ptr<GeomAPI_Ellipse> anEllipseEdge = aResEdge->ellipse();
298 GeomPointPtr aCP3d = anEllipseEdge->center();
299 GeomPnt2dPtr aCP = sketch()->to2D(aCP3d);
301 GeomPointPtr aFocus3d = anEllipseEdge->firstFocus();
302 GeomPnt2dPtr aFocus = sketch()->to2D(aFocus3d);
304 if (aFP3d && aLP3d) {
306 aResFeature = sketch()->addFeature(SketchPlugin_EllipticArc::ID());
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);
321 aResFeature = sketch()->addFeature(SketchPlugin_Ellipse::ID());
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());
330 else if (aResEdge->isBSpline()) {
331 mkBSpline(aResFeature, aResEdge);
336 if (aResFeature.get()) {
337 myCreatedFeatures.insert(aResFeature);
339 aResFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue
340 (boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value());
341 aResFeature->execute();
347 void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult,
348 const GeomEdgePtr& theEdge)
350 if (!theEdge->isBSpline())
353 GeomCurvePtr aCurve (new GeomAPI_Curve (theEdge));
354 GeomAPI_BSpline aBSpline (aCurve);
356 if (aBSpline.isPeriodic())
357 theResult = sketch()->addFeature(SketchPlugin_BSplinePeriodic::ID());
359 theResult = sketch()->addFeature(SketchPlugin_BSpline::ID());
361 theResult->integer(SketchPlugin_BSpline::DEGREE_ID())->setValue(aBSpline.degree());
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);
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);
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);
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);
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);
408 void SketchPlugin_Offset::attributeChanged(const std::string& theID)
410 ModelAPI_Tools::removeFeaturesAndReferences(myCreatedFeatures);
411 myCreatedFeatures.clear();
414 bool SketchPlugin_Offset::customAction(const std::string& theActionId)
417 if (theActionId == ADD_WIRE_ACTION_ID()) {
421 std::string aMsg = "Error: Feature \"%1\" does not support action \"%2\".";
422 Events_InfoMessage("SketchPlugin_Offset", aMsg).arg(getKind()).arg(theActionId).send();
427 bool SketchPlugin_Offset::findWires()
429 AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
430 std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
433 std::set<FeaturePtr> anEdgesSet;
436 std::set<FeaturePtr> aProcessedSet;
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);
444 aSelectedSet.insert(aFeature);
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())
454 aProcessedSet.insert(aFeature);
456 // End points (if any)
457 std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
458 SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
460 std::list<FeaturePtr> aChain;
461 aChain.push_back(aFeature);
462 bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet, aChain);
464 findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aChain);
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());
476 // TODO: hilight selection in the viewer