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);
125 findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aChain);
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)
161 // 1. Find a single edge, coincident to theEndPoint by one of its ends
162 if (!theEndPoint) return false;
164 std::shared_ptr<GeomAPI_Pnt2d> aP2d = theEndPoint->pnt();
166 FeaturePtr aNextEdgeFeature;
169 std::set<AttributePoint2DPtr> aCoincPoints;
170 std::map<AttributePoint2DArrayPtr, int> aCoincPointsInArray;
171 SketchPlugin_Tools::findPointsCoincidentToPoint(theEndPoint, aCoincPoints, aCoincPointsInArray);
173 // store all found attributes to a single array
174 std::set<AttributePtr> anAllCoincPoints;
175 anAllCoincPoints.insert(aCoincPoints.begin(), aCoincPoints.end());
176 for (auto it = aCoincPointsInArray.begin(); it != aCoincPointsInArray.end(); ++it)
177 anAllCoincPoints.insert(it->first);
179 std::set<AttributePtr>::iterator aPointsIt = anAllCoincPoints.begin();
180 for (; aPointsIt != anAllCoincPoints.end(); aPointsIt++) {
181 AttributePtr aP = (*aPointsIt);
182 FeaturePtr aCoincFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aP->owner());
184 // Condition 1: not a point feature
185 if (aCoincFeature->getKind() != SketchPlugin_Point::ID()) {
186 // Condition 2: it is not the current edge
187 if (aCoincFeature != theEdge) {
188 // Condition 3: it is in the set of interest.
189 // Empty set means all sketch edges.
191 if (theEdgesSet.size()) {
192 isInSet = (theEdgesSet.find(aCoincFeature) != theEdgesSet.end());
195 // Condition 4: consider only features with two end points
196 std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
197 SketchPlugin_SegmentationTools::getFeaturePoints(aCoincFeature, aP1, aP2);
199 // Condition 5: consider only features, that have aP as one of they ends.
200 // For example, we do not need an arc, coincident to aP by its center.
201 if (theEndPoint->pnt()->isEqual(aP1->pnt()) ||
202 theEndPoint->pnt()->isEqual(aP2->pnt())) {
203 // Condition 6: only one edge can prolongate the chain. If several, we stop here.
209 aNextEdgeFeature = aCoincFeature;
217 // Only one edge can prolongate the chain. If several or none, we stop here.
221 // 2. So, we have the single edge, that prolongate the chain
223 // Condition 7: if we reached the very first edge of the chain
224 if (aNextEdgeFeature == theFirstEdge)
225 // Closed chain found
228 // 3. Add the found edge to the chain
229 theChain.push_back(aNextEdgeFeature);
230 // remove from the set, if the set is used
231 if (theEdgesSet.size()) {
232 std::set<FeaturePtr>::iterator aPos = theEdgesSet.find(aNextEdgeFeature);
233 if (aPos != theEdgesSet.end())
234 theEdgesSet.erase(aPos);
237 // 4. Which end of aNextEdgeFeature we need to proceed
238 std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
239 SketchPlugin_SegmentationTools::getFeaturePoints(aNextEdgeFeature, aP1, aP2);
240 if (aP2->pnt()->isEqual(theEndPoint->pnt())) {
245 // 5. Continue gathering the chain (recursive)
246 return findWireOneWay (theFirstEdge, aNextEdgeFeature, aP2, theEdgesSet, theChain);
249 void SketchPlugin_Offset::addToSketch(const std::shared_ptr<GeomAPI_Shape>& anOffsetShape)
251 //GeomAPI_ShapeExplorer::GeomAPI_ShapeExplorer
252 ListOfShape aResEdges = GeomAlgoAPI_ShapeTools::getLowLevelSubShapes(anOffsetShape);
253 std::list<GeomShapePtr>::const_iterator aResEdgesIt = aResEdges.begin();
254 for (; aResEdgesIt != aResEdges.end(); aResEdgesIt++) {
255 GeomShapePtr aResShape = (*aResEdgesIt);
256 if (aResShape->shapeType() == GeomAPI_Shape::EDGE) {
258 FeaturePtr aResFeature;
259 std::shared_ptr<GeomAPI_Edge> aResEdge (new GeomAPI_Edge(aResShape));
261 std::shared_ptr<GeomAPI_Pnt2d> aFP, aLP;
262 std::shared_ptr<GeomAPI_Pnt> aFP3d = aResEdge->firstPoint();
263 std::shared_ptr<GeomAPI_Pnt> aLP3d = aResEdge->lastPoint();
264 //if (aFP3d.get() && aLP3d.get()) {
265 if (aFP3d && aLP3d) {
266 aFP = sketch()->to2D(aFP3d);
267 aLP = sketch()->to2D(aLP3d);
270 if (aResEdge->isLine()) {
271 aResFeature = sketch()->addFeature(SketchPlugin_Line::ID());
273 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
274 (aResFeature->attribute(SketchPlugin_Line::START_ID()))->setValue(aFP);
275 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
276 (aResFeature->attribute(SketchPlugin_Line::END_ID()))->setValue(aLP);
278 else if (aResEdge->isArc()) {
279 std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
280 std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
281 std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
283 aResFeature = sketch()->addFeature(SketchPlugin_Arc::ID());
285 bool aWasBlocked = aResFeature->data()->blockSendAttributeUpdated(true);
286 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
287 (aResFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(aCP);
288 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
289 (aResFeature->attribute(SketchPlugin_Arc::START_ID()))->setValue(aFP);
290 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
291 (aResFeature->attribute(SketchPlugin_Arc::END_ID()))->setValue(aLP);
292 aResFeature->data()->blockSendAttributeUpdated(aWasBlocked);
294 else if (aResEdge->isCircle()) {
295 std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
296 std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
297 std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
299 aResFeature = sketch()->addFeature(SketchPlugin_Circle::ID());
300 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
301 (aResFeature->attribute(SketchPlugin_Circle::CENTER_ID()))->setValue(aCP);
302 aResFeature->real(SketchPlugin_Circle::RADIUS_ID())->setValue(aCircEdge->radius());
304 else if (aResEdge->isEllipse()) {
305 std::shared_ptr<GeomAPI_Ellipse> anEllipseEdge = aResEdge->ellipse();
307 GeomPointPtr aCP3d = anEllipseEdge->center();
308 GeomPnt2dPtr aCP = sketch()->to2D(aCP3d);
310 GeomPointPtr aFocus3d = anEllipseEdge->firstFocus();
311 GeomPnt2dPtr aFocus = sketch()->to2D(aFocus3d);
313 if (aFP3d && aLP3d) {
315 aResFeature = sketch()->addFeature(SketchPlugin_EllipticArc::ID());
317 bool aWasBlocked = aResFeature->data()->blockSendAttributeUpdated(true);
318 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
319 (aResFeature->attribute(SketchPlugin_EllipticArc::CENTER_ID()))->setValue(aCP);
320 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
321 (aResFeature->attribute(SketchPlugin_EllipticArc::FIRST_FOCUS_ID()))->setValue(aFocus);
322 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
323 (aResFeature->attribute(SketchPlugin_EllipticArc::START_POINT_ID()))->setValue(aFP);
324 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
325 (aResFeature->attribute(SketchPlugin_EllipticArc::END_POINT_ID()))->setValue(aLP);
326 aResFeature->data()->blockSendAttributeUpdated(aWasBlocked);
330 aResFeature = sketch()->addFeature(SketchPlugin_Ellipse::ID());
332 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
333 (aResFeature->attribute(SketchPlugin_Ellipse::CENTER_ID()))->setValue(aCP);
334 std::dynamic_pointer_cast<GeomDataAPI_Point2D>
335 (aResFeature->attribute(SketchPlugin_Ellipse::FIRST_FOCUS_ID()))->setValue(aFocus);
336 aResFeature->real(SketchPlugin_Ellipse::MINOR_RADIUS_ID())->setValue(anEllipseEdge->minorRadius());
339 else if (aResEdge->isBSpline()) {
340 mkBSpline(aResFeature, aResEdge);
345 if (aResFeature.get()) {
346 myCreatedFeatures.insert(aResFeature);
348 aResFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue
349 (boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value());
350 aResFeature->execute();
356 void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult,
357 const GeomEdgePtr& theEdge)
359 if (!theEdge->isBSpline())
362 GeomCurvePtr aCurve (new GeomAPI_Curve (theEdge));
363 GeomAPI_BSpline aBSpline (aCurve);
365 if (aBSpline.isPeriodic())
366 theResult = sketch()->addFeature(SketchPlugin_BSplinePeriodic::ID());
368 theResult = sketch()->addFeature(SketchPlugin_BSpline::ID());
370 theResult->integer(SketchPlugin_BSpline::DEGREE_ID())->setValue(aBSpline.degree());
372 AttributePoint2DArrayPtr aPolesAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>
373 (theResult->attribute(SketchPlugin_BSpline::POLES_ID()));
374 std::list<GeomPointPtr> aPoles = aBSpline.poles();
375 aPolesAttr->setSize((int)aPoles.size());
376 std::list<GeomPointPtr>::iterator anIt = aPoles.begin();
377 for (int anIndex = 0; anIt != aPoles.end(); ++anIt, ++anIndex) {
378 GeomPnt2dPtr aPoleInSketch = sketch()->to2D(*anIt);
379 aPolesAttr->setPnt(anIndex, aPoleInSketch);
382 AttributeDoubleArrayPtr aWeightsAttr =
383 theResult->data()->realArray(SketchPlugin_BSpline::WEIGHTS_ID());
384 std::list<double> aWeights = aBSpline.weights();
385 if (aWeights.empty()) { // rational B-spline
386 int aSize = (int)aPoles.size();
387 aWeightsAttr->setSize(aSize);
388 for (int anIndex = 0; anIndex < aSize; ++anIndex)
389 aWeightsAttr->setValue(anIndex, 1.0);
391 else { // non-rational B-spline
392 aWeightsAttr->setSize((int)aWeights.size());
393 std::list<double>::iterator anIt = aWeights.begin();
394 for (int anIndex = 0; anIt != aWeights.end(); ++anIt, ++anIndex)
395 aWeightsAttr->setValue(anIndex, *anIt);
398 AttributeDoubleArrayPtr aKnotsAttr =
399 theResult->data()->realArray(SketchPlugin_BSpline::KNOTS_ID());
400 std::list<double> aKnots = aBSpline.knots();
401 int aSize = (int)aKnots.size();
402 aKnotsAttr->setSize(aSize);
403 std::list<double>::iterator aKIt = aKnots.begin();
404 for (int index = 0; index < aSize; ++index, ++aKIt)
405 aKnotsAttr->setValue(index, *aKIt);
407 AttributeIntArrayPtr aMultsAttr =
408 theResult->data()->intArray(SketchPlugin_BSpline::MULTS_ID());
409 std::list<int> aMultiplicities = aBSpline.mults();
410 aSize = (int)aMultiplicities.size();
411 aMultsAttr->setSize(aSize);
412 std::list<int>::iterator aMIt = aMultiplicities.begin();
413 for (int index = 0; index < aSize; ++index, ++aMIt)
414 aMultsAttr->setValue(index, *aMIt);
417 void SketchPlugin_Offset::attributeChanged(const std::string& theID)
419 ModelAPI_Tools::removeFeaturesAndReferences(myCreatedFeatures);
420 myCreatedFeatures.clear();
423 bool SketchPlugin_Offset::customAction(const std::string& theActionId)
426 if (theActionId == ADD_WIRE_ACTION_ID()) {
430 std::string aMsg = "Error: Feature \"%1\" does not support action \"%2\".";
431 Events_InfoMessage("SketchPlugin_Offset", aMsg).arg(getKind()).arg(theActionId).send();
436 bool SketchPlugin_Offset::findWires()
438 AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
439 std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
442 std::set<FeaturePtr> anEdgesSet;
445 std::set<FeaturePtr> aProcessedSet;
447 // Put all selected edges in a set to avoid adding them in reflist(EDGES_ID())
448 std::set<FeaturePtr> aSelectedSet;
449 std::list<ObjectPtr>::const_iterator anEdgesIt = anEdgesList.begin();
450 for (; anEdgesIt != anEdgesList.end(); anEdgesIt++) {
451 FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
453 aSelectedSet.insert(aFeature);
457 // Gather chains of edges
458 for (anEdgesIt = anEdgesList.begin(); anEdgesIt != anEdgesList.end(); anEdgesIt++) {
459 FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
460 if (aFeature.get()) {
461 if (aProcessedSet.find(aFeature) != aProcessedSet.end())
463 aProcessedSet.insert(aFeature);
465 // End points (if any)
466 std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
467 SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
469 std::list<FeaturePtr> aChain;
470 aChain.push_back(aFeature);
471 bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet, aChain);
473 findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aChain);
475 std::list<FeaturePtr>::iterator aChainIt = aChain.begin();
476 for (; aChainIt != aChain.end(); ++aChainIt) {
477 FeaturePtr aChainFeature = (*aChainIt);
478 aProcessedSet.insert(aChainFeature);
479 if (aSelectedSet.find(aChainFeature) == aSelectedSet.end()) {
480 aSelectedEdges->append(aChainFeature->lastResult());
485 // TODO: hilight selection in the viewer
491 AISObjectPtr SketchPlugin_Offset::getAISObject(AISObjectPtr thePrevious)
496 AISObjectPtr anAIS = SketcherPrs_Factory::offsetObject(this, sketch(),