changed line mapping (incomplete)

This commit is contained in:
kwikrick 2012-10-05 07:31:00 +00:00
parent b5121c5bd6
commit 193b224850
3 changed files with 210 additions and 74 deletions

View File

@ -18,6 +18,7 @@ from intersections import distance_point_line
from intersections import is_left_handed, is_right_handed from intersections import is_left_handed, is_right_handed
from intersections import is_clockwise, is_counterclockwise from intersections import is_clockwise, is_counterclockwise
from intersections import transform_point, make_hcs_3d from intersections import transform_point, make_hcs_3d
from intersections import perp_2d
# ----------- GeometricProblem ------------- # ----------- GeometricProblem -------------
@ -32,8 +33,9 @@ class GeometricProblem (Notifier, Listener):
Prototypes are of type vector Prototypes are of type vector
A point prototype must have length equal to the dimensionality as the problem (D). 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 line prototype must have length 2*D: it represents two points though which the line passes
A plane prototype must have length 3*D: it represents a point though which the plane passes and two direction vectors. A plane prototype must have length 3*D: it represents three points though which the plane passes
Supported constraints are instances of ParametricConstraint, FixConstraint, SelectionConstraint, etc. 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
@ -216,7 +218,7 @@ class GeometricProblem (Notifier, Listener):
if len(candidates) == 0: if len(candidates) == 0:
return None return None
elif len(candidates) == 1: elif len(candidates) == 1:
return candidates[0] return list(candidates)[0]
else: # >= 1 else: # >= 1
raise StandardError, "multiple constraints found" raise StandardError, "multiple constraints found"
@ -491,19 +493,17 @@ class GeometricSolver (Listener):
if point in configuration: if point in configuration:
solution[var] = configuration[point] solution[var] = configuration[point]
elif isinstance(var, Line): elif isinstance(var, Line):
if var in self._map: line_rigid = self._map[var]
vertices = list(self._map[var].vars) line_vertex = line_rigid.vertex
else: line_normal = line_rigid.normal
# when line coincident with 2 points, then not mapped to cluster... use any two coincident points if line_vertex in configuration and line_normal in configuration:
points = self.problem.get_coincident_points(var) p1 = configuration[line_vertex]
assert len(points) >= 2 n = configuration[line_normal]
print "points",points if self.dimension == 2:
vertices = map(lambda v: iter(self._map[v].vars).next(), list(points)[0:2]) p2 = p1 + perp_2d(n-p1)
print "vertices",vertices else:
assert len(vertices) == 2 raise NotImplementedError
if vertices[0] in configuration and vertices[1] in configuration: solution[var] = p1.concatonated(p2)
solution[var] = vector.vector(configuration[vertices[0]]).concatonated( vector.vector(configuration[vertices[1]]) )
else: else:
raise StandardError, "unknown variable type" raise StandardError, "unknown variable type"
#for #for
@ -610,44 +610,42 @@ class GeometricSolver (Listener):
diag_print("on "+str(points),"GeometricSolver") diag_print("on "+str(points),"GeometricSolver")
if len(points) == 0: if len(points) == 0:
self._map_line_distance(var) self._map_line_distance(var)
elif len(points) == 1: elif len(points) >= 1:
self._map_line_point_distance(var, points[0]) self._map_line_point_distance(var, points[0])
elif len(points) == 2:
self._map_line_point_point(var, points[0], points[1])
else: # >=3
self._map_line_points_radial(var, points)
def _map_line_distance(self,line): def _map_line_distance(self,line):
# map a line (coincident with no points) to a distance cluster (on two new point variables) # map a line (coincident with no points) to a distance cluster (on two new point variables)
v1 = str(line)+"_v1" v = str(line)+"_vertex"
v2 = str(line)+"_v2" n = str(line)+"_normal"
dist = Rigid([v1,v2]) dist = Rigid([v,n])
# add add-hoc attributes to rigid, so we can distinguish vertex and normal!
dist.vertex = v
dist.normal = n
# add to mapping
self._map[line] = dist self._map[line] = dist
self._map[dist] = line self._map[dist] = line
self.dr.add(dist) self.dr.add(dist)
diag_print("mapped "+str(line)+" to "+str(dist),"GeometricSolver")
# update configurations
self._update_variable(line) self._update_variable(line)
def _map_line_point_distance(self,line, point): def _map_line_point_distance(self,line, point):
# map a line coincident with one point to a distance clusters (and one new point variable) # map a line coincident with one point to a distance clusters (and one new point variable)
v1 = list(self._map[point].vars)[0] v = list(self._map[point].vars)[0]
v2 = str(line)+"_v2" n = str(line)+"_normal"
dist = Rigid([v1,v2]) dist = Rigid([v,n])
# add add-hoc attributes to rigid, so we can distinguish vertex and normal!
dist.vertex = v
dist.normal = n
# add to mapping
self._map[line] = dist self._map[line] = dist
self._map[dist] = line self._map[dist] = line
self.dr.add(dist) self.dr.add(dist)
diag_print("mapped "+str(line)+" to "+str(dist),"GeometricSolver")
self._update_variable(line) self._update_variable(line)
def _map_line_point_point(self, line, point1, point2):
# map a line coincident with two existing point variables; no new clusters created
self._update_variable(line)
def _map_line_points_radial(self, line, points):
# map a line coincient with three or more points to a radial clusters
raise NotImplementedError
def _rem_variable(self, var): def _rem_variable(self, var):
diag_print("GeometricSolver._rem_variable","gcs") diag_print("GeometricSolver._rem_variable","GeometricSolver")
if var in self._map: if var in self._map:
self.dr.remove(self._map[var]) self.dr.remove(self._map[var])
# Note: CLSolver automatically removes variables with no dependent clusters # Note: CLSolver automatically removes variables with no dependent clusters
@ -706,17 +704,34 @@ class GeometricSolver (Listener):
elif isinstance(con, CoincidenceConstraint): elif isinstance(con, CoincidenceConstraint):
# re-map lines, planes, etc # re-map lines, planes, etc
lines = filter(lambda var: isinstance(var,Line),con.variables()) lines = filter(lambda var: isinstance(var,Line),con.variables())
if len(lines)==1: points = filter(lambda var: isinstance(var,Point),con.variables())
if len(lines)==1 and len(points)==1:
line = iter(lines).next() line = iter(lines).next()
self._rem_variable(line) point = iter(points).next()
self._add_line(line) # re-map line if needed
#self._rem_variable(line)
#self._add_line(line)
# map coincience constraint of a point with a line
line_rigid = self._map[line]
point_rigid = self._map[point]
point_vertex = iter(point_rigid.vars).next()
if point_vertex not in line_rigid.vars:
line_vertex = line_rigid.vertex
line_normal = line_rigid.normal
angle_hog = Hedgehog(line_vertex,[line_normal, point_vertex])
self._map[con] = angle_hog
self._map[angle_hog] = con
self.dr.add(angle_hog)
diag_print("mapped "+str(con)+" to "+str(angle_hog),"GeometricSolver")
self._update_constraint(con)
#endif
#endif #endif
else: else:
raise StandardError, "unknown constraint type" raise StandardError, "unknown constraint type"
pass pass
def _rem_constraint(self, con): def _rem_constraint(self, con):
diag_print("GeometricSolver._rem_constraint","gcs") diag_print("GeometricSolver._rem_constraint","GeometricSolver")
if isinstance(con,FixConstraint): if isinstance(con,FixConstraint):
if self.fixcluster != None: if self.fixcluster != None:
self.dr.remove(self.fixcluster) self.dr.remove(self.fixcluster)
@ -818,6 +833,30 @@ class GeometricSolver (Listener):
assert con.satisfied(conf.map) assert con.satisfied(conf.map)
elif isinstance(con, FixConstraint): elif isinstance(con, FixConstraint):
self._update_fix() self._update_fix()
elif isinstance(con, CoincidenceConstraint):
lines = filter(lambda var: isinstance(var,Line),con.variables())
points = filter(lambda var: isinstance(var,Point),con.variables())
if len(lines)==1 and len(points)==1:
line = iter(lines).next()
point = iter(points).next()
if self.dimension == 2:
line_rigid = self._map[line]
point_rigid = self._map[point]
point_vertex = iter(point_rigid.vars).next()
print "point_vertex", point_vertex
line_vertex = line_rigid.vertex
line_normal = line_rigid.normal
angle_hog = self._map[con]
pv = vector.vector([1.0,0.0])
lv = vector.vector([0.0,0.0])
ln = vector.vector([0.0,1.0])
conf1 = Configuration({line_vertex:lv, line_normal:ln, point_vertex: 1.0*pv})
conf2 = Configuration({line_vertex:lv, line_normal:ln, point_vertex:-1.0*pv})
self.dr.set(angle_hog, [conf1,conf2])
diag_print("set "+str(angle_hog)+" to "+str(conf1),"GeometricSolver")
diag_print("set "+str(angle_hog)+" to "+str(conf2),"GeometricSolver")
else:
raise NotImplementedError
else: else:
raise StandardError, "unknown constraint type" raise StandardError, "unknown constraint type"
@ -839,22 +878,20 @@ class GeometricSolver (Listener):
self.dr.set(cluster, [conf]) self.dr.set(cluster, [conf])
def _update_line(self, variable): def _update_line(self, variable):
# note: line may not be mapped to a cluster at all! if self.dimension == 2:
if variable in self._map:
cluster = self._map[variable] cluster = self._map[variable]
proto = self.problem.get_prototype(variable) proto = self.problem.get_prototype(variable)
vertices = list(cluster.vars); line_vertex = cluster.vertex
v1 = vertices[0] line_normal = cluster.normal
v2 = vertices[1] p1 = proto[0:2]
assert self.problem.dimension==2 or self.problem.dimension==3 p2 = proto[2:4]
if self.problem.dimension == 2: v = p1
p1 = proto[0:2] n = perp_2d(p2-p1)
p2 = proto[2:4] conf = Configuration({line_vertex:v, line_normal:n})
elif self.problem.dimension == 3:
p1 = proto[0:3]
p2 = proto[3:6]
conf = Configuration({v1:p1, v2:p2})
self.dr.set(cluster, [conf]) self.dr.set(cluster, [conf])
diag_print("set "+str(cluster)+" to "+str(conf),"GeometricSolver")
elif self.dimension == 3:
raise NotImplementedError
def _update_fix(self): def _update_fix(self):
if self.fixcluster: if self.fixcluster:
@ -1240,9 +1277,21 @@ class CoincidenceConstraint(Constraint):
elif isinstance(self._geometry, Line): elif isinstance(self._geometry, Line):
p = mapping[self._point] p = mapping[self._point]
l = mapping[self._geometry] l = mapping[self._geometry]
p1 = l[0:3] if len(l)==4: #2D
p2 = l[3:6] p1 = l[0:2]
return tol_eq(distance_point_line(p, p1, p2),0) p2 = l[2:4]
elif len(l)==6: # 3D
p1 = l[0:3]
p2 = l[3:6]
else:
raise Exception, "line has invalid number of values"
d = distance_point_line(p, p1, p2)
if not tol_eq(d,0):
diag_print("not satisfied "+ str(self)+" distance="+str(d),"CoincidenceConstraint")
print "distance="+str(d),"CoincidenceConstraint"
return tol_eq(d,0)
elif isinstance(self._geometry, Plane): elif isinstance(self._geometry, Plane):
p = mapping[self._point] p = mapping[self._point]
l = mapping[self._geometry] l = mapping[self._geometry]

