From 98796430d93bf80d5cff315a4ee49741813e43fe Mon Sep 17 00:00:00 2001 From: azv Date: Mon, 18 Jan 2016 10:55:25 +0300 Subject: [PATCH] Update sketcher unit tests for code coverage --- .../Test/TestConstraintConcidence.py | 128 ++++++++++++++++-- .../Test/TestConstraintDistance.py | 81 +++++++++-- src/SketchPlugin/Test/TestConstraintEqual.py | 112 +++++++++++++-- src/SketchPlugin/Test/TestConstraintMirror.py | 100 ++++++++++---- src/SketchPlugin/Test/TestFillet.py | 111 +++++++++++++-- src/SketchPlugin/Test/TestMultiRotation.py | 58 +++++++- src/SketchPlugin/Test/TestMultiTranslation.py | 60 +++++++- .../SketchSolver_ConstraintMulti.cpp | 37 ++++- .../SketchSolver_ConstraintMulti.h | 6 + src/SketchSolver/SketchSolver_Group.cpp | 27 ---- src/SketchSolver/SketchSolver_Group.h | 4 - 11 files changed, 614 insertions(+), 110 deletions(-) diff --git a/src/SketchPlugin/Test/TestConstraintConcidence.py b/src/SketchPlugin/Test/TestConstraintConcidence.py index 580a45e32..81e34ef70 100644 --- a/src/SketchPlugin/Test/TestConstraintConcidence.py +++ b/src/SketchPlugin/Test/TestConstraintConcidence.py @@ -18,20 +18,59 @@ """ from GeomDataAPI import * from ModelAPI import * +import math #========================================================================= # Initialization of the test #========================================================================= __updated__ = "2014-10-28" +TOLERANCE = 1.e-7 + +#========================================================================= +# Auxiliary functions +#========================================================================= +def checkPointOnLine(point, line): + aStart = geomDataAPI_Point2D(line.attribute("StartPoint")) + aEnd = geomDataAPI_Point2D(line.attribute("EndPoint")) + aDirX = aEnd.x() - aStart.x() + aDirY = aEnd.y() - aStart.y() + aVecX = point.x() - aStart.x() + aVecY = point.y() - aStart.y() + assert (math.fabs(aDirX * aVecY - aDirY * aVecX) <= TOLERANCE) + +def checkPointOnCircle(point, circle): + aCenter = geomDataAPI_Point2D(circle.attribute("CircleCenter")) + aRadius = circle.real("CircleRadius").value() + aDist = math.hypot(point.x() - aCenter.x(), point.y() - aCenter.y()) + assert (math.fabs(aDist - aRadius) <= TOLERANCE) + +def checkPointOnArc(point, arc): + aStart = geomDataAPI_Point2D(arc.attribute("ArcStartPoint")) + aCenter = geomDataAPI_Point2D(arc.attribute("ArcCenter")) + aRadius = math.hypot(aStart.x() - aCenter.x(), aStart.y() - aCenter.y()) + aDist = math.hypot(point.x() - aCenter.x(), point.y() - aCenter.y()) + assert (math.fabs(aDist - aRadius) <= TOLERANCE) + + +#========================================================================= +# Start of test +#========================================================================= aSession = ModelAPI_Session.get() aDocument = aSession.moduleDocument() +# add an origin +aSession.startOperation() +aFeature = aDocument.addFeature("Point") +aFeature.real("x").setValue(0.) +aFeature.real("y").setValue(0.) +aFeature.real("z").setValue(0.) +anOriginName = aFeature.name() +aSession.finishOperation() #========================================================================= # Creation of a sketch #========================================================================= aSession.startOperation() -aSketchCommonFeature = aDocument.addFeature("Sketch") -aSketchFeature = featureToCompositeFeature(aSketchCommonFeature) +aSketchFeature = featureToCompositeFeature(aDocument.addFeature("Sketch")) origin = geomDataAPI_Point(aSketchFeature.attribute("Origin")) origin.setValue(0, 0, 0) dirx = geomDataAPI_Dir(aSketchFeature.attribute("DirX")) @@ -79,20 +118,93 @@ assert (aLineStartPoint.y() == 0) deltaX = deltaY = 40. # move line aSession.startOperation() -anArcStartPoint.setValue(aLineStartPoint.x() + deltaX, +aLineStartPoint.setValue(aLineStartPoint.x() + deltaX, aLineStartPoint.y() + deltaY) -anArcEndPoint.setValue(aLineEndPoint.x() + deltaX, +aLineEndPoint.setValue(aLineEndPoint.x() + deltaX, aLineEndPoint.y() + deltaY) aSession.finishOperation() # check that arc's points are moved also assert (anArcEndPoint.x() == aLineStartPoint.x()) assert (anArcEndPoint.y() == aLineStartPoint.y()) #========================================================================= -# TODO: improve test -# 1. remove constraint, move line to check that constraint are not applied -# 2. make a new constraint when the points are distanced from each other, -# check that one from constrainted objects has moved +# Remove coincidence and move the line +#========================================================================= +aSession.startOperation() +aDocument.removeFeature(aConstraint) +aSession.finishOperation() +aSession.startOperation() +aLineStartPoint.setValue(50., 0.) +aSession.finishOperation() +assert (anArcEndPoint.x() != aLineStartPoint.x() or anArcEndPoint.y() != aLineStartPoint.y()) + +#========================================================================= +# Add constraint point-on-line +#========================================================================= +aSession.startOperation() +aConstraint = aSketchFeature.addFeature("SketchConstraintCoincidence") +reflistA = aConstraint.refattr("ConstraintEntityA") +reflistB = aConstraint.refattr("ConstraintEntityB") +reflistA.setAttr(anArcStartPoint) +reflistB.setObject(aSketchLine.lastResult()) +aConstraint.execute() +aSession.finishOperation() +checkPointOnLine(anArcStartPoint, aSketchLine) +#========================================================================= +# Add constraint point-on-circle +#========================================================================= +aSession.startOperation() +# create circle with center coincident with origin +aSketchCircle = aSketchFeature.addFeature("SketchCircle") +aCircleCenter = geomDataAPI_Point2D(aSketchCircle.attribute("CircleCenter")) +aCircleRadius = aSketchCircle.real("CircleRadius") +aCircleCenter.setValue(10., 10.) +aCircleRadius.setValue(25.) +aSession.finishOperation() +# create origin +aSession.startOperation() +anOrigRes = modelAPI_Result(aDocument.objectByName("Construction", anOriginName)) +assert (anOrigRes) +anOrigShape = anOrigRes.shape() +assert (anOrigShape) +anOrigin = aSketchFeature.addFeature("SketchPoint") +anOriginCoord = geomDataAPI_Point2D(anOrigin.attribute("PointCoordindates")) +anOriginCoord.setValue(0., 0.) +anOrigin.selection("External").setValue(anOrigRes, anOrigShape) +aSession.finishOperation() +# coincidence between center of circle and the origin +aSession.startOperation() +aConstraint = aSketchFeature.addFeature("SketchConstraintCoincidence") +reflistA = aConstraint.refattr("ConstraintEntityA") +reflistB = aConstraint.refattr("ConstraintEntityB") +reflistA.setAttr(aCircleCenter) +reflistB.setObject(anOrigin.lastResult()) +aSession.finishOperation() +# point-on-circle +aSession.startOperation() +aConstraint = aSketchFeature.addFeature("SketchConstraintCoincidence") +reflistA = aConstraint.refattr("ConstraintEntityA") +reflistB = aConstraint.refattr("ConstraintEntityB") +reflistA.setObject(aSketchCircle.lastResult()) +reflistB.setAttr(aLineEndPoint) +aConstraint.execute() +aSession.finishOperation() +checkPointOnCircle(aLineEndPoint, aSketchCircle) +#========================================================================= +# Add constraint point-on-arc +#========================================================================= +aSession.startOperation() +aConstraint = aSketchFeature.addFeature("SketchConstraintCoincidence") +reflistA = aConstraint.refattr("ConstraintEntityA") +reflistB = aConstraint.refattr("ConstraintEntityB") +reflistA.setAttr(aCircleCenter) +reflistB.setObject(aSketchArc.lastResult()) +aConstraint.execute() +aSession.finishOperation() +checkPointOnArc(aCircleCenter, aSketchArc) +#========================================================================= +# Check center of circle is still in origin #========================================================================= +assert (aCircleCenter.x() == 0. and aCircleCenter.y() == 0.) #========================================================================= # End of test #========================================================================= diff --git a/src/SketchPlugin/Test/TestConstraintDistance.py b/src/SketchPlugin/Test/TestConstraintDistance.py index 1e0880e68..f1fa087b1 100644 --- a/src/SketchPlugin/Test/TestConstraintDistance.py +++ b/src/SketchPlugin/Test/TestConstraintDistance.py @@ -29,7 +29,7 @@ import math __updated__ = "2014-10-28" -def distance(pointA, pointB): +def distancePointPoint(pointA, pointB): """ subroutine to calculate distance between two points result of calculated distance is has 10**-5 precision @@ -38,6 +38,23 @@ def distance(pointA, pointB): ydiff = math.pow((pointA.y() - pointB.y()), 2) return round(math.sqrt(xdiff + ydiff), 5) +def distancePointLine(point, line): + """ + subroutine to calculate distance between point and line + result of calculated distance is has 10**-5 precision + """ + aStartPoint = geomDataAPI_Point2D(line.attribute("StartPoint")) + aEndPoint = geomDataAPI_Point2D(line.attribute("EndPoint")) + # orthogonal direction + aDirX = -(aEndPoint.y() - aStartPoint.y()) + aDirY = (aEndPoint.x() - aStartPoint.x()) + aLen = math.sqrt(aDirX**2 + aDirY**2) + aDirX = aDirX / aLen + aDirY = aDirY / aLen + aVecX = point.x() - aStartPoint.x() + aVecY = point.y() - aStartPoint.y() + return round(math.fabs(aVecX * aDirX + aVecY * aDirY), 5) + aSession = ModelAPI_Session.get() aDocument = aSession.moduleDocument() #========================================================================= @@ -70,7 +87,9 @@ aSession.finishOperation() #========================================================================= # Make a constraint to keep the distance #========================================================================= -assert (distance(aSketchPointCoords, aLineAStartPoint) != 25.) +PT_PT_DIST = 25. +aDist = distancePointPoint(aSketchPointCoords, aLineAStartPoint); +assert (aDist != PT_PT_DIST) aSession.startOperation() aConstraint = aSketchFeature.addFeature("SketchConstraintDistance") aDistance = aConstraint.real("ConstraintValue") @@ -79,32 +98,70 @@ refattrB = aConstraint.refattr("ConstraintEntityB") assert (not aDistance.isInitialized()) assert (not refattrA.isInitialized()) assert (not refattrB.isInitialized()) -aDistance.setValue(25.) aLineResult = aSketchLine.firstResult() assert (aLineResult is not None) refattrA.setAttr(aSketchPointCoords) refattrB.setAttr(aLineAStartPoint) aSession.finishOperation() -assert (aDistance.isInitialized()) assert (refattrA.isInitialized()) assert (refattrB.isInitialized()) +assert (aDistance.isInitialized()) +assert math.fabs(aDistance.value() - aDist) < 1.e-4, "Distance values are different: {0} != {1}".format(aDistance.value(), aDist) +#========================================================================= +# Change distance value +#========================================================================= +aSession.startOperation() +aDistance.setValue(PT_PT_DIST) +aSession.finishOperation() +assert (distancePointPoint(aSketchPointCoords, aLineAStartPoint) == PT_PT_DIST) #========================================================================= # Move line, check that distance is constant #========================================================================= -assert (distance(aSketchPointCoords, aLineAStartPoint) == 25.) aSession.startOperation() aLineAStartPoint.setValue(0., 40.) aLineAEndPoint.setValue(100., 40.) aSession.finishOperation() -assert (distance(aSketchPointCoords, aLineAStartPoint) == 25.) +assert (distancePointPoint(aSketchPointCoords, aLineAStartPoint) == PT_PT_DIST) #========================================================================= -# TODO: improve test -# 1. remove constraint, move line's start point to -# check that constraint are not applied -# 2. check constrained distance between: -# * point and line -# * two lines +# Remove constraint, check the points are unconstrained now #========================================================================= +aSession.startOperation() +aDocument.removeFeature(aConstraint) +aSession.finishOperation() +aSession.startOperation() +aSketchPointCoords.setValue(0., 0.) +aSession.finishOperation() +assert (distancePointPoint(aSketchPointCoords, aLineAStartPoint) != PT_PT_DIST) + +#========================================================================= +# Add distance between point and line +#========================================================================= +PT_LINE_DIST = 50. +aDist = distancePointLine(aSketchPointCoords, aSketchLine) +aSession.startOperation() +aConstraint = aSketchFeature.addFeature("SketchConstraintDistance") +aDistance = aConstraint.real("ConstraintValue") +refattrA = aConstraint.refattr("ConstraintEntityA") +refattrB = aConstraint.refattr("ConstraintEntityB") +assert (not aDistance.isInitialized()) +assert (not refattrA.isInitialized()) +assert (not refattrB.isInitialized()) +aLineResult = aSketchLine.firstResult() +assert (aLineResult is not None) +refattrA.setObject(aLineResult) +refattrB.setAttr(aSketchPointCoords) +aSession.finishOperation() +assert (refattrA.isInitialized()) +assert (refattrB.isInitialized()) +assert (aDistance.isInitialized()) +assert math.fabs(aDistance.value() - aDist) < 1.e-4, "Distance values are different: {0} != {1}".format(aDistance.value(), aDist) +#========================================================================= +# Change distance value +#========================================================================= +aSession.startOperation() +aDistance.setValue(PT_LINE_DIST) +aSession.finishOperation() +assert (distancePointLine(aSketchPointCoords, aSketchLine) == PT_LINE_DIST) #========================================================================= # End of test #========================================================================= diff --git a/src/SketchPlugin/Test/TestConstraintEqual.py b/src/SketchPlugin/Test/TestConstraintEqual.py index 751be751d..cd3fb37ca 100644 --- a/src/SketchPlugin/Test/TestConstraintEqual.py +++ b/src/SketchPlugin/Test/TestConstraintEqual.py @@ -17,9 +17,50 @@ import math __updated__ = "2015-03-16" +#========================================================================= +# Auxiliary functions +#========================================================================= +def externalSketch(theDoc): + aSketchFeature = featureToCompositeFeature(theDoc.addFeature("Sketch")) + origin = geomDataAPI_Point(aSketchFeature.attribute("Origin")) + origin.setValue(0, 0, 0) + dirx = geomDataAPI_Dir(aSketchFeature.attribute("DirX")) + dirx.setValue(1, 0, 0) + norm = geomDataAPI_Dir(aSketchFeature.attribute("Norm")) + norm.setValue(0, 0, 1) + # add circle + circle = aSketchFeature.addFeature("SketchCircle") + circleCenter = geomDataAPI_Point2D(circle.attribute("CircleCenter")) + circleRadius = circle.real("CircleRadius") + circleCenter.setValue(-50., 50.) + circleRadius.setValue(10.) + # add line + line = aSketchFeature.addFeature("SketchLine") + lineStart = geomDataAPI_Point2D(line.attribute("StartPoint")) + lineEnd = geomDataAPI_Point2D(line.attribute("EndPoint")) + lineStart.setValue(-40., 35.) + lineEnd.setValue(-60., 25.) + +def lineLength(theLine): + aLineStart = geomDataAPI_Point2D(theLine.attribute("StartPoint")) + aLineEnd = geomDataAPI_Point2D(theLine.attribute("EndPoint")) + aVecX = aLineStart.x() - aLineEnd.x() + aVecY = aLineStart.y() - aLineEnd.y() + return math.hypot(aVecX, aVecY) + + +#========================================================================= +# Start of test +#========================================================================= aSession = ModelAPI_Session.get() aDocument = aSession.moduleDocument() #========================================================================= +# Creation external sketch +#========================================================================= +aSession.startOperation() +externalSketch(aDocument) +aSession.finishOperation() +#========================================================================= # Creation of a sketch #========================================================================= aSession.startOperation() @@ -49,7 +90,7 @@ aSession.startOperation() aSketchCircle = aSketchFeature.addFeature("SketchCircle") anCircleCentr = geomDataAPI_Point2D(aSketchCircle.attribute("CircleCenter")) aCircleRadius = aSketchCircle.real("CircleRadius") -anCircleCentr.setValue(-25., -25) +anCircleCentr.setValue(-25., -25.) aCircleRadius.setValue(25.) aSession.finishOperation() #========================================================================= @@ -68,10 +109,38 @@ aRefObjectB.setObject(aResultB) aConstraintEqRad.execute() aSession.finishOperation() aCircRadius = aCircleRadius.value(); -anArcVecX = anArcStartPoint.x() - anArcCentr.x(); -anArcVecY = anArcStartPoint.y() - anArcCentr.y(); +anArcVecX = anArcStartPoint.x() - anArcCentr.x() +anArcVecY = anArcStartPoint.y() - anArcCentr.y() anArcRadius = math.sqrt(anArcVecX**2 + anArcVecY**2) assert (math.fabs(aCircRadius - anArcRadius) <= 1.e-10) +#========================================================================= +# A constraint to make equal radii of arc and external circle +#========================================================================= +aSession.startOperation() +anExtCircRes = modelAPI_Result(aDocument.objectByName("Construction", "SketchCircle_1")) +assert(anExtCircRes) +anExtCircShape = anExtCircRes.shape() +assert(anExtCircShape) +anExtCircle = aSketchFeature.addFeature("SketchCircle") +anExtCircleCenter = geomDataAPI_Point2D(anExtCircle.attribute("CircleCenter")) +anExtCircleRadius = anExtCircle.real("CircleRadius") +anExtCircleCenter.setValue(-50., 50.) +anExtCircleRadius.setValue(10.) +anExtCircle.selection("External").setValue(anExtCircRes, anExtCircShape) +aSession.finishOperation() +aSession.startOperation() +aConstraintEqRad2 = aSketchFeature.addFeature("SketchConstraintEqual") +aRefObjectA = aConstraintEqRad2.refattr("ConstraintEntityA") +aRefObjectB = aConstraintEqRad2.refattr("ConstraintEntityB") +aRefObjectA.setObject(aSketchCircle.lastResult()) +aRefObjectB.setObject(anExtCircle.lastResult()) +aSession.finishOperation() +assert (math.fabs(anExtCircleRadius.value() - aCircleRadius.value()) < 1.e-10) +anArcVecX = anArcStartPoint.x() - anArcCentr.x() +anArcVecY = anArcStartPoint.y() - anArcCentr.y() +anArcRadius = math.sqrt(anArcVecX**2 + anArcVecY**2) +assert (math.fabs(anExtCircleRadius.value() - anArcRadius) <= 1.e-10) + #========================================================================= # Creation of two different lines #========================================================================= @@ -106,13 +175,36 @@ aRefObjectA.setObject(aResultA) aRefObjectB.setObject(aResultB) aConstraintEqLen.execute() aSession.finishOperation() -aVecX = aLine1StartPoint.x() - aLine1EndPoint.x(); -aVecY = aLine1StartPoint.y() - aLine1EndPoint.y(); -aLine1Len = math.sqrt(anArcVecX * anArcVecX + anArcVecY * anArcVecY) -aVecX = aLine2StartPoint.x() - aLine2EndPoint.x(); -aVecY = aLine2StartPoint.y() - aLine2EndPoint.y(); -aLine2Len = math.sqrt(anArcVecX**2 + anArcVecY**2) -assert (aLine1Len == aLine2Len) +aLine1Len = lineLength(aSketchLine1) +aLine2Len = lineLength(aSketchLine2) +assert (math.fabs(aLine1Len - aLine2Len) < 1.e-10) +#========================================================================= +# A constraint to make equal length of line with external line +#========================================================================= +aSession.startOperation() +anExtLineRes = modelAPI_Result(aDocument.objectByName("Construction", "SketchLine_1")) +assert(anExtLineRes) +anExtLineShape = anExtLineRes.shape() +assert(anExtLineShape) +anExtLine = aSketchFeature.addFeature("SketchLine") +anExtLineStart = geomDataAPI_Point2D(anExtLine.attribute("StartPoint")) +anExtLineEnd = geomDataAPI_Point2D(anExtLine.attribute("EndPoint")) +anExtLineStart.setValue(-40., 35.) +anExtLineEnd.setValue(-60., 25.) +anExtLine.selection("External").setValue(anExtLineRes, anExtLineShape) +anExtLineLen = lineLength(anExtLine) +aSession.finishOperation() +aSession.startOperation() +aConstraintEqLen2 = aSketchFeature.addFeature("SketchConstraintEqual") +aRefObjectA = aConstraintEqLen2.refattr("ConstraintEntityA") +aRefObjectB = aConstraintEqLen2.refattr("ConstraintEntityB") +aRefObjectA.setObject(anExtLine.lastResult()) +aRefObjectB.setObject(aSketchLine2.lastResult()) +aSession.finishOperation() +aLine1Len = lineLength(aSketchLine1) +aLine2Len = lineLength(aSketchLine2) +assert (math.fabs(aLine1Len - anExtLineLen) < 1.e-10) +assert (math.fabs(aLine2Len - anExtLineLen) < 1.e-10) #========================================================================= # End of test #========================================================================= diff --git a/src/SketchPlugin/Test/TestConstraintMirror.py b/src/SketchPlugin/Test/TestConstraintMirror.py index 43d2c45bb..6c3678970 100644 --- a/src/SketchPlugin/Test/TestConstraintMirror.py +++ b/src/SketchPlugin/Test/TestConstraintMirror.py @@ -18,6 +18,46 @@ import math __updated__ = "2015-03-17" +#========================================================================= +# Auxiliary functions +#========================================================================= +def checkMirror(theListInit, theListMirr, theMirrorLine): + aListSize = theListInit.size() + + aLineStartPoint = geomDataAPI_Point2D(theMirrorLine.attribute("StartPoint")) + aLineEndPoint = geomDataAPI_Point2D(theMirrorLine.attribute("EndPoint")) + aLineDirX = aLineEndPoint.x() - aLineStartPoint.x() + aLineDirY = aLineEndPoint.y() - aLineStartPoint.y() + + for ind in range(0, aListSize): + aFeatureB = ModelAPI_Feature.feature(theListInit.object(ind)) + aFeatureC = ModelAPI_Feature.feature(theListMirr.object(ind)) + assert(aFeatureB is not None) + assert(aFeatureC is not None) + assert(aFeatureB.getKind() == aFeatureC.getKind()) + + anAttributes = {} + if (aFeatureB.getKind() == "SketchLine"): + anAttributes = {'StartPoint':'StartPoint', 'EndPoint':'EndPoint'} + elif (aFeatureB.getKind() == "SketchArc"): + anAttributes = {'ArcCenter':'ArcCenter', 'ArcStartPoint':'ArcEndPoint', 'ArcEndPoint':'ArcStartPoint'} + + for key in anAttributes: + aPointB = geomDataAPI_Point2D(aFeatureB.attribute(key)) + aPointC = geomDataAPI_Point2D(aFeatureC.attribute(anAttributes[key])) + aDirX = aPointC.x() - aPointB.x() + aDirY = aPointC.y() - aPointB.y() + aDot = aLineDirX * aDirX + aLineDirY * aDirY + assert(math.fabs(aDot) < 1.e-10) + aDirX = aLineEndPoint.x() - 0.5 * (aPointB.x() + aPointC.x()) + aDirY = aLineEndPoint.y() - 0.5 * (aPointB.y() + aPointC.y()) + aCross = aLineDirX * aDirY - aLineDirY * aDirX + assert(math.fabs(aCross) < 1.e-10) + + +#========================================================================= +# Start of test +#========================================================================= aSession = ModelAPI_Session.get() aDocument = aSession.moduleDocument() #========================================================================= @@ -113,44 +153,44 @@ aSession.startOperation() aMirror = aSketchFeature.addFeature("SketchConstraintMirror") aRefObjectA = aMirror.refattr("ConstraintEntityA") aRefObjectA.setObject(modelAPI_ResultConstruction(aMirrorLine.firstResult())) -aRefListB = aMirror.reflist("ConstraintEntityB") -aRefListB.append(aSketchArc1) -aRefListB.append(aSketchLine1) -aRefListB.append(aSketchLine2) +aRefListInitial = aMirror.reflist("ConstraintMirrorList") +aRefListInitial.append(aSketchArc1.lastResult()) +aRefListInitial.append(aSketchLine1.lastResult()) +aRefListInitial.append(aSketchLine2.lastResult()) aMirror.execute() aSession.finishOperation() #========================================================================= # Verify the simmetricity of all mirrored objects #========================================================================= +aRefListB = aMirror.reflist("ConstraintEntityB") aRefListC = aMirror.reflist("ConstraintEntityC") -aListSize = aRefListB.size() -aLineDirX = aLineEndPoint.x() - aLineStartPoint.x() -aLineDirY = aLineEndPoint.y() - aLineStartPoint.y() +assert (aRefListB.size() == 3) +assert (aRefListC.size() == 3) +checkMirror(aRefListB, aRefListC, aMirrorLine) -for ind in range(0, aListSize): - aFeatureB = modelAPI_Feature(aRefListB.object(ind)) - aFeatureC = modelAPI_Feature(aRefListC.object(ind)) - assert(aFeatureB is not None) - assert(aFeatureC is not None) - assert(aFeatureB.getKind() == aFeatureC.getKind()) - anAttributes = {} - print aFeatureB.getKind() - if (aFeatureB.getKind() == "SketchLine"): - anAttributes = {'StartPoint':'StartPoint', 'EndPoint':'EndPoint'} - elif (aFeatureB.getKind() == "SketchArc"): - anAttributes = {'ArcCenter':'ArcCenter', 'ArcStartPoint':'ArcEndPoint', 'ArcEndPoint':'ArcStartPoint'} +#========================================================================= +# Remove object from mirror +#========================================================================= +aSession.startOperation() +aRefListInitial.remove(aSketchLine2.lastResult()) +aSession.finishOperation() +assert (aRefListB.size() == 2) +assert (aRefListC.size() == 2) +checkMirror(aRefListB, aRefListC, aMirrorLine) - for key in anAttributes: - aPointB = geomDataAPI_Point2D(aFeatureB.attribute(key)) - aPointC = geomDataAPI_Point2D(aFeatureC.attribute(anAttributes[key])) - aDirX = aPointC.x() - aPointB.x() - aDirY = aPointC.y() - aPointB.y() - aDot = aLineDirX * aDirX + aLineDirY * aDirY - assert(math.fabs(aDot) < 1.e-10) - aDirX = aLineEndPoint.x() - 0.5 * (aPointB.x() + aPointC.x()) - aDirY = aLineEndPoint.y() - 0.5 * (aPointB.y() + aPointC.y()) - aCross = aLineDirX * aDirY - aLineDirY * aDirX - assert(math.fabs(aCross) < 1.e-10) +#========================================================================= +# Clear list of mirrored features +#========================================================================= +aSession.startOperation() +aRefListInitial.clear() +assert (aRefListB.size() == 0) +assert (aRefListC.size() == 0) +# add arc once again +aRefListInitial.append(aSketchArc1.lastResult()) +aSession.finishOperation() +assert (aRefListB.size() == 1) +assert (aRefListC.size() == 1) +checkMirror(aRefListB, aRefListC, aMirrorLine) #========================================================================= # End of test #========================================================================= diff --git a/src/SketchPlugin/Test/TestFillet.py b/src/SketchPlugin/Test/TestFillet.py index 72bf010eb..b4f648693 100644 --- a/src/SketchPlugin/Test/TestFillet.py +++ b/src/SketchPlugin/Test/TestFillet.py @@ -19,7 +19,7 @@ import math #========================================================================= aStartPoint1 = [] -def createSketch(theSketch): +def createSketch1(theSketch): global aStartPoint1 # Initialize sketch by two lines with coincident boundary allFeatures = [] @@ -44,9 +44,39 @@ def createSketch(theSketch): theSketch.execute() return allFeatures + + +def createSketch2(theSketch): + global aStartPoint1 + # Initialize sketch by line and arc with coincident boundary + allFeatures = [] + # Line + aSketchLine = theSketch.addFeature("SketchLine") + aStartPoint1 = geomDataAPI_Point2D(aSketchLine.attribute("StartPoint")) + aEndPoint1 = geomDataAPI_Point2D(aSketchLine.attribute("EndPoint")) + aStartPoint1.setValue(10., 10.) + aEndPoint1.setValue(30., 5.) + allFeatures.append(aSketchLine) + # Arc + aSketchArc = theSketch.addFeature("SketchArc") + aStartPoint2 = geomDataAPI_Point2D(aSketchArc.attribute("ArcStartPoint")) + aEndPoint2 = geomDataAPI_Point2D(aSketchArc.attribute("ArcEndPoint")) + aCenterPoint = geomDataAPI_Point2D(aSketchArc.attribute("ArcCenter")) + aCenterPoint.setValue(20., 10.) + aStartPoint2.setValue(10., 10.) + aEndPoint2.setValue(20., 0.) + allFeatures.append(aSketchArc) + # Coincidence + aCoincidence = theSketch.addFeature("SketchConstraintCoincidence") + aCoincidence.refattr("ConstraintEntityA").setAttr(aStartPoint1) + aCoincidence.refattr("ConstraintEntityB").setAttr(aStartPoint2) + + theSketch.execute() + return allFeatures def checkFillet(theObjects, theRadius): # Verify the arc and lines are connected smoothly + print "Check Fillet" aLine = [] anArc = [] aSize = theObjects.size() @@ -57,20 +87,22 @@ def checkFillet(theObjects, theRadius): aLine.append(feat) elif (feat.getKind() == "SketchArc"): anArc.append(feat) - assert(anArc) - assert(anArc[0] is not None) + aFilletArc = anArc[-1] + assert(aFilletArc is not None) + anArc.pop() anArcPoints = [] - aPoint = geomDataAPI_Point2D(anArc[0].attribute("ArcStartPoint")) + aPoint = geomDataAPI_Point2D(aFilletArc.attribute("ArcStartPoint")) print "ArcStartPoint " + repr(aPoint.x()) + " " + repr(aPoint.y()) anArcPoints.append((aPoint.x(), aPoint.y())) - aPoint = geomDataAPI_Point2D(anArc[0].attribute("ArcEndPoint")) + aPoint = geomDataAPI_Point2D(aFilletArc.attribute("ArcEndPoint")) print "ArcEndPoint " + repr(aPoint.x()) + " " + repr(aPoint.y()) anArcPoints.append((aPoint.x(), aPoint.y())) - aPoint = geomDataAPI_Point2D(anArc[0].attribute("ArcCenter")) + aPoint = geomDataAPI_Point2D(aFilletArc.attribute("ArcCenter")) print "ArcCenter " + repr(aPoint.x()) + " " + repr(aPoint.y()) aCenterX = aPoint.x() aCenterY = aPoint.y() + aFilletRadius = math.hypot(anArcPoints[0][0]-aCenterX, anArcPoints[0][1]-aCenterY) for line in aLine: aStartPoint = geomDataAPI_Point2D(line.attribute("StartPoint")) @@ -95,6 +127,22 @@ def checkFillet(theObjects, theRadius): break; + for arc in anArc: + aStartPoint = geomDataAPI_Point2D(arc.attribute("ArcStartPoint")) + aEndPoint = geomDataAPI_Point2D(arc.attribute("ArcEndPoint")) + aCenterPoint = geomDataAPI_Point2D(arc.attribute("ArcCenter")) + + aBaseArcPoints = [] + aBaseArcPoints.append((aStartPoint.x(), aStartPoint.y())) + print "anArcStartPoint " + repr(aStartPoint.x()) + " " + repr(aStartPoint.y()) + aBaseArcPoints.append((aEndPoint.x(), aEndPoint.y())) + print "anArcEndPoint " + repr(aEndPoint.x()) + " " + repr(aEndPoint.y()) + print "anArcCenter " + repr(aCenterPoint.x()) + " " + repr(aCenterPoint.y()) + + aRadius = math.hypot(aStartPoint.x()-aCenterPoint.x(), aStartPoint.y()-aCenterPoint.y()) + aDist = math.hypot(aCenterPoint.x() - aCenterX, aCenterPoint.y() - aCenterY) + assert math.fabs(aFilletRadius + aRadius - aDist) < 1.e-7 or math.fabs(math.fabs(aFilletRadius - aRadius) - aDist) < 1.e-7, \ + "Fillet radius = {0}, Base arc radius = {1}, distance between centers = {2}".format(aFilletRadius, aRadius, aDist) #========================================================================= @@ -119,10 +167,10 @@ norm = geomDataAPI_Dir(aSketchFeature.attribute("Norm")) norm.setValue(0, 0, 1) aSession.finishOperation() #========================================================================= -# Initialize sketch +# Initialize sketch by two lines #========================================================================= aSession.startOperation() -aFeaturesList = createSketch(aSketchFeature) +aFeaturesList = createSketch1(aSketchFeature) aSession.finishOperation() #========================================================================= # Global variables @@ -146,8 +194,55 @@ aResObjects = aFillet.reflist("ConstraintEntityB") # Verify the objects of fillet are created #========================================================================= assert(aResObjects) +checkFillet(aResObjects, FILLET_RADIUS1) +#========================================================================= +# Change Fillet radius +#========================================================================= +aRadius.setValue(FILLET_RADIUS2) +aSession.finishOperation() +checkFillet(aResObjects, FILLET_RADIUS2) + +#========================================================================= +# Create another sketch +#========================================================================= +aSession.startOperation() +aSketchCommonFeature = aDocument.addFeature("Sketch") +aSketchFeature = featureToCompositeFeature(aSketchCommonFeature) +origin = geomDataAPI_Point(aSketchFeature.attribute("Origin")) +origin.setValue(0, 0, 0) +dirx = geomDataAPI_Dir(aSketchFeature.attribute("DirX")) +dirx.setValue(1, 0, 0) +norm = geomDataAPI_Dir(aSketchFeature.attribute("Norm")) +norm.setValue(0, 1, 0) +aSession.finishOperation() +#========================================================================= +# Initialize sketch by line and arc +#========================================================================= +aSession.startOperation() +aFeaturesList = createSketch2(aSketchFeature) aSession.finishOperation() +#========================================================================= +# Create the Fillet +#========================================================================= +aSession.startOperation() +aFillet = aSketchFeature.addFeature("SketchConstraintFillet") +aRefAttrA = aFillet.refattr("ConstraintEntityA"); +aRefAttrA.setAttr(aStartPoint1) +aRadius = aFillet.real("ConstraintValue") +aRadius.setValue(FILLET_RADIUS1) +aFillet.execute() +aResObjects = aFillet.reflist("ConstraintEntityB") +#========================================================================= +# Verify the objects of fillet are created +#========================================================================= +assert(aResObjects) checkFillet(aResObjects, FILLET_RADIUS1) #========================================================================= +# Change Fillet radius +#========================================================================= +aRadius.setValue(FILLET_RADIUS2) +aSession.finishOperation() +checkFillet(aResObjects, FILLET_RADIUS2) +#========================================================================= # End of test #========================================================================= diff --git a/src/SketchPlugin/Test/TestMultiRotation.py b/src/SketchPlugin/Test/TestMultiRotation.py index 34cb0387f..27b0aae24 100644 --- a/src/SketchPlugin/Test/TestMultiRotation.py +++ b/src/SketchPlugin/Test/TestMultiRotation.py @@ -35,6 +35,15 @@ def createSketch(theSketch): theSketch.execute() return allFeatures +def createLine(theSketch): + aSketchLine = theSketch.addFeature("SketchLine") + aStartPoint = geomDataAPI_Point2D(aSketchLine.attribute("StartPoint")) + aEndPoint = geomDataAPI_Point2D(aSketchLine.attribute("EndPoint")) + aStartPoint.setValue(7., 5.) + aEndPoint.setValue(1., 3.) + theSketch.execute() + return aSketchLine + def checkRotation(theObjects, theNbObjects, theCenterX, theCenterY, theAngle): # Verify distances of the objects and the number of copies aFeatures = [] @@ -159,11 +168,54 @@ aNbCopies.setValue(3) aSession.finishOperation() aRotated = aMultiRotation.reflist("ConstraintEntityB") checkRotation(aRotated, aNbCopies.value(), CENTER_X, CENTER_Y, ANGLE) + +#========================================================================= +# Create new feature and add it into the Rotation +#========================================================================= +aSession.startOperation() +aLine = createLine(aSketchFeature) +aSession.finishOperation() +aSession.startOperation() +aResult = modelAPI_ResultConstruction(aLine.lastResult()) +assert(aResult is not None) +aRotList.append(aResult) +aSession.finishOperation() +checkRotation(aRotated, aNbCopies.value(), CENTER_X, CENTER_Y, ANGLE) +#========================================================================= +# Move line and check the copies are moved too +#========================================================================= +aSession.startOperation() +aStartPoint = geomDataAPI_Point2D(aLine.attribute("StartPoint")) +aStartPoint.setValue(12., 5.) +aSession.finishOperation() +checkRotation(aRotated, aNbCopies.value(), CENTER_X, CENTER_Y, ANGLE) +#========================================================================= +# Change number of copies and verify Rotation +#========================================================================= +aSession.startOperation() +aNbCopies.setValue(2) +aSession.finishOperation() +checkRotation(aRotated, aNbCopies.value(), CENTER_X, CENTER_Y, ANGLE) + +#========================================================================= +# Remove a feature from the Rotation +#========================================================================= +aSession.startOperation() +aRemoveIt = aRotList.object(0) +aRotList.remove(aRemoveIt) +aSession.finishOperation() +checkRotation(aRotated, aNbCopies.value(), CENTER_X, CENTER_Y, ANGLE) + #========================================================================= -# TODO: improve test -# 1. Add more features into Rotation -# 2. Move one of initial features and check the Rotated is moved too +# Clear the list of rotated features #========================================================================= +aSession.startOperation() +aRotList.clear() +checkRotation(aRotated, 1, CENTER_X, CENTER_Y, ANGLE) +# Add line once again +aRotList.append(aResult) +aSession.finishOperation() +checkRotation(aRotated, aNbCopies.value(), CENTER_X, CENTER_Y, ANGLE) #========================================================================= # End of test #========================================================================= diff --git a/src/SketchPlugin/Test/TestMultiTranslation.py b/src/SketchPlugin/Test/TestMultiTranslation.py index 4dca56c49..2c0a2b937 100644 --- a/src/SketchPlugin/Test/TestMultiTranslation.py +++ b/src/SketchPlugin/Test/TestMultiTranslation.py @@ -34,6 +34,15 @@ def createSketch(theSketch): theSketch.execute() return allFeatures +def createLine(theSketch): + aSketchLine = theSketch.addFeature("SketchLine") + aStartPoint = geomDataAPI_Point2D(aSketchLine.attribute("StartPoint")) + aEndPoint = geomDataAPI_Point2D(aSketchLine.attribute("EndPoint")) + aStartPoint.setValue(7., 5.) + aEndPoint.setValue(1., 3.) + theSketch.execute() + return aSketchLine + def checkTranslation(theObjects, theNbObjects, theDeltaX, theDeltaY): # Verify distances of the objects and the number of copies aFeatures = [] @@ -46,7 +55,7 @@ def checkTranslation(theObjects, theNbObjects, theDeltaX, theDeltaY): anInd = 0 for feat, next in zip(aFeatures[:-1], aFeatures[1:]): anInd = anInd + 1 - if (anInd > theNbObjects): + if (anInd > theNbObjects-1): anInd = 0 continue assert(feat.getKind() == next.getKind()) @@ -150,11 +159,54 @@ aNbCopies.setValue(3) aSession.finishOperation() aTranslated = aMultiTranslation.reflist("ConstraintEntityB") checkTranslation(aTranslated, aNbCopies.value(), DELTA_X, DELTA_Y) + +#========================================================================= +# Create new feature and add it into the Rotation +#========================================================================= +aSession.startOperation() +aLine = createLine(aSketchFeature) +aSession.finishOperation() +aSession.startOperation() +aResult = modelAPI_ResultConstruction(aLine.lastResult()) +assert(aResult is not None) +aTransList.append(aResult) +aSession.finishOperation() +checkTranslation(aTranslated, aNbCopies.value(), DELTA_X, DELTA_Y) +#========================================================================= +# Move line and check the copies are moved too +#========================================================================= +aSession.startOperation() +aStartPoint = geomDataAPI_Point2D(aLine.attribute("StartPoint")) +aStartPoint.setValue(12., 5.) +aSession.finishOperation() +checkTranslation(aTranslated, aNbCopies.value(), DELTA_X, DELTA_Y) #========================================================================= -# TODO: improve test -# 1. Add more features into translation -# 2. Move one of initial features and check the translated is moved too +# Change number of copies and verify Rotation #========================================================================= +aSession.startOperation() +aNbCopies.setValue(2) +aSession.finishOperation() +checkTranslation(aTranslated, aNbCopies.value(), DELTA_X, DELTA_Y) + +#========================================================================= +# Remove a feature from the Rotation +#========================================================================= +aSession.startOperation() +aRemoveIt = aTransList.object(0) +aTransList.remove(aRemoveIt) +aSession.finishOperation() +checkTranslation(aTranslated, aNbCopies.value(), DELTA_X, DELTA_Y) + +#========================================================================= +# Clear the list of rotated features +#========================================================================= +aSession.startOperation() +aTransList.clear() +checkTranslation(aTranslated, 1, DELTA_X, DELTA_Y) +# Add line once again +aTransList.append(aResult) +aSession.finishOperation() +checkTranslation(aTranslated, aNbCopies.value(), DELTA_X, DELTA_Y) #========================================================================= # End of test #========================================================================= diff --git a/src/SketchSolver/SketchSolver_ConstraintMulti.cpp b/src/SketchSolver/SketchSolver_ConstraintMulti.cpp index d2135179c..96fa9ea2f 100644 --- a/src/SketchSolver/SketchSolver_ConstraintMulti.cpp +++ b/src/SketchSolver/SketchSolver_ConstraintMulti.cpp @@ -42,24 +42,53 @@ void SketchSolver_ConstraintMulti::getEntities(std::list& theE myStorage->update(aFeature); theEntities.push_back(myStorage->entity(aFeature)); - for (int i = 0; i < myNumberOfCopies && anObjIt != anObjectList.end(); ++i, ++anObjIt) - ; // just skip copied features + myFeatures.insert(aFeature); + for (int i = 0; i < myNumberOfCopies && anObjIt != anObjectList.end(); ++i, ++anObjIt) { + // just add copied features into the list of objects + aFeature = ModelAPI_Feature::feature(*anObjIt); + if (aFeature) + myFeatures.insert(aFeature); + } } } +bool SketchSolver_ConstraintMulti::remove() +{ + myFeatures.clear(); + return SketchSolver_Constraint::remove(); +} + void SketchSolver_ConstraintMulti::update() { update(false); } - void SketchSolver_ConstraintMulti::update(bool isForce) { cleanErrorMsg(); AttributeRefListPtr anInitialRefList = std::dynamic_pointer_cast( myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); AttributeIntegerPtr aNbObjects = myBaseConstraint->integer(nameNbObjects()); - if (anInitialRefList->size() != myNumberOfObjects || aNbObjects->value()-1 != myNumberOfCopies) { + bool isUpdated= anInitialRefList->size() != myNumberOfObjects || aNbObjects->value()-1 != myNumberOfCopies; + if (!isUpdated) { + // additional check that the features and their copies are changed + AttributeRefListPtr aRefList = std::dynamic_pointer_cast( + myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_B())); + if (aRefList && aRefList->size() != 0) { + FeaturePtr aFeature; + std::list anObjectList = aRefList->list(); + std::list::iterator anObjIt = anObjectList.begin(); + for (; anObjIt != anObjectList.end(); ++anObjIt) { + aFeature = ModelAPI_Feature::feature(*anObjIt); + if (aFeature && myFeatures.find(aFeature) == myFeatures.end()) { + isUpdated = true; + break; + } + } + } else + isUpdated = true; + } + if (isUpdated) { remove(); process(); return; diff --git a/src/SketchSolver/SketchSolver_ConstraintMulti.h b/src/SketchSolver/SketchSolver_ConstraintMulti.h index 16db54dbd..03f0a6923 100644 --- a/src/SketchSolver/SketchSolver_ConstraintMulti.h +++ b/src/SketchSolver/SketchSolver_ConstraintMulti.h @@ -32,6 +32,10 @@ public: /// \brief Update constraint void update(bool isForce); + /// \brief Tries to remove constraint + /// \return \c false, if current constraint contains another SketchPlugin constraints (like for multiple coincidence) + virtual bool remove(); + protected: /// \brief Converts SketchPlugin constraint to a list of SolveSpace constraints virtual void process() @@ -68,6 +72,8 @@ protected: int myNumberOfCopies; ///< number of previous copies of initial objects bool myAdjusted; ///< the constraint is already adjusted (to not do it several times) + + std::set myFeatures; ///< list of features and their copies to find whether some of them are disappeared }; #endif diff --git a/src/SketchSolver/SketchSolver_Group.cpp b/src/SketchSolver/SketchSolver_Group.cpp index 7ef43366f..c74580b07 100644 --- a/src/SketchSolver/SketchSolver_Group.cpp +++ b/src/SketchSolver/SketchSolver_Group.cpp @@ -172,30 +172,6 @@ bool SketchSolver_Group::changeConstraint( return true; } - -void SketchSolver_Group::updateConstraints() -{ - std::set aPostponed; // postponed constraints Multi-Rotation and Multi-Translation - - ConstraintConstraintMap::iterator anIt = myConstraints.begin(); - for (; anIt != myConstraints.end(); ++anIt) { - if (myChangedConstraints.find(anIt->first) == myChangedConstraints.end()) - continue; - if (anIt->first->getKind() == SketchPlugin_MultiRotation::ID() || - anIt->first->getKind() == SketchPlugin_MultiTranslation::ID()) - aPostponed.insert(anIt->second); - else - anIt->second->update(); - } - - // Update postponed constraints - std::set::iterator aSCIter = aPostponed.begin(); - for (; aSCIter != aPostponed.end(); ++aSCIter) - (*aSCIter)->update(); - - myChangedConstraints.clear(); -} - static void updateMultiConstraints(ConstraintConstraintMap& theConstraints, FeaturePtr theFeature) { ConstraintConstraintMap::iterator aCIt = theConstraints.begin(); @@ -293,9 +269,6 @@ bool SketchSolver_Group::updateWorkplane() // ============================================================================ bool SketchSolver_Group::resolveConstraints() { - if (!myChangedConstraints.empty()) - updateConstraints(); - bool aResolved = false; bool isGroupEmpty = isEmpty(); if (myStorage->isNeedToResolve() && !isGroupEmpty) { diff --git a/src/SketchSolver/SketchSolver_Group.h b/src/SketchSolver/SketchSolver_Group.h index f1520d3bd..7544fbaa2 100644 --- a/src/SketchSolver/SketchSolver_Group.h +++ b/src/SketchSolver/SketchSolver_Group.h @@ -152,9 +152,6 @@ private: /// \brief Verifies is the feature valid bool checkFeatureValidity(FeaturePtr theFeature); - /// \brief Update just changed constraints - void updateConstraints(); - private: GroupID myID; ///< Index of the group EntityID myWorkplaneID; ///< Index of workplane, the group is based on @@ -162,7 +159,6 @@ private: ConstraintConstraintMap myConstraints; ///< List of constraints std::set myTempConstraints; ///< List of temporary constraints std::map myParametricConstraints; ///< List of parametric constraints - std::set myChangedConstraints; ///< List of just updated constraints StoragePtr myStorage; ///< Container for the set of SolveSpace constraints and their entities -- 2.39.2