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>
15 void SketchSolver_ConstraintMirror::getAttributes(
16 Slvs_Entity& theMirrorLine,
17 std::vector<Slvs_Entity>& theBaseEntities,
18 std::vector<Slvs_Entity>& theMirrorEntities)
20 DataPtr aData = myBaseConstraint->data();
21 AttributeRefAttrPtr aMirLineAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
22 myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
23 if (!aMirLineAttr || !aMirLineAttr->isInitialized() || !aMirLineAttr->isObject()) {
24 myErrorMsg = SketchSolver_Error::NOT_INITIALIZED();
27 int aType = SLVS_E_UNKNOWN; // type of created entity
28 Slvs_hEntity anEntity = myGroup->getAttributeId(aMirLineAttr);
29 if (anEntity == SLVS_E_UNKNOWN)
30 anEntity = changeEntity(aMirLineAttr, aType);
31 theMirrorLine = myStorage->getEntity(anEntity);
33 // Create SolveSpace entity for all features
34 AttributeRefListPtr aBaseRefList = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
35 myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
36 AttributeRefListPtr aMirroredRefList = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
37 myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_C()));
38 myNumberOfObjects = aMirroredRefList->size();
39 if (!aBaseRefList || !aMirroredRefList) {
40 myErrorMsg = SketchSolver_Error::INCORRECT_MIRROR_ATTRIBUTE();
44 std::list<ObjectPtr> aBaseList = aBaseRefList->list();
45 std::list<ObjectPtr> aMirroredList = aMirroredRefList->list();
48 ResultConstructionPtr aRC;
49 for (int i = 0; i < 2; i++) {
50 std::list<ObjectPtr>::iterator anIter = i == 0 ? aBaseList.begin() : aMirroredList.begin();
51 std::list<ObjectPtr>::iterator aEndIter = i == 0 ? aBaseList.end() : aMirroredList.end();
52 std::vector<Slvs_Entity>* aList = i == 0 ? &theBaseEntities : & theMirrorEntities;
53 for ( ; anIter != aEndIter; anIter++) {
54 aRC = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(*anIter);
55 aFeature = aRC ? aRC->document()->feature(aRC) :
56 std::dynamic_pointer_cast<ModelAPI_Feature>(*anIter);
60 anEntity = changeEntity(aFeature, aType);
61 // Sort entities by their type
62 std::vector<Slvs_Entity>::iterator anIt = aList->begin();
63 for (; anIt != aList->end(); anIt++)
64 if (aType < anIt->type)
66 // aList->push_back(myStorage->getEntity(anEntity));
67 aList->insert(anIt, myStorage->getEntity(anEntity));
71 if (theBaseEntities.size() > theMirrorEntities.size())
72 myErrorMsg = SketchSolver_Error::NOT_INITIALIZED();
75 void SketchSolver_ConstraintMirror::process()
78 if (!myBaseConstraint || !myStorage || myGroup == 0) {
79 /// TODO: Put error message here
82 if (!mySlvsConstraints.empty()) // some data is changed, update constraint
83 update(myBaseConstraint);
85 Slvs_Entity aMirrorLine;
86 std::vector<Slvs_Entity> aBaseList;
87 std::vector<Slvs_Entity> aMirrorList;
88 getAttributes(aMirrorLine, aBaseList, aMirrorList);
89 if (!myErrorMsg.empty())
92 if (aBaseList.size() != aMirrorList.size()) {
93 myErrorMsg = SketchSolver_Error::INCORRECT_MIRROR_ATTRIBUTE();
97 Slvs_Constraint aConstraint;
98 // Get coordinates of mirror line points for speed up
100 for (int i = 0; i < 2; i++) {
101 Slvs_Entity aPoint = myStorage->getEntity(aMirrorLine.point[i]);
102 for (int j = 0; j < 2; j++)
103 aStartEnd[2*i+j] = myStorage->getParameter(aPoint.param[j]).val;
106 std::vector<Slvs_Entity>::iterator aBaseIter = aBaseList.begin();
107 std::vector<Slvs_Entity>::iterator aMirrorIter = aMirrorList.begin();
108 for (; aBaseIter != aBaseList.end(); aBaseIter++, aMirrorIter++) {
109 // Make aMirrorEnt parameters to be symmetric with aBaseEnt
110 makeMirrorEntity(*aBaseIter, *aMirrorIter, aStartEnd);
112 if (aBaseIter->type == SLVS_E_POINT_IN_2D) {
113 aConstraint = Slvs_MakeConstraint(
114 SLVS_E_UNKNOWN, myGroup->getId(), getType(), myGroup->getWorkplaneId(),
115 0.0, aBaseIter->h, aMirrorIter->h, aMirrorLine.h, SLVS_E_UNKNOWN);
116 aConstraint.h = myStorage->addConstraint(aConstraint);
117 mySlvsConstraints.push_back(aConstraint.h);
118 } else if (aBaseIter->type == SLVS_E_LINE_SEGMENT) {
119 for (int i = 0; i < 2; i++) {
120 aConstraint = Slvs_MakeConstraint(
121 SLVS_E_UNKNOWN, myGroup->getId(), getType(), myGroup->getWorkplaneId(), 0.0,
122 aBaseIter->point[i], aMirrorIter->point[i], aMirrorLine.h, SLVS_E_UNKNOWN);
123 aConstraint.h = myStorage->addConstraint(aConstraint);
124 mySlvsConstraints.push_back(aConstraint.h);
126 } else if (aBaseIter->type == SLVS_E_CIRCLE) {
127 aConstraint = Slvs_MakeConstraint(
128 SLVS_E_UNKNOWN, myGroup->getId(), getType(), myGroup->getWorkplaneId(), 0.0,
129 aBaseIter->point[0], aMirrorIter->point[0], aMirrorLine.h, SLVS_E_UNKNOWN);
130 aConstraint.h = myStorage->addConstraint(aConstraint);
131 mySlvsConstraints.push_back(aConstraint.h);
132 // Additional constraint for equal radii
133 Slvs_Constraint anEqRadConstr = Slvs_MakeConstraint(
134 SLVS_E_UNKNOWN, myGroup->getId(), SLVS_C_EQUAL_RADIUS, myGroup->getWorkplaneId(),
135 0.0, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, aBaseIter->h, aMirrorIter->h);
136 anEqRadConstr.h = myStorage->addConstraint(anEqRadConstr);
137 mySlvsConstraints.push_back(anEqRadConstr.h);
138 } else if (aBaseIter->type == SLVS_E_ARC_OF_CIRCLE) {
139 // Workaround to avoid problems in SolveSpace.
140 // The symmetry of two arcs will be done using symmetry of three points on these arcs:
141 // start point, end point, and any other point on the arc
142 Slvs_hEntity aBaseArcPoints[3] = {
146 Slvs_hEntity aMirrorArcPoints[3] = { // indices of points of arc, center corresponds center, first point corresponds last point
147 aMirrorIter->point[2],
148 aMirrorIter->point[1],
151 Slvs_Entity aBothArcs[2] = {*aBaseIter, *aMirrorIter};
152 Slvs_hEntity aBothMiddlePoints[2];
153 for (int i = 0; i < 2; i++) {
155 calculateMiddlePoint(aBothArcs[i], 0.5, x, y);
156 Slvs_Param aParamX = Slvs_MakeParam(SLVS_E_UNKNOWN, myGroup->getId(), x);
157 Slvs_Param aParamY = Slvs_MakeParam(SLVS_E_UNKNOWN, myGroup->getId(), y);
158 aParamX.h = myStorage->addParameter(aParamX);
159 aParamY.h = myStorage->addParameter(aParamY);
160 Slvs_Entity aPoint = Slvs_MakePoint2d(SLVS_E_UNKNOWN, myGroup->getId(),
161 myGroup->getWorkplaneId(), aParamX.h, aParamY.h);
162 aBothMiddlePoints[i] = myStorage->addEntity(aPoint);
163 // additional constraint point-on-curve
164 Slvs_Constraint aPonCircConstr = Slvs_MakeConstraint(
165 SLVS_E_UNKNOWN, myGroup->getId(), SLVS_C_PT_ON_CIRCLE, myGroup->getWorkplaneId(),
166 0.0, aBothMiddlePoints[i], SLVS_E_UNKNOWN, aBothArcs[i].h, SLVS_E_UNKNOWN);
167 aPonCircConstr.h = myStorage->addConstraint(aPonCircConstr);
168 mySlvsConstraints.push_back(aPonCircConstr.h);
170 // additional constraint for the point to be in the middle of a base arc
171 Slvs_Entity aLine1 = Slvs_MakeLineSegment(SLVS_E_UNKNOWN, myGroup->getId(),
172 myGroup->getWorkplaneId(), aBothArcs[i].point[1], aBothMiddlePoints[i]);
173 aLine1.h = myStorage->addEntity(aLine1);
174 Slvs_Entity aLine2 = Slvs_MakeLineSegment(SLVS_E_UNKNOWN, myGroup->getId(),
175 myGroup->getWorkplaneId(), aBothArcs[i].point[2], aBothMiddlePoints[i]);
176 aLine2.h = myStorage->addEntity(aLine2);
177 Slvs_Constraint aMiddleConstr = Slvs_MakeConstraint(SLVS_E_UNKNOWN, myGroup->getId(),
178 SLVS_C_EQUAL_LENGTH_LINES, myGroup->getWorkplaneId(),
179 0.0, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, aLine1.h, aLine2.h);
180 aMiddleConstr.h = myStorage->addConstraint(aMiddleConstr);
181 mySlvsConstraints.push_back(aMiddleConstr.h);
185 aBaseArcPoints[2] = aBothMiddlePoints[0];
186 aMirrorArcPoints[2] = aBothMiddlePoints[1];
187 for (int ind = 0; ind < 3; ind++) {
188 Slvs_Constraint aConstraint = Slvs_MakeConstraint(
189 SLVS_E_UNKNOWN, myGroup->getId(), getType(), myGroup->getWorkplaneId(), 0.0,
190 aBaseArcPoints[ind], aMirrorArcPoints[ind], aMirrorLine.h, SLVS_E_UNKNOWN);
191 aConstraint.h = myStorage->addConstraint(aConstraint);
192 mySlvsConstraints.push_back(aConstraint.h);
199 void SketchSolver_ConstraintMirror::update(ConstraintPtr theConstraint)
202 if (!theConstraint || theConstraint == myBaseConstraint) {
203 AttributeRefListPtr aMirroredRefList = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
204 myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_C()));
205 if (aMirroredRefList->size() != myNumberOfObjects) {
206 remove(myBaseConstraint);
211 SketchSolver_Constraint::update();
214 bool SketchSolver_ConstraintMirror::remove(ConstraintPtr theConstraint)
217 if (theConstraint && theConstraint != myBaseConstraint)
219 bool isFullyRemoved = true;
220 std::vector<Slvs_hEntity>::iterator aCIter = mySlvsConstraints.begin();
221 for (; aCIter != mySlvsConstraints.end(); aCIter++)
222 isFullyRemoved = myStorage->removeConstraint(*aCIter) && isFullyRemoved;
223 mySlvsConstraints.clear();
225 std::map<FeaturePtr, Slvs_hEntity>::iterator aFeatIt = myFeatureMap.begin();
226 for (; aFeatIt != myFeatureMap.end(); aFeatIt++)
227 myStorage->removeEntity(aFeatIt->second);
229 if (isFullyRemoved) {
230 myFeatureMap.clear();
231 myAttributeMap.clear();
234 cleanRemovedEntities();
238 bool SketchSolver_ConstraintMirror::checkAttributesChanged(ConstraintPtr theConstraint)
240 // First of all, check the mirror line is changed.
241 // It may be changed to one of mirrored lines, which is already in this constraint
242 // (this case is not marked as attribute changing)
243 ConstraintPtr aConstraint = theConstraint ? theConstraint : myBaseConstraint;
244 AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
245 aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
246 if (!aRefAttr || !aRefAttr->isObject() || !aRefAttr->object())
248 FeaturePtr aMirrorLine = ModelAPI_Feature::feature(aRefAttr->object());
252 std::map<FeaturePtr, Slvs_hEntity>::iterator aMirrorIter = myFeatureMap.find(aMirrorLine);
253 if (aMirrorIter == myFeatureMap.end())
256 // Check the entity is not used as mirror line
257 std::vector<Slvs_hConstraint>::iterator aCIter = mySlvsConstraints.begin();
258 for (; aCIter != mySlvsConstraints.end(); aCIter++) {
259 Slvs_Constraint aMirrorConstr = myStorage->getConstraint(*aCIter);
260 if (aMirrorConstr.type != SLVS_C_SYMMETRIC_LINE)
262 if (aMirrorConstr.entityA != aMirrorIter->second)
264 else break; // check just one symmetric constraint
268 return SketchSolver_Constraint::checkAttributesChanged(theConstraint);
271 void SketchSolver_ConstraintMirror::makeMirrorEntity(
272 const Slvs_Entity& theBase,
273 const Slvs_Entity& theMirror,
274 const double theMirrorLine[]) const
276 Slvs_hEntity aBasePoint[4];
277 Slvs_hEntity aMirrorPoint[4];
278 for (int i = 0; i < 4; i++) {
279 aBasePoint[i] = theBase.point[i];
280 aMirrorPoint[i] = theMirror.point[i];
282 if (theBase.type == SLVS_E_ARC_OF_CIRCLE) {
283 Slvs_hEntity aTmp = aMirrorPoint[2];
284 aMirrorPoint[2] = aMirrorPoint[1];
285 aMirrorPoint[1] = aTmp;
286 adjustArcPoints(theBase);
288 if (theBase.type == SLVS_E_POINT_IN_2D || theBase.type == SLVS_E_POINT_IN_3D) {
289 aBasePoint[0] = theBase.h;
290 aMirrorPoint[0] = theMirror.h;
293 // Mirror line parameters
294 std::shared_ptr<GeomAPI_XY> aLinePoints[2];
295 for (int i = 0; i < 2; i++)
296 aLinePoints[i] = std::shared_ptr<GeomAPI_XY>(
297 new GeomAPI_XY(theMirrorLine[2*i], theMirrorLine[2*i+1]));
298 // direction of a mirror line
299 std::shared_ptr<GeomAPI_Dir2d> aDir = std::shared_ptr<GeomAPI_Dir2d>(
300 new GeomAPI_Dir2d(aLinePoints[1]->added(aLinePoints[0]->multiplied(-1.0))));
301 // orthogonal direction
302 aDir = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(aDir->y(), -aDir->x()));
304 Slvs_hConstraint aFixed; // transient variable
305 for (int i = 0; i < 4; i++) {
306 if (aBasePoint[i] == SLVS_E_UNKNOWN || aMirrorPoint[i] == SLVS_E_UNKNOWN)
308 // check the mirror point is not fixed
309 if (myStorage->isPointFixed(aMirrorPoint[i], aFixed, true))
312 Slvs_Entity aPointEnt = myStorage->getEntity(aBasePoint[i]);
313 double aBaseX = myStorage->getParameter(aPointEnt.param[0]).val;
314 double aBaseY = myStorage->getParameter(aPointEnt.param[1]).val;
315 std::shared_ptr<GeomAPI_XY> aPoint = std::shared_ptr<GeomAPI_XY>(new GeomAPI_XY(aBaseX, aBaseY));
317 std::shared_ptr<GeomAPI_XY> aVec = std::shared_ptr<GeomAPI_XY>(
318 new GeomAPI_XY(aPoint->x() - aLinePoints[0]->x(), aPoint->y() - aLinePoints[0]->y()));
319 double aDist = aVec->dot(aDir->xy());
320 aPoint = aPoint->added(aDir->xy()->multiplied(-2.0 * aDist));
322 Slvs_Entity aMirrorEnt = myStorage->getEntity(aMirrorPoint[i]);
323 Slvs_Param aParam = Slvs_MakeParam(aMirrorEnt.param[0], myGroup->getId(), aPoint->x());
324 myStorage->updateParameter(aParam);
325 aParam = Slvs_MakeParam(aMirrorEnt.param[1], myGroup->getId(), aPoint->y());
326 myStorage->updateParameter(aParam);
330 void SketchSolver_ConstraintMirror::adjustArcPoints(const Slvs_Entity& theArc) const
334 double anArcParams[3][2];
335 for (int i = 0; i < 3; i++) {
336 aPoint = myStorage->getEntity(theArc.point[i]);
337 for (int j = 0; j < 2; j++) {
338 aParam = myStorage->getParameter(aPoint.param[j]);
339 anArcParams[i][j] = aParam.val;
341 anArcParams[i][j] -= anArcParams[0][j];
344 double aRad2 = anArcParams[1][0] * anArcParams[1][0] + anArcParams[1][1] * anArcParams[1][1];
345 double aDist2 = anArcParams[2][0] * anArcParams[2][0] + anArcParams[2][1] * anArcParams[2][1];
346 if (std::fabs(aRad2 - aDist2) < tolerance)
347 return; // nothing to update (last point already on the arc)
348 if (aDist2 < tolerance)
349 return; // unable to update
350 double aCoeff = std::sqrt(aRad2 / aDist2);
351 anArcParams[2][0] *= aCoeff;
352 anArcParams[2][1] *= aCoeff;
355 aPoint = myStorage->getEntity(theArc.point[2]);
356 for (int i = 0; i < 2; i++) {
357 aParam = Slvs_MakeParam(aPoint.param[i], myGroup->getId(),
358 anArcParams[0][i] + anArcParams[2][i]);
359 myStorage->updateParameter(aParam);
363 void SketchSolver_ConstraintMirror::adjustConstraint()
365 AttributeRefAttrPtr aMirLineAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
366 myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
367 if (!aMirLineAttr || !aMirLineAttr->isInitialized() || !aMirLineAttr->isObject()) {
368 myErrorMsg = SketchSolver_Error::NOT_INITIALIZED();
371 ResultConstructionPtr aRC =
372 std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aMirLineAttr->object());
373 FeaturePtr aFeature = aRC ? aRC->document()->feature(aRC) :
374 std::dynamic_pointer_cast<ModelAPI_Feature>(aMirLineAttr->object());
375 std::map<FeaturePtr, Slvs_hEntity>::iterator aMirLineIter = myFeatureMap.find(aFeature);
376 if (aMirLineIter == myFeatureMap.end())
378 Slvs_Entity aMirrorLine = myStorage->getEntity(aMirLineIter->second);
380 Slvs_Constraint aMirror;
382 for (int i = 0; i < 2; i++) {
383 Slvs_Entity aPoint = myStorage->getEntity(aMirrorLine.point[i]);
384 for (int j = 0; j < 2; j++)
385 aStartEnd[2*i+j] = myStorage->getParameter(aPoint.param[j]).val;
388 // Search mirror between middle points on the arcs and recompute their coordinates
389 std::map<Slvs_hEntity, Slvs_hEntity> aPointsOnCircles;
390 std::list<Slvs_Constraint> aMirrorPonCirc;
391 std::list<Slvs_Constraint> aPonCirc = myStorage->getConstraintsByType(SLVS_C_PT_ON_CIRCLE);
392 std::vector<Slvs_hConstraint>::iterator aConstrIter = mySlvsConstraints.begin();
393 for (; aConstrIter != mySlvsConstraints.end(); aConstrIter++) {
394 aMirror = myStorage->getConstraint(*aConstrIter);
395 if (aMirror.type != SLVS_C_SYMMETRIC_LINE)
397 if (aMirror.entityA != aMirrorLine.h)
398 continue; // don't update another Mirror constraints
399 Slvs_Constraint aPonCircA, aPonCircB;
400 aPonCircA.h = SLVS_E_UNKNOWN;
401 aPonCircB.h = SLVS_E_UNKNOWN;
402 std::list<Slvs_Constraint>::iterator aPtIter = aPonCirc.begin();
403 for (; aPtIter != aPonCirc.end(); aPtIter++) {
404 if (aMirror.ptA == aPtIter->ptA)
405 aPonCircA = *aPtIter;
406 if (aMirror.ptB == aPtIter->ptA)
407 aPonCircB = *aPtIter;
409 if (aPonCircA.h == SLVS_E_UNKNOWN || aPonCircB.h == SLVS_E_UNKNOWN)
411 aMirrorPonCirc.push_back(aMirror);
412 // Store point IDs to avoid their recalculation twice
413 aPointsOnCircles[aPonCircA.ptA] = aPonCircA.entityA;
414 aPointsOnCircles[aPonCircB.ptA] = aPonCircB.entityA;
417 // Recalculate positions of mirroring points
418 std::list<Slvs_Constraint> aMirrorList = myStorage->getConstraintsByType(SLVS_C_SYMMETRIC_LINE);
419 std::list<Slvs_Constraint>::iterator aMirIter = aMirrorList.begin();
420 for (; aMirIter != aMirrorList.end(); aMirIter++) {
421 if (aMirIter->entityA != aMirrorLine.h)
422 continue; // don't update another Mirror constraints
423 if (aPointsOnCircles.find(aMirIter->ptA) != aPointsOnCircles.end())
424 continue; // Avoid mirroring points on circles
425 Slvs_Entity aBase = myStorage->getEntity(aMirIter->ptA);
426 Slvs_Entity aMirror = myStorage->getEntity(aMirIter->ptB);
427 makeMirrorEntity(aBase, aMirror, aStartEnd);
430 bool aNeedToResolve = myStorage->isNeedToResolve();
431 for (aMirIter = aMirrorPonCirc.begin(); aMirIter != aMirrorPonCirc.end(); aMirIter++) {
432 // Make centers of arcs symmetric
433 Slvs_Entity aBaseArc = myStorage->getEntity(aPointsOnCircles[aMirIter->ptA]);
434 Slvs_Entity aBasePoint = myStorage->getEntity(aBaseArc.point[0]);
435 Slvs_Entity aMirrorArc = myStorage->getEntity(aPointsOnCircles[aMirIter->ptB]);
436 Slvs_Entity aMirrorPoint = myStorage->getEntity(aMirrorArc.point[0]);
437 makeMirrorEntity(aBasePoint, aMirrorPoint, aStartEnd);
438 // Calculate middle point for base arc and mirrored point on mirror arc
439 aBasePoint = myStorage->getEntity(aMirIter->ptA);
440 Slvs_Param aParamX = myStorage->getParameter(aBasePoint.param[0]);
441 Slvs_Param aParamY = myStorage->getParameter(aBasePoint.param[1]);
442 calculateMiddlePoint(aBaseArc, 0.5, aParamX.val, aParamY.val);
443 myStorage->updateParameter(aParamX);
444 myStorage->updateParameter(aParamY);
445 aMirrorPoint = myStorage->getEntity(aMirIter->ptB);
446 aParamX = myStorage->getParameter(aMirrorPoint.param[0]);
447 aParamY = myStorage->getParameter(aMirrorPoint.param[1]);
448 calculateMiddlePoint(aMirrorArc, 0.5, aParamX.val, aParamY.val);
449 myStorage->updateParameter(aParamX);
450 myStorage->updateParameter(aParamY);
452 // Restore previous value to avoid looped recalculations of sketch
453 myStorage->setNeedToResolve(aNeedToResolve);