def solveConstraints( doc ): if not constraintsObjectsAllExist(doc): return T_start = time.time() updateOldStyleConstraintProperties(doc) constraintObjectQue = [ obj for obj in doc.Objects if 'ConstraintInfo' in obj.Content ] #doc.Objects already in tree order so no additional sorting / order checking required for constraints. objectNames = [] for c in constraintObjectQue: for attr in ['Object1','Object2']: objectName = getattr(c, attr, None) if objectName <> None and not objectName in objectNames: objectNames.append( objectName ) variableManager = VariableManager( doc, objectNames ) debugPrint(3,' variableManager.X0 %s' % variableManager.X0 ) constraintSystem = FixedObjectSystem( variableManager, findBaseObject(doc, objectNames) ) debugPrint(4, 'solveConstraints base system: %s' % constraintSystem.str() ) solved = True for constraintObj in constraintObjectQue: debugPrint( 3, ' parsing %s, type:%s' % (constraintObj.Name, constraintObj.Type )) try: cArgs = [variableManager, constraintObj] if not constraintSystem.containtsObject( constraintObj.Object1) and not constraintSystem.containtsObject( constraintObj.Object2): constraintSystem = AddFreeObjectsUnion(constraintSystem, *cArgs) if constraintObj.Type == 'plane': if constraintObj.SubElement2.startswith('Face'): #otherwise vertext constraintSystem = AxisAlignmentUnion(constraintSystem, *cArgs, constraintValue = constraintObj.directionConstraint ) constraintSystem = PlaneOffsetUnion(constraintSystem, *cArgs, constraintValue = constraintObj.offset.Value) elif constraintObj.Type == 'angle_between_planes': constraintSystem = AngleUnion(constraintSystem, *cArgs, constraintValue = constraintObj.angle.Value*pi/180 ) elif constraintObj.Type == 'axial': constraintSystem = AxisAlignmentUnion(constraintSystem, *cArgs, constraintValue = constraintObj.directionConstraint) constraintSystem = AxisDistanceUnion(constraintSystem, *cArgs, constraintValue = 0) if constraintObj.lockRotation: constraintSystem = LockRelativeAxialRotationUnion(constraintSystem, *cArgs, constraintValue = 0) elif constraintObj.Type == 'circularEdge': constraintSystem = AxisAlignmentUnion(constraintSystem, *cArgs, constraintValue=constraintObj.directionConstraint) constraintSystem = AxisDistanceUnion(constraintSystem, *cArgs, constraintValue=0) constraintSystem = PlaneOffsetUnion(constraintSystem, *cArgs, constraintValue=constraintObj.offset.Value) if constraintObj.lockRotation: constraintSystem = LockRelativeAxialRotationUnion(constraintSystem, *cArgs, constraintValue = 0) elif constraintObj.Type == 'sphericalSurface': constraintSystem = VertexUnion(constraintSystem, *cArgs, constraintValue=0) else: raise NotImplementedError, 'constraintType %s not supported yet' % constraintObj.Type except Assembly2SolverError, msg: FreeCAD.Console.PrintError('UNABLE TO SOLVE CONSTRAINTS! info:') FreeCAD.Console.PrintError(msg) solved = False break except:
def solveConstraints(doc): if not constraintsObjectsAllExist(doc): return updateOldStyleConstraintProperties(doc) constraintObjectQue = [ obj for obj in doc.Objects if 'ConstraintInfo' in obj.Content ] #doc.Objects already in tree order so no additional sorting / order checking required for constraints. conObjects = sum([[c.Object1] for c in constraintObjectQue], []) + sum( [[c.Object2] for c in constraintObjectQue], []) objectNames = [ obj.Name for obj in doc.Objects if hasattr(obj, 'Placement') and obj.Name in conObjects ] variableManager = VariableManager(doc, objectNames) debugPrint(3, ' variableManager.X0 %s' % variableManager.X0) constraintSystem = FixedObjectSystem(variableManager, findBaseObject(doc, objectNames)) debugPrint(4, 'solveConstraints base system: %s' % constraintSystem.str()) solved = True for constraintObj in constraintObjectQue: obj1Name = constraintObj.Object1 obj2Name = constraintObj.Object2 debugPrint( 3, ' parsing %s, type:%s' % (constraintObj.Name, constraintObj.Type)) cArgs = [ variableManager, obj1Name, obj2Name, constraintObj.SubElement1, constraintObj.SubElement2 ] try: if constraintObj.Type == 'plane': if constraintObj.SubElement2.startswith( 'Face'): #otherwise vertext constraintSystem = AxisAlignmentUnion( constraintSystem, *cArgs, constraintValue=constraintObj.directionConstraint) constraintSystem = PlaneOffsetUnion( constraintSystem, *cArgs, constraintValue=constraintObj.offset.Value) elif constraintObj.Type == 'angle_between_planes': constraintSystem = AngleUnion( constraintSystem, *cArgs, constraintValue=constraintObj.angle.Value * pi / 180) elif constraintObj.Type == 'axial': constraintSystem = AxisAlignmentUnion( constraintSystem, *cArgs, constraintValue=constraintObj.directionConstraint) constraintSystem = AxisDistanceUnion(constraintSystem, *cArgs, constraintValue=0) elif constraintObj.Type == 'circularEdge': constraintSystem = AxisAlignmentUnion( constraintSystem, *cArgs, constraintValue=constraintObj.directionConstraint) constraintSystem = AxisDistanceUnion(constraintSystem, *cArgs, constraintValue=0) constraintSystem = PlaneOffsetUnion( constraintSystem, *cArgs, constraintValue=constraintObj.offset.Value) else: raise NotImplementedError, 'constraintType %s not supported yet' % constraintObj.Type except Assembly2SolverError, msg: FreeCAD.Console.PrintError('UNABLE TO SOLVE CONSTRAINTS! info:') FreeCAD.Console.PrintError(msg) solved = False break except:
def solveConstraints( doc, showFailureErrorDialog=True, printErrors=True, use_cache=False ): T_start = time.time() constraintObjectQue = [ obj for obj in doc.Objects if 'ConstraintInfo' in obj.Content ] #doc.Objects already in tree order so no additional sorting / order checking required for constraints. objectNames = [] for c in constraintObjectQue: for attr in ['Object1','Object2']: objectName = getattr(c, attr, None) if objectName != None and not objectName in objectNames: objectNames.append( objectName ) variableManager = VariableManager( doc, objectNames ) debugPrint(3,' variableManager.X0 %s' % variableManager.X0 ) constraintSystem = FixedObjectSystem( variableManager, findBaseObject(doc, objectNames) ) debugPrint(4, 'solveConstraints base system: %s' % constraintSystem.str() ) solved = True if use_cache: t_cache_start = time.time() constraintSystem, que_start = cache.retrieve( constraintSystem, constraintObjectQue) debugPrint(3,"~cached solution available for first %i out-off %i constraints (retrieved in %3.2fs)" % (que_start, len(constraintObjectQue), time.time() - t_cache_start ) ) cache.prepare() else: que_start = 0 for constraintObj in constraintObjectQue[que_start:]: debugPrint( 3, ' parsing %s, type:%s' % (constraintObj.Name, constraintObj.Type )) try: cArgs = [variableManager, constraintObj] if not constraintSystem.containtsObject( constraintObj.Object1) and not constraintSystem.containtsObject( constraintObj.Object2): constraintSystem = AddFreeObjectsUnion(constraintSystem, *cArgs) if constraintObj.Type == 'plane': if constraintObj.SubElement2.startswith('Face'): #otherwise vertex constraintSystem = AxisAlignmentUnion(constraintSystem, *cArgs, constraintValue = constraintObj.directionConstraint ) constraintSystem = PlaneOffsetUnion(constraintSystem, *cArgs, constraintValue = constraintObj.offset.Value) elif constraintObj.Type == 'angle_between_planes': constraintSystem = AngleUnion(constraintSystem, *cArgs, constraintValue = constraintObj.angle.Value*pi/180 ) elif constraintObj.Type == 'axial': constraintSystem = AxisAlignmentUnion(constraintSystem, *cArgs, constraintValue = constraintObj.directionConstraint) constraintSystem = AxisDistanceUnion(constraintSystem, *cArgs, constraintValue = 0) if constraintObj.lockRotation: constraintSystem = LockRelativeAxialRotationUnion(constraintSystem, *cArgs, constraintValue = 0) elif constraintObj.Type == 'circularEdge': constraintSystem = AxisAlignmentUnion(constraintSystem, *cArgs, constraintValue=constraintObj.directionConstraint) constraintSystem = AxisDistanceUnion(constraintSystem, *cArgs, constraintValue=0) constraintSystem = PlaneOffsetUnion(constraintSystem, *cArgs, constraintValue=constraintObj.offset.Value) if constraintObj.lockRotation: constraintSystem = LockRelativeAxialRotationUnion(constraintSystem, *cArgs, constraintValue = 0) elif constraintObj.Type == 'sphericalSurface': constraintSystem = VertexUnion(constraintSystem, *cArgs, constraintValue=0) else: raise NotImplementedError('constraintType %s not supported yet' % constraintObj.Type) if use_cache: cache.record( constraintSystem ) except Assembly2SolverError as e: if printErrors: FreeCAD.Console.PrintError('UNABLE TO SOLVE CONSTRAINTS! info:') FreeCAD.Console.PrintError(e) solved = False break except: if printErrors: FreeCAD.Console.PrintError('UNABLE TO SOLVE CONSTRAINTS! info:') FreeCAD.Console.PrintError( traceback.format_exc()) solved = False break if solved: debugPrint(4,'placement X %s' % constraintSystem.variableManager.X ) if use_cache: t_cache_record_start = time.time() cache.commit( constraintSystem, constraintObjectQue, que_start) debugPrint( 4,' time cache.record %3.2fs' % (time.time()-t_cache_record_start) ) t_update_freecad_start = time.time() variableManager.updateFreeCADValues( constraintSystem.variableManager.X ) debugPrint( 4,' time to update FreeCAD placement variables %3.2fs' % (time.time()-t_update_freecad_start) ) debugPrint(2,'Constraint system solved in %2.2fs; resulting system has %i degrees-of-freedom' % (time.time()-T_start, len( constraintSystem.degreesOfFreedom))) elif showFailureErrorDialog and QtGui.qApp != None: #i.e. GUI active # http://www.blog.pythonlibrary.org/2013/04/16/pyside-standard-dialogs-and-message-boxes/ flags = QtGui.QMessageBox.StandardButton.Yes flags |= QtGui.QMessageBox.StandardButton.No #flags |= QtGui.QMessageBox.Ignore message = """The assembly2 solver failed to satisfy the constraint "%s". possible causes - impossible/contridictorary constraints have be specified, or - the contraint problem is too difficult for the solver, or - a bug in the assembly 2 workbench potential solutions - redefine the constraint (popup menu item in the treeView) - delete constraint, and try again using a different constraint scheme. Delete constraint "%s"? """ % (constraintObj.Name, constraintObj.Name) response = QtGui.QMessageBox.critical(QtGui.QApplication.activeWindow(), "Solver Failure!", message, flags) if response == QtGui.QMessageBox.Yes: from assembly2.constraints import removeConstraint removeConstraint( constraintObj ) #elif response == QtGui.QMessageBox.Ignore: # variableManager.updateFreeCADValues( constraintSystem.variableManager.X ) return constraintSystem if solved else None
#FreeCAD.setActiveDocument("box") #FreeCAD.ActiveDocument = FreeCAD.getDocument("box") objName = "box" box = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", objName) box.Shape = Part.makeBox(2, 3, 2) #FreeCAD.ActiveDocument.recompute() box.Placement.Base.x = rand() box.Placement.Base.y = rand() + 1 box.Placement.Base.z = rand() + 2 print(box.Placement) class FakeSystem: def __init__(self, variableManager): self.variableManager = variableManager vM = VariableManager(FreeCAD.ActiveDocument) print(vM.X) constaintSystem = FakeSystem(vM) print('\nTesting PlacementDegreeOfFreedom') for object_dof in range(6): d = PlacementDegreeOfFreedom(constaintSystem, objName, object_dof) print(d) for i in range(6): value = pi * (rand() - 0.5) d.setValue(value) assert d.getValue() == value print('\nTesting LinearMotionDegreeOfFreedom') tol = 10**-14 for i in range(3):
def solveConstraints( doc, showFailureErrorDialog=True, printErrors=True, use_cache=False, solver=solve_via_slsqp, random_restart_attempts=1 ): assert not use_cache, "cache not implemented for Newton solver" T_start = time.time() variableManager = VariableManager( doc ) constraints = [] mapper = { 'axial':AxialConstraint, 'plane':PlaneConstraint, 'circularEdge':CircularEdgeConstraint, 'angle_between_planes':AngleConstraint, 'sphericalSurface':SphericalSurfaceConstraint } for obj in doc.Objects: if 'ConstraintInfo' in obj.Content: debugPrint(3, "assembly2solver parsing %s" % obj.Name ) #try: constraints.append( mapper[obj.Type]( doc, obj, variableManager) ) #except AttributeError, msg: # if str(msg).strip() == "'NoneType' object has no attribute 'Placement'": # flags = QtGui.QMessageBox.StandardButton.Yes | QtGui.QMessageBox.StandardButton.Abort # message = "%s is refering to an object no longer in the assembly. Delete constraint? otherwise abort solving." % obj.Name # response = QtGui.QMessageBox.critical(QtGui.qApp.activeWindow(), "Broken Constraint", message, flags ) # if response == QtGui.QMessageBox.Yes: # FreeCAD.Console.PrintError("removing constraint %s" % obj.Name) # doc.removeObject(obj.Name) # else: # FreeCAD.Console.PrintError("aborted solving constraints due to %s refering a non-existent object" % obj.Name) # return # else: # raise AttributeError(msg) violatedConstraints = [c for c in constraints if not c.satisfied() ] vNames = [ vc.constraintObj.Name for vc in violatedConstraints ] debugPrint( 3, "violated constraints: " + ', '.join(vNames) ) vObjects = sum( [ vc.objectNames() for vc in violatedConstraints ], [] ) debugPrint( 3, "objects associated to these constraints: " + ', '.join( list(set(vObjects)))) vObjects_connectivety = [ sum( obj in c.objectNames() for c in constraints ) for obj in vObjects ] debugPrint( 3, "repective connectivety %s " % vObjects_connectivety) if len(violatedConstraints) == 1 and len(vObjects_connectivety) == 2 and sum(vObjects_connectivety) > 2: if not variableManager.objFixed(vObjects[0]) and not variableManager.objFixed(vObjects[1]): for obj, conn in zip(vObjects, vObjects_connectivety): if conn == 1: # makes vObjects_connectivety[0] == 1 or vObjects_connectivety[1] == 1 unnessary variableManager.fixEveryObjectExcept( obj ) debugPrint( 3, "moving %s as to satisfy %s, everything else fixed" % ( obj, vNames[0]) ) constraints = violatedConstraints def constraintEqs(x): #equations which need to solved inorder to assemble parts variableManager.setValues(x) errors = sum( [c.errors() for c in constraints], [] ) debugPrint( 4, "constraint errors %s" % errors ) return errors x0 = variableManager.getValues() debugPrint(3, "variableManager.getValues() %s" % x0) algName, warningMsg, optResults = solver(constraintEqs, x0, variableManager.bounds() ) debugPrint(3, str(optResults)) if warningMsg <> '' or optResults['fOpt'] > 10**-4 and random_restart_attempts > 0: for i in range(random_restart_attempts): variableManager.setValues(x0) xN = variableManager.peturbValues( vObjects ) algName, warningMsg, optResults = solver(constraintEqs, xN, variableManager.bounds() ) debugPrint(3, str(optResults)) if warningMsg == '' and optResults['fOpt'] < 10**-4: break if warningMsg == '' and optResults['fOpt'] < 10**-4: #then constraints satisfied variableManager.setValues( optResults['xOpt'] ) variableManager.updateFreeCADValues( ) return optResults['xOpt'] elif showFailureErrorDialog and QtGui.qApp != None: FreeCAD.Console.PrintError("UNABLE TO SOLVE ASSEMBLY CONSTRAINTS. Info:\n") FreeCAD.Console.PrintError(" optimization algorithm could not minimize the norm of constraint errors\n" ) FreeCAD.Console.PrintError(" optimization algorithm used : %s\n" % algName ) FreeCAD.Console.PrintError(" optimization warning message : %s\n" % warningMsg) for k,v in optResults.iteritems(): FreeCAD.Console.PrintError(" %s: %s\n" % (k,v)) FreeCAD.Console.PrintError("UNABLE TO SOLVE ASSEMBLY CONSTRAINTS. refer to the Report View window for info\n") # http://www.blog.pythonlibrary.org/2013/04/16/pyside-standard-dialogs-and-message-boxes/ flags = QtGui.QMessageBox.StandardButton.Yes flags |= QtGui.QMessageBox.StandardButton.No message = """The Assembly 2 Netwon solver failed to find a solution to the specified constraints. This is due to either - the contraint problem being too difficult for the solver, or - impossible/contridictorary constraints have be specified. Either way, the solution is most likely to delete the problematic constraints, and try again using a different constraint scheme. Delete constraints [%s]? """ % ', '.join(vNames) response = QtGui.QMessageBox.critical( QtGui.qApp.activeWindow(), "Solver Failure!", message, flags ) if response == QtGui.QMessageBox.Yes: for name in vNames: doc.removeObject(name) FreeCAD.Console.PrintError("removed constraint %s" % name) return None
def solveConstraints(doc, showFailureErrorDialog=True, printErrors=True, cache=None): if not constraintsObjectsAllExist(doc): return T_start = time.time() updateOldStyleConstraintProperties(doc) constraintObjectQue = [ obj for obj in doc.Objects if 'ConstraintInfo' in obj.Content ] #doc.Objects already in tree order so no additional sorting / order checking required for constraints. objectNames = [] for c in constraintObjectQue: for attr in ['Object1', 'Object2']: objectName = getattr(c, attr, None) if objectName != None and not objectName in objectNames: objectNames.append(objectName) variableManager = VariableManager(doc, objectNames) debugPrint(3, ' variableManager.X0 %s' % variableManager.X0) constraintSystem = FixedObjectSystem(variableManager, findBaseObject(doc, objectNames)) debugPrint(4, 'solveConstraints base system: %s' % constraintSystem.str()) solved = True if cache != None: t_cache_start = time.time() constraintSystem, que_start = cache.retrieve(constraintSystem, constraintObjectQue) debugPrint( 3, "~cached solution available for first %i out-off %i constraints (retrieved in %3.2fs)" % (que_start, len(constraintObjectQue), time.time() - t_cache_start)) else: que_start = 0 for constraintObj in constraintObjectQue[que_start:]: debugPrint( 3, ' parsing %s, type:%s' % (constraintObj.Name, constraintObj.Type)) try: cArgs = [variableManager, constraintObj] if not constraintSystem.containtsObject( constraintObj.Object1 ) and not constraintSystem.containtsObject(constraintObj.Object2): constraintSystem = AddFreeObjectsUnion(constraintSystem, *cArgs) if constraintObj.Type == 'plane': if constraintObj.SubElement2.startswith( 'Face'): #otherwise vertex constraintSystem = AxisAlignmentUnion( constraintSystem, *cArgs, constraintValue=constraintObj.directionConstraint) constraintSystem = PlaneOffsetUnion( constraintSystem, *cArgs, constraintValue=constraintObj.offset.Value) elif constraintObj.Type == 'angle_between_planes': constraintSystem = AngleUnion( constraintSystem, *cArgs, constraintValue=constraintObj.angle.Value * pi / 180) elif constraintObj.Type == 'axial': constraintSystem = AxisAlignmentUnion( constraintSystem, *cArgs, constraintValue=constraintObj.directionConstraint) constraintSystem = AxisDistanceUnion(constraintSystem, *cArgs, constraintValue=0) if constraintObj.lockRotation: constraintSystem = LockRelativeAxialRotationUnion( constraintSystem, *cArgs, constraintValue=0) elif constraintObj.Type == 'circularEdge': constraintSystem = AxisAlignmentUnion( constraintSystem, *cArgs, constraintValue=constraintObj.directionConstraint) constraintSystem = AxisDistanceUnion(constraintSystem, *cArgs, constraintValue=0) constraintSystem = PlaneOffsetUnion( constraintSystem, *cArgs, constraintValue=constraintObj.offset.Value) if constraintObj.lockRotation: constraintSystem = LockRelativeAxialRotationUnion( constraintSystem, *cArgs, constraintValue=0) elif constraintObj.Type == 'sphericalSurface': constraintSystem = VertexUnion(constraintSystem, *cArgs, constraintValue=0) else: raise NotImplementedError( 'constraintType %s not supported yet' % constraintObj.Type) if cache: cache.record_levels.append( constraintSystem.numberOfParentSystems()) except Assembly2SolverError as e: if printErrors: FreeCAD.Console.PrintError( 'UNABLE TO SOLVE CONSTRAINTS! info:') FreeCAD.Console.PrintError(e) solved = False break except: if printErrors: FreeCAD.Console.PrintError( 'UNABLE TO SOLVE CONSTRAINTS! info:') FreeCAD.Console.PrintError(traceback.format_exc()) solved = False break if solved: debugPrint(4, 'placement X %s' % constraintSystem.variableManager.X) t_cache_record_start = time.time() if cache: cache.record(constraintSystem, constraintObjectQue, que_start) debugPrint( 4, ' time cache.record %3.2fs' % (time.time() - t_cache_record_start)) t_update_freecad_start = time.time() variableManager.updateFreeCADValues(constraintSystem.variableManager.X) debugPrint( 4, ' time to update FreeCAD placement variables %3.2fs' % (time.time() - t_update_freecad_start)) debugPrint( 2, 'Constraint system solved in %2.2fs; resulting system has %i degrees-of-freedom' % (time.time() - T_start, len(constraintSystem.degreesOfFreedom))) elif showFailureErrorDialog and QtGui.qApp != None: #i.e. GUI active # http://www.blog.pythonlibrary.org/2013/04/16/pyside-standard-dialogs-and-message-boxes/ flags = QtGui.QMessageBox.StandardButton.Yes flags |= QtGui.QMessageBox.StandardButton.No #flags |= QtGui.QMessageBox.Ignore message = """The assembly2 solver failed to satisfy the constraint "%s". possible causes - impossible/contridictorary constraints have be specified, or - the contraint problem is too difficult for the solver, or - a bug in the assembly 2 workbench potential solutions - redefine the constraint (popup menu item in the treeView) - delete constraint, and try again using a different constraint scheme. Delete constraint "%s"? """ % (constraintObj.Name, constraintObj.Name) response = QtGui.QMessageBox.critical(QtGui.qApp.activeWindow(), "Solver Failure!", message, flags) if response == QtGui.QMessageBox.Yes: removeConstraint(constraintObj) #elif response == QtGui.QMessageBox.Ignore: # variableManager.updateFreeCADValues( constraintSystem.variableManager.X ) return constraintSystem if solved else None
def test(self): from degreesOfFreedom import PlacementDegreeOfFreedom, LinearMotionDegreeOfFreedom, AxisRotationDegreeOfFreedom, pi, normalize from numpy.random import rand from variableManager import VariableManager #print('creating test FreeCAD document, constraining a single Cube') import FreeCAD, Part FreeCAD.newDocument("testDoc") #FreeCAD.setActiveDocument("box") #FreeCAD.ActiveDocument = FreeCAD.getDocument("box") objName = "box" box = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", objName) box.Shape = Part.makeBox(2, 3, 2) #FreeCAD.ActiveDocument.recompute() box.Placement.Base.x = rand() box.Placement.Base.y = rand() + 1 box.Placement.Base.z = rand() + 2 #print(box.Placement) class FakeSystem: def __init__(self, variableManager): self.variableManager = variableManager vM = VariableManager(FreeCAD.ActiveDocument) #print(vM.X) constaintSystem = FakeSystem(vM) #print('\nTesting PlacementDegreeOfFreedom') for object_dof in range(6): d = PlacementDegreeOfFreedom(constaintSystem, objName, object_dof) #print(d) for i in range(6): value = pi * (rand() - 0.5) d.setValue(value) assert d.getValue() == value #print('\nTesting LinearMotionDegreeOfFreedom') tol = 10**-14 for i in range(3): d = LinearMotionDegreeOfFreedom(constaintSystem, objName) d.setDirection(normalize(rand(3) - 0.5)) #print(d) for i in range(12): value = 12 * (rand() - 0.5) d.setValue(value) returnedValue = d.getValue() if abs(returnedValue - value) > tol: raise ValueError( "d.getValue() - value != %1.0e, [diff %e]" % (tol, returnedValue - value)) #print('\nTesting AxisRotationDegreeOfFreedom') tol = 10**-12 for i in range(3): d = AxisRotationDegreeOfFreedom(constaintSystem, objName) axis_r = normalize( rand(3) - 0.5) #axis in parts co-ordinate system (i.e. relative to part) axis = normalize(rand(3) - 0.5) # desired axis in global co-ordinate system d.setAxis(axis, axis_r) d.setValue( 0) #update azi,ela,theta to statify aligment of axis vector #print(d) for i in range(6): value = 2 * pi * (rand() - 0.5) d.setValue(value) returnedValue = d.getValue() #print(' d.getValue() %f value %f, diff %e' % (returnedValue, value, returnedValue - value)) if abs(returnedValue - value) > tol: raise ValueError( "d.getValue() - value != %1.0e, [diff %e]" % (tol, returnedValue - value))