View File

@ -237,11 +237,11 @@ def distance_point_line(p,l1,l2):
v = p-l1 v = p-l1
w = l2-l1 w = l2-l1
# x = projection v on w # x = projection v on w
l = (vector.norm(v) * vector.norm(w)) lw = vector.norm(w)
if tol_eq(l,0): if tol_eq(lw,0):
x = 0*v x = 0*w
else: else:
x = v * vector.dot(v,w) / l x = w * vector.dot(v,w) / lw
# result is distance x,v # result is distance x,v
return vector.norm(x-v) return vector.norm(x-v)

View File

@ -10,15 +10,17 @@ from geosolver.geometric import Point, Line, CoincidenceConstraint
from geosolver.vector import vector from geosolver.vector import vector
from geosolver.diagnostic import diag_select, diag_print from geosolver.diagnostic import diag_select, diag_print
def line_problem1(): # ---------- 3d ----------
"""A problem with a Point, a Line and a CoincicentConstraint"""
def line_problem_3d_0():
"""A problem with a Line (and no CoincicentConstraints)"""
problem = GeometricProblem(dimension=3) problem = GeometricProblem(dimension=3)
problem.add_variable(Line('l1'),vector([0.0, 0.0, 0.0, 1.0, 1.0, 1.0])) problem.add_variable(Line('l1'),vector([0.0, 0.0, 0.0, 1.0, 1.0, 1.0]))
return problem return problem
def line_problem2(): def line_problem_3d_1():
"""A problem with a Point, a Line and a CoincicentConstraint""" """A problem with a Line and 1 CoincicentConstraint"""
problem = GeometricProblem(dimension=3) problem = GeometricProblem(dimension=3)
problem.add_variable(Point('p1'),vector([3.0, 2.0, 1.0])) problem.add_variable(Point('p1'),vector([3.0, 2.0, 1.0]))
problem.add_variable(Line('l1'),vector([0.0, 0.0, 0.0, 1.0, 1.0, 1.0])) problem.add_variable(Line('l1'),vector([0.0, 0.0, 0.0, 1.0, 1.0, 1.0]))
@ -26,8 +28,8 @@ def line_problem2():
return problem return problem
def line_problem3(): def line_problem_3d_2():
"""A problem with a Point, a Line and a CoincicentConstraint""" """A problem with a Line and 2 CoincicentConstraints"""
problem = GeometricProblem(dimension=3) problem = GeometricProblem(dimension=3)
problem.add_variable(Point('p1'),vector([3.0, 2.0, 1.0])) problem.add_variable(Point('p1'),vector([3.0, 2.0, 1.0]))
problem.add_variable(Point('p2'),vector([1.0, 1.0, 1.0])) problem.add_variable(Point('p2'),vector([1.0, 1.0, 1.0]))
@ -37,8 +39,8 @@ def line_problem3():
problem.add_constraint(DistanceConstraint(Point('p1'), Point('p2'), 5.0)) problem.add_constraint(DistanceConstraint(Point('p1'), Point('p2'), 5.0))
return problem return problem
def line_problem4(): def line_problem_3d_3():
"""A problem with a Point, a Line and a CoincicentConstraint""" """A problem with a Line and a 3 CoincicentConstraints"""
problem = GeometricProblem(dimension=3) problem = GeometricProblem(dimension=3)
problem.add_variable(Point('p1'),vector([3.0, 2.0, 1.0])) problem.add_variable(Point('p1'),vector([3.0, 2.0, 1.0]))
problem.add_variable(Point('p2'),vector([1.0, 1.0, 1.0])) problem.add_variable(Point('p2'),vector([1.0, 1.0, 1.0]))
@ -51,12 +53,97 @@ def line_problem4():
problem.add_constraint(DistanceConstraint(Point('p1'), Point('p3'), 8.0)) problem.add_constraint(DistanceConstraint(Point('p1'), Point('p3'), 8.0))
return problem return problem
def line_problem_3d_4():
"""A problem with a Line and a 4 CoincicentConstraints"""
problem = GeometricProblem(dimension=3)
problem.add_variable(Point('p1'),vector([3.0, 2.0, 1.0]))
problem.add_variable(Point('p2'),vector([1.0, 1.0, 1.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')))
problem.add_constraint(CoincidenceConstraint(Point('p2'), Line('l1')))
problem.add_constraint(DistanceConstraint(Point('p1'), Point('p2'), 5.0))
problem.add_variable(Point('p3'),vector([0.0, 0.0, 1.0]))
problem.add_constraint(CoincidenceConstraint(Point('p3'), Line('l1')))
problem.add_constraint(DistanceConstraint(Point('p1'), Point('p3'), 8.0))
problem.add_variable(Point('p4'),vector([1.0, 0.0, 1.0]))
problem.add_constraint(CoincidenceConstraint(Point('p4'), Line('l1')))
problem.add_constraint(DistanceConstraint(Point('p1'), Point('p4'), 0.1))
return problem
# ------------2d
def line_problem_2d_0():
"""A problem with a Line (and no CoincicentConstraints)"""
problem = GeometricProblem(dimension=2)
problem.add_variable(Line('l1'),vector([0.0, 0.0, 1.0, 1.0]))
return problem
def line_problem_2d_1():
"""A problem with a Line and 1 CoincicentConstraint"""
problem = GeometricProblem(dimension=2)
problem.add_variable(Point('p1'),vector([3.0, 2.0]))
problem.add_variable(Line('l1'),vector([0.0, 0.0, 1.0, 1.0]))
problem.add_constraint(CoincidenceConstraint(Point('p1'), Line('l1')))
return problem
def line_problem_2d_2():
"""A problem with a Line and 2 CoincicentConstraints"""
problem = GeometricProblem(dimension=2)
problem.add_variable(Point('p1'),vector([3.0, 2.0]))
problem.add_variable(Point('p2'),vector([1.0, 1.0]))
problem.add_variable(Line('l1'),vector([0.0, 0.0, 1.0, 1.0]))
problem.add_constraint(CoincidenceConstraint(Point('p1'), Line('l1')))
problem.add_constraint(CoincidenceConstraint(Point('p2'), Line('l1')))
problem.add_constraint(DistanceConstraint(Point('p1'), Point('p2'), 5.0))
return problem
def line_problem_2d_3():
"""A problem with a Line and a 3 CoincicentConstraints"""
problem = GeometricProblem(dimension=2)
problem.add_variable(Point('p1'),vector([3.0, 2.0]))
problem.add_variable(Point('p2'),vector([1.0, 1.0]))
problem.add_variable(Line('l1'),vector([0.0, 0.0, 1.0, 1.0]))
problem.add_constraint(CoincidenceConstraint(Point('p1'), Line('l1')))
problem.add_constraint(CoincidenceConstraint(Point('p2'), Line('l1')))
problem.add_constraint(DistanceConstraint(Point('p1'), Point('p2'), 5.0))
problem.add_variable(Point('p3'),vector([1.0, 0.0]))
problem.add_constraint(CoincidenceConstraint(Point('p3'), Line('l1')))
problem.add_constraint(DistanceConstraint(Point('p1'), Point('p3'), 8.0))
return problem
def line_problem_2d_4():
"""A problem with a Line and a 4 CoincicentConstraints"""
problem = GeometricProblem(dimension=2)
problem.add_variable(Point('p1'),vector([3.0, 2.0]))
problem.add_variable(Point('p2'),vector([1.0, 1.0]))
problem.add_variable(Line('l1'),vector([0.0, 0.0, 1.0, 1.0]))
problem.add_constraint(CoincidenceConstraint(Point('p1'), Line('l1')))
problem.add_constraint(CoincidenceConstraint(Point('p2'), Line('l1')))
problem.add_constraint(DistanceConstraint(Point('p1'), Point('p2'), 5.0))
problem.add_variable(Point('p3'),vector([0.0, 0.0, 1.0]))
problem.add_constraint(CoincidenceConstraint(Point('p3'), Line('l1')))
problem.add_constraint(DistanceConstraint(Point('p1'), Point('p3'), 8.0))
problem.add_variable(Point('p4'),vector([1.0, 0.0, 1.0]))
problem.add_constraint(CoincidenceConstraint(Point('p4'), Line('l1')))
problem.add_constraint(DistanceConstraint(Point('p1'), Point('p4'), 0.1))
return problem
def test_line(): def test_line():
test(line_problem1()) diag_select("(GeometricSolver)|(CoincidenceConstraint)")
test(line_problem2()) #test(line_problem_2d_0())
test(line_problem3()) #test(line_problem_2d_1())
test(line_problem4()) #test(line_problem_2d_2())
test(line_problem_2d_3())
#test(line_problem_2d_4())
#test(line_problem_3d_0())
#test(line_problem_3d_1())
#test(line_problem_3d_2())
#test(line_problem_3d_3())
#test(line_problem_3d_4())
if __name__ == "__main__": if __name__ == "__main__":
test_line() test_line()