1 #include <SketchSolver_ConstraintMirror.h>
2 #include <SketchSolver_Group.h>
3 #include <SketchSolver_Error.h>
5 #include <ModelAPI_AttributeDouble.h>
6 #include <ModelAPI_AttributeRefAttr.h>
7 #include <ModelAPI_AttributeRefList.h>
8 #include <ModelAPI_ResultConstruction.h>
10 #include <GeomAPI_Dir2d.h>
11 #include <GeomAPI_XY.h>
14 void SketchSolver_ConstraintMirror::getAttributes(
15 Slvs_Entity& theMirrorLine,
16 std::vector<Slvs_Entity>& theBaseEntities,
17 std::vector<Slvs_Entity>& theMirrorEntities)
19 DataPtr aData = myBaseConstraint->data();
20 AttributeRefAttrPtr aMirLineAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
21 myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
22 if (!aMirLineAttr || !aMirLineAttr->isInitialized() || !aMirLineAttr->isObject()) {
23 myErrorMsg = SketchSolver_Error::NOT_INITIALIZED();
26 int aType = SLVS_E_UNKNOWN; // type of created entity
27 Slvs_hEntity anEntity = myGroup->getAttributeId(aMirLineAttr);
28 if (anEntity == SLVS_E_UNKNOWN)
29 anEntity = changeEntity(aMirLineAttr, aType);
30 theMirrorLine = myStorage->getEntity(anEntity);
32 // Create SolveSpace entity for all features
33 AttributeRefListPtr aBaseRefList = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
34 myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
35 AttributeRefListPtr aMirroredRefList = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
36 myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_C()));
37 myNumberOfObjects = aMirroredRefList->size();
38 if (!aBaseRefList || !aMirroredRefList) {
39 myErrorMsg = SketchSolver_Error::INCORRECT_MIRROR_ATTRIBUTE();
43 std::list<ObjectPtr> aBaseList = aBaseRefList->list();
44 std::list<ObjectPtr> aMirroredList = aMirroredRefList->list();
47 ResultConstructionPtr aRC;
48 for (int i = 0; i < 2; i++) {
49 std::list<ObjectPtr>::iterator anIter = i == 0 ? aBaseList.begin() : aMirroredList.begin();
50 std::list<ObjectPtr>::iterator aEndIter = i == 0 ? aBaseList.end() : aMirroredList.end();
51 std::vector<Slvs_Entity>* aList = i == 0 ? &theBaseEntities : & theMirrorEntities;
52 for ( ; anIter != aEndIter; anIter++) {
53 aRC = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(*anIter);
54 aFeature = aRC ? aRC->document()->feature(aRC) :
55 std::dynamic_pointer_cast<ModelAPI_Feature>(*anIter);
59 anEntity = changeEntity(aFeature, aType);
60 // Sort entities by their type
61 std::vector<Slvs_Entity>::iterator anIt = aList->begin();
62 for (; anIt != aList->end(); anIt++)
63 if (aType < anIt->type)
65 // aList->push_back(myStorage->getEntity(anEntity));
66 aList->insert(anIt, myStorage->getEntity(anEntity));
70 if (theBaseEntities.size() > theMirrorEntities.size())
71 myErrorMsg = SketchSolver_Error::NOT_INITIALIZED();
74 void SketchSolver_ConstraintMirror::process()
77 if (!myBaseConstraint || !myStorage || myGroup == 0) {
78 /// TODO: Put error message here
81 if (!mySlvsConstraints.empty()) // some data is changed, update constraint
82 update(myBaseConstraint);
84 Slvs_Entity aMirrorLine;
85 std::vector<Slvs_Entity> aBaseList;
86 std::vector<Slvs_Entity> aMirrorList;
87 getAttributes(aMirrorLine, aBaseList, aMirrorList);
88 if (!myErrorMsg.empty())
91 if (aBaseList.size() != aMirrorList.size()) {
92 myErrorMsg = SketchSolver_Error::INCORRECT_MIRROR_ATTRIBUTE();
96 Slvs_Constraint aConstraint;
97 // Get coordinates of mirror line points for speed up
99 for (int i = 0; i < 2; i++) {
100 Slvs_Entity aPoint = myStorage->getEntity(aMirrorLine.point[i]);
101 for (int j = 0; j < 2; j++)
102 aStartEnd[2*i+j] = myStorage->getParameter(aPoint.param[j]).val;
105 std::vector<Slvs_Entity>::iterator aBaseIter = aBaseList.begin();
106 std::vector<Slvs_Entity>::iterator aMirrorIter = aMirrorList.begin();
107 for (; aBaseIter != aBaseList.end(); aBaseIter++, aMirrorIter++) {
108 // Make aMirrorEnt parameters to be symmetric with aBaseEnt
109 makeMirrorEntity(*aBaseIter, *aMirrorIter, aStartEnd);
111 if (aBaseIter->type == SLVS_E_POINT_IN_2D) {
112 aConstraint = Slvs_MakeConstraint(
113 SLVS_E_UNKNOWN, myGroup->getId(), getType(), myGroup->getWorkplaneId(),
114 0.0, aBaseIter->h, aMirrorIter->h, aMirrorLine.h, SLVS_E_UNKNOWN);
115 aConstraint.h = myStorage->addConstraint(aConstraint);
116 mySlvsConstraints.push_back(aConstraint.h);
117 } else if (aBaseIter->type == SLVS_E_LINE_SEGMENT) {
118 for (int i = 0; i < 2; i++) {
119 aConstraint = Slvs_MakeConstraint(
120 SLVS_E_UNKNOWN, myGroup->getId(), getType(), myGroup->getWorkplaneId(), 0.0,
121 aBaseIter->point[i], aMirrorIter->point[i], aMirrorLine.h, SLVS_E_UNKNOWN);
122 aConstraint.h = myStorage->addConstraint(aConstraint);
123 mySlvsConstraints.push_back(aConstraint.h);
125 } else if (aBaseIter->type == SLVS_E_CIRCLE) {
126 aConstraint = Slvs_MakeConstraint(
127 SLVS_E_UNKNOWN, myGroup->getId(), getType(), myGroup->getWorkplaneId(), 0.0,
128 aBaseIter->point[0], aMirrorIter->point[0], aMirrorLine.h, SLVS_E_UNKNOWN);
129 aConstraint.h = myStorage->addConstraint(aConstraint);
130 mySlvsConstraints.push_back(aConstraint.h);
131 // Additional constraint for equal radii
132 Slvs_Constraint anEqRadConstr = Slvs_MakeConstraint(
133 SLVS_E_UNKNOWN, myGroup->getId(), SLVS_C_EQUAL_RADIUS, myGroup->getWorkplaneId(),
134 0.0, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, aBaseIter->h, aMirrorIter->h);
135 anEqRadConstr.h = myStorage->addConstraint(anEqRadConstr);
136 mySlvsConstraints.push_back(anEqRadConstr.h);
137 } else if (aBaseIter->type == SLVS_E_ARC_OF_CIRCLE) {
138 // Workaround to avoid problems in SolveSpace.
139 // The symmetry of two arcs will be done using symmetry of three points on these arcs:
140 // start point, end point, and any other point on the arc
141 Slvs_hEntity aBaseArcPoints[3] = {
145 Slvs_hEntity aMirrorArcPoints[3] = { // indices of points of arc, center corresponds center, first point corresponds last point
146 aMirrorIter->point[2],
147 aMirrorIter->point[1],
150 Slvs_Entity aBothArcs[2] = {*aBaseIter, *aMirrorIter};
151 Slvs_hEntity aBothMiddlePoints[2];
152 for (int i = 0; i < 2; i++) {
154 calculateMiddlePoint(aBothArcs[i], 0.5, x, y);
155 Slvs_Param aParamX = Slvs_MakeParam(SLVS_E_UNKNOWN, myGroup->getId(), x);
156 Slvs_Param aParamY = Slvs_MakeParam(SLVS_E_UNKNOWN, myGroup->getId(), y);
157 aParamX.h = myStorage->addParameter(aParamX);
158 aParamY.h = myStorage->addParameter(aParamY);
159 Slvs_Entity aPoint = Slvs_MakePoint2d(SLVS_E_UNKNOWN, myGroup->getId(),
160 myGroup->getWorkplaneId(), aParamX.h, aParamY.h);
161 aBothMiddlePoints[i] = myStorage->addEntity(aPoint);
162 // additional constraint point-on-curve
163 Slvs_Constraint aPonCircConstr = Slvs_MakeConstraint(
164 SLVS_E_UNKNOWN, myGroup->getId(), SLVS_C_PT_ON_CIRCLE, myGroup->getWorkplaneId(),
165 0.0, aBothMiddlePoints[i], SLVS_E_UNKNOWN, aBothArcs[i].h, SLVS_E_UNKNOWN);
166 aPonCircConstr.h = myStorage->addConstraint(aPonCircConstr);
167 mySlvsConstraints.push_back(aPonCircConstr.h);
169 // additional constraint for the point to be in the middle of a base arc
170 Slvs_Entity aLine1 = Slvs_MakeLineSegment(SLVS_E_UNKNOWN, myGroup->getId(),
171 myGroup->getWorkplaneId(), aBothArcs[i].point[1], aBothMiddlePoints[i]);
172 aLine1.h = myStorage->addEntity(aLine1);
173 Slvs_Entity aLine2 = Slvs_MakeLineSegment(SLVS_E_UNKNOWN, myGroup->getId(),
174 myGroup->getWorkplaneId(), aBothArcs[i].point[2], aBothMiddlePoints[i]);
175 aLine2.h = myStorage->addEntity(aLine2);
176 Slvs_Constraint aMiddleConstr = Slvs_MakeConstraint(SLVS_E_UNKNOWN, myGroup->getId(),
177 SLVS_C_EQUAL_LENGTH_LINES, myGroup->getWorkplaneId(),
178 0.0, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, aLine1.h, aLine2.h);
179 aMiddleConstr.h = myStorage->addConstraint(aMiddleConstr);
180 mySlvsConstraints.push_back(aMiddleConstr.h);
184 aBaseArcPoints[2] = aBothMiddlePoints[0];
185 aMirrorArcPoints[2] = aBothMiddlePoints[1];
186 for (int ind = 0; ind < 3; ind++) {
187 Slvs_Constraint aConstraint = Slvs_MakeConstraint(
188 SLVS_E_UNKNOWN, myGroup->getId(), getType(), myGroup->getWorkplaneId(), 0.0,
189 aBaseArcPoints[ind], aMirrorArcPoints[ind], aMirrorLine.h, SLVS_E_UNKNOWN);
190 aConstraint.h = myStorage->addConstraint(aConstraint);
191 mySlvsConstraints.push_back(aConstraint.h);
198 void SketchSolver_ConstraintMirror::update(ConstraintPtr theConstraint)
201 if (!theConstraint || theConstraint == myBaseConstraint) {
202 AttributeRefListPtr aMirroredRefList = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
203 myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_C()));
204 if (aMirroredRefList->size() != myNumberOfObjects) {
205 remove(myBaseConstraint);
210 SketchSolver_Constraint::update();
213 bool SketchSolver_ConstraintMirror::remove(ConstraintPtr theConstraint)
216 if (theConstraint && theConstraint != myBaseConstraint)
218 bool isFullyRemoved = true;
219 std::vector<Slvs_hEntity>::iterator aCIter = mySlvsConstraints.begin();
220 for (; aCIter != mySlvsConstraints.end(); aCIter++)
221 isFullyRemoved = myStorage->removeConstraint(*aCIter) && isFullyRemoved;
222 mySlvsConstraints.clear();
224 std::map<FeaturePtr, Slvs_hEntity>::iterator aFeatIt = myFeatureMap.begin();
225 for (; aFeatIt != myFeatureMap.end(); aFeatIt++)
226 myStorage->removeEntity(aFeatIt->second);
228 if (isFullyRemoved) {
229 myFeatureMap.clear();
230 myAttributeMap.clear();
233 cleanRemovedEntities();
237 bool SketchSolver_ConstraintMirror::checkAttributesChanged(ConstraintPtr theConstraint)
239 // First of all, check the mirror line is changed.
240 // It may be changed to one of mirrored lines, which is already in this constraint
241 // (this case is not marked as attribute changing)
242 ConstraintPtr aConstraint = theConstraint ? theConstraint : myBaseConstraint;
243 AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
244 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
245 if (!aRefAttr || !aRefAttr->isObject() || !aRefAttr->object())
247 FeaturePtr aMirrorLine = ModelAPI_Feature::feature(aRefAttr->object());
251 std::map<FeaturePtr, Slvs_hEntity>::iterator aMirrorIter = myFeatureMap.find(aMirrorLine);
252 if (aMirrorIter == myFeatureMap.end())
255 // Check the entity is not used as mirror line
256 std::vector<Slvs_hConstraint>::iterator aCIter = mySlvsConstraints.begin();
257 for (; aCIter != mySlvsConstraints.end(); aCIter++) {
258 Slvs_Constraint aMirrorConstr = myStorage->getConstraint(*aCIter);
259 if (aMirrorConstr.type != SLVS_C_SYMMETRIC_LINE)
261 if (aMirrorConstr.entityA != aMirrorIter->second)
263 else break; // check just one symmetric constraint
267 return SketchSolver_Constraint::checkAttributesChanged(theConstraint);
270 void SketchSolver_ConstraintMirror::makeMirrorEntity(
271 const Slvs_Entity& theBase,
272 const Slvs_Entity& theMirror,
273 const double theMirrorLine[]) const
275 Slvs_hEntity aBasePoint[4];
276 Slvs_hEntity aMirrorPoint[4];
277 for (int i = 0; i < 4; i++) {
278 aBasePoint[i] = theBase.point[i];
279 aMirrorPoint[i] = theMirror.point[i];
281 if (theBase.type == SLVS_E_ARC_OF_CIRCLE) {
282 Slvs_hEntity aTmp = aMirrorPoint[2];
283 aMirrorPoint[2] = aMirrorPoint[1];
284 aMirrorPoint[1] = aTmp;
285 adjustArcPoints(theBase);
287 if (theBase.type == SLVS_E_POINT_IN_2D || theBase.type == SLVS_E_POINT_IN_3D) {
288 aBasePoint[0] = theBase.h;
289 aMirrorPoint[0] = theMirror.h;
292 // Mirror line parameters
293 std::shared_ptr<GeomAPI_XY> aLinePoints[2];
294 for (int i = 0; i < 2; i++)
295 aLinePoints[i] = std::shared_ptr<GeomAPI_XY>(
296 new GeomAPI_XY(theMirrorLine[2*i], theMirrorLine[2*i+1]));
297 // direction of a mirror line
298 std::shared_ptr<GeomAPI_Dir2d> aDir = std::shared_ptr<GeomAPI_Dir2d>(
299 new GeomAPI_Dir2d(aLinePoints[1]->added(aLinePoints[0]->multiplied(-1.0))));
300 // orthogonal direction
301 aDir = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(aDir->y(), -aDir->x()));
303 Slvs_hConstraint aFixed; // transient variable
304 for (int i = 0; i < 4; i++) {
305 if (aBasePoint[i] == SLVS_E_UNKNOWN || aMirrorPoint[i] == SLVS_E_UNKNOWN)
307 // check the mirror point is not fixed
308 if (myStorage->isPointFixed(aMirrorPoint[i], aFixed, true))
311 Slvs_Entity aPointEnt = myStorage->getEntity(aBasePoint[i]);
312 double aBaseX = myStorage->getParameter(aPointEnt.param[0]).val;
313 double aBaseY = myStorage->getParameter(aPointEnt.param[1]).val;
314 std::shared_ptr<GeomAPI_XY> aPoint = std::shared_ptr<GeomAPI_XY>(new GeomAPI_XY(aBaseX, aBaseY));
316 std::shared_ptr<GeomAPI_XY> aVec = std::shared_ptr<GeomAPI_XY>(
317 new GeomAPI_XY(aPoint->x() - aLinePoints[0]->x(), aPoint->y() - aLinePoints[0]->y()));
318 double aDist = aVec->dot(aDir->xy());
319 aPoint = aPoint->added(aDir->xy()->multiplied(-2.0 * aDist));
321 Slvs_Entity aMirrorEnt = myStorage->getEntity(aMirrorPoint[i]);
322 Slvs_Param aParam = Slvs_MakeParam(aMirrorEnt.param[0], myGroup->getId(), aPoint->x());
323 myStorage->updateParameter(aParam);
324 aParam = Slvs_MakeParam(aMirrorEnt.param[1], myGroup->getId(), aPoint->y());
325 myStorage->updateParameter(aParam);
329 void SketchSolver_ConstraintMirror::adjustArcPoints(const Slvs_Entity& theArc) const
333 double anArcParams[3][2];
334 for (int i = 0; i < 3; i++) {
335 aPoint = myStorage->getEntity(theArc.point[i]);
336 for (int j = 0; j < 2; j++) {
337 aParam = myStorage->getParameter(aPoint.param[j]);
338 anArcParams[i][j] = aParam.val;
340 anArcParams[i][j] -= anArcParams[0][j];
343 double aRad2 = anArcParams[1][0] * anArcParams[1][0] + anArcParams[1][1] * anArcParams[1][1];
344 double aDist2 = anArcParams[2][0] * anArcParams[2][0] + anArcParams[2][1] * anArcParams[2][1];
345 if (fabs(aRad2 - aDist2) < tolerance)
346 return; // nothing to update (last point already on the arc)
347 if (aDist2 < tolerance)
348 return; // unable to update
349 double aCoeff = sqrt(aRad2 / aDist2);
350 anArcParams[2][0] *= aCoeff;
351 anArcParams[2][1] *= aCoeff;
354 aPoint = myStorage->getEntity(theArc.point[2]);
355 for (int i = 0; i < 2; i++) {
356 aParam = Slvs_MakeParam(aPoint.param[i], myGroup->getId(),
357 anArcParams[0][i] + anArcParams[2][i]);
358 myStorage->updateParameter(aParam);
362 void SketchSolver_ConstraintMirror::adjustConstraint()
364 AttributeRefAttrPtr aMirLineAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
365 myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
366 if (!aMirLineAttr || !aMirLineAttr->isInitialized() || !aMirLineAttr->isObject()) {
367 myErrorMsg = SketchSolver_Error::NOT_INITIALIZED();
370 ResultConstructionPtr aRC =
371 std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aMirLineAttr->object());
372 FeaturePtr aFeature = aRC ? aRC->document()->feature(aRC) :
373 std::dynamic_pointer_cast<ModelAPI_Feature>(aMirLineAttr->object());
374 std::map<FeaturePtr, Slvs_hEntity>::iterator aMirLineIter = myFeatureMap.find(aFeature);
375 if (aMirLineIter == myFeatureMap.end())
377 Slvs_Entity aMirrorLine = myStorage->getEntity(aMirLineIter->second);
379 Slvs_Constraint aMirror;
381 for (int i = 0; i < 2; i++) {
382 Slvs_Entity aPoint = myStorage->getEntity(aMirrorLine.point[i]);
383 for (int j = 0; j < 2; j++)
384 aStartEnd[2*i+j] = myStorage->getParameter(aPoint.param[j]).val;
387 // Search mirror between middle points on the arcs and recompute their coordinates
388 std::map<Slvs_hEntity, Slvs_hEntity> aPointsOnCircles;
389 std::list<Slvs_Constraint> aMirrorPonCirc;
390 std::list<Slvs_Constraint> aPonCirc = myStorage->getConstraintsByType(SLVS_C_PT_ON_CIRCLE);
391 std::vector<Slvs_hConstraint>::iterator aConstrIter = mySlvsConstraints.begin();
392 for (; aConstrIter != mySlvsConstraints.end(); aConstrIter++) {
393 aMirror = myStorage->getConstraint(*aConstrIter);
394 if (aMirror.type != SLVS_C_SYMMETRIC_LINE)
396 Slvs_Constraint aPonCircA, aPonCircB;
397 aPonCircA.h = SLVS_E_UNKNOWN;
398 aPonCircB.h = SLVS_E_UNKNOWN;
399 std::list<Slvs_Constraint>::iterator aPtIter = aPonCirc.begin();
400 for (; aPtIter != aPonCirc.end(); aPtIter++) {
401 if (aMirror.ptA == aPtIter->ptA)
402 aPonCircA = *aPtIter;
403 if (aMirror.ptB == aPtIter->ptA)
404 aPonCircB = *aPtIter;
406 if (aPonCircA.h == SLVS_E_UNKNOWN || aPonCircB.h == SLVS_E_UNKNOWN)
408 aMirrorPonCirc.push_back(aMirror);
409 // Store point IDs to avoid their recalculation twice
410 aPointsOnCircles[aPonCircA.ptA] = aPonCircA.entityA;
411 aPointsOnCircles[aPonCircB.ptA] = aPonCircB.entityA;
414 // Recalculate positions of mirroring points
415 std::list<Slvs_Constraint> aMirrorList = myStorage->getConstraintsByType(SLVS_C_SYMMETRIC_LINE);
416 std::list<Slvs_Constraint>::iterator aMirIter = aMirrorList.begin();
417 for (; aMirIter != aMirrorList.end(); aMirIter++) {
418 if (aPointsOnCircles.find(aMirIter->ptA) != aPointsOnCircles.end())
419 continue; // Avoid mirroring points on circles
420 Slvs_Entity aBase = myStorage->getEntity(aMirIter->ptA);
421 Slvs_Entity aMirror = myStorage->getEntity(aMirIter->ptB);
422 makeMirrorEntity(aBase, aMirror, aStartEnd);
425 bool aNeedToResolve = myStorage->isNeedToResolve();
426 for (aMirIter = aMirrorPonCirc.begin(); aMirIter != aMirrorPonCirc.end(); aMirIter++) {
427 // Calculate middle point for base arc and mirrored point on mirror arc
428 Slvs_Entity aBaseArc = myStorage->getEntity(aPointsOnCircles[aMirIter->ptA]);
429 Slvs_Entity aBasePoint = myStorage->getEntity(aMirIter->ptA);
430 Slvs_Param aParamX = myStorage->getParameter(aBasePoint.param[0]);
431 Slvs_Param aParamY = myStorage->getParameter(aBasePoint.param[1]);
432 calculateMiddlePoint(aBaseArc, 0.5, aParamX.val, aParamY.val);
433 myStorage->updateParameter(aParamX);
434 myStorage->updateParameter(aParamY);
435 Slvs_Entity aMirrorArc = myStorage->getEntity(aPointsOnCircles[aMirIter->ptB]);
436 Slvs_Entity aMirrorPoint = myStorage->getEntity(aMirIter->ptB);
437 aParamX = myStorage->getParameter(aMirrorPoint.param[0]);
438 aParamY = myStorage->getParameter(aMirrorPoint.param[1]);
439 calculateMiddlePoint(aMirrorArc, 0.5, aParamX.val, aParamY.val);
440 myStorage->updateParameter(aParamX);
441 myStorage->updateParameter(aParamY);
442 // make centers of arcs symmetric
443 aBasePoint = myStorage->getEntity(aBaseArc.point[0]);
444 aMirrorPoint = myStorage->getEntity(aMirrorArc.point[0]);
445 makeMirrorEntity(aBasePoint, aMirrorPoint, aStartEnd);
447 // Restore previous value to avoid looped recalculations of sketch
448 myStorage->setNeedToResolve(aNeedToResolve);