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(false),
66 myRadiusInitialized(false)
70 void SketchPlugin_ConstraintFillet::initAttributes()
72 data()->addAttribute(SketchPlugin_Constraint::VALUE(), ModelAPI_AttributeDouble::typeId());
73 data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttrList::typeId());
76 void SketchPlugin_ConstraintFillet::execute()
78 std::shared_ptr<ModelAPI_Data> aData = data();
80 // Check the base objects are initialized.
81 AttributeRefAttrListPtr aPointsRefList = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttrList>(
82 aData->attribute(SketchPlugin_Constraint::ENTITY_A()));
83 if(!aPointsRefList->isInitialized()) {
84 setError("Error: List of points is not initialized.");
89 double aFilletRadius = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
90 aData->attribute(SketchPlugin_Constraint::VALUE()))->value();
92 // Wait all constraints being created, then send update events
93 static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
94 bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
96 Events_Loop::loop()->setFlushed(anUpdateEvent, false);
98 // Remove unused items.
99 for(std::map<AttributePtr, FilletFeatures>::iterator aPointsIter = myPointFeaturesMap.begin();
100 aPointsIter != myPointFeaturesMap.end();) {
101 if(myNewPoints.find(aPointsIter->first) == myNewPoints.end()) {
102 // Clear auxiliary flag on initial objects.
103 const FilletFeatures& aFilletFeatures = aPointsIter->second;
104 std::list<FeaturePtr>::const_iterator aFeatureIt;
105 for(aFeatureIt = aFilletFeatures.baseEdges.cbegin();
106 aFeatureIt != aFilletFeatures.baseEdges.cend();
108 (*aFeatureIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(false);
114 DocumentPtr aDoc = sketch()->document();
115 for(std::map<AttributePtr, FilletFeatures>::iterator aPointsIter = myPointFeaturesMap.begin();
116 aPointsIter != myPointFeaturesMap.end();) {
117 if(myNewPoints.find(aPointsIter->first) != myNewPoints.end()) {
118 ++aPointsIter; // keep this point and result features.
120 // Remove all produced constraints.
121 const FilletFeatures& aFilletFeatures = aPointsIter->second;
122 std::list<FeaturePtr>::const_iterator aFeatureIt;
123 for(aFeatureIt = aFilletFeatures.resultConstraints.cbegin();
124 aFeatureIt != aFilletFeatures.resultConstraints.cend();
126 aDoc->removeFeature(*aFeatureIt);
129 // Remove all result edges.
130 for(aFeatureIt = aFilletFeatures.resultEdges.cbegin();
131 aFeatureIt != aFilletFeatures.resultEdges.cend();
133 aDoc->removeFeature(*aFeatureIt);
136 // Remove point from map.
137 myPointFeaturesMap.erase(aPointsIter++);
141 for(std::set<AttributePtr>::iterator aPointsIter = myNewPoints.begin();
142 aPointsIter != myNewPoints.end();
144 AttributePtr aPointAttr = *aPointsIter;
145 std::shared_ptr<GeomDataAPI_Point2D> aFilletPoint2d = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aPointAttr);
146 if(!aFilletPoint2d.get()) {
147 setError("Error: One of the selected points is empty.");
150 std::shared_ptr<GeomAPI_Pnt2d> aFilletPnt2d = aFilletPoint2d->pnt();
152 // Obtain base lines for fillet.
153 bool anIsNeedNewObjects = true;
154 FilletFeatures aFilletFeatures;
155 std::map<AttributePtr, FilletFeatures>::iterator aPrevPointsIter = myPointFeaturesMap.find(aPointAttr);
156 if(aPrevPointsIter != myPointFeaturesMap.end()) {
157 anIsNeedNewObjects = false;
158 aFilletFeatures = aPrevPointsIter->second;
160 FeaturePtr aBaseEdgeA, aBaseEdgeB;
161 if(!anIsNeedNewObjects) {
162 aBaseEdgeA = aFilletFeatures.baseEdges.front();
163 aBaseEdgeB = aFilletFeatures.baseEdges.back();
165 // Obtain constraint coincidence for the fillet point.
166 FeaturePtr aConstraintCoincidence;
167 const std::set<AttributePtr>& aRefsList = aFilletPoint2d->owner()->data()->refsToMe();
168 for(std::set<AttributePtr>::const_iterator anIt = aRefsList.cbegin(); anIt != aRefsList.cend(); ++anIt) {
169 std::shared_ptr<ModelAPI_Attribute> anAttr = (*anIt);
170 FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->owner());
171 if(aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
172 AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
173 aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_A()));
174 AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
175 aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_B()));
176 if(anAttrRefA.get() && !anAttrRefA->isObject()) {
177 AttributePtr anAttrA = anAttrRefA->attr();
178 if(aFilletPoint2d == anAttrA) {
179 aConstraintCoincidence = aConstrFeature;
183 if(anAttrRefB.get() && !anAttrRefB->isObject()) {
184 AttributePtr anAttrB = anAttrRefB->attr();
185 if(aFilletPoint2d == anAttrB) {
186 aConstraintCoincidence = aConstrFeature;
193 if(!aConstraintCoincidence.get()) {
194 setError("Error: No coincident edges at one of the selected points.");
198 // Get coincide edges.
199 std::set<FeaturePtr> aCoincides = getCoincides(aConstraintCoincidence);
200 if(aCoincides.size() != 2) {
201 setError("Error: One of the selected points does not have two suitable edges for fillet.");
205 std::set<FeaturePtr>::iterator aLinesIt = aCoincides.begin();
206 aBaseEdgeA = *aLinesIt++;
207 aBaseEdgeB = *aLinesIt;
209 aFilletFeatures.baseEdges.push_back(aBaseEdgeA);
210 aFilletFeatures.baseEdges.push_back(aBaseEdgeB);
213 if(!aBaseEdgeA.get() || !aBaseEdgeB.get()) {
214 setError("Error: One of the base edges is empty.");
218 // Create new edges and arc if needed.
219 FeaturePtr aResultEdgeA, aResultEdgeB, aResultArc;
220 if(!anIsNeedNewObjects) {
221 // Obtain features from the list.
222 std::list<FeaturePtr>::iterator aResultEdgesIt = aFilletFeatures.resultEdges.begin();
223 aResultEdgeA = *aResultEdgesIt++;
224 aResultEdgeB = *aResultEdgesIt++;
225 aResultArc = *aResultEdgesIt;
227 // Copy edges and create arc.
228 aResultEdgeA = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(aBaseEdgeA, sketch());
229 aResultEdgeB = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(aBaseEdgeB, sketch());
230 aResultArc = sketch()->addFeature(SketchPlugin_Arc::ID());
232 aFilletFeatures.resultEdges.push_back(aResultEdgeA);
233 aFilletFeatures.resultEdges.push_back(aResultEdgeB);
234 aFilletFeatures.resultEdges.push_back(aResultArc);
237 // Calculate arc attributes
238 static const int aNbFeatures = 2;
239 FeaturePtr aBaseFeatures[aNbFeatures] = {aBaseEdgeA, aBaseEdgeB};
240 FeaturePtr aResultFeatures[aNbFeatures] = {aResultEdgeA, aResultEdgeB};
241 std::shared_ptr<GeomAPI_Dir2d> aTangentDir[aNbFeatures]; // tangent directions of the features in coincident point
242 bool isStart[aNbFeatures]; // indicates which point the features share
243 std::shared_ptr<GeomAPI_Pnt2d> aStartEndPnt[aNbFeatures * 2]; // first pair of points relate to first feature, second pair - to second
244 std::string aFeatAttributes[aNbFeatures * 2]; // attributes of features
245 for (int i = 0; i < aNbFeatures; i++) {
246 std::string aStartAttr, aEndAttr;
247 if (aResultFeatures[i]->getKind() == SketchPlugin_Line::ID()) {
248 aStartAttr = SketchPlugin_Line::START_ID();
249 aEndAttr = SketchPlugin_Line::END_ID();
250 } else if (aResultFeatures[i]->getKind() == SketchPlugin_Arc::ID()) {
251 aStartAttr = SketchPlugin_Arc::START_ID();
252 aEndAttr = SketchPlugin_Arc::END_ID();
253 } else { // wrong argument
254 setError("Error: One of the points has wrong coincide feature");
257 aFeatAttributes[2*i] = aStartAttr;
258 aStartEndPnt[2*i] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
259 aBaseFeatures[i]->attribute(aStartAttr))->pnt();
260 aFeatAttributes[2*i+1] = aEndAttr;
261 aStartEndPnt[2*i+1] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
262 aBaseFeatures[i]->attribute(aEndAttr))->pnt();
264 for (int aFeatInd = 0; aFeatInd < aNbFeatures; aFeatInd++) {
265 for (int j = 0; j < 2; j++) // loop on start-end of each feature
266 if (aStartEndPnt[aFeatInd * aNbFeatures + j]->distance(aFilletPnt2d) < 1.e-10) {
267 isStart[aFeatInd] = (j==0);
271 // tangent directions of the features
272 for (int i = 0; i < aNbFeatures; i++) {
273 std::shared_ptr<GeomAPI_XY> aDir;
274 if (aResultFeatures[i]->getKind() == SketchPlugin_Line::ID()) {
275 aDir = aStartEndPnt[2*i+1]->xy()->decreased(aStartEndPnt[2*i]->xy());
277 aDir = aDir->multiplied(-1.0);
278 } else if (aResultFeatures[i]->getKind() == SketchPlugin_Arc::ID()) {
279 std::shared_ptr<GeomAPI_Pnt2d> aCenterPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
280 aResultFeatures[i]->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt();
281 aDir = isStart[i] ? aStartEndPnt[2*i]->xy() : aStartEndPnt[2*i+1]->xy();
282 aDir = aDir->decreased(aCenterPoint->xy());
284 double x = aDir->x();
285 double y = aDir->y();
288 if (isStart[i] == std::dynamic_pointer_cast<SketchPlugin_Arc>(aBaseFeatures[i])->isReversed())
289 aDir = aDir->multiplied(-1.0);
291 aTangentDir[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(aDir));
294 // By default, the start point of fillet arc is connected to FeatureA,
295 // and the end point - to FeatureB. But when the angle between TangentDirA and
296 // TangentDirB greater 180 degree, the sequaence of features need to be reversed.
297 double cosBA = aTangentDir[0]->cross(aTangentDir[1]); // cos(B-A), where A and B - angles between corresponding tanget direction and the X axis
298 bool isReversed = cosBA > 0.0;
300 // Calculate fillet arc parameters
301 std::shared_ptr<GeomAPI_XY> aCenter, aTangentPntA, aTangentPntB;
302 calculateFilletCenter(aBaseEdgeA, aBaseEdgeB, aFilletRadius, isStart, aCenter, aTangentPntA, aTangentPntB);
303 if(!aCenter.get() || !aTangentPntA.get() || !aTangentPntB.get()) {
304 setError("Can not create fillet with the specified parameters.");
308 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
309 aResultEdgeA->attribute(aFeatAttributes[isStart[0] ? 0 : 1]))->setValue(aTangentPntA->x(), aTangentPntA->y());
310 aResultEdgeA->execute();
311 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
312 aResultEdgeB->attribute(aFeatAttributes[2 + (isStart[1] ? 0 : 1)]))->setValue(aTangentPntB->x(), aTangentPntB->y());
313 aResultEdgeB->execute();
314 // update fillet arc: make the arc correct for sure, so, it is not needed to process the "attribute updated"
315 // by arc; moreover, it may cause cyclicity in hte mechanism of updater
316 aResultArc->data()->blockSendAttributeUpdated(true);
317 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
318 aResultArc->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(aCenter->x(), aCenter->y());
320 std::shared_ptr<GeomAPI_XY> aTmp = aTangentPntA;
321 aTangentPntA = aTangentPntB;
324 std::shared_ptr<GeomDataAPI_Point2D> aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
325 aResultArc->attribute(SketchPlugin_Arc::START_ID()));
326 std::shared_ptr<GeomDataAPI_Point2D> aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
327 aResultArc->attribute(SketchPlugin_Arc::END_ID()));
328 if(aStartPoint->isInitialized() && aEndPoint->isInitialized() &&
329 (aStartPoint->pnt()->xy()->distance(aTangentPntA) > tolerance ||
330 aEndPoint->pnt()->xy()->distance(aTangentPntB) > tolerance)) {
331 std::dynamic_pointer_cast<SketchPlugin_Arc>(aResultArc)->setReversed(false);
333 aStartPoint->setValue(aTangentPntA->x(), aTangentPntA->y());
334 aEndPoint->setValue(aTangentPntB->x(), aTangentPntB->y());
335 aResultArc->data()->blockSendAttributeUpdated(false);
336 aResultArc->execute();
338 if(anIsNeedNewObjects) {
339 // Create list of additional constraints:
340 // 1. Coincidence of boundary points of features (copied lines/arcs) and fillet arc
342 FeaturePtr aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
343 AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
344 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
345 aRefAttr->setAttr(aResultArc->attribute(SketchPlugin_Arc::START_ID()));
346 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
347 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
348 int aFeatInd = isReversed ? 1 : 0;
349 int anAttrInd = (isReversed ? 2 : 0) + (isStart[isReversed ? 1 : 0] ? 0 : 1);
350 aRefAttr->setAttr(aResultFeatures[aFeatInd]->attribute(aFeatAttributes[anAttrInd]));
351 recalculateAttributes(aResultArc, SketchPlugin_Arc::START_ID(), aResultFeatures[aFeatInd], aFeatAttributes[anAttrInd]);
352 aConstraint->execute();
353 aFilletFeatures.resultConstraints.push_back(aConstraint);
354 ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
356 aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
357 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
358 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
359 aRefAttr->setAttr(aResultArc->attribute(SketchPlugin_Arc::END_ID()));
360 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
361 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
362 aFeatInd = isReversed ? 0 : 1;
363 anAttrInd = (isReversed ? 0 : 2) + (isStart[isReversed ? 0 : 1] ? 0 : 1);
364 aRefAttr->setAttr(aResultFeatures[aFeatInd]->attribute(aFeatAttributes[anAttrInd]));
365 recalculateAttributes(aResultArc, SketchPlugin_Arc::END_ID(), aResultFeatures[aFeatInd], aFeatAttributes[anAttrInd]);
366 aConstraint->execute();
367 aFilletFeatures.resultConstraints.push_back(aConstraint);
368 ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
369 // 2. Fillet arc radius
370 //aConstraint = sketch()->addFeature(SketchPlugin_ConstraintRadius::ID());
371 //aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
372 // aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
373 //aRefAttr->setObject(aNewArc->lastResult());
374 //std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
375 // aConstraint->attribute(SketchPlugin_Constraint::VALUE()))->setValue(aFilletRadius);
376 //std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
377 // aConstraint->attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT()))->setValue(
378 // isStart[0] ? aStartEndPnt[0] : aStartEndPnt[1]);
379 //aConstraint->execute();
380 //myProducedFeatures.push_back(aConstraint);
381 //ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
382 // 3. Tangency of fillet arc and features
383 for (int i = 0; i < aNbFeatures; i++) {
384 aConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID());
385 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
386 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
387 aRefAttr->setObject(aResultArc->lastResult());
388 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
389 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
390 bool isArc = aResultFeatures[i]->getKind() == SketchPlugin_Arc::ID();
391 aRefAttr->setObject(isArc ? aResultFeatures[i]->lastResult() : aResultFeatures[i]->firstResult());
392 aConstraint->execute();
393 aFilletFeatures.resultConstraints.push_back(aConstraint);
394 ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
396 // 4. Coincidence of free boundaries of base and copied features
397 for (int i = 0; i < aNbFeatures; i++) {
398 anAttrInd = 2*i + (isStart[i] ? 1 : 0);
399 aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
400 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
401 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
402 aRefAttr->setAttr(aBaseFeatures[i]->attribute(aFeatAttributes[anAttrInd]));
403 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
404 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
405 aRefAttr->setAttr(aResultFeatures[i]->attribute(aFeatAttributes[anAttrInd]));
406 aFilletFeatures.resultConstraints.push_back(aConstraint);
408 // 4.1. Additional tangency constraints when the fillet is based on arcs.
409 // It is used to verify the created arc will be placed on a source.
410 for (int i = 0; i < aNbFeatures; ++i) {
411 if (aResultFeatures[i]->getKind() != SketchPlugin_Arc::ID())
413 aConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID());
414 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
415 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
416 aRefAttr->setObject(aBaseFeatures[i]->lastResult());
417 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
418 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
419 aRefAttr->setObject(aResultFeatures[i]->lastResult());
420 aConstraint->execute();
421 aFilletFeatures.resultConstraints.push_back(aConstraint);
422 ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
424 // 5. Tangent points should be placed on the base features
425 for (int i = 0; i < aNbFeatures; i++) {
426 anAttrInd = 2*i + (isStart[i] ? 0 : 1);
427 aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
428 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
429 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
430 aRefAttr->setAttr(aResultFeatures[i]->attribute(aFeatAttributes[anAttrInd]));
431 aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
432 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
433 aRefAttr->setObject(aBaseFeatures[i]->lastResult());
434 aFilletFeatures.resultConstraints.push_back(aConstraint);
436 // make base features auxiliary
437 aBaseEdgeA->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true);
438 aBaseEdgeB->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true);
440 // exchange the naming IDs of newly created and old line that become auxiliary
441 sketch()->exchangeIDs(aBaseEdgeA, aResultEdgeA);
442 sketch()->exchangeIDs(aBaseEdgeB, aResultEdgeB);
444 // store point and features in the map.
445 myPointFeaturesMap[aPointAttr] = aFilletFeatures;
447 // Update radius value
448 int aNbSubs = sketch()->numberOfSubs();
449 FeaturePtr aSubFeature;
450 for (int aSub = 0; aSub < aNbSubs; aSub++) {
451 aSubFeature = sketch()->subFeature(aSub);
452 if (aSubFeature->getKind() != SketchPlugin_ConstraintRadius::ID())
454 AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
455 aSubFeature->attribute(SketchPlugin_Constraint::ENTITY_A()));
456 if (!aRefAttr || !aRefAttr->isObject())
458 FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object());
459 if (aFeature == aResultArc) {
460 AttributeDoublePtr aRadius = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
461 aSubFeature->attribute(SketchPlugin_Constraint::VALUE()));
462 aRadius->setValue(aFilletRadius);
469 // Send events to update the sub-features by the solver.
470 if(isUpdateFlushed) {
471 Events_Loop::loop()->setFlushed(anUpdateEvent, true);
475 void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
477 if(theID == SketchPlugin_Constraint::ENTITY_A()) {
478 if(myListOfPointsChangedInCode) {
482 // Clear list of new points.
485 // Get list of points for fillets and current radius.
486 AttributeRefAttrListPtr aRefListOfFilletPoints = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttrList>(
487 data()->attribute(SketchPlugin_Constraint::ENTITY_A()));
488 AttributeDoublePtr aRadiusAttribute = real(VALUE());
489 double aPrevRadius = aRadiusAttribute->value();
490 int aListSize = aRefListOfFilletPoints->size();
492 // If list is empty reset radius to zero (if it was not changed by user).
493 if(!myRadiusChangedByUser) {
494 myRadiusChangedInCode = true;
495 aRadiusAttribute->setValue(0);
496 myRadiusChangedInCode = false;
504 // Iterate over points to get base lines an calculate radius for fillets.
505 double aMinimumRadius = 0;
506 std::list<std::pair<ObjectPtr, AttributePtr>> aSelectedPointsList = aRefListOfFilletPoints->list();
507 std::list<std::pair<ObjectPtr, AttributePtr>>::iterator anIter = aSelectedPointsList.begin();
508 std::set<AttributePtr> aPointsToSkeep;
509 for(int anIndex = 0; anIndex < aListSize; anIndex++, anIter++) {
510 AttributePtr aFilletPointAttr = (*anIter).second;
511 std::shared_ptr<GeomDataAPI_Point2D> aFilletPoint2D =
512 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aFilletPointAttr);
513 if(!aFilletPoint2D.get()) {
515 setError("Error: One of the selected points is invalid.");
519 // If point was previously selected, skip it.
520 if(myPointFeaturesMap.find(aFilletPointAttr) != myPointFeaturesMap.end()) {
521 myNewPoints.insert(aFilletPointAttr);
522 aMinimumRadius = aPrevRadius;
526 // If point or coincident point is already in list remove it from attribute.
527 if(aPointsToSkeep.find(aFilletPointAttr) != aPointsToSkeep.end()) {
528 myListOfPointsChangedInCode = true;
529 aRefListOfFilletPoints->remove(aFilletPointAttr);
530 myListOfPointsChangedInCode = false;
534 // Obtain constraint coincidence for the fillet point.
535 FeaturePtr aConstraintCoincidence;
536 const std::set<AttributePtr>& aRefsList = aFilletPointAttr->owner()->data()->refsToMe();
537 for(std::set<AttributePtr>::const_iterator anIt = aRefsList.cbegin(); anIt != aRefsList.cend(); ++anIt) {
538 std::shared_ptr<ModelAPI_Attribute> anAttr = (*anIt);
539 FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->owner());
540 if(aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
541 AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
542 aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_A()));
543 AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
544 aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_B()));
545 if(anAttrRefA.get()) {
546 AttributePtr anAttrA = anAttrRefA->attr();
547 if(aFilletPointAttr == anAttrA) {
548 aConstraintCoincidence = aConstrFeature;
552 if(anAttrRefB.get()) {
553 AttributePtr anAttrB = anAttrRefB->attr();
554 if(aFilletPointAttr == anAttrB) {
555 aConstraintCoincidence = aConstrFeature;
562 if(!aConstraintCoincidence.get()) {
564 setError("Error: No coincident edges at one of the selected points.");
568 // Get coincides from constraint.
569 std::set<FeaturePtr> aCoincides;
572 SketchPlugin_Tools::findCoincidences(aConstraintCoincidence,
573 SketchPlugin_ConstraintCoincidence::ENTITY_A(),
575 SketchPlugin_Tools::findCoincidences(aConstraintCoincidence,
576 SketchPlugin_ConstraintCoincidence::ENTITY_B(),
579 // Remove points from set of coincides. Also get all attributes which is equal to this point to exclude it.
580 std::shared_ptr<GeomAPI_Pnt2d> aFilletPnt2d = aFilletPoint2D->pnt();
581 std::set<FeaturePtr> aNewSetOfCoincides;
582 for(std::set<FeaturePtr>::iterator anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
583 std::string aFeatureKind = (*anIt)->getKind();
584 if(aFeatureKind == SketchPlugin_Point::ID()) {
585 AttributePtr anAttr = (*anIt)->attribute(SketchPlugin_Point::COORD_ID());
586 std::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
587 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttr);
588 if(aPoint2D.get() && aFilletPnt2d->isEqual(aPoint2D->pnt())) {
589 aPointsToSkeep.insert(anAttr);
591 } else if(aFeatureKind == SketchPlugin_Line::ID()) {
592 AttributePtr anAttrStart = (*anIt)->attribute(SketchPlugin_Line::START_ID());
593 std::shared_ptr<GeomDataAPI_Point2D> aPointStart2D =
594 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttrStart);
595 if(aPointStart2D.get() && aFilletPnt2d->isEqual(aPointStart2D->pnt())) {
596 aPointsToSkeep.insert(anAttrStart);
598 AttributePtr anAttrEnd = (*anIt)->attribute(SketchPlugin_Line::END_ID());
599 std::shared_ptr<GeomDataAPI_Point2D> aPointEnd2D =
600 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttrEnd);
601 if(aPointEnd2D.get() && aFilletPnt2d->isEqual(aPointEnd2D->pnt())) {
602 aPointsToSkeep.insert(anAttrEnd);
604 aNewSetOfCoincides.insert(*anIt);
605 } else if(aFeatureKind == SketchPlugin_Arc::ID() ) {
606 AttributePtr anAttrStart = (*anIt)->attribute(SketchPlugin_Arc::START_ID());
607 std::shared_ptr<GeomDataAPI_Point2D> aPointStart2D =
608 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttrStart);
609 if(aPointStart2D.get() && aFilletPnt2d->isEqual(aPointStart2D->pnt())) {
610 aPointsToSkeep.insert(anAttrStart);
612 AttributePtr anAttrEnd = (*anIt)->attribute(SketchPlugin_Arc::END_ID());
613 std::shared_ptr<GeomDataAPI_Point2D> aPointEnd2D =
614 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttrEnd);
615 if(aPointEnd2D.get() && aFilletPnt2d->isEqual(aPointEnd2D->pnt())) {
616 aPointsToSkeep.insert(anAttrEnd);
618 aNewSetOfCoincides.insert(*anIt);
621 aCoincides = aNewSetOfCoincides;
623 // If we still have more than two coincides remove auxilary entities from set of coincides.
624 if(aCoincides.size() > 2) {
625 aNewSetOfCoincides.clear();
626 for(std::set<FeaturePtr>::iterator anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
627 if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) {
628 aNewSetOfCoincides.insert(*anIt);
631 aCoincides = aNewSetOfCoincides;
634 if(aCoincides.size() != 2) {
636 setError("Error: One of the selected points does not have two suitable edges for fillet.");
640 // Store base point for fillet.
641 aPointsToSkeep.insert(aFilletPointAttr);
642 myNewPoints.insert(aFilletPointAttr);
644 // Get base lines for fillet.
645 FeaturePtr anOldFeatureA, anOldFeatureB;
646 std::set<FeaturePtr>::iterator aLinesIt = aCoincides.begin();
647 anOldFeatureA = *aLinesIt++;
648 anOldFeatureB = *aLinesIt;
650 // Getting radius value if it was not changed by user.
651 if(!myRadiusChangedByUser) {
652 // Getting points located at 1/3 of edge length from fillet point.
653 std::shared_ptr<GeomAPI_Pnt2d> aFilletPnt2d = aFilletPoint2D->pnt();
654 std::shared_ptr<GeomAPI_Pnt2d> aPntA, aPntB;
655 getPointOnEdge(anOldFeatureA, aFilletPnt2d, aPntA);
656 getPointOnEdge(anOldFeatureB, aFilletPnt2d, aPntB);
658 /// Getting distances.
659 double aDistanceA = getProjectionDistance(anOldFeatureB, aPntA);
660 double aDistanceB = getProjectionDistance(anOldFeatureA, aPntB);
661 double aRadius = aDistanceA < aDistanceB ? aDistanceA / 2.0 : aDistanceB / 2.0;
662 aMinimumRadius = aMinimumRadius == 0 ? aRadius : aRadius < aMinimumRadius ? aRadius : aMinimumRadius;
666 if(abs(aPrevRadius - aMinimumRadius) > tolerance) {
667 clearResults(); // if radius changed clear all results;
670 // Set new default radius if it was not changed by user.
671 if(!myRadiusChangedByUser) {
672 myRadiusChangedInCode = true;
673 aRadiusAttribute->setValue(aMinimumRadius);
674 myRadiusChangedInCode = false;
677 } else if(theID == SketchPlugin_Constraint::VALUE()) {
678 if(myRadiusInitialized && !myRadiusChangedInCode) {
679 myRadiusChangedByUser = true;
681 if(!myRadiusInitialized) {
682 myRadiusInitialized = true;
687 AISObjectPtr SketchPlugin_ConstraintFillet::getAISObject(AISObjectPtr thePrevious)
692 AISObjectPtr anAIS = thePrevious;
693 /// TODO: Equal constraint presentation should be put here
697 bool SketchPlugin_ConstraintFillet::isMacro() const
702 void SketchPlugin_ConstraintFillet::clearResults()
704 // Clear auxiliary flag on initial objects.
705 for(std::map<AttributePtr, FilletFeatures>::iterator aPointsIter = myPointFeaturesMap.begin();
706 aPointsIter != myPointFeaturesMap.end();) {
707 const FilletFeatures& aFilletFeatures = aPointsIter->second;
708 std::list<FeaturePtr>::const_iterator aFeatureIt;
709 for(aFeatureIt = aFilletFeatures.baseEdges.cbegin();
710 aFeatureIt != aFilletFeatures.baseEdges.cend();
712 (*aFeatureIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(false);
717 // And remove all produced features.
718 DocumentPtr aDoc = sketch()->document();
719 for(std::map<AttributePtr, FilletFeatures>::iterator aPointsIter = myPointFeaturesMap.begin();
720 aPointsIter != myPointFeaturesMap.end();) {
721 // Remove all produced constraints.
722 const FilletFeatures& aFilletFeatures = aPointsIter->second;
723 std::list<FeaturePtr>::const_iterator aFeatureIt;
724 for(aFeatureIt = aFilletFeatures.resultConstraints.cbegin();
725 aFeatureIt != aFilletFeatures.resultConstraints.cend();
727 aDoc->removeFeature(*aFeatureIt);
730 // Remove all result edges.
731 for(aFeatureIt = aFilletFeatures.resultEdges.cbegin();
732 aFeatureIt != aFilletFeatures.resultEdges.cend();
734 aDoc->removeFeature(*aFeatureIt);
737 // Remove point from map.
738 myPointFeaturesMap.erase(aPointsIter++);
743 // ========= Auxiliary functions =================
744 void recalculateAttributes(FeaturePtr theNewArc, const std::string& theNewArcAttribute,
745 FeaturePtr theFeature, const std::string& theFeatureAttribute)
747 std::shared_ptr<GeomAPI_Pnt2d> anArcPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
748 theNewArc->attribute(theNewArcAttribute))->pnt();
749 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
750 theFeature->attribute(theFeatureAttribute))->setValue(anArcPoint->x(), anArcPoint->y());
753 /// \brief Find intersections of lines shifted along normal direction
754 void possibleFilletCenterLineLine(
755 std::shared_ptr<GeomAPI_XY> thePointA, std::shared_ptr<GeomAPI_Dir2d> theDirA,
756 std::shared_ptr<GeomAPI_XY> thePointB, std::shared_ptr<GeomAPI_Dir2d> theDirB,
757 double theRadius, std::list< std::shared_ptr<GeomAPI_XY> >& theCenters)
759 std::shared_ptr<GeomAPI_Dir2d> aDirAT(new GeomAPI_Dir2d(-theDirA->y(), theDirA->x()));
760 std::shared_ptr<GeomAPI_Dir2d> aDirBT(new GeomAPI_Dir2d(-theDirB->y(), theDirB->x()));
761 std::shared_ptr<GeomAPI_XY> aPntA, aPntB;
762 double aDet = theDirA->cross(theDirB);
763 for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) {
764 aPntA = thePointA->added(aDirAT->xy()->multiplied(aStepA * theRadius));
765 for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) {
766 aPntB = thePointB->added(aDirBT->xy()->multiplied(aStepB * theRadius));
767 double aVX = aDirAT->xy()->dot(aPntA);
768 double aVY = aDirBT->xy()->dot(aPntB);
769 std::shared_ptr<GeomAPI_XY> aPoint(new GeomAPI_XY(
770 (theDirB->x() * aVX - theDirA->x() * aVY) / aDet,
771 (theDirB->y() * aVX - theDirA->y() * aVY) / aDet));
772 theCenters.push_back(aPoint);
777 /// \brief Find intersections of line shifted along normal direction in both sides
778 /// and a circle with extended radius
779 void possibleFilletCenterLineArc(
780 std::shared_ptr<GeomAPI_XY> theStartLine, std::shared_ptr<GeomAPI_Dir2d> theDirLine,
781 std::shared_ptr<GeomAPI_XY> theCenterArc, double theRadiusArc,
782 double theRadius, std::list< std::shared_ptr<GeomAPI_XY> >& theCenters)
784 std::shared_ptr<GeomAPI_Dir2d> aDirT(new GeomAPI_Dir2d(-theDirLine->y(), theDirLine->x()));
785 std::shared_ptr<GeomAPI_XY> aPnt;
786 double aDirNorm2 = theDirLine->dot(theDirLine);
788 double aDirX = theDirLine->x();
789 double aDirX2 = theDirLine->x() * theDirLine->x();
790 double aDirY2 = theDirLine->y() * theDirLine->y();
791 double aDirXY = theDirLine->x() * theDirLine->y();
792 for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) {
793 aPnt = theStartLine->added(aDirT->xy()->multiplied(aStepA * theRadius));
794 double aCoeff = aDirT->xy()->dot(aPnt->decreased(theCenterArc));
795 double aCoeff2 = aCoeff * aCoeff;
796 for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) {
797 aRad = theRadiusArc + aStepB * theRadius;
798 double aD = aRad * aRad * aDirNorm2 - aCoeff2;
801 double aDs = sqrt(aD);
802 double x1 = theCenterArc->x() + (aCoeff * aDirT->x() - aDirT->y() * aDs) / aDirNorm2;
803 double x2 = theCenterArc->x() + (aCoeff * aDirT->x() + aDirT->y() * aDs) / aDirNorm2;
804 double y1 = (aDirX2 * aPnt->y() + aDirY2 * theCenterArc->y() -
805 aDirXY * (aPnt->x() - theCenterArc->x()) - theDirLine->y() * aDs) / aDirNorm2;
806 double y2 = (aDirX2 * aPnt->y() + aDirY2 * theCenterArc->y() -
807 aDirXY * (aPnt->x() - theCenterArc->x()) + theDirLine->y() * aDs) / aDirNorm2;
809 std::shared_ptr<GeomAPI_XY> aPoint1(new GeomAPI_XY(x1, y1));
810 theCenters.push_back(aPoint1);
811 std::shared_ptr<GeomAPI_XY> aPoint2(new GeomAPI_XY(x2, y2));
812 theCenters.push_back(aPoint2);
817 /// \brief Find intersections of two circles with extended radii
818 void possibleFilletCenterArcArc(
819 std::shared_ptr<GeomAPI_XY> theCenterA, double theRadiusA,
820 std::shared_ptr<GeomAPI_XY> theCenterB, double theRadiusB,
821 double theRadius, std::list< std::shared_ptr<GeomAPI_XY> >& theCenters)
823 std::shared_ptr<GeomAPI_XY> aCenterDir = theCenterB->decreased(theCenterA);
824 double aCenterDist2 = aCenterDir->dot(aCenterDir);
825 double aCenterDist = sqrt(aCenterDist2);
828 for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) {
829 aRadA = theRadiusA + aStepA * theRadius;
830 for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) {
831 aRadB = theRadiusB + aStepB * theRadius;
832 if (aRadA + aRadB < aCenterDist || fabs(aRadA - aRadB) > aCenterDist)
833 continue; // there is no intersections
835 double aMedDist = (aRadA * aRadA - aRadB * aRadB + aCenterDist2) / (2.0 * aCenterDist);
836 double aHeight = sqrt(aRadA * aRadA - aMedDist * aMedDist);
838 double x1 = theCenterA->x() + (aMedDist * aCenterDir->x() + aCenterDir->y() * aHeight) / aCenterDist;
839 double y1 = theCenterA->y() + (aMedDist * aCenterDir->y() - aCenterDir->x() * aHeight) / aCenterDist;
841 double x2 = theCenterA->x() + (aMedDist * aCenterDir->x() - aCenterDir->y() * aHeight) / aCenterDist;
842 double y2 = theCenterA->y() + (aMedDist * aCenterDir->y() + aCenterDir->x() * aHeight) / aCenterDist;
844 std::shared_ptr<GeomAPI_XY> aPoint1(new GeomAPI_XY(x1, y1));
845 theCenters.push_back(aPoint1);
846 std::shared_ptr<GeomAPI_XY> aPoint2(new GeomAPI_XY(x2, y2));
847 theCenters.push_back(aPoint2);
852 void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB,
853 double theRadius, bool theNotInversed[2],
854 std::shared_ptr<GeomAPI_XY>& theCenter,
855 std::shared_ptr<GeomAPI_XY>& theTangentA,
856 std::shared_ptr<GeomAPI_XY>& theTangentB)
858 static const int aNbFeatures = 2;
859 FeaturePtr aFeature[aNbFeatures] = {theFeatureA, theFeatureB};
860 std::shared_ptr<GeomAPI_XY> aStart[aNbFeatures], aEnd[aNbFeatures], aCenter[aNbFeatures];
861 std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, aEndPoint;
863 for (int i = 0; i < aNbFeatures; i++) {
864 if (aFeature[i]->getKind() == SketchPlugin_Line::ID()) {
865 aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
866 aFeature[i]->attribute(SketchPlugin_Line::START_ID()));
867 aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
868 aFeature[i]->attribute(SketchPlugin_Line::END_ID()));
869 } else if (aFeature[i]->getKind() == SketchPlugin_Arc::ID()) {
870 aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
871 aFeature[i]->attribute(SketchPlugin_Arc::START_ID()));
872 aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
873 aFeature[i]->attribute(SketchPlugin_Arc::END_ID()));
874 aCenter[i] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
875 aFeature[i]->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt()->xy();
878 aStart[i] = std::shared_ptr<GeomAPI_XY>(theNotInversed[i] ?
879 new GeomAPI_XY(aStartPoint->x(), aStartPoint->y()) :
880 new GeomAPI_XY(aEndPoint->x(), aEndPoint->y()));
881 aEnd[i] = std::shared_ptr<GeomAPI_XY>(theNotInversed[i] ?
882 new GeomAPI_XY(aEndPoint->x(), aEndPoint->y()) :
883 new GeomAPI_XY(aStartPoint->x(), aStartPoint->y()));
886 if (theFeatureA->getKind() == SketchPlugin_Line::ID() &&
887 theFeatureB->getKind() == SketchPlugin_Line::ID()) {
888 std::shared_ptr<GeomAPI_Dir2d> aDir[2];
889 std::shared_ptr<GeomAPI_Dir2d> aDirT[2];
890 for (int i = 0; i < aNbFeatures; i++) {
891 aDir[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(aEnd[i]->decreased(aStart[i])));
892 aDirT[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(-aDir[i]->y(), aDir[i]->x()));
895 // get and filter possible centers
896 std::list< std::shared_ptr<GeomAPI_XY> > aSuspectCenters;
897 possibleFilletCenterLineLine(aStart[0], aDir[0], aStart[1], aDir[1], theRadius, aSuspectCenters);
899 std::list< std::shared_ptr<GeomAPI_XY> >::iterator anIt = aSuspectCenters.begin();
900 for (; anIt != aSuspectCenters.end(); anIt++) {
901 aDot = aDirT[0]->xy()->dot(aStart[0]->decreased(*anIt));
902 theTangentA = (*anIt)->added(aDirT[0]->xy()->multiplied(aDot));
903 if (theTangentA->decreased(aStart[0])->dot(aDir[0]->xy()) < 0.0)
904 continue; // incorrect position
905 aDot = aDirT[1]->xy()->dot(aStart[1]->decreased(*anIt));
906 theTangentB = (*anIt)->added(aDirT[1]->xy()->multiplied(aDot));
907 if (theTangentB->decreased(aStart[1])->dot(aDir[1]->xy()) < 0.0)
908 continue; // incorrect position
909 // the center is found, stop searching
913 } else if ((theFeatureA->getKind() == SketchPlugin_Arc::ID() &&
914 theFeatureB->getKind() == SketchPlugin_Line::ID()) ||
915 (theFeatureA->getKind() == SketchPlugin_Line::ID() &&
916 theFeatureB->getKind() == SketchPlugin_Arc::ID())) {
917 int aLineInd = theFeatureA->getKind() == SketchPlugin_Line::ID() ? 0 : 1;
918 double anArcRadius = aStart[1-aLineInd]->distance(aCenter[1-aLineInd]);
919 std::shared_ptr<GeomAPI_Dir2d> aDirLine = std::shared_ptr<GeomAPI_Dir2d>(
920 new GeomAPI_Dir2d(aEnd[aLineInd]->decreased(aStart[aLineInd])));
921 std::shared_ptr<GeomAPI_Dir2d> aDirT = std::shared_ptr<GeomAPI_Dir2d>(
922 new GeomAPI_Dir2d(-aDirLine->y(), aDirLine->x()));
924 std::shared_ptr<GeomAPI_Dir2d> aStartArcDir = std::shared_ptr<GeomAPI_Dir2d>(
925 new GeomAPI_Dir2d(aStart[1-aLineInd]->decreased(aCenter[1-aLineInd])));
926 std::shared_ptr<GeomAPI_Dir2d> aEndArcDir = std::shared_ptr<GeomAPI_Dir2d>(
927 new GeomAPI_Dir2d(aEnd[1-aLineInd]->decreased(aCenter[1-aLineInd])));
928 double anArcAngle = aEndArcDir->angle(aStartArcDir);
930 // get possible centers and filter them
931 std::list< std::shared_ptr<GeomAPI_XY> > aSuspectCenters;
932 possibleFilletCenterLineArc(aStart[aLineInd], aDirLine, aCenter[1-aLineInd], anArcRadius, theRadius, aSuspectCenters);
934 // the line is forward into the arc
935 double innerArc = aCenter[1-aLineInd]->decreased(aStart[aLineInd])->dot(aDirLine->xy());
936 std::shared_ptr<GeomAPI_XY> aLineTgPoint, anArcTgPoint;
937 // The possible centers are ranged by their positions.
938 // If the point is not satisfy one of criteria, the weight is decreased with penalty.
940 std::list< std::shared_ptr<GeomAPI_XY> >::iterator anIt = aSuspectCenters.begin();
941 for (; anIt != aSuspectCenters.end(); anIt++) {
943 aDot = aDirT->xy()->dot(aStart[aLineInd]->decreased(*anIt));
944 aLineTgPoint = (*anIt)->added(aDirT->xy()->multiplied(aDot));
945 // Check the point is placed on the correct arc (penalty if false)
946 if (aCenter[1-aLineInd]->distance(*anIt) * innerArc > anArcRadius * innerArc)
948 std::shared_ptr<GeomAPI_Dir2d> aCurDir = std::shared_ptr<GeomAPI_Dir2d>(
949 new GeomAPI_Dir2d((*anIt)->decreased(aCenter[1-aLineInd])));
950 double aCurAngle = aCurDir->angle(aStartArcDir);
951 if (anArcAngle < 0.0) aCurAngle *= -1.0;
952 if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle))
954 if (aWeight > aBestWeight)
955 aBestWeight = aWeight;
956 else if (aWeight < aBestWeight ||
957 aStart[aLineInd]->distance(*anIt) >
958 aStart[aLineInd]->distance(theCenter)) // <-- take closer point
960 // the center is found, stop searching
962 anArcTgPoint = aCenter[1-aLineInd]->added(aCurDir->xy()->multiplied(anArcRadius));
963 if (theFeatureA->getKind() == SketchPlugin_Line::ID()) {
964 theTangentA = aLineTgPoint;
965 theTangentB = anArcTgPoint;
967 theTangentA = anArcTgPoint;
968 theTangentB = aLineTgPoint;
972 } else if (theFeatureA->getKind() == SketchPlugin_Arc::ID() &&
973 theFeatureB->getKind() == SketchPlugin_Arc::ID()) {
974 double anArcRadius[aNbFeatures];
975 double anArcAngle[aNbFeatures];
976 std::shared_ptr<GeomAPI_Dir2d> aStartArcDir[aNbFeatures];
977 for (int i = 0; i < aNbFeatures; i++) {
978 anArcRadius[i] = aStart[i]->distance(aCenter[i]);
979 aStartArcDir[i] = std::shared_ptr<GeomAPI_Dir2d>(
980 new GeomAPI_Dir2d(aStart[i]->decreased(aCenter[i])));
981 std::shared_ptr<GeomAPI_Dir2d> aEndArcDir = std::shared_ptr<GeomAPI_Dir2d>(
982 new GeomAPI_Dir2d(aEnd[i]->decreased(aCenter[i])));
983 anArcAngle[i] = aEndArcDir->angle(aStartArcDir[i]);
986 // get and filter possible centers
987 std::list< std::shared_ptr<GeomAPI_XY> > aSuspectCenters;
988 possibleFilletCenterArcArc(aCenter[0], anArcRadius[0], aCenter[1], anArcRadius[1], theRadius, aSuspectCenters);
990 std::shared_ptr<GeomAPI_XY> aLineTgPoint, anArcTgPoint;
991 std::list< std::shared_ptr<GeomAPI_XY> >::iterator anIt = aSuspectCenters.begin();
992 for (; anIt != aSuspectCenters.end(); anIt++) {
993 std::shared_ptr<GeomAPI_Dir2d> aCurDir = std::shared_ptr<GeomAPI_Dir2d>(
994 new GeomAPI_Dir2d((*anIt)->decreased(aCenter[0])));
995 double aCurAngle = aCurDir->angle(aStartArcDir[0]);
996 if (anArcAngle[0] < 0.0) aCurAngle *= -1.0;
997 if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle[0]))
998 continue; // incorrect position
999 theTangentA = aCenter[0]->added(aCurDir->xy()->multiplied(anArcRadius[0]));
1001 aCurDir = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d((*anIt)->decreased(aCenter[1])));
1002 aCurAngle = aCurDir->angle(aStartArcDir[1]);
1003 if (anArcAngle[1] < 0.0) aCurAngle *= -1.0;
1004 if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle[1]))
1005 continue; // incorrect position
1006 theTangentB = aCenter[1]->added(aCurDir->xy()->multiplied(anArcRadius[1]));
1008 // the center is found, stop searching
1015 void getPointOnEdge(const FeaturePtr theFeature,
1016 const std::shared_ptr<GeomAPI_Pnt2d> theFilletPoint,
1017 std::shared_ptr<GeomAPI_Pnt2d>& thePoint) {
1018 if(theFeature->getKind() == SketchPlugin_Line::ID()) {
1019 std::shared_ptr<GeomAPI_Pnt2d> aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1020 theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt();
1021 std::shared_ptr<GeomAPI_Pnt2d> aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1022 theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt();
1023 if(aPntStart->distance(theFilletPoint) > 1.e-7) {
1024 aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1025 theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt();
1026 aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1027 theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt();
1029 thePoint.reset( new GeomAPI_Pnt2d(aPntStart->xy()->added( aPntEnd->xy()->decreased( aPntStart->xy() )->multiplied(1.0 / 3.0) ) ) );
1031 std::shared_ptr<GeomAPI_Pnt2d> aPntTemp;
1032 std::shared_ptr<GeomAPI_Pnt2d> aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1033 theFeature->attribute(SketchPlugin_Arc::START_ID()))->pnt();
1034 std::shared_ptr<GeomAPI_Pnt2d> aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1035 theFeature->attribute(SketchPlugin_Arc::END_ID()))->pnt();
1036 if(theFeature->attribute(SketchPlugin_Arc::INVERSED_ID())) {
1037 aPntTemp = aPntStart;
1038 aPntStart = aPntEnd;
1041 std::shared_ptr<GeomAPI_Pnt2d> aCenterPnt = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1042 theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt();
1043 std::shared_ptr<GeomAPI_Circ2d> aCirc(new GeomAPI_Circ2d(aCenterPnt, aPntStart));
1044 double aStartParameter(0), anEndParameter(0);
1045 aCirc->parameter(aPntStart, paramTolerance, aStartParameter);
1046 aCirc->parameter(aPntEnd, paramTolerance, anEndParameter);
1047 if(aPntStart->distance(theFilletPoint) > tolerance) {
1048 double aTmpParameter = aStartParameter;
1049 aStartParameter = anEndParameter;
1050 anEndParameter = aTmpParameter;
1052 double aPntParameter = aStartParameter + (anEndParameter - aStartParameter) / 3.0;
1053 aCirc->D0(aPntParameter, thePoint);
1057 double getProjectionDistance(const FeaturePtr theFeature,
1058 const std::shared_ptr<GeomAPI_Pnt2d> thePoint)
1060 std::shared_ptr<GeomAPI_Pnt2d> aProjectPnt;
1061 if(theFeature->getKind() == SketchPlugin_Line::ID()) {
1062 std::shared_ptr<GeomAPI_Pnt2d> aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1063 theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt();
1064 std::shared_ptr<GeomAPI_Pnt2d> aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1065 theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt();
1066 std::shared_ptr<GeomAPI_Lin2d> aLin(new GeomAPI_Lin2d(aPntStart, aPntEnd));
1067 aProjectPnt = aLin->project(thePoint);
1069 std::shared_ptr<GeomAPI_Pnt2d> aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1070 theFeature->attribute(SketchPlugin_Arc::START_ID()))->pnt();
1071 std::shared_ptr<GeomAPI_Pnt2d> aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1072 theFeature->attribute(SketchPlugin_Arc::END_ID()))->pnt();
1073 std::shared_ptr<GeomAPI_Pnt2d> aCenterPnt = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1074 theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt();
1075 std::shared_ptr<GeomAPI_Circ2d> aCirc(new GeomAPI_Circ2d(aCenterPnt, aPntStart));
1076 aProjectPnt = aCirc->project(thePoint);
1078 if(aProjectPnt.get()) {
1079 return aProjectPnt->distance(thePoint);
1084 std::set<FeaturePtr> getCoincides(const FeaturePtr& theConstraintCoincidence)
1086 std::set<FeaturePtr> aCoincides;
1088 SketchPlugin_Tools::findCoincidences(theConstraintCoincidence,
1089 SketchPlugin_ConstraintCoincidence::ENTITY_A(),
1091 SketchPlugin_Tools::findCoincidences(theConstraintCoincidence,
1092 SketchPlugin_ConstraintCoincidence::ENTITY_B(),
1095 // Remove points from set of coincides.
1096 std::set<FeaturePtr> aNewSetOfCoincides;
1097 for(std::set<FeaturePtr>::iterator anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
1098 if((*anIt)->getKind() == SketchPlugin_Line::ID() ||
1099 (*anIt)->getKind() == SketchPlugin_Arc::ID() ) {
1100 aNewSetOfCoincides.insert(*anIt);
1103 aCoincides = aNewSetOfCoincides;
1105 // If we still have more than two coincides remove auxilary entities from set of coincides.
1106 if(aCoincides.size() > 2) {
1107 aNewSetOfCoincides.clear();
1108 for(std::set<FeaturePtr>::iterator anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
1109 if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) {
1110 aNewSetOfCoincides.insert(*anIt);
1113 aCoincides = aNewSetOfCoincides;