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 <SketcherPrs_Factory.h>
35 #include <Events_InfoMessage.h>
37 #include <ModelAPI_AttributeBoolean.h>
38 #include <ModelAPI_AttributeDouble.h>
39 #include <ModelAPI_AttributeDoubleArray.h>
40 #include <ModelAPI_AttributeInteger.h>
41 #include <ModelAPI_AttributeRefList.h>
42 #include <ModelAPI_ResultConstruction.h>
43 #include <ModelAPI_Tools.h>
45 #include <GeomAlgoAPI_Offset.h>
46 #include <GeomAlgoAPI_ShapeTools.h>
47 #include <GeomAlgoAPI_WireBuilder.h>
49 #include <GeomAPI_Edge.h>
50 #include <GeomAPI_Circ.h>
51 #include <GeomAPI_Ellipse.h>
52 #include <GeomAPI_BSpline.h>
54 #include <GeomDataAPI_Point2D.h>
55 #include <GeomDataAPI_Point2DArray.h>
59 SketchPlugin_Offset::SketchPlugin_Offset()
60 : SketchPlugin_SketchEntity()
64 void SketchPlugin_Offset::initDerivedClassAttributes()
66 data()->addAttribute(EDGES_ID(), ModelAPI_AttributeRefList::typeId());
67 data()->addAttribute(VALUE_ID(), ModelAPI_AttributeDouble::typeId());
68 data()->addAttribute(REVERSED_ID(), ModelAPI_AttributeBoolean::typeId());
71 void SketchPlugin_Offset::execute()
73 ModelAPI_Tools::removeFeaturesAndReferences(myCreatedFeatures);
74 myCreatedFeatures.clear();
76 SketchPlugin_Sketch* aSketch = sketch();
80 std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
83 AttributeDoublePtr aValueAttr = real(VALUE_ID());
84 if (!aValueAttr->isInitialized()) return;
85 double aValue = aValueAttr->value();
86 const double tolerance = 1.e-7;
87 if (aValue < tolerance) return;
90 AttributeBooleanPtr aReversedAttr = boolean(REVERSED_ID());
91 if (!aReversedAttr->isInitialized()) return;
92 if (aReversedAttr->value()) aValue = -aValue; // reverse offset direction
94 // 3. List of all selected edges
95 AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
96 std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
98 // 4. Put all selected edges in a set to pass them into findWireOneWay() below
99 std::set<FeaturePtr> anEdgesSet;
100 std::list<ObjectPtr>::const_iterator anEdgesIt = anEdgesList.begin();
101 for (; anEdgesIt != anEdgesList.end(); anEdgesIt++) {
102 FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
104 anEdgesSet.insert(aFeature);
108 // 5. Gather wires and make offset for each wire
109 for (anEdgesIt = anEdgesList.begin(); anEdgesIt != anEdgesList.end(); anEdgesIt++) {
110 FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
111 if (aFeature.get()) {
112 if (anEdgesSet.find(aFeature) == anEdgesSet.end())
115 // 5.a. End points (if any)
116 std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
117 SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
119 // 5.b. Find a chain of edges
120 std::list<FeaturePtr> aChain;
121 aChain.push_back(aFeature);
122 if (aStartPoint && anEndPoint) { // not closed edge
123 bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet, aChain, true);
125 findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aChain, false);
127 std::set<FeaturePtr>::iterator aPos = anEdgesSet.find(aFeature);
128 if (aPos != anEdgesSet.end())
129 anEdgesSet.erase(aPos);
132 ListOfShape aTopoChain;
133 std::list<FeaturePtr>::iterator aChainIt = aChain.begin();
134 for (; aChainIt != aChain.end(); ++aChainIt) {
135 FeaturePtr aChainFeature = (*aChainIt);
136 GeomShapePtr aTopoEdge = aChainFeature->lastResult()->shape();
137 if (aTopoEdge->shapeType() == GeomAPI_Shape::EDGE) {
138 aTopoChain.push_back(aTopoEdge);
141 GeomShapePtr anEdgeOrWire = GeomAlgoAPI_WireBuilder::wire(aTopoChain);
143 // 5.d. Make offset for each wire
144 std::shared_ptr<GeomAPI_Shape> anOffsetShape =
145 GeomAlgoAPI_Offset::OffsetInPlane(aPlane, anEdgeOrWire, aValue);
147 // 5.e. Store offset results.
148 // Create sketch feature for each edge of anOffsetShape, and also store
149 // created features in myCreatedFeatures to remove them on next execute()
150 addToSketch(anOffsetShape);
155 bool SketchPlugin_Offset::findWireOneWay (const FeaturePtr& theFirstEdge,
156 const FeaturePtr& theEdge,
157 const std::shared_ptr<GeomDataAPI_Point2D>& theEndPoint,
158 std::set<FeaturePtr>& theEdgesSet,
159 std::list<FeaturePtr>& theChain,
160 const bool isPrepend)
162 // 1. Find a single edge, coincident to theEndPoint by one of its ends
163 if (!theEndPoint) return false;
165 std::shared_ptr<GeomAPI_Pnt2d> aP2d = theEndPoint->pnt();
167 FeaturePtr aNextEdgeFeature;
170 std::set<AttributePoint2DPtr> aCoincPoints;
171 std::map<AttributePoint2DArrayPtr, int> aCoincPointsInArray;
172 SketchPlugin_Tools::findPointsCoincidentToPoint(theEndPoint, aCoincPoints, aCoincPointsInArray);
174 // store all found attributes to a single array
175 std::set<AttributePtr> anAllCoincPoints;
176 anAllCoincPoints.insert(aCoincPoints.begin(), aCoincPoints.end());
177 for (auto it = aCoincPointsInArray.begin(); it != aCoincPointsInArray.end(); ++it)
178 anAllCoincPoints.insert(it->first);
180 std::set<AttributePtr>::iterator aPointsIt = anAllCoincPoints.begin();
181 for (; aPointsIt != anAllCoincPoints.end(); aPointsIt++) {
182 AttributePtr aP = (*aPointsIt);
183 FeaturePtr aCoincFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aP->owner());
185 // Condition 0: not auxiliary
186 if (aCoincFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) continue;
188 // Condition 1: not a point feature
189 if (aCoincFeature->getKind() != SketchPlugin_Point::ID()) {
190 // Condition 2: it is not the current edge
191 if (aCoincFeature != theEdge) {
192 // Condition 3: it is in the set of interest.
193 // Empty set means all sketch edges.
195 if (theEdgesSet.size()) {
196 isInSet = (theEdgesSet.find(aCoincFeature) != theEdgesSet.end());
199 // Condition 4: consider only features with two end points
200 std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
201 SketchPlugin_SegmentationTools::getFeaturePoints(aCoincFeature, aP1, aP2);
203 // Condition 5: consider only features, that have aP as one of they ends.
204 // For example, we do not need an arc, coincident to aP by its center.
205 if (theEndPoint->pnt()->isEqual(aP1->pnt()) ||
206 theEndPoint->pnt()->isEqual(aP2->pnt())) {
207 // Condition 6: only one edge can prolongate the chain. If several, we stop here.
213 aNextEdgeFeature = aCoincFeature;
221 // Only one edge can prolongate the chain. If several or none, we stop here.
225 // 2. So, we have the single edge, that prolongate the chain
227 // Condition 7: if we reached the very first edge of the chain
228 if (aNextEdgeFeature == theFirstEdge)
229 // Closed chain found
232 // 3. Add the found edge to the chain
234 theChain.push_front(aNextEdgeFeature);
236 theChain.push_back(aNextEdgeFeature);
237 // remove from the set, if the set is used
238 if (theEdgesSet.size()) {
239 std::set<FeaturePtr>::iterator aPos = theEdgesSet.find(aNextEdgeFeature);
240 if (aPos != theEdgesSet.end())
241 theEdgesSet.erase(aPos);
244 // 4. Which end of aNextEdgeFeature we need to proceed
245 std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
246 SketchPlugin_SegmentationTools::getFeaturePoints(aNextEdgeFeature, aP1, aP2);
247 if (aP2->pnt()->isEqual(theEndPoint->pnt())) {
252 // 5. Continue gathering the chain (recursive)
253 return findWireOneWay (theFirstEdge, aNextEdgeFeature, aP2, theEdgesSet, theChain, isPrepend);
256 void SketchPlugin_Offset::addToSketch(const std::shared_ptr<GeomAPI_Shape>& anOffsetShape)
258 //GeomAPI_ShapeExplorer::GeomAPI_ShapeExplorer
259 ListOfShape aResEdges = GeomAlgoAPI_ShapeTools::getLowLevelSubShapes(anOffsetShape);
260 std::list<GeomShapePtr>::const_iterator aResEdgesIt = aResEdges.begin();
261 for (; aResEdgesIt != aResEdges.end(); aResEdgesIt++) {
262 GeomShapePtr aResShape = (*aResEdgesIt);
263 if (aResShape->shapeType() == GeomAPI_Shape::EDGE) {
265 FeaturePtr aResFeature;
266 std::shared_ptr<GeomAPI_Edge> aResEdge (new GeomAPI_Edge(aResShape));
268 std::shared_ptr<GeomAPI_Pnt2d> aFP, aLP;
269 std::shared_ptr<GeomAPI_Pnt> aFP3d = aResEdge->firstPoint();
270 std::shared_ptr<GeomAPI_Pnt> aLP3d = aResEdge->lastPoint();
271 //if (aFP3d.get() && aLP3d.get()) {
272 if (aFP3d && aLP3d) {
273 aFP = sketch()->to2D(aFP3d);
274 aLP = sketch()->to2D(aLP3d);
277 if (aResEdge->isLine()) {
278 aResFeature = sketch()->addFeature(SketchPlugin_Line::ID());
280 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
281 (aResFeature->attribute(SketchPlugin_Line::START_ID()))->setValue(aFP);
282 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
283 (aResFeature->attribute(SketchPlugin_Line::END_ID()))->setValue(aLP);
285 else if (aResEdge->isArc()) {
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_Arc::ID());
292 bool aWasBlocked = aResFeature->data()->blockSendAttributeUpdated(true);
293 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
294 (aResFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(aCP);
295 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
296 (aResFeature->attribute(SketchPlugin_Arc::START_ID()))->setValue(aFP);
297 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
298 (aResFeature->attribute(SketchPlugin_Arc::END_ID()))->setValue(aLP);
299 aResFeature->data()->blockSendAttributeUpdated(aWasBlocked);
301 else if (aResEdge->isCircle()) {
302 std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
303 std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
304 std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
306 aResFeature = sketch()->addFeature(SketchPlugin_Circle::ID());
307 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
308 (aResFeature->attribute(SketchPlugin_Circle::CENTER_ID()))->setValue(aCP);
309 aResFeature->real(SketchPlugin_Circle::RADIUS_ID())->setValue(aCircEdge->radius());
311 else if (aResEdge->isEllipse()) {
312 std::shared_ptr<GeomAPI_Ellipse> anEllipseEdge = aResEdge->ellipse();
314 GeomPointPtr aCP3d = anEllipseEdge->center();
315 GeomPnt2dPtr aCP = sketch()->to2D(aCP3d);
317 GeomPointPtr aFocus3d = anEllipseEdge->firstFocus();
318 GeomPnt2dPtr aFocus = sketch()->to2D(aFocus3d);
320 if (aFP3d && aLP3d) {
322 aResFeature = sketch()->addFeature(SketchPlugin_EllipticArc::ID());
324 bool aWasBlocked = aResFeature->data()->blockSendAttributeUpdated(true);
325 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
326 (aResFeature->attribute(SketchPlugin_EllipticArc::CENTER_ID()))->setValue(aCP);
327 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
328 (aResFeature->attribute(SketchPlugin_EllipticArc::FIRST_FOCUS_ID()))->setValue(aFocus);
329 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
330 (aResFeature->attribute(SketchPlugin_EllipticArc::START_POINT_ID()))->setValue(aFP);
331 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
332 (aResFeature->attribute(SketchPlugin_EllipticArc::END_POINT_ID()))->setValue(aLP);
333 aResFeature->data()->blockSendAttributeUpdated(aWasBlocked);
337 aResFeature = sketch()->addFeature(SketchPlugin_Ellipse::ID());
339 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
340 (aResFeature->attribute(SketchPlugin_Ellipse::CENTER_ID()))->setValue(aCP);
341 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
342 (aResFeature->attribute(SketchPlugin_Ellipse::FIRST_FOCUS_ID()))->setValue(aFocus);
343 aResFeature->real(SketchPlugin_Ellipse::MINOR_RADIUS_ID())->setValue(anEllipseEdge->minorRadius());
346 else if (aResEdge->isBSpline()) {
347 mkBSpline(aResFeature, aResEdge);
350 // convert to b-spline
351 mkBSpline(aResFeature, aResEdge);
354 if (aResFeature.get()) {
355 myCreatedFeatures.insert(aResFeature);
357 aResFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue
358 (boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value());
359 aResFeature->execute();
365 void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult,
366 const GeomEdgePtr& theEdge)
368 GeomCurvePtr aCurve (new GeomAPI_Curve (theEdge));
369 // Forced conversion to b-spline, if aCurve is not b-spline
370 GeomAPI_BSpline aBSpline (aCurve, /*isForced*/true);
372 if (aBSpline.isPeriodic())
373 theResult = sketch()->addFeature(SketchPlugin_BSplinePeriodic::ID());
375 theResult = sketch()->addFeature(SketchPlugin_BSpline::ID());
377 theResult->integer(SketchPlugin_BSpline::DEGREE_ID())->setValue(aBSpline.degree());
379 AttributePoint2DArrayPtr aPolesAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>
380 (theResult->attribute(SketchPlugin_BSpline::POLES_ID()));
381 std::list<GeomPointPtr> aPoles = aBSpline.poles();
382 aPolesAttr->setSize((int)aPoles.size());
383 std::list<GeomPointPtr>::iterator anIt = aPoles.begin();
384 for (int anIndex = 0; anIt != aPoles.end(); ++anIt, ++anIndex) {
385 GeomPnt2dPtr aPoleInSketch = sketch()->to2D(*anIt);
386 aPolesAttr->setPnt(anIndex, aPoleInSketch);
389 AttributeDoubleArrayPtr aWeightsAttr =
390 theResult->data()->realArray(SketchPlugin_BSpline::WEIGHTS_ID());
391 std::list<double> aWeights = aBSpline.weights();
392 if (aWeights.empty()) { // rational B-spline
393 int aSize = (int)aPoles.size();
394 aWeightsAttr->setSize(aSize);
395 for (int anIndex = 0; anIndex < aSize; ++anIndex)
396 aWeightsAttr->setValue(anIndex, 1.0);
398 else { // non-rational B-spline
399 aWeightsAttr->setSize((int)aWeights.size());
400 std::list<double>::iterator anIt = aWeights.begin();
401 for (int anIndex = 0; anIt != aWeights.end(); ++anIt, ++anIndex)
402 aWeightsAttr->setValue(anIndex, *anIt);
405 AttributeDoubleArrayPtr aKnotsAttr =
406 theResult->data()->realArray(SketchPlugin_BSpline::KNOTS_ID());
407 std::list<double> aKnots = aBSpline.knots();
408 int aSize = (int)aKnots.size();
409 aKnotsAttr->setSize(aSize);
410 std::list<double>::iterator aKIt = aKnots.begin();
411 for (int index = 0; index < aSize; ++index, ++aKIt)
412 aKnotsAttr->setValue(index, *aKIt);
414 AttributeIntArrayPtr aMultsAttr =
415 theResult->data()->intArray(SketchPlugin_BSpline::MULTS_ID());
416 std::list<int> aMultiplicities = aBSpline.mults();
417 aSize = (int)aMultiplicities.size();
418 aMultsAttr->setSize(aSize);
419 std::list<int>::iterator aMIt = aMultiplicities.begin();
420 for (int index = 0; index < aSize; ++index, ++aMIt)
421 aMultsAttr->setValue(index, *aMIt);
424 void SketchPlugin_Offset::attributeChanged(const std::string& theID)
426 ModelAPI_Tools::removeFeaturesAndReferences(myCreatedFeatures);
427 myCreatedFeatures.clear();
430 bool SketchPlugin_Offset::customAction(const std::string& theActionId)
433 if (theActionId == ADD_WIRE_ACTION_ID()) {
437 std::string aMsg = "Error: Feature \"%1\" does not support action \"%2\".";
438 Events_InfoMessage("SketchPlugin_Offset", aMsg).arg(getKind()).arg(theActionId).send();
443 bool SketchPlugin_Offset::findWires()
445 AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
446 std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
449 std::set<FeaturePtr> anEdgesSet;
452 std::set<FeaturePtr> aProcessedSet;
454 // Put all selected edges in a set to avoid adding them in reflist(EDGES_ID())
455 std::set<FeaturePtr> aSelectedSet;
456 std::list<ObjectPtr>::const_iterator anEdgesIt = anEdgesList.begin();
457 for (; anEdgesIt != anEdgesList.end(); anEdgesIt++) {
458 FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
460 aSelectedSet.insert(aFeature);
464 // Gather chains of edges
465 for (anEdgesIt = anEdgesList.begin(); anEdgesIt != anEdgesList.end(); anEdgesIt++) {
466 FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
467 if (aFeature.get()) {
468 if (aProcessedSet.find(aFeature) != aProcessedSet.end())
470 aProcessedSet.insert(aFeature);
472 // End points (if any)
473 std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
474 SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
476 std::list<FeaturePtr> aChain;
477 aChain.push_back(aFeature);
478 bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet, aChain, true);
480 findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aChain, false);
482 std::list<FeaturePtr>::iterator aChainIt = aChain.begin();
483 for (; aChainIt != aChain.end(); ++aChainIt) {
484 FeaturePtr aChainFeature = (*aChainIt);
485 aProcessedSet.insert(aChainFeature);
486 if (aSelectedSet.find(aChainFeature) == aSelectedSet.end()) {
487 aSelectedEdges->append(aChainFeature->lastResult());
492 // TODO: hilight selection in the viewer
498 AISObjectPtr SketchPlugin_Offset::getAISObject(AISObjectPtr thePrevious)
503 AISObjectPtr anAIS = SketcherPrs_Factory::offsetObject(this, sketch(),