diff --git a/TODO.txt b/TODO.txt index a6f5a32..738d464 100644 --- a/TODO.txt +++ b/TODO.txt @@ -2,23 +2,59 @@ TODO/WISH LIST -------------- Code: Some bits of the code are ugly: - - vector module, matpy module and Numpy package, all offer similar functionality. - Use only one, get rid of two. - - more documentation! + - vector module, matpy module and Numpy package, all offer similar + functionality. Use only one, get rid of two. + - more documentation! -Speed: The solver is currently much too slow. The problem is the pattern matching algorithms that is used to find -combinations of clusters that can be rewritten/merged. Solutions: - - hand-coded matching algorithms per merge rule (labourous and error prone) - - incremental pattern matching system (work in progress) - - compliled implementation (Psycho/C++/Haskell/???) +Speed: The solver is currently much too slow. The problem is the pattern + matching algorithms that is used to find combinations of clusters that + can be rewritten/merged. Solutions: + - incremental pattern matching system (work in progress) + - compliled implementation (Psycho/C++/Haskell/???) Rules: More rewrite rules to increase the problem domain: - - larger subproblems (octahedron, variable radius spheres/cylinders) - - new clusters types (N degrees of freedom) + - larger subproblems (octahedron, variable radius spheres/cylinders) + - new clusters types (N degrees of freedom) Extentions: - - implement geometry: lines, planes, spheres, cylinders, tori (mappings to cluster) - - constraints on parameters (equality, algebraic constraints) - - different variable domains (integers, reals, n-dimensional points, surfaces, volumes, logic variables, lists) + - implement geometry: lines, planes, spheres, cylinders, tori (mappings to + cluster) + - constraints on parameters (equality, algebraic constraints) + - different variable domains (integers, reals, n-dimensional points, + surfaces, volumes, logic variables, lists) + +Easy of use: + - move use_prototype flag to GeometricProblem (instead of in + GeometricSolver) + - check dimension of prototype points when adding to problem +BUGS: + +- following should be well-constraint, gives underconstrained (need extra rule/pattern) +def diamond_3d(): + """creates a diamond shape with point 'v1'...'v4' in 3D with one solution""" + L=10.0 + problem = GeometricProblem(dimension=3) + problem.add_point('v1', vector([0.0, 0.0, 0.0])) + problem.add_point('v2', vector([-5.0, 5.0, 0.0])) + problem.add_point('v3', vector([5.0, 5.0, 0.0])) + problem.add_point('v4', vector([0.0, 10.0, 0.0])) + problem.add_constraint(DistanceConstraint('v1', 'v2', L)) + problem.add_constraint(DistanceConstraint('v1', 'v3', L)) + problem.add_constraint(DistanceConstraint('v2', 'v3', L)) + problem.add_constraint(DistanceConstraint('v2', 'v4', L)) + problem.add_constraint(DistanceConstraint('v3', 'v4', L)) + # this bit of code constrains the points v1...v4 in a plane with point p above v1 + problem.add_point('p', vector([0.0, 0.0, 1.0])) + problem.add_constraint(DistanceConstraint('v1', 'p', 1.0)) + problem.add_constraint(AngleConstraint('v2','v1','p', math.pi/2)) + problem.add_constraint(AngleConstraint('v3','v1','p', math.pi/2)) + problem.add_constraint(AngleConstraint('v4','v1','p', math.pi/2)) + +- when fixed (by swapping v1 and v3 in last bit of code) + sometimes raises: + StandardError: more than one candidate prototype cluster for variable v2 + or: + FixConstraint(v2=[-5.0, 5.0, 0.0]) not satisfied + FixConstraint(v1=[0.0, 0.0, 0.0]) not satisfied diff --git a/geosolver/clsolver.py b/geosolver/clsolver.py index 3148a0a..50cb936 100644 --- a/geosolver/clsolver.py +++ b/geosolver/clsolver.py @@ -280,8 +280,10 @@ class ClusterSolver(Notifier): clusters = self._graph.outgoing_vertices(var) clusters = filter(lambda c: isinstance(c, Rigid), clusters) clusters = filter(lambda c: len(c.vars) == 1, clusters) - if len(clusters) != 1: - raise StandardError, "no prototype cluster for variable "+str(v) + if len(clusters) < 1: + raise StandardError, "no prototype cluster for variable "+str(var) + elif len(clusters) > 1: + raise StandardError, "more than one candidate prototype cluster for variable "+str(var) selclusters.append(clusters[0]) outcluster = incluster.copy() selector = PrototypeMethod(incluster, selclusters, outcluster, constraints, self._prototype_selection_var) @@ -357,7 +359,7 @@ class ClusterSolver(Notifier): if len(self._applicable_methods) > 0: method = iter(self._applicable_methods).next() #print "applicable methods:", map(str, self._applicable_methods) - print "incremental search found:", method + diag_print("incremental search found:"+str(method),"clsolver._process_new") self._add_method_complete(method) else: newobject = self._new.pop() diff --git a/geosolver/geometric.py b/geosolver/geometric.py index 2d4ea62..30217d4 100644 --- a/geosolver/geometric.py +++ b/geosolver/geometric.py @@ -14,7 +14,9 @@ from notify import Notifier, Listener from tolerance import tol_eq from intersections import angle_3p, distance_2p from selconstr import SelectionConstraint -from geosolver.intersections import is_left_handed, is_right_handed, transform_point, make_hcs_3d +from geosolver.intersections import is_left_handed, is_right_handed +from geosolver.intersections import is_clockwise, is_counterclockwise +from geosolver.intersections import transform_point, make_hcs_3d # ----------- GeometricProblem ------------- @@ -50,6 +52,7 @@ class GeometricProblem (Notifier, Listener): def add_point(self, variable,pos): """add a point variable with a prototype position""" position = vector.vector(pos) + assert len(position) == self.dimension if variable not in self.prototype: self.prototype[variable] = position self.cg.add_variable(variable) @@ -881,19 +884,28 @@ class RigidConstraint(ParametricConstraint): def __str__(self): return "RigidConstraint("+str(self._variables)+")" +class ClockwiseConstraint (SelectionConstraint): + """A selection constraint for 3 points to have a clockwise orientation (not co-linear!)""" + def __init__(self, v1, v2, v3): + SelectionConstraint.__init__(self, is_clockwise, [v1,v2,v3]) + +class CounterClockwiseConstraint (SelectionConstraint): + """A selection constraint for 3 points to have a counter-clockwise orientation (not co-linear!)""" + def __init__(self, v1, v2, v3): + SelectionConstraint.__init__(self, is_counterclockwise, [v1,v2,v3]) class RightHandedConstraint (SelectionConstraint): - """A selection constraint for 4 points to have a right-handed orientation""" + """A selection constraint for 4 points to have a right-handed orientation (not co-planar!)""" def __init__(self, v1, v2, v3, v4): SelectionConstraint.__init__(self, is_right_handed, [v1,v2,v3,v4]) class LeftHandedConstraint (SelectionConstraint): - """A selection constraint for 4 points to have a left-handed orientation""" + """A selection constraint for 4 points to have a left-handed orientation (not co-planar!)""" def __init__(self, v1, v2, v3, v4): SelectionConstraint.__init__(self, is_left_handed, [v1,v2,v3,v4]) class NotRightHandedConstraint (SelectionConstraint): - """A selection constraint for 4 points to not have a rigth-handed orientation, i.e. left-handed or co-planar""" + """A selection constraint for 4 points to not have a right-handed orientation, i.e. left-handed or co-planar""" def __init__(self, v1, v2, v3, v4): SelectionConstraint.__init__(self, fnot(is_right_handed), [v1,v2,v3,v4]) diff --git a/geosolver/intersections.py b/geosolver/intersections.py index f06af7b..51f7928 100644 --- a/geosolver/intersections.py +++ b/geosolver/intersections.py @@ -227,13 +227,19 @@ def distance_2p(p1, p2): def is_clockwise(p1,p2,p3): """ returns True iff triangle p1,p2,p3 is clockwise oriented""" + assert len(p1)==2 + assert len(p1)==len(p2) + assert len(p2)==len(p3) u = p2 - p1 - v = p3 - p2; + v = p3 - p2 perp_u = vector.vector([-u[1], u[0]]) return tol_lt(vector.dot(perp_u,v),0) def is_counterclockwise(p1,p2,p3): """ returns True iff triangle p1,p2,p3 is counterclockwise oriented""" + assert len(p1)==2 + assert len(p1)==len(p2) + assert len(p2)==len(p3) u = p2 - p1 v = p3 - p2; perp_u = vector.vector([-u[1], u[0]]) @@ -241,6 +247,10 @@ def is_counterclockwise(p1,p2,p3): def is_colinear(p1,p2,p3): """ returns True iff triangle p1,p2,p3 is colinear (neither clockwise of counterclockwise oriented)""" + assert len(p1)==2 + assert len(p1)==len(p2) + assert len(p2)==len(p3) + u = p2 - p1 v = p3 - p2; perp_u = vector.vector([-u[1], u[0]]) diff --git a/test/test.py b/test/test.py index 38e7f9a..c10c8f1 100644 --- a/test/test.py +++ b/test/test.py @@ -225,6 +225,27 @@ def overconstrained_tetra(): #problem.add_constraint(AngleConstraint('v1', 'v2', 'v3', math.pi/4)) return problem +def diamond_3d(): + """creates a diamond shape with point 'v1'...'v4' in 3D with one solution""" + # Following should be well-constraint, gives underconstrained (need extra rule/pattern) + L=10.0 + problem = GeometricProblem(dimension=3) + problem.add_point('v1', vector([0.0, 0.0, 0.0])) + problem.add_point('v2', vector([-5.0, 5.0, 0.0])) + problem.add_point('v3', vector([5.0, 5.0, 0.0])) + problem.add_point('v4', vector([0.0, 10.0, 0.0])) + problem.add_constraint(DistanceConstraint('v1', 'v2', L)) + problem.add_constraint(DistanceConstraint('v1', 'v3', L)) + problem.add_constraint(DistanceConstraint('v2', 'v3', L)) + problem.add_constraint(DistanceConstraint('v2', 'v4', L)) + problem.add_constraint(DistanceConstraint('v3', 'v4', L)) + # this bit of code constrains the points v1...v4 in a plane with point p above it + problem.add_point('p', vector([0.0, 0.0, 1.0])) + problem.add_constraint(DistanceConstraint('v1', 'p', 1.0)) + problem.add_constraint(AngleConstraint('v2','v1','p', math.pi/2)) + problem.add_constraint(AngleConstraint('v3','v1','p', math.pi/2)) + problem.add_constraint(AngleConstraint('v4','v1','p', math.pi/2)) + # -------- 2D problems def ddd_problem():