1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
3 // File: SketchPlugin_ConstraintFillet.cpp
4 // Created: 19 Mar 2015
5 // Author: Artem ZHIDKOV
7 #include "SketchPlugin_ConstraintFillet.h"
9 #include <GeomAPI_Circ2d.h>
10 #include <GeomAPI_Dir2d.h>
11 #include <GeomAPI_Lin2d.h>
12 #include <GeomAPI_Pnt2d.h>
13 #include <GeomAPI_XY.h>
14 #include <GeomDataAPI_Point2D.h>
15 #include <ModelAPI_AttributeDouble.h>
16 #include <ModelAPI_AttributeRefList.h>
17 #include <ModelAPI_AttributeRefAttrList.h>
18 #include <ModelAPI_Data.h>
19 #include <ModelAPI_Events.h>
20 #include <ModelAPI_Session.h>
21 #include <ModelAPI_Validator.h>
23 #include <SketchPlugin_Arc.h>
24 #include <SketchPlugin_Line.h>
25 #include <SketchPlugin_Point.h>
26 #include <SketchPlugin_Sketch.h>
27 #include <SketchPlugin_ConstraintCoincidence.h>
28 #include <SketchPlugin_ConstraintTangent.h>
29 #include <SketchPlugin_ConstraintRadius.h>
30 #include <SketchPlugin_Tools.h>
32 #include <Events_Loop.h>
36 const double tolerance = 1.e-7;
37 const double paramTolerance = 1.e-4;
39 /// \brief Attract specified point on theNewArc to the attribute of theFeature
40 static void recalculateAttributes(FeaturePtr theNewArc, const std::string& theNewArcAttribute,
41 FeaturePtr theFeature, const std::string& theFeatureAttribute);
43 /// \brief Calculates center of fillet arc and coordinates of tangency points
44 static void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB,
45 double theRadius, bool theNotInversed[2],
46 std::shared_ptr<GeomAPI_XY>& theCenter,
47 std::shared_ptr<GeomAPI_XY>& theTangentA,
48 std::shared_ptr<GeomAPI_XY>& theTangentB);
50 /// Get point on 1/3 length of edge from fillet point
51 static void getPointOnEdge(const FeaturePtr theFeature,
52 const std::shared_ptr<GeomAPI_Pnt2d> theFilletPoint,
53 std::shared_ptr<GeomAPI_Pnt2d>& thePoint);
55 /// Get distance from point to feature
56 static double getProjectionDistance(const FeaturePtr theFeature,
57 const std::shared_ptr<GeomAPI_Pnt2d> thePoint);
59 /// Get coincide edges for fillet
60 static std::set<FeaturePtr> getCoincides(const FeaturePtr& theConstraintCoincidence);
62 SketchPlugin_ConstraintFillet::SketchPlugin_ConstraintFillet()
63 : myListOfPointsChangedInCode(false),
64 myRadiusChangedByUser(false),
65 myRadiusChangedInCode(true)
69 void SketchPlugin_ConstraintFillet::initAttributes()
71 data()->addAttribute(SketchPlugin_Constraint::VALUE(), ModelAPI_AttributeDouble::typeId());
72 data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttrList::typeId());
75 void SketchPlugin_ConstraintFillet::execute()
77 std::shared_ptr<ModelAPI_Data> aData = data();
79 // Check the base objects are initialized.
80 AttributeRefAttrListPtr aPointsRefList = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttrList>(
81 aData->attribute(SketchPlugin_Constraint::ENTITY_A()));
82 if(!aPointsRefList->isInitialized()) {
83 setError("Error: List of points is not initialized.");
88 double aFilletRadius = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
89 aData->attribute(SketchPlugin_Constraint::VALUE()))->value();
91 // Check the fillet result edges is not initialized yet.
92 bool anIsNeedNewObjects = myResultEdges.size() == 0;
94 // Wait all constraints being created, then send update events
95 static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
96 bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
98 Events_Loop::loop()->setFlushed(anUpdateEvent, false);
100 std::list<FeaturePtr>::iterator aResultEdgesIt = myResultEdges.begin();
101 for(int anIndex = 0; anIndex < aPointsRefList->size(); ++anIndex) {
102 std::shared_ptr<GeomDataAPI_Point2D> aFilletPoint2d =
103 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aPointsRefList->attribute(anIndex));
104 if(!aFilletPoint2d.get()) {
105 setError("Error: One of the selected points is empty.");
108 std::shared_ptr<GeomAPI_Pnt2d> aFilletPnt2d = aFilletPoint2d->pnt();
110 // Obtain base lines for fillet.
111 FeaturePtr aBaseEdgeA, aBaseEdgeB;
112 if(myBaseEdges.size() > (unsigned int)(anIndex * 2)) {
113 std::list<FeaturePtr>::iterator anIter = myBaseEdges.begin();
114 std::advance(anIter, anIndex * 2);
115 aBaseEdgeA = *anIter++;
116 aBaseEdgeB = *anIter;
118 // Obtain constraint coincidence for the fillet point.
119 FeaturePtr aConstraintCoincidence;
120 const std::set<AttributePtr>& aRefsList = aFilletPoint2d->owner()->data()->refsToMe();
121 for(std::set<AttributePtr>::const_iterator anIt = aRefsList.cbegin(); anIt != aRefsList.cend(); ++anIt) {
122 std::shared_ptr<ModelAPI_Attribute> anAttr = (*anIt);
123 FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->owner());
124 if(aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
125 AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
126 aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_A()));
127 AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
128 aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_B()));
129 if(anAttrRefA.get() && !anAttrRefA->isObject()) {
130 AttributePtr anAttrA = anAttrRefA->attr();
131 if(aFilletPoint2d == anAttrA) {
132 aConstraintCoincidence = aConstrFeature;
136 if(anAttrRefB.get() && !anAttrRefB->isObject()) {
137 AttributePtr anAttrB = anAttrRefB->attr();
138 if(aFilletPoint2d == anAttrB) {
139 aConstraintCoincidence = aConstrFeature;
146 if(!aConstraintCoincidence.get()) {
147 setError("Error: No coincident edges at one of the selected points.");
151 // Get coincide edges.
152 std::set<FeaturePtr> aCoincides = getCoincides(aConstraintCoincidence);
153 if(aCoincides.size() != 2) {
154 setError("Error: One of the selected points does not have two suitable edges for fillet.");
158 std::set<FeaturePtr>::iterator aLinesIt = aCoincides.begin();
159 aBaseEdgeA = *aLinesIt++;
160 aBaseEdgeB = *aLinesIt;
163 if(!aBaseEdgeA.get() || !aBaseEdgeB.get()) {
164 setError("Error: One of the base edges is empty.");
168 // Create new edges and arc if needed.
169 FeaturePtr aResultEdgeA, aResultEdgeB, aResultArc;
170 if(anIsNeedNewObjects) {
171 // Copy edges and create arc.
172 aResultEdgeA = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(aBaseEdgeA, sketch());
173 aResultEdgeB = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(aBaseEdgeB, sketch());
174 aResultArc = sketch()->addFeature(SketchPlugin_Arc::ID());
176 // Obtain features from the list.
177 aResultEdgeA = *aResultEdgesIt++;
178 aResultEdgeB = *aResultEdgesIt++;
179 aResultArc = *aResultEdgesIt++;
182 // Calculate arc attributes
183 static const int aNbFeatures = 2;
184 FeaturePtr aBaseFeatures[aNbFeatures] = {aBaseEdgeA, aBaseEdgeB};
185 FeaturePtr aResultFeatures[aNbFeatures] = {aResultEdgeA, aResultEdgeB};
186 std::shared_ptr<GeomAPI_Dir2d> aTangentDir[aNbFeatures]; // tangent directions of the features in coincident point
187 bool isStart[aNbFeatures]; // indicates which point the features share
188 std::shared_ptr<GeomAPI_Pnt2d> aStartEndPnt[aNbFeatures * 2]; // first pair of points relate to first feature, second pair - to second
189 std::string aFeatAttributes[aNbFeatures * 2]; // attributes of features
190 for (int i = 0; i < aNbFeatures; i++) {
191 std::string aStartAttr, aEndAttr;
192 if (aResultFeatures[i]->getKind() == SketchPlugin_Line::ID()) {
193 aStartAttr = SketchPlugin_Line::START_ID();
194 aEndAttr = SketchPlugin_Line::END_ID();
195 } else if (aResultFeatures[i]->getKind() == SketchPlugin_Arc::ID()) {
196 aStartAttr = SketchPlugin_Arc::START_ID();
197 aEndAttr = SketchPlugin_Arc::END_ID();
198 } else { // wrong argument
199 myResultEdges.clear();
200 setError("Error: One of the points has wrong coincide feature");
203 aFeatAttributes[2*i] = aStartAttr;
204 aStartEndPnt[2*i] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
205 aBaseFeatures[i]->attribute(aStartAttr))->pnt();
206 aFeatAttributes[2*i+1] = aEndAttr;
207 aStartEndPnt[2*i+1] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
208 aBaseFeatures[i]->attribute(aEndAttr))->pnt();
210 for (int aFeatInd = 0; aFeatInd < aNbFeatures; aFeatInd++) {
211 for (int j = 0; j < 2; j++) // loop on start-end of each feature
212 if (aStartEndPnt[aFeatInd * aNbFeatures + j]->distance(aFilletPnt2d) < 1.e-10) {
213 isStart[aFeatInd] = (j==0);
217 // tangent directions of the features
218 for (int i = 0; i < aNbFeatures; i++) {
219 std::shared_ptr<GeomAPI_XY> aDir;
220 if (aResultFeatures[i]->getKind() == SketchPlugin_Line::ID()) {
221 aDir = aStartEndPnt[2*i+1]->xy()->decreased(aStartEndPnt[2*i]->xy());
223 aDir = aDir->multiplied(-1.0);
224 } else if (aResultFeatures[i]->getKind() == SketchPlugin_Arc::ID()) {
225 std::shared_ptr<GeomAPI_Pnt2d> aCenterPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
226 aResultFeatures[i]->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt();
227 aDir = isStart[i] ? aStartEndPnt[2*i]->xy() : aStartEndPnt[2*i+1]->xy();
228 aDir = aDir->decreased(aCenterPoint->xy());
230 double x = aDir->x();
231 double y = aDir->y();
234 if (isStart[i] == std::dynamic_pointer_cast<SketchPlugin_Arc>(aBaseFeatures[i])->isReversed())
235 aDir = aDir->multiplied(-1.0);
237 aTangentDir[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(aDir));
240 // By default, the start point of fillet arc is connected to FeatureA,
241 // and the end point - to FeatureB. But when the angle between TangentDirA and
242 // TangentDirB greater 180 degree, the sequaence of features need to be reversed.
243 double cosBA = aTangentDir[0]->cross(aTangentDir[1]); // cos(B-A), where A and B - angles between corresponding tanget direction and the X axis
244 bool isReversed = cosBA > 0.0;
246 // Calculate fillet arc parameters
247 std::shared_ptr<GeomAPI_XY> aCenter, aTangentPntA, aTangentPntB;
248 calculateFilletCenter(aBaseEdgeA, aBaseEdgeB, aFilletRadius, isStart, aCenter, aTangentPntA, aTangentPntB);
249 if(!aCenter.get() || !aTangentPntA.get() || !aTangentPntB.get()) {
250 setError("Can not create fillet with the specified parameters.");
254 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
255 aResultEdgeA->attribute(aFeatAttributes[isStart[0] ? 0 : 1]))->setValue(aTangentPntA->x(), aTangentPntA->y());
256 aResultEdgeA->execute();
257 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
258 aResultEdgeB->attribute(aFeatAttributes[2 + (isStart[1] ? 0 : 1)]))->setValue(aTangentPntB->x(), aTangentPntB->y());
259 aResultEdgeB->execute();
260 // update fillet arc: make the arc correct for sure, so, it is not needed to process the "attribute updated"
261 // by arc; moreover, it may cause cyclicity in hte mechanism of updater
262 aResultArc->data()->blockSendAttributeUpdated(true);
263 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
264 aResultArc->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(aCenter->x(), aCenter->y());
266 std::shared_ptr<GeomAPI_XY> aTmp = aTangentPntA;
267 aTangentPntA = aTangentPntB;
270 std::shared_ptr<GeomDataAPI_Point2D> aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
271 aResultArc->attribute(SketchPlugin_Arc::START_ID()));
272 std::shared_ptr<GeomDataAPI_Point2D> aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
273 aResultArc->attribute(SketchPlugin_Arc::END_ID()));
274 if(aStartPoint->isInitialized() && aEndPoint->isInitialized() &&
275 (aStartPoint->pnt()->xy()->distance(aTangentPntA) > tolerance ||
276 aEndPoint->pnt()->xy()->distance(aTangentPntB) > tolerance)) {
277 std::dynamic_pointer_cast<SketchPlugin_Arc>(aResultArc)->setReversed(false);
279 aStartPoint->setValue(aTangentPntA->x(), aTangentPntA->y());
280 aEndPoint->setValue(aTangentPntB->x(), aTangentPntB->y());
281 aResultArc->data()->blockSendAttributeUpdated(false);
282 aResultArc->execute();
284 if(anIsNeedNewObjects) {
285 // attach new arc to the list
286 myResultEdges.push_back(aResultEdgeA);
287 myResultEdges.push_back(aResultEdgeB);
288 myResultEdges.push_back(aResultArc);
290 myProducedFeatures.push_back(aResultEdgeA);
291 myProducedFeatures.push_back(aResultEdgeB);
292 myProducedFeatures.push_back(aResultArc);
294 // Create list of additional constraints:
295 // 1. Coincidence of boundary points of features (copied lines/arcs) and fillet arc
297 FeaturePtr aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
298 AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
299 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
300 aRefAttr->setAttr(aResultArc->attribute(SketchPlugin_Arc::START_ID()));
301 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
302 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
303 int aFeatInd = isReversed ? 1 : 0;
304 int anAttrInd = (isReversed ? 2 : 0) + (isStart[isReversed ? 1 : 0] ? 0 : 1);
305 aRefAttr->setAttr(aResultFeatures[aFeatInd]->attribute(aFeatAttributes[anAttrInd]));
306 recalculateAttributes(aResultArc, SketchPlugin_Arc::START_ID(), aResultFeatures[aFeatInd], aFeatAttributes[anAttrInd]);
307 aConstraint->execute();
308 myProducedFeatures.push_back(aConstraint);
309 ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
311 aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
312 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
313 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
314 aRefAttr->setAttr(aResultArc->attribute(SketchPlugin_Arc::END_ID()));
315 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
316 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
317 aFeatInd = isReversed ? 0 : 1;
318 anAttrInd = (isReversed ? 0 : 2) + (isStart[isReversed ? 0 : 1] ? 0 : 1);
319 aRefAttr->setAttr(aResultFeatures[aFeatInd]->attribute(aFeatAttributes[anAttrInd]));
320 recalculateAttributes(aResultArc, SketchPlugin_Arc::END_ID(), aResultFeatures[aFeatInd], aFeatAttributes[anAttrInd]);
321 aConstraint->execute();
322 myProducedFeatures.push_back(aConstraint);
323 ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
324 // 2. Fillet arc radius
325 //aConstraint = sketch()->addFeature(SketchPlugin_ConstraintRadius::ID());
326 //aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
327 // aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
328 //aRefAttr->setObject(aNewArc->lastResult());
329 //std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
330 // aConstraint->attribute(SketchPlugin_Constraint::VALUE()))->setValue(aFilletRadius);
331 //std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
332 // aConstraint->attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT()))->setValue(
333 // isStart[0] ? aStartEndPnt[0] : aStartEndPnt[1]);
334 //aConstraint->execute();
335 //myProducedFeatures.push_back(aConstraint);
336 //ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
337 // 3. Tangency of fillet arc and features
338 for (int i = 0; i < aNbFeatures; i++) {
339 aConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID());
340 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
341 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
342 aRefAttr->setObject(aResultArc->lastResult());
343 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
344 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
345 bool isArc = aResultFeatures[i]->getKind() == SketchPlugin_Arc::ID();
346 aRefAttr->setObject(isArc ? aResultFeatures[i]->lastResult() : aResultFeatures[i]->firstResult());
347 aConstraint->execute();
348 myProducedFeatures.push_back(aConstraint);
349 ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
351 // 4. Coincidence of free boundaries of base and copied features
352 for (int i = 0; i < aNbFeatures; i++) {
353 anAttrInd = 2*i + (isStart[i] ? 1 : 0);
354 aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
355 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
356 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
357 aRefAttr->setAttr(aBaseFeatures[i]->attribute(aFeatAttributes[anAttrInd]));
358 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
359 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
360 aRefAttr->setAttr(aResultFeatures[i]->attribute(aFeatAttributes[anAttrInd]));
361 myProducedFeatures.push_back(aConstraint);
363 // 4.1. Additional tangency constraints when the fillet is based on arcs.
364 // It is used to verify the created arc will be placed on a source.
365 for (int i = 0; i < aNbFeatures; ++i) {
366 if (aResultFeatures[i]->getKind() != SketchPlugin_Arc::ID())
368 aConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID());
369 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
370 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
371 aRefAttr->setObject(aBaseFeatures[i]->lastResult());
372 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
373 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
374 aRefAttr->setObject(aResultFeatures[i]->lastResult());
375 aConstraint->execute();
376 myProducedFeatures.push_back(aConstraint);
377 ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
379 // 5. Tangent points should be placed on the base features
380 for (int i = 0; i < aNbFeatures; i++) {
381 anAttrInd = 2*i + (isStart[i] ? 0 : 1);
382 aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
383 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
384 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
385 aRefAttr->setAttr(aResultFeatures[i]->attribute(aFeatAttributes[anAttrInd]));
386 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
387 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
388 aRefAttr->setObject(aBaseFeatures[i]->lastResult());
389 myProducedFeatures.push_back(aConstraint);
391 // make base features auxiliary
392 aBaseEdgeA->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true);
393 aBaseEdgeB->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true);
395 myBaseEdges.push_back(aBaseEdgeA);
396 myBaseEdges.push_back(aBaseEdgeB);
397 // exchange the naming IDs of newly created and old line that become auxiliary
398 sketch()->exchangeIDs(aBaseEdgeA, aResultEdgeA);
399 sketch()->exchangeIDs(aBaseEdgeB, aResultEdgeB);
401 // Update radius value
402 int aNbSubs = sketch()->numberOfSubs();
403 FeaturePtr aSubFeature;
404 for (int aSub = 0; aSub < aNbSubs; aSub++) {
405 aSubFeature = sketch()->subFeature(aSub);
406 if (aSubFeature->getKind() != SketchPlugin_ConstraintRadius::ID())
408 AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
409 aSubFeature->attribute(SketchPlugin_Constraint::ENTITY_A()));
410 if (!aRefAttr || !aRefAttr->isObject())
412 FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object());
413 if (aFeature == aResultArc) {
414 AttributeDoublePtr aRadius = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
415 aSubFeature->attribute(SketchPlugin_Constraint::VALUE()));
416 aRadius->setValue(aFilletRadius);
423 // Send events to update the sub-features by the solver.
424 if(isUpdateFlushed) {
425 Events_Loop::loop()->setFlushed(anUpdateEvent, true);
429 void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
431 if(theID == SketchPlugin_Constraint::ENTITY_A()) {
432 if(myListOfPointsChangedInCode) {
436 // Clear the list of fillet entities.
437 myResultEdges.clear();
439 // Clear the list of base points.
440 myBasePoints.clear();
442 // Clear auxiliary flag on initial objects.
443 std::list<FeaturePtr>::const_iterator aFeatureIt;
444 for(aFeatureIt = myBaseEdges.cbegin(); aFeatureIt != myBaseEdges.cend(); ++aFeatureIt) {
445 (*aFeatureIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(false);
449 // Remove all produced objects and constraints.
450 DocumentPtr aDoc = sketch()->document();
451 for(aFeatureIt = myProducedFeatures.cbegin(); aFeatureIt != myProducedFeatures.cend(); ++aFeatureIt) {
452 aDoc->removeFeature(*aFeatureIt);
454 myProducedFeatures.clear();
456 // Get list of points for fillets and current radius.
457 AttributeRefAttrListPtr aRefListOfFilletPoints = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttrList>(
458 data()->attribute(SketchPlugin_Constraint::ENTITY_A()));
459 AttributeDoublePtr aRadiusAttribute = real(SketchPlugin_Constraint::VALUE());
460 int aListSize = aRefListOfFilletPoints->size();
461 if(aListSize == 0 && !myRadiusChangedByUser) {
462 // If list is empty just reset radius to zero (if it was not changed by user).
463 myRadiusChangedInCode = true;
464 aRadiusAttribute->setValue(0);
465 myRadiusChangedInCode = false;
469 // Iterate over points to get base lines an calculate radius for fillets.
470 double aMinimumRadius = 0;
471 std::list<std::pair<ObjectPtr, AttributePtr>> aSelectedPointsList = aRefListOfFilletPoints->list();
472 std::list<std::pair<ObjectPtr, AttributePtr>>::iterator anIter = aSelectedPointsList.begin();
473 std::set<AttributePtr> aBasePoints;
474 for(int anIndex = 0; anIndex < aListSize; anIndex++, anIter++) {
475 AttributePtr aFilletPointAttr = (*anIter).second;
476 std::shared_ptr<GeomDataAPI_Point2D> aFilletPoint2D =
477 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aFilletPointAttr);
478 if(!aFilletPoint2D.get()) {
479 setError("Error: One of the selected points is invalid.");
483 // If point or coincident point is already in list remove it from attribute.
484 if(aBasePoints.find(aFilletPointAttr) != aBasePoints.end()) {
485 myListOfPointsChangedInCode = true;
486 aRefListOfFilletPoints->remove(aFilletPointAttr);
487 myListOfPointsChangedInCode = false;
491 // Obtain constraint coincidence for the fillet point.
492 FeaturePtr aConstraintCoincidence;
493 const std::set<AttributePtr>& aRefsList = aFilletPointAttr->owner()->data()->refsToMe();
494 for(std::set<AttributePtr>::const_iterator anIt = aRefsList.cbegin(); anIt != aRefsList.cend(); ++anIt) {
495 std::shared_ptr<ModelAPI_Attribute> anAttr = (*anIt);
496 FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->owner());
497 if(aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
498 AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
499 aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_A()));
500 AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
501 aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_B()));
502 if(anAttrRefA.get()) {
503 AttributePtr anAttrA = anAttrRefA->attr();
504 if(aFilletPointAttr == anAttrA) {
505 aConstraintCoincidence = aConstrFeature;
509 if(anAttrRefB.get()) {
510 AttributePtr anAttrB = anAttrRefB->attr();
511 if(aFilletPointAttr == anAttrB) {
512 aConstraintCoincidence = aConstrFeature;
519 if(!aConstraintCoincidence.get()) {
520 setError("Error: No coincident edges at one of the selected points.");
524 // Get coincides from constraint.
525 std::set<FeaturePtr> aCoincides;
528 SketchPlugin_Tools::findCoincidences(aConstraintCoincidence,
529 SketchPlugin_ConstraintCoincidence::ENTITY_A(),
531 SketchPlugin_Tools::findCoincidences(aConstraintCoincidence,
532 SketchPlugin_ConstraintCoincidence::ENTITY_B(),
535 // Remove points from set of coincides. Also get all attributes which is equal to this point to exclude it.
536 std::shared_ptr<GeomAPI_Pnt2d> aFilletPnt2d = aFilletPoint2D->pnt();
537 std::set<FeaturePtr> aNewSetOfCoincides;
538 for(std::set<FeaturePtr>::iterator anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
539 std::string aFeatureKind = (*anIt)->getKind();
540 if(aFeatureKind == SketchPlugin_Point::ID()) {
541 AttributePtr anAttr = (*anIt)->attribute(SketchPlugin_Point::COORD_ID());
542 std::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
543 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttr);
544 if(aPoint2D.get() && aFilletPnt2d->isEqual(aPoint2D->pnt())) {
545 aBasePoints.insert(anAttr);
547 } else if(aFeatureKind == SketchPlugin_Line::ID()) {
548 AttributePtr anAttrStart = (*anIt)->attribute(SketchPlugin_Line::START_ID());
549 std::shared_ptr<GeomDataAPI_Point2D> aPointStart2D =
550 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttrStart);
551 if(aPointStart2D.get() && aFilletPnt2d->isEqual(aPointStart2D->pnt())) {
552 aBasePoints.insert(anAttrStart);
554 AttributePtr anAttrEnd = (*anIt)->attribute(SketchPlugin_Line::END_ID());
555 std::shared_ptr<GeomDataAPI_Point2D> aPointEnd2D =
556 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttrEnd);
557 if(aPointEnd2D.get() && aFilletPnt2d->isEqual(aPointEnd2D->pnt())) {
558 aBasePoints.insert(anAttrEnd);
560 aNewSetOfCoincides.insert(*anIt);
561 } else if(aFeatureKind == SketchPlugin_Arc::ID() ) {
562 AttributePtr anAttrStart = (*anIt)->attribute(SketchPlugin_Arc::START_ID());
563 std::shared_ptr<GeomDataAPI_Point2D> aPointStart2D =
564 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttrStart);
565 if(aPointStart2D.get() && aFilletPnt2d->isEqual(aPointStart2D->pnt())) {
566 aBasePoints.insert(anAttrStart);
568 AttributePtr anAttrEnd = (*anIt)->attribute(SketchPlugin_Arc::END_ID());
569 std::shared_ptr<GeomDataAPI_Point2D> aPointEnd2D =
570 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttrEnd);
571 if(aPointEnd2D.get() && aFilletPnt2d->isEqual(aPointEnd2D->pnt())) {
572 aBasePoints.insert(anAttrEnd);
574 aNewSetOfCoincides.insert(*anIt);
577 aCoincides = aNewSetOfCoincides;
579 // If we still have more than two coincides remove auxilary entities from set of coincides.
580 if(aCoincides.size() > 2) {
581 aNewSetOfCoincides.clear();
582 for(std::set<FeaturePtr>::iterator anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
583 if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) {
584 aNewSetOfCoincides.insert(*anIt);
587 aCoincides = aNewSetOfCoincides;
590 if(aCoincides.size() != 2) {
591 setError("Error: One of the selected points does not have two suitable edges for fillet.");
595 // Store base point for fillet.
596 aBasePoints.insert(aFilletPointAttr);
597 myBasePoints.push_back(aFilletPointAttr);
599 // Get base lines for fillet.
600 FeaturePtr anOldFeatureA, anOldFeatureB;
601 std::set<FeaturePtr>::iterator aLinesIt = aCoincides.begin();
602 anOldFeatureA = *aLinesIt++;
603 anOldFeatureB = *aLinesIt;
605 // Getting radius value if it was not changed by user.
606 if(!myRadiusChangedByUser) {
607 // Getting points located at 1/3 of edge length from fillet point.
608 std::shared_ptr<GeomAPI_Pnt2d> aFilletPnt2d = aFilletPoint2D->pnt();
609 std::shared_ptr<GeomAPI_Pnt2d> aPntA, aPntB;
610 getPointOnEdge(anOldFeatureA, aFilletPnt2d, aPntA);
611 getPointOnEdge(anOldFeatureB, aFilletPnt2d, aPntB);
613 /// Getting distances.
614 double aDistanceA = getProjectionDistance(anOldFeatureB, aPntA);
615 double aDistanceB = getProjectionDistance(anOldFeatureA, aPntB);
616 double aRadius = aDistanceA < aDistanceB ? aDistanceA / 2.0 : aDistanceB / 2.0;
617 aMinimumRadius = aMinimumRadius == 0 ? aRadius : aRadius < aMinimumRadius ? aRadius : aMinimumRadius;
621 // Set new default radius if it was not changed by user.
622 if(!myRadiusChangedByUser) {
623 myRadiusChangedInCode = true;
624 aRadiusAttribute->setValue(aMinimumRadius);
625 myRadiusChangedInCode = false;
628 } else if(theID == SketchPlugin_Constraint::VALUE()) {
629 if(!myRadiusChangedInCode) {
630 myRadiusChangedByUser = true;
635 AISObjectPtr SketchPlugin_ConstraintFillet::getAISObject(AISObjectPtr thePrevious)
640 AISObjectPtr anAIS = thePrevious;
641 /// TODO: Equal constraint presentation should be put here
645 bool SketchPlugin_ConstraintFillet::isMacro() const
651 // ========= Auxiliary functions =================
652 void recalculateAttributes(FeaturePtr theNewArc, const std::string& theNewArcAttribute,
653 FeaturePtr theFeature, const std::string& theFeatureAttribute)
655 std::shared_ptr<GeomAPI_Pnt2d> anArcPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
656 theNewArc->attribute(theNewArcAttribute))->pnt();
657 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
658 theFeature->attribute(theFeatureAttribute))->setValue(anArcPoint->x(), anArcPoint->y());
661 /// \brief Find intersections of lines shifted along normal direction
662 void possibleFilletCenterLineLine(
663 std::shared_ptr<GeomAPI_XY> thePointA, std::shared_ptr<GeomAPI_Dir2d> theDirA,
664 std::shared_ptr<GeomAPI_XY> thePointB, std::shared_ptr<GeomAPI_Dir2d> theDirB,
665 double theRadius, std::list< std::shared_ptr<GeomAPI_XY> >& theCenters)
667 std::shared_ptr<GeomAPI_Dir2d> aDirAT(new GeomAPI_Dir2d(-theDirA->y(), theDirA->x()));
668 std::shared_ptr<GeomAPI_Dir2d> aDirBT(new GeomAPI_Dir2d(-theDirB->y(), theDirB->x()));
669 std::shared_ptr<GeomAPI_XY> aPntA, aPntB;
670 double aDet = theDirA->cross(theDirB);
671 for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) {
672 aPntA = thePointA->added(aDirAT->xy()->multiplied(aStepA * theRadius));
673 for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) {
674 aPntB = thePointB->added(aDirBT->xy()->multiplied(aStepB * theRadius));
675 double aVX = aDirAT->xy()->dot(aPntA);
676 double aVY = aDirBT->xy()->dot(aPntB);
677 std::shared_ptr<GeomAPI_XY> aPoint(new GeomAPI_XY(
678 (theDirB->x() * aVX - theDirA->x() * aVY) / aDet,
679 (theDirB->y() * aVX - theDirA->y() * aVY) / aDet));
680 theCenters.push_back(aPoint);
685 /// \brief Find intersections of line shifted along normal direction in both sides
686 /// and a circle with extended radius
687 void possibleFilletCenterLineArc(
688 std::shared_ptr<GeomAPI_XY> theStartLine, std::shared_ptr<GeomAPI_Dir2d> theDirLine,
689 std::shared_ptr<GeomAPI_XY> theCenterArc, double theRadiusArc,
690 double theRadius, std::list< std::shared_ptr<GeomAPI_XY> >& theCenters)
692 std::shared_ptr<GeomAPI_Dir2d> aDirT(new GeomAPI_Dir2d(-theDirLine->y(), theDirLine->x()));
693 std::shared_ptr<GeomAPI_XY> aPnt;
694 double aDirNorm2 = theDirLine->dot(theDirLine);
696 double aDirX = theDirLine->x();
697 double aDirX2 = theDirLine->x() * theDirLine->x();
698 double aDirY2 = theDirLine->y() * theDirLine->y();
699 double aDirXY = theDirLine->x() * theDirLine->y();
700 for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) {
701 aPnt = theStartLine->added(aDirT->xy()->multiplied(aStepA * theRadius));
702 double aCoeff = aDirT->xy()->dot(aPnt->decreased(theCenterArc));
703 double aCoeff2 = aCoeff * aCoeff;
704 for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) {
705 aRad = theRadiusArc + aStepB * theRadius;
706 double aD = aRad * aRad * aDirNorm2 - aCoeff2;
709 double aDs = sqrt(aD);
710 double x1 = theCenterArc->x() + (aCoeff * aDirT->x() - aDirT->y() * aDs) / aDirNorm2;
711 double x2 = theCenterArc->x() + (aCoeff * aDirT->x() + aDirT->y() * aDs) / aDirNorm2;
712 double y1 = (aDirX2 * aPnt->y() + aDirY2 * theCenterArc->y() -
713 aDirXY * (aPnt->x() - theCenterArc->x()) - theDirLine->y() * aDs) / aDirNorm2;
714 double y2 = (aDirX2 * aPnt->y() + aDirY2 * theCenterArc->y() -
715 aDirXY * (aPnt->x() - theCenterArc->x()) + theDirLine->y() * aDs) / aDirNorm2;
717 std::shared_ptr<GeomAPI_XY> aPoint1(new GeomAPI_XY(x1, y1));
718 theCenters.push_back(aPoint1);
719 std::shared_ptr<GeomAPI_XY> aPoint2(new GeomAPI_XY(x2, y2));
720 theCenters.push_back(aPoint2);
725 /// \brief Find intersections of two circles with extended radii
726 void possibleFilletCenterArcArc(
727 std::shared_ptr<GeomAPI_XY> theCenterA, double theRadiusA,
728 std::shared_ptr<GeomAPI_XY> theCenterB, double theRadiusB,
729 double theRadius, std::list< std::shared_ptr<GeomAPI_XY> >& theCenters)
731 std::shared_ptr<GeomAPI_XY> aCenterDir = theCenterB->decreased(theCenterA);
732 double aCenterDist2 = aCenterDir->dot(aCenterDir);
733 double aCenterDist = sqrt(aCenterDist2);
736 for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) {
737 aRadA = theRadiusA + aStepA * theRadius;
738 for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) {
739 aRadB = theRadiusB + aStepB * theRadius;
740 if (aRadA + aRadB < aCenterDist || fabs(aRadA - aRadB) > aCenterDist)
741 continue; // there is no intersections
743 double aMedDist = (aRadA * aRadA - aRadB * aRadB + aCenterDist2) / (2.0 * aCenterDist);
744 double aHeight = sqrt(aRadA * aRadA - aMedDist * aMedDist);
746 double x1 = theCenterA->x() + (aMedDist * aCenterDir->x() + aCenterDir->y() * aHeight) / aCenterDist;
747 double y1 = theCenterA->y() + (aMedDist * aCenterDir->y() - aCenterDir->x() * aHeight) / aCenterDist;
749 double x2 = theCenterA->x() + (aMedDist * aCenterDir->x() - aCenterDir->y() * aHeight) / aCenterDist;
750 double y2 = theCenterA->y() + (aMedDist * aCenterDir->y() + aCenterDir->x() * aHeight) / aCenterDist;
752 std::shared_ptr<GeomAPI_XY> aPoint1(new GeomAPI_XY(x1, y1));
753 theCenters.push_back(aPoint1);
754 std::shared_ptr<GeomAPI_XY> aPoint2(new GeomAPI_XY(x2, y2));
755 theCenters.push_back(aPoint2);
760 void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB,
761 double theRadius, bool theNotInversed[2],
762 std::shared_ptr<GeomAPI_XY>& theCenter,
763 std::shared_ptr<GeomAPI_XY>& theTangentA,
764 std::shared_ptr<GeomAPI_XY>& theTangentB)
766 static const int aNbFeatures = 2;
767 FeaturePtr aFeature[aNbFeatures] = {theFeatureA, theFeatureB};
768 std::shared_ptr<GeomAPI_XY> aStart[aNbFeatures], aEnd[aNbFeatures], aCenter[aNbFeatures];
769 std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, aEndPoint;
771 for (int i = 0; i < aNbFeatures; i++) {
772 if (aFeature[i]->getKind() == SketchPlugin_Line::ID()) {
773 aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
774 aFeature[i]->attribute(SketchPlugin_Line::START_ID()));
775 aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
776 aFeature[i]->attribute(SketchPlugin_Line::END_ID()));
777 } else if (aFeature[i]->getKind() == SketchPlugin_Arc::ID()) {
778 aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
779 aFeature[i]->attribute(SketchPlugin_Arc::START_ID()));
780 aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
781 aFeature[i]->attribute(SketchPlugin_Arc::END_ID()));
782 aCenter[i] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
783 aFeature[i]->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt()->xy();
786 aStart[i] = std::shared_ptr<GeomAPI_XY>(theNotInversed[i] ?
787 new GeomAPI_XY(aStartPoint->x(), aStartPoint->y()) :
788 new GeomAPI_XY(aEndPoint->x(), aEndPoint->y()));
789 aEnd[i] = std::shared_ptr<GeomAPI_XY>(theNotInversed[i] ?
790 new GeomAPI_XY(aEndPoint->x(), aEndPoint->y()) :
791 new GeomAPI_XY(aStartPoint->x(), aStartPoint->y()));
794 if (theFeatureA->getKind() == SketchPlugin_Line::ID() &&
795 theFeatureB->getKind() == SketchPlugin_Line::ID()) {
796 std::shared_ptr<GeomAPI_Dir2d> aDir[2];
797 std::shared_ptr<GeomAPI_Dir2d> aDirT[2];
798 for (int i = 0; i < aNbFeatures; i++) {
799 aDir[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(aEnd[i]->decreased(aStart[i])));
800 aDirT[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(-aDir[i]->y(), aDir[i]->x()));
803 // get and filter possible centers
804 std::list< std::shared_ptr<GeomAPI_XY> > aSuspectCenters;
805 possibleFilletCenterLineLine(aStart[0], aDir[0], aStart[1], aDir[1], theRadius, aSuspectCenters);
807 std::list< std::shared_ptr<GeomAPI_XY> >::iterator anIt = aSuspectCenters.begin();
808 for (; anIt != aSuspectCenters.end(); anIt++) {
809 aDot = aDirT[0]->xy()->dot(aStart[0]->decreased(*anIt));
810 theTangentA = (*anIt)->added(aDirT[0]->xy()->multiplied(aDot));
811 if (theTangentA->decreased(aStart[0])->dot(aDir[0]->xy()) < 0.0)
812 continue; // incorrect position
813 aDot = aDirT[1]->xy()->dot(aStart[1]->decreased(*anIt));
814 theTangentB = (*anIt)->added(aDirT[1]->xy()->multiplied(aDot));
815 if (theTangentB->decreased(aStart[1])->dot(aDir[1]->xy()) < 0.0)
816 continue; // incorrect position
817 // the center is found, stop searching
821 } else if ((theFeatureA->getKind() == SketchPlugin_Arc::ID() &&
822 theFeatureB->getKind() == SketchPlugin_Line::ID()) ||
823 (theFeatureA->getKind() == SketchPlugin_Line::ID() &&
824 theFeatureB->getKind() == SketchPlugin_Arc::ID())) {
825 int aLineInd = theFeatureA->getKind() == SketchPlugin_Line::ID() ? 0 : 1;
826 double anArcRadius = aStart[1-aLineInd]->distance(aCenter[1-aLineInd]);
827 std::shared_ptr<GeomAPI_Dir2d> aDirLine = std::shared_ptr<GeomAPI_Dir2d>(
828 new GeomAPI_Dir2d(aEnd[aLineInd]->decreased(aStart[aLineInd])));
829 std::shared_ptr<GeomAPI_Dir2d> aDirT = std::shared_ptr<GeomAPI_Dir2d>(
830 new GeomAPI_Dir2d(-aDirLine->y(), aDirLine->x()));
832 std::shared_ptr<GeomAPI_Dir2d> aStartArcDir = std::shared_ptr<GeomAPI_Dir2d>(
833 new GeomAPI_Dir2d(aStart[1-aLineInd]->decreased(aCenter[1-aLineInd])));
834 std::shared_ptr<GeomAPI_Dir2d> aEndArcDir = std::shared_ptr<GeomAPI_Dir2d>(
835 new GeomAPI_Dir2d(aEnd[1-aLineInd]->decreased(aCenter[1-aLineInd])));
836 double anArcAngle = aEndArcDir->angle(aStartArcDir);
838 // get possible centers and filter them
839 std::list< std::shared_ptr<GeomAPI_XY> > aSuspectCenters;
840 possibleFilletCenterLineArc(aStart[aLineInd], aDirLine, aCenter[1-aLineInd], anArcRadius, theRadius, aSuspectCenters);
842 // the line is forward into the arc
843 double innerArc = aCenter[1-aLineInd]->decreased(aStart[aLineInd])->dot(aDirLine->xy());
844 std::shared_ptr<GeomAPI_XY> aLineTgPoint, anArcTgPoint;
845 // The possible centers are ranged by their positions.
846 // If the point is not satisfy one of criteria, the weight is decreased with penalty.
848 std::list< std::shared_ptr<GeomAPI_XY> >::iterator anIt = aSuspectCenters.begin();
849 for (; anIt != aSuspectCenters.end(); anIt++) {
851 aDot = aDirT->xy()->dot(aStart[aLineInd]->decreased(*anIt));
852 aLineTgPoint = (*anIt)->added(aDirT->xy()->multiplied(aDot));
853 // Check the point is placed on the correct arc (penalty if false)
854 if (aCenter[1-aLineInd]->distance(*anIt) * innerArc > anArcRadius * innerArc)
856 std::shared_ptr<GeomAPI_Dir2d> aCurDir = std::shared_ptr<GeomAPI_Dir2d>(
857 new GeomAPI_Dir2d((*anIt)->decreased(aCenter[1-aLineInd])));
858 double aCurAngle = aCurDir->angle(aStartArcDir);
859 if (anArcAngle < 0.0) aCurAngle *= -1.0;
860 if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle))
862 if (aWeight > aBestWeight)
863 aBestWeight = aWeight;
864 else if (aWeight < aBestWeight ||
865 aStart[aLineInd]->distance(*anIt) >
866 aStart[aLineInd]->distance(theCenter)) // <-- take closer point
868 // the center is found, stop searching
870 anArcTgPoint = aCenter[1-aLineInd]->added(aCurDir->xy()->multiplied(anArcRadius));
871 if (theFeatureA->getKind() == SketchPlugin_Line::ID()) {
872 theTangentA = aLineTgPoint;
873 theTangentB = anArcTgPoint;
875 theTangentA = anArcTgPoint;
876 theTangentB = aLineTgPoint;
880 } else if (theFeatureA->getKind() == SketchPlugin_Arc::ID() &&
881 theFeatureB->getKind() == SketchPlugin_Arc::ID()) {
882 double anArcRadius[aNbFeatures];
883 double anArcAngle[aNbFeatures];
884 std::shared_ptr<GeomAPI_Dir2d> aStartArcDir[aNbFeatures];
885 for (int i = 0; i < aNbFeatures; i++) {
886 anArcRadius[i] = aStart[i]->distance(aCenter[i]);
887 aStartArcDir[i] = std::shared_ptr<GeomAPI_Dir2d>(
888 new GeomAPI_Dir2d(aStart[i]->decreased(aCenter[i])));
889 std::shared_ptr<GeomAPI_Dir2d> aEndArcDir = std::shared_ptr<GeomAPI_Dir2d>(
890 new GeomAPI_Dir2d(aEnd[i]->decreased(aCenter[i])));
891 anArcAngle[i] = aEndArcDir->angle(aStartArcDir[i]);
894 // get and filter possible centers
895 std::list< std::shared_ptr<GeomAPI_XY> > aSuspectCenters;
896 possibleFilletCenterArcArc(aCenter[0], anArcRadius[0], aCenter[1], anArcRadius[1], theRadius, aSuspectCenters);
898 std::shared_ptr<GeomAPI_XY> aLineTgPoint, anArcTgPoint;
899 std::list< std::shared_ptr<GeomAPI_XY> >::iterator anIt = aSuspectCenters.begin();
900 for (; anIt != aSuspectCenters.end(); anIt++) {
901 std::shared_ptr<GeomAPI_Dir2d> aCurDir = std::shared_ptr<GeomAPI_Dir2d>(
902 new GeomAPI_Dir2d((*anIt)->decreased(aCenter[0])));
903 double aCurAngle = aCurDir->angle(aStartArcDir[0]);
904 if (anArcAngle[0] < 0.0) aCurAngle *= -1.0;
905 if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle[0]))
906 continue; // incorrect position
907 theTangentA = aCenter[0]->added(aCurDir->xy()->multiplied(anArcRadius[0]));
909 aCurDir = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d((*anIt)->decreased(aCenter[1])));
910 aCurAngle = aCurDir->angle(aStartArcDir[1]);
911 if (anArcAngle[1] < 0.0) aCurAngle *= -1.0;
912 if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle[1]))
913 continue; // incorrect position
914 theTangentB = aCenter[1]->added(aCurDir->xy()->multiplied(anArcRadius[1]));
916 // the center is found, stop searching
923 void getPointOnEdge(const FeaturePtr theFeature,
924 const std::shared_ptr<GeomAPI_Pnt2d> theFilletPoint,
925 std::shared_ptr<GeomAPI_Pnt2d>& thePoint) {
926 if(theFeature->getKind() == SketchPlugin_Line::ID()) {
927 std::shared_ptr<GeomAPI_Pnt2d> aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
928 theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt();
929 std::shared_ptr<GeomAPI_Pnt2d> aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
930 theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt();
931 if(aPntStart->distance(theFilletPoint) > 1.e-7) {
932 aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
933 theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt();
934 aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
935 theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt();
937 thePoint.reset( new GeomAPI_Pnt2d(aPntStart->xy()->added( aPntEnd->xy()->decreased( aPntStart->xy() )->multiplied(1.0 / 3.0) ) ) );
939 std::shared_ptr<GeomAPI_Pnt2d> aPntTemp;
940 std::shared_ptr<GeomAPI_Pnt2d> aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
941 theFeature->attribute(SketchPlugin_Arc::START_ID()))->pnt();
942 std::shared_ptr<GeomAPI_Pnt2d> aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
943 theFeature->attribute(SketchPlugin_Arc::END_ID()))->pnt();
944 if(theFeature->attribute(SketchPlugin_Arc::INVERSED_ID())) {
945 aPntTemp = aPntStart;
949 std::shared_ptr<GeomAPI_Pnt2d> aCenterPnt = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
950 theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt();
951 std::shared_ptr<GeomAPI_Circ2d> aCirc(new GeomAPI_Circ2d(aCenterPnt, aPntStart));
952 double aStartParameter(0), anEndParameter(0);
953 aCirc->parameter(aPntStart, paramTolerance, aStartParameter);
954 aCirc->parameter(aPntEnd, paramTolerance, anEndParameter);
955 if(aPntStart->distance(theFilletPoint) > tolerance) {
956 double aTmpParameter = aStartParameter;
957 aStartParameter = anEndParameter;
958 anEndParameter = aTmpParameter;
960 double aPntParameter = aStartParameter + (anEndParameter - aStartParameter) / 3.0;
961 aCirc->D0(aPntParameter, thePoint);
965 double getProjectionDistance(const FeaturePtr theFeature,
966 const std::shared_ptr<GeomAPI_Pnt2d> thePoint)
968 std::shared_ptr<GeomAPI_Pnt2d> aProjectPnt;
969 if(theFeature->getKind() == SketchPlugin_Line::ID()) {
970 std::shared_ptr<GeomAPI_Pnt2d> aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
971 theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt();
972 std::shared_ptr<GeomAPI_Pnt2d> aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
973 theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt();
974 std::shared_ptr<GeomAPI_Lin2d> aLin(new GeomAPI_Lin2d(aPntStart, aPntEnd));
975 aProjectPnt = aLin->project(thePoint);
977 std::shared_ptr<GeomAPI_Pnt2d> aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
978 theFeature->attribute(SketchPlugin_Arc::START_ID()))->pnt();
979 std::shared_ptr<GeomAPI_Pnt2d> aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
980 theFeature->attribute(SketchPlugin_Arc::END_ID()))->pnt();
981 std::shared_ptr<GeomAPI_Pnt2d> aCenterPnt = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
982 theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt();
983 std::shared_ptr<GeomAPI_Circ2d> aCirc(new GeomAPI_Circ2d(aCenterPnt, aPntStart));
984 aProjectPnt = aCirc->project(thePoint);
986 if(aProjectPnt.get()) {
987 return aProjectPnt->distance(thePoint);
992 std::set<FeaturePtr> getCoincides(const FeaturePtr& theConstraintCoincidence)
994 std::set<FeaturePtr> aCoincides;
996 SketchPlugin_Tools::findCoincidences(theConstraintCoincidence,
997 SketchPlugin_ConstraintCoincidence::ENTITY_A(),
999 SketchPlugin_Tools::findCoincidences(theConstraintCoincidence,
1000 SketchPlugin_ConstraintCoincidence::ENTITY_B(),
1003 // Remove points from set of coincides.
1004 std::set<FeaturePtr> aNewSetOfCoincides;
1005 for(std::set<FeaturePtr>::iterator anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
1006 if((*anIt)->getKind() == SketchPlugin_Line::ID() ||
1007 (*anIt)->getKind() == SketchPlugin_Arc::ID() ) {
1008 aNewSetOfCoincides.insert(*anIt);
1011 aCoincides = aNewSetOfCoincides;
1013 // If we still have more than two coincides remove auxilary entities from set of coincides.
1014 if(aCoincides.size() > 2) {
1015 aNewSetOfCoincides.clear();
1016 for(std::set<FeaturePtr>::iterator anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
1017 if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) {
1018 aNewSetOfCoincides.insert(*anIt);
1021 aCoincides = aNewSetOfCoincides;