added Point,Line,Plane,Coincidence classes, towards mapping geometric primitives

This commit is contained in:
kwikrick 2012-08-23 18:53:44 +00:00
parent 27a1d793aa
commit f60475eb3d
4 changed files with 203 additions and 82 deletions

View File

@ -32,7 +32,7 @@ from geometric import MateConstraint
from geometric import FixConstraint from geometric import FixConstraint
from geometric import LeftHandedConstraint from geometric import LeftHandedConstraint
from geometric import RightHandedConstraint from geometric import RightHandedConstraint
from geometric import NotRightHandedConstraint from geometric import NotLeftHandedConstraint
from geometric import NotRightHandedConstraint from geometric import NotRightHandedConstraint
from geometric import CounterClockwiseConstraint from geometric import CounterClockwiseConstraint
from geometric import ClockwiseConstraint from geometric import ClockwiseConstraint

View File

@ -14,41 +14,43 @@ from notify import Notifier, Listener
from tolerance import tol_eq from tolerance import tol_eq
from intersections import angle_3p, distance_2p from intersections import angle_3p, distance_2p
from selconstr import SelectionConstraint from selconstr import SelectionConstraint
from geosolver.intersections import is_left_handed, is_right_handed from intersections import is_left_handed, is_right_handed
from geosolver.intersections import is_clockwise, is_counterclockwise from intersections import is_clockwise, is_counterclockwise
from geosolver.intersections import transform_point, make_hcs_3d from intersections import transform_point, make_hcs_3d
# ----------- GeometricProblem ------------- # ----------- GeometricProblem -------------
class GeometricProblem (Notifier, Listener): class GeometricProblem (Notifier, Listener):
"""A geometric constraint problem with a prototype. """A geometric constraint problem with a prototpe.
A problem consists of point variables (just variables for short), prototype A problem consists of geometric variables (just variables for short), a prototype
points for each variable and constraints. for each variable and constraints.
Variables are just names and can be any hashable object (recommend strings)
Supported constraints are instances of DistanceConstraint,AngleConstraint,
FixConstraint or SelectionConstraint.
Prototype points are instances of vector. Variables are of type Point, Line, etc. Alternatively, variables of any other hashable type
(e.g. strings) are assumed to be points. (Depricated - used for backwards compatibility)
Prototypes are of type vector
A point prototype must have length equal to the dimensionality as the problem (D).
A line prototype must have length 2*D: it represents a point though which the line passes and a direction vector.
A plane prototype must have length 3*D: it represents a point though which the plane passes and two direction vectors.
Supported constraints are instances of ParametricConstraint, FixConstraint, SelectionConstraint, etc.
GeometricProblem listens for changes in constraint parameters and passes GeometricProblem listens for changes in constraint parameters and passes
these changes, and changes in the system of constraints and the prototype, these changes, and changes in the system of constraints and the prototype,
to any other listerers (e.g. GeometricSolver) to any other listerers (e.g. GeometricSolver)
instance attributes:
cg - a ConstraintGraph instance
prototype - a dictionary mapping variables to points
""" """
def __init__(self, dimension): def __init__(self, dimension, use_prototype=True):
"""initialize a new problem""" """Initialize a new problem. Must specify dimensionality of the problem (i.e. dimension of points) and
wheter to use the prototype for solution selection."""
Notifier.__init__(self) Notifier.__init__(self)
Listener.__init__(self) Listener.__init__(self)
self.dimension = dimension self.dimension = dimension # dimensionality of points
self.prototype = {} self.prototype = {} # mapping from variables to prototypes
self.cg = ConstraintGraph() self.cg = ConstraintGraph() # constraint graph
self.use_prototype = True; self.use_prototype = use_prototype; # whether to use prototype for solution selection
# ----------- prototype --------
def set_prototype_selection(self, enabled): def set_prototype_selection(self, enabled):
"""Enable (True, the default) or disable (False) use of prototype for solution selection""" """Enable (True, the default) or disable (False) use of prototype for solution selection"""
@ -60,89 +62,105 @@ class GeometricProblem (Notifier, Listener):
"""Return True if prototype selection has been enabled (the default) or False otherwise""" """Return True if prototype selection has been enabled (the default) or False otherwise"""
return self.use_prototype return self.use_prototype
def add_point(self, variable,pos): # ---------------- variables -----------
"""add a point variable with a prototype position"""
position = vector.vector(pos) def add_variable(self, variable, prototype):
assert len(position) == self.dimension """add a variable with a prototype"""
prototypevector = vector.vector(prototype)
# check dimension of prototype
if isinstance(variable, Point):
assert len(prototypevector) == self.dimension
elif isinstance(variable, Line):
assert len(prototypevector) == 2 * self.dimension
elif isinstance(variable, Plane):
assert len(prototypevector) == 3 * self.dimension
else:
# assume point
assert len(prototypevector) == self.dimension
if variable not in self.prototype: if variable not in self.prototype:
self.prototype[variable] = position self.prototype[variable] = prototypevector
self.cg.add_variable(variable) self.cg.add_variable(variable)
else: else:
raise StandardError, "point already in problem" raise StandardError, "variable already in problem"
def set_point(self, variable, pos): def has_variable(self, variable):
"""set prototype position of point variable""" """returns True if variable in problem"""
position = vector.vector(pos) return variable in self.prototype
def rem_variable(self, variable):
"""remove a variable (and all constraints incident imposed on it)"""
if variable in self.prototype: if variable in self.prototype:
self.prototype[variable] = position del self.prototype[variable]
self.send_notify(("set_point", (variable,position))) self.cg.rem_variable(variable)
else:
raise StandardError, "unknown point variable"
def get_point(self, variable): def set_prototype(self, variable, prototype):
"""get prototype position of point variable""" """set prototype of variable"""
prototypevector = vector.vector(prototype)
if variable in self.prototype:
self.prototype[variable] = prototypevector
self.send_notify(("set_prototype", (variable,prototypevector)))
else:
raise StandardError, "unknown variable variable"
def get_prototype(self, variable):
"""get prototype of variable"""
if variable in self.prototype: if variable in self.prototype:
return self.prototype[variable] return self.prototype[variable]
else: else:
raise StandardError, "unknown point variable" raise StandardError, "unknown variable variable"
# --------------- points - depricated -------------
def add_point(self, variable, prototype):
"""depricated - use add_variable"""
return self.add_variable(variable, prototype)
def has_point(self, variable): def has_point(self, variable):
return variable in self.prototype """depricated - use has_variable"""
return self.has_variable(variable)
def set_point(self, variable, prototype):
"""deprictaed - use set_prototype"""
return self.set_prototype(variable, prototype)
def get_point(self, variable):
"""depricated - use get_prototype"""
return self.get_prototype(variable)
# ----------- constraints --------
def add_constraint(self, con): def add_constraint(self, con):
"""add a constraint""" """add a constraint"""
if isinstance(con, DistanceConstraint): # check that variables in problem
for var in con.variables(): for var in con.variables():
if var not in self.prototype: if var not in self.prototype:
raise StandardError, "point variable %s not in problem"%(var) raise StandardError, "variable %s not in problem"%(var)
# check that constraint not already in problem
if isinstance(con, DistanceConstraint):
if self.get_distance(con.variables()[0],con.variables()[1]): if self.get_distance(con.variables()[0],con.variables()[1]):
raise StandardError, "distance already in problem" raise StandardError, "distance already in problem"
else:
con.add_listener(self)
self.cg.add_constraint(con)
elif isinstance(con, AngleConstraint): elif isinstance(con, AngleConstraint):
for var in con.variables():
if var not in self.prototype:
raise StandardError, "point variable %s not in problem"%(var)
if self.get_angle(con.variables()[0],con.variables()[1], con.variables()[2]): if self.get_angle(con.variables()[0],con.variables()[1], con.variables()[2]):
raise StandardError, "angle already in problem" raise StandardError, "angle already in problem"
else:
con.add_listener(self)
self.cg.add_constraint(con)
elif isinstance(con, RigidConstraint): elif isinstance(con, RigidConstraint):
for var in con.variables(): if self.get_rigid(con.variables()):
if var not in self.prototype: raise StandardError, "rigid already in problem"
raise StandardError, "point variable %s not in problem"%(var)
#if self.get_rigid(con.variables())
# raise StandardError, "rigid already in problem"
#else:
if True:
con.add_listener(self)
self.cg.add_constraint(con)
elif isinstance(con, MateConstraint): elif isinstance(con, MateConstraint):
for var in con.variables(): if self.get_mate(con.variables()):
if var not in self.prototype: raise StandardError, "mate constraint already in problem"
raise StandardError, "point variable %s not in problem"%(var)
#if self.get_mate(con.variables())
# raise StandardError, "rigid already in problem"
#else:
if True:
con.add_listener(self)
self.cg.add_constraint(con)
elif isinstance(con, SelectionConstraint): elif isinstance(con, SelectionConstraint):
for var in con.variables(): pass
if var not in self.prototype:
raise StandardError, "point variable %s not in problem"%(var)
self.cg.add_constraint(con)
elif isinstance(con, FixConstraint): elif isinstance(con, FixConstraint):
for var in con.variables():
if var not in self.prototype:
raise StandardError, "point variable %s not in problem"%(var)
if self.get_fix(con.variables()[0]): if self.get_fix(con.variables()[0]):
raise StandardError, "fix already in problem" raise StandardError, "fix already in problem"
self.cg.add_constraint(con) elif isinstance(con, CoincidenceConstraint):
if self.get_coincidence(con.variables()[0], con.variables()[1]):
raise StandardError, "coincidence already in problem"
else: else:
raise StandardError, "unsupported constraint type" raise StandardError, "unsupported constraint type"
# passed tests, add to poblem
if isinstance(self, ParametricConstraint):
con.add_listener(self)
self.cg.add_constraint(con)
def get_distance(self, a, b): def get_distance(self, a, b):
"""return the distance constraint on given points, or None""" """return the distance constraint on given points, or None"""
@ -172,6 +190,10 @@ class GeometricProblem (Notifier, Listener):
else: else:
return None return None
def get_rigid(self, vars):
print "GeometricProblem.get_rigid NOT IMPLEMENTED"
return None
def get_fix(self, p): def get_fix(self, p):
"""return the fix constraint on given point, or None""" """return the fix constraint on given point, or None"""
on_p = self.cg.get_constraints_on(p) on_p = self.cg.get_constraints_on(p)
@ -183,6 +205,10 @@ class GeometricProblem (Notifier, Listener):
else: else:
return None return None
def get_coincidence(self, p, g):
print "GeometricProblem.get_coincidence NOT IMPLEMENTED"
return None
def verify(self, solution): def verify(self, solution):
"""returns true iff all constraints satisfied by given solution. """returns true iff all constraints satisfied by given solution.
solution is a dictionary mapping variables (names) to values (points)""" solution is a dictionary mapping variables (names) to values (points)"""
@ -233,7 +259,7 @@ class GeometricProblem (Notifier, Listener):
def __str__(self): def __str__(self):
s = "" s = ""
for v in self.prototype: for v in self.prototype:
s += v + " = " + str(self.prototype[v]) + "\n" s += str(v) + " = " + str(self.prototype[v]) + "\n"
for con in self.cg.constraints(): for con in self.cg.constraints():
s += str(con) + "\n" s += str(con) + "\n"
s+= "prototype-based selection = " + str(self.use_prototype) s+= "prototype-based selection = " + str(self.use_prototype)
@ -268,7 +294,7 @@ class GeometricSolver (Listener):
elif self.problem.dimension == 3: elif self.problem.dimension == 3:
self.dr = ClusterSolver3D() self.dr = ClusterSolver3D()
else: else:
raise StandardError, "Do not know how to solve problems of dimension > 3." raise StandardError, "Sorry, can't solve problems of dimension < 2 or > 3."
self._map = {} self._map = {}
# enable prototype based selection by default # enable prototype based selection by default
@ -478,6 +504,17 @@ class GeometricSolver (Listener):
self.dr.set_prototype_selection(enabled) self.dr.set_prototype_selection(enabled)
def _add_variable(self, var): def _add_variable(self, var):
if isinstance(var, Point):
self._add_point(var)
elif isinstance(var, Line):
self._add_line(var)
elif isinstance(var, Plane):
self._add_plane(var)
else:
# assume point - depricated
self._add_point(var)
def _add_point(self, var):
if var not in self._map: if var not in self._map:
rigid = Rigid([var]) rigid = Rigid([var])
self._map[var] = rigid self._map[var] = rigid
@ -746,6 +783,45 @@ class GeometricDecomposition:
# def # def
# ------------------- variable type -------------
class GeometricVariable:
"""Abstract base class for geometric variabes (Point, Line, etc)
A geometric variable is identified by its name attibute and its type.
It is hasable so it can be used in sets etc.
"""
def __eq__(self, other):
return self.__class__ == other.__class__ and self.name == other.name
def __hash__(self):
return hash(self.name)
def __repr__(self):
return repr(self.__class__)+"("+repr(self.name)+")"
class Point(GeometricVariable):
def __init__(self, name):
self.name = name
def __str__(self):
return "Point("+str(self.name)+")"
class Line(GeometricVariable):
def __init__(self, name):
self.name = name
def __str__(self):
return "Line("+str(self.name)+")"
class Plane(GeometricVariable):
def __init__(self, name):
self.name = name
def __str__(self):
return "Plane("+str(self.name)+")"
# --------------------- constraint types -------------------- # --------------------- constraint types --------------------
@ -978,4 +1054,38 @@ class MateConstraint(ParametricConstraint):
return "MateConstraint("+str(self._variables)+")" return "MateConstraint("+str(self._variables)+")"
class CoincidenceConstraint(Constraint):
"""defines a coincidence between a point and another geometricvariable (i.e. point, line, plane)"""
def __init__(self, point, geometry):
assert isinstance(point, Point)
assert isinstance(geometry, GeometricVariable)
self._point = point
self._geometry = geometry
self._variables = [point, geometry]
def satisfied(self, mapping):
"""return True iff mapping from variable names to points satisfies constraint"""
if isinstance(geometry, Point):
p1 = mapping[self._point]
p2 = mapping[self._geometry]
return tol_eq(distance_2p(p1,p2),0)
elif isinstance(geometry, Line):
p = mapping[self._point]
l = mapping[self._geometry]
p1 = l[0:3]
p2 = l[3:6]
return tol_eq(distance_point_line(p, p1, p2),0)
elif isinstance(geometry, Plane):
p = mapping[self._point]
l = mapping[self._geometry]
p1 = l[0:3]
p2 = l[3:6]
p2 = l[6:9]
return tol_eq(distance_point_plane(p, p1, p2, p3),0)
else:
raise StandardError, "unknown geometry type"""
def __str__(self):
return "CoincidenceConstraint("+str(self._point)+","+str(self._geometry)+")"

View File

@ -18,7 +18,7 @@ def test(problem):
#diag_select(".*") #diag_select(".*")
print "problem:" print "problem:"
print problem print problem
solver = GeometricSolver(problem, use_prototype=True) solver = GeometricSolver(problem)
#print "drplan:" #print "drplan:"
#print solver.dr #print solver.dr
#print "number of top-level rigids:",len(solver.dr.top_level()) #print "number of top-level rigids:",len(solver.dr.top_level())

View File

@ -970,5 +970,16 @@ def test2d():
#test(ada_problem()) #test(ada_problem())
#test(aad_problem()) #test(aad_problem())
def line_problem():
"""A problem with a fix constraint"""
problem = GeometricProblem(dimension=3)
problem.add_variable(Point('p1'),vector([0.0, 0.0, 0.0]))
problem.add_variable(Line('l1'),vector([0.0, 0.0, 0.0, 1.0, 1.0, 1.0]))
problem.add_constraint(CoincidenceConstraint(Point('p1'), Line('l1')))
return problem
def test_line():
test(line_problem())
if __name__ == "__main__": if __name__ == "__main__":
test2d() test_line()