New 2D solver (clsolver2D.py), far from complete

Lots of changes to make clsolver.py more generic
New incremental 3D methods (clsolver3D.py) but still not complete
New 2D tests
This commit is contained in:
kwikrick 2011-01-29 10:21:01 +00:00
parent 5288253718
commit fdf7300eef
12 changed files with 599 additions and 1760 deletions

View File

@ -1,42 +0,0 @@
# __init__.py - initialisation script of the Delny package
#
# Copyright 2004 Floris Bruynooghe
#
# This file is part of Delny.
#
# Delny is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Delny is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Delny; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
#
# Authors: Floris Bruynooghe (flub)
"""Delaunay triangulation.
This package provides a python interface to the Delaunay triangulation
provided by the qhull software package using the C library libqhull.
It is possible to make a triangulations of any dimension you want.
The Delaunay module imports the core module directy in it's namespace
so you can for example use `Delaunay.Triangulation()' after a `import
Delaunay'. So see the Delaunay.core documentation for the use of the
module.
Whenever a "sequence object" is mentioned in this package all the
builtin Python sequences are accepted as well as an array from the
Numeric module.
"""
from core import *
__all__ = ["core", "_qhull"]

View File

@ -1,110 +0,0 @@
# core.py - the main user interface to the Delny package
#
# Copyright 2004-2006 Floris Bruynooghe
#
# This file is part of Delny.
#
# Delny is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Delny is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Delny; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
#
# Authors: Floris Bruynooghe (flub)
"""The main user interface to the Delny package
Most users will want to use the Triangulate class to access the
routines provided in the package.
"""
import Numeric
import _qhull
class Triangulation:
"""Represents a Delaunay triangulation of a set of points
All sequences used in this class can be either Python builtin
sequences or Numeric sequences. Results are returned as Python
builtin sequences however.
"""
# Data attributes:
# self.neighbours
# self.facets
# self.indices
def __init__(self, inputset, dim=None):
"""Creates the Delaunay triangulation
The `set' can be a 2 dimensional sequence with the last
dimension being the coordinates of every point.
Alternatively the set is a sequence objet of any dimension.
In this case the `dim' argument should be given and indicate
the number of dimensions. In this case the sequence will be
flattened (made 1-dimensional) and the first point will be
represented by the `dim' first elements of it and so on.
"""
if(dim != None):
self.set = Numeric.array(inputset)
self.set.shape = (-1, dim)
else:
self.set = inputset
self.neighbours, self.facets, self.indices = _qhull.delny(self.set)
def get_set(self):
"""Returns the set as it is being used by this class
This could be any sequence object, however if the `dim'
argument was not passed along to the constructor of the object
you can be sure this is the same sequence object as you passed
to the constructor.
"""
return self.set
def get_neighbours(self):
"""Returns the neighbours of each point in the set
This is a dictionnary with the points of the sets as key and a
list of it's nearest neighbours as value. Every neighbour is
a tuple with <dimension> floats.
"""
return self.neighbours
def get_elements(self):
"""Returns the elements of the Delaunay triangulation
This is a list of elements where every element is a list of
nodes and every node is a tuple of <dimesnion> floats. An
element is a triangle in 2D and a tetraheron in 3D.
"""
return self.facets
def get_elements_indices(self):
"""Returns the elements of the Delaunay triangulation
This is a list of elements where every element is a list of
node indices corresponding to the point index given in the inputset.
An element is a triangle in 2D and a tetraheron in 3D.
"""
return self.indices
def update_set(self, newset):
"""Recalculate the neighbours with a new input set
This has the same effect as creating a new instance but
without doing so.
"""
#FIXME: should this be renamed to set_set()?
#FIXME: should this also take the `dim' argument?
self.__init__(newset)

View File

@ -1,42 +0,0 @@
# __init__.py - initialisation script of the Delny package
#
# Copyright 2004 Floris Bruynooghe
#
# This file is part of Delny.
#
# Delny is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Delny is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Delny; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
#
# Authors: Floris Bruynooghe (flub)
"""Delaunay triangulation.
This package provides a python interface to the Delaunay triangulation
provided by the qhull software package using the C library libqhull.
It is possible to make a triangulations of any dimension you want.
The Delaunay module imports the core module directy in it's namespace
so you can for example use `Delaunay.Triangulation()' after a `import
Delaunay'. So see the Delaunay.core documentation for the use of the
module.
Whenever a "sequence object" is mentioned in this package all the
builtin Python sequences are accepted as well as an array from the
Numeric module.
"""
from core import *
__all__ = ["core", "_qhull"]

View File

@ -1,110 +0,0 @@
# core.py - the main user interface to the Delny package
#
# Copyright 2004-2006 Floris Bruynooghe
#
# This file is part of Delny.
#
# Delny is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Delny is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Delny; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
#
# Authors: Floris Bruynooghe (flub)
"""The main user interface to the Delny package
Most users will want to use the Triangulate class to access the
routines provided in the package.
"""
import Numeric
import _qhull
class Triangulation:
"""Represents a Delaunay triangulation of a set of points
All sequences used in this class can be either Python builtin
sequences or Numeric sequences. Results are returned as Python
builtin sequences however.
"""
# Data attributes:
# self.neighbours
# self.facets
# self.indices
def __init__(self, inputset, dim=None):
"""Creates the Delaunay triangulation
The `set' can be a 2 dimensional sequence with the last
dimension being the coordinates of every point.
Alternatively the set is a sequence objet of any dimension.
In this case the `dim' argument should be given and indicate
the number of dimensions. In this case the sequence will be
flattened (made 1-dimensional) and the first point will be
represented by the `dim' first elements of it and so on.
"""
if(dim != None):
self.set = Numeric.array(inputset)
self.set.shape = (-1, dim)
else:
self.set = inputset
self.neighbours, self.facets, self.indices = _qhull.delny(self.set)
def get_set(self):
"""Returns the set as it is being used by this class
This could be any sequence object, however if the `dim'
argument was not passed along to the constructor of the object
you can be sure this is the same sequence object as you passed
to the constructor.
"""
return self.set
def get_neighbours(self):
"""Returns the neighbours of each point in the set
This is a dictionnary with the points of the sets as key and a
list of it's nearest neighbours as value. Every neighbour is
a tuple with <dimension> floats.
"""
return self.neighbours
def get_elements(self):
"""Returns the elements of the Delaunay triangulation
This is a list of elements where every element is a list of
nodes and every node is a tuple of <dimesnion> floats. An
element is a triangle in 2D and a tetraheron in 3D.
"""
return self.facets
def get_elements_indices(self):
"""Returns the elements of the Delaunay triangulation
This is a list of elements where every element is a list of
node indices corresponding to the point index given in the inputset.
An element is a triangle in 2D and a tetraheron in 3D.
"""
return self.indices
def update_set(self, newset):
"""Recalculate the neighbours with a new input set
This has the same effect as creating a new instance but
without doing so.
"""
#FIXME: should this be renamed to set_set()?
#FIXME: should this also take the `dim' argument?
self.__init__(newset)

View File

@ -67,7 +67,7 @@ class ClusterSolver(Notifier):
self._toplevel = MutableSet()
# incrementally updated set of applicable methods
self._incremental_matchers = map(lambda method: method.incremental_matcher(self), self._incremental_methods)
print "incremental matchers:",self._incremental_matchers
#print "incremental matchers:",self._incremental_matchers
self._applicable_methods = Union(*self._incremental_matchers)
# ------- methods for setting up constraint problems ------------
@ -347,26 +347,28 @@ class ClusterSolver(Notifier):
# --------------
# search methods
# isearch methods
# --------------
def _process_new(self):
# try incremental matchers
while len(self._applicable_methods) > 0:
method = iter(self._applicable_methods).next()
print "applicable methods:", map(str, self._applicable_methods)
print "found applicable method:", method
self._add_method_complete(method)
# try old style matching
while len(self._new) > 0:
newobject = self._new.pop()
diag_print("search from "+str(newobject), "clsolver")
succes = self._search(newobject)
if succes and self.is_top_level(newobject):
# maybe more rules applicable.... push back on stack
self._new.append(newobject)
# while
# try incremental matchers and old style matching alternatingly
while len(self._applicable_methods) > 0 or len(self._new) > 0:
# check incremental matches
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
self._add_method_complete(method)
else:
newobject = self._new.pop()
diag_print("search from "+str(newobject), "clsolver")
succes = self._search(newobject)
if succes and self.is_top_level(newobject):
# maybe more rules applicable.... push back on stack
self._new.append(newobject)
#endif
# endif
# endwhile
#end def
def _search(self, newcluster):
@ -382,7 +384,7 @@ class ClusterSolver(Notifier):
# first try handcoded matching
for methodclass in self._handcoded_methods:
diag_print("trying incremental matching for "+str(methodclass), "clsolver3D")
diag_print("trying handcoded match for "+str(methodclass), "clsolver3D")
matches = methodclass.handcoded_match(self, newcluster, connected)
if self._try_matches(methodclass, matches):
return True
@ -423,7 +425,7 @@ class ClusterSolver(Notifier):
return False
def _add_method_complete(self, merge):
# diag_print("add_method_complete "+str(merge), "clsolver")
diag_print("add_method_complete "+str(merge), "clsolver")
# check that method has one output
if len(merge.outputs()) != 1:
raise StandardError, "merge number of outputs != 1"
@ -488,7 +490,8 @@ class ClusterSolver(Notifier):
# do not remove rigids from toplevel if method does not consider root
if isinstance(cluster, Rigid):
if hasattr(merge,"noremove") and merge.noremove == True:
continue
diag_print("block top-level", "clsolver")
break
# remove input clusters when all its constraints are in output cluster
if num_constraints(cluster.intersection(output)) >= num_constraints(cluster):
diag_print("remove from top-level: "+str(cluster),"clsolver")

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@ import incremental
class ClusterSolver3D(ClusterSolver):
"""A generic 3D geometric constraint solver. See ClusterSolver for details."""
"""A 3D geometric constraint solver. See ClusterSolver for details."""
# ------- PUBLIC METHODS --------
def __init__(self):
@ -227,44 +227,44 @@ class MergePR(ClusterMethod):
def _incremental_matcher(solver):
toplevel = solver.top_level()
rigids = incremental.Filter(lambda c: isinstance(c, Rigid), toplevel)
points = incremental.Filter(lambda c: len(c.vars)==1, rigids)
rigids = Rigids(solver)
points = Points(solver)
connectedpairs = ConnectedPairs(solver, points, rigids)
matcher = incremental.Map(lambda (p,r): MergePR({"$p":p, "$r":r}), connectedpairs)
return matcher
incremental_matcher = staticmethod(_incremental_matcher)
def _handcoded_match(problem, newcluster, connected):
connected = set()
for var in newcluster.vars:
dependend = problem.find_dependend(var)
dependend = filter(lambda x: problem.is_top_level(x), dependend)
connected.update(dependend)
matches = [];
if isinstance(newcluster, Rigid) and len(newcluster.vars)==1:
points = [newcluster]
distances = filter(lambda x: isinstance(x, Rigid) and len(x.vars)==2, connected)
elif isinstance(newcluster, Rigid) and len(newcluster.vars)==2:
distances = [newcluster]
points = filter(lambda x: isinstance(x, Rigid) and len(x.vars)==1, connected)
else:
return []
for p in points:
for d in distances:
m = Map({
"$p": p,
"$r": d,
"$a": list(p.vars)[0]
})
matches.append(m)
return matches;
handcoded_match = staticmethod(_handcoded_match)
#def _handcoded_match(problem, newcluster, connected):
# connected = set()
# for var in newcluster.vars:
# dependend = problem.find_dependend(var)
# dependend = filter(lambda x: problem.is_top_level(x), dependend)
# connected.update(dependend)
# matches = [];
# if isinstance(newcluster, Rigid) and len(newcluster.vars)==1:
# points = [newcluster]
# distances = filter(lambda x: isinstance(x, Rigid) and len(x.vars)==2, connected)
# elif isinstance(newcluster, Rigid) and len(newcluster.vars)==2:
# distances = [newcluster]
# points = filter(lambda x: isinstance(x, Rigid) and len(x.vars)==1, connected)
# else:
# return []
# for p in points:
# for d in distances:
# m = Map({
# "$p": p,
# "$r": d,
# "$a": list(p.vars)[0]
# })
# matches.append(m)
# return matches;
#handcoded_match = staticmethod(_handcoded_match)
def _pattern():
pattern = [["point","$p",["$a"]], ["rigid", "$r", ["$a"]]]
return pattern2graph(pattern)
pattern = staticmethod(_pattern)
patterngraph = _pattern()
#def _pattern():
# pattern = [["point","$p",["$a"]], ["rigid", "$r", ["$a"]]]
# return pattern2graph(pattern)
#pattern = staticmethod(_pattern)
#patterngraph = _pattern()
def __str__(self):
s = "MergePR("+str(self._inputs[0])+"+"+str(self._inputs[1])+"->"+str(self._outputs[0])+")"
@ -303,11 +303,28 @@ class MergeDR(ClusterMethod):
self._outputs = [out]
ClusterMethod.__init__(self)
def _pattern():
pattern = [["distance","$d",["$a","$b"]], ["rigid", "$r",["$a", "$b"]]]
return pattern2graph(pattern)
pattern = staticmethod(_pattern)
patterngraph = _pattern()
def _incremental_matcher(solver):
toplevel = solver.top_level()
rigids = Rigids(solver)
distances = Distances(solver)
connectedpairs = ConnectedPairs(solver, distances, rigids)
twoconnectedpairs = incremental.Filter(lambda (d,r): len(d.vars.intersection(r.vars))==2, connectedpairs);
matcher = incremental.Map(lambda (d,r): MergeDR({"$d":d, "$r":r}), twoconnectedpairs)
#
#global debugger
#debugger = incremental.Debugger(connectedpairs)
#
return matcher
incremental_matcher = staticmethod(_incremental_matcher)
#def _pattern():
# pattern = [["distance","$d",["$a","$b"]], ["rigid", "$r",["$a", "$b"]]]
# return pattern2graph(pattern)
#pattern = staticmethod(_pattern)
#patterngraph = _pattern()
def __str__(self):
s = "MergeDR("+str(self._inputs[0])+"+"+str(self._inputs[1])+"->"+str(self._outputs[0])+")"
@ -788,19 +805,19 @@ class Connected(incremental.IncrementalSet):
incremental.IncrementalSet.__init__(self, [incrset])
return
def _receive_add(self,source, object):
def _receive_add(self,source, obj):
connected = set()
for var in object.vars:
for var in obj.vars:
dependend = self._solver.find_dependend(var)
dependend = filter(lambda x: x in self._incrset, dependend)
connected.update(dependend)
connected.remove(object)
for object2 in connected:
self._add(frozenset((object, object2)))
connected.remove(obj)
for obj2 in connected:
self._add(frozenset((obj, obj2)))
def _receive_remove(self,source, object):
def _receive_remove(self,source, obj):
for frozen in list(self):
if object in frozen:
if obj in frozen:
self._remove(frozen)
def __eq__(self, other):
@ -822,26 +839,26 @@ class ConnectedPairs(incremental.IncrementalSet):
incremental.IncrementalSet.__init__(self, [incrset1, incrset2])
return
def _receive_add(self,source, object):
def _receive_add(self,source, obj):
connected = set()
for var in object.vars:
for var in obj.vars:
dependend = self._solver.find_dependend(var)
if source == self._incrset1:
dependend = filter(lambda x: x in self._incrset2, dependend)
elif source == self._incrset2:
dependend = filter(lambda x: x in self._incrset1, dependend)
connected.update(dependend)
if object in connected:
connected.remove(object)
for object2 in connected:
if obj in connected:
connected.remove(obj)
for obj2 in connected:
if source == self._incrset1:
self._add((object, object2))
self._add((obj, obj2))
elif source == self._incrset2:
self._add((object2, object))
self._add((obj2, obj))
def _receive_remove(self,source, object):
def _receive_remove(self,source, obj):
for (c1,c2) in list(self):
if c1==object or c2==object:
if c1==obj or c2==obj:
self._remove((c1,c2))
def __eq__(self, other):
@ -854,3 +871,63 @@ class ConnectedPairs(incremental.IncrementalSet):
return hash((self._solver, self._incrset1, self._incrset2))
class Rigids(incremental.Filter):
def __init__(self, solver):
self._solver = solver
incremental.Filter.__init__(self, lambda c: isinstance(c, Rigid), self._solver.top_level())
def __hash__(self):
return hash((self.__class__, self._solver))
def __eq__(self, other):
if isinstance(other, self.__class__):
return self._solver == other._solver
else:
return False
def __repr__(self):
return "Rigids("+repr(self._solver)+")"
class Points(incremental.Filter):
def __init__(self, solver):
self._solver = solver
rigids = Rigids(solver)
incremental.Filter.__init__(self, lambda c: len(c.vars)==1, rigids)
def __hash__(self):
return hash((self.__class__, self._solver))
def __eq__(self, other):
if isinstance(other, self.__class__):
return self._solver == other._solver
else:
return False
def __repr__(self):
return "Points("+repr(self._solver)+")"
class Distances(incremental.Filter):
def __init__(self, solver):
self._solver = solver
rigids = Rigids(solver)
incremental.Filter.__init__(self, lambda c: len(c.vars)==2, rigids)
def __hash__(self):
return hash((self.__class__, self._solver))
def __eq__(self, other):
if isinstance(other, self.__class__):
return self._solver == other._solver
else:
return False
def __repr__(self):
return "Distances("+repr(self._solver)+")"

View File

@ -5,6 +5,7 @@ import vector
import math
from clsolver import PrototypeMethod, SelectionMethod
from clsolver3D import ClusterSolver3D
from clsolver2D import ClusterSolver2D
from cluster import *
from configuration import Configuration
from diagnostic import diag_print

View File

@ -41,6 +41,9 @@ class IncrementalSet(notify.Notifier, notify.Listener):
(self._ref, count) = self._all[self]
count += 1
self._all[self] = (self._ref, count)
self.listeners = self._ref().listeners
self.notifiers = self._ref().notifiers
else:
# set self._ref and update self._all
self._ref = None
@ -378,6 +381,32 @@ def combinations(listofiters):
z.add(tuple(frozenset([e]).union(y)))
return z
class Debugger(IncrementalSet):
"""A set-like container that incrementally determines all combinations of its inputs (IncrementalSets)"""
def __init__(self, watch_iset):
self._watch = watch_iset
IncrementalSet.__init__(self, [self._watch])
def _receive_add(self, source, obj):
print "add", obj, "to", source
def _receive_remove(self, source, obj):
print "remove", obj, "to", source
def __eq__(self, other):
if isinstance(other, Debugger):
return self._watch == other.watch
else:
return False
def __hash__(self):
return hash((self.__class__, self._watch))
def __repr__(self):
return "Debugger(%s)"%str(self._watch)
def test1():
s = MutableSet([5,-3])
@ -434,6 +463,7 @@ def test3():
print set(intersection)
print set(difference)
if __name__ == '__main__':
test1()
test2()

View File

@ -5,8 +5,8 @@
# This also implies that there is no order of notifications sent, and objects can only register as listener/notifier once!
# Notes:
# - member variables "listeners" and "notifiers" are not hidden, but should never be modified independently, so be careful!
# - subclasses will need to override the receive_notify class.
# - member variables "listeners" and "notifiers" are not hidden, but should never be modified directly, so be careful!
# - subclasses of Listener will want to override the receive_notify class.
# - Notifier/Listener subclasses __init__ method must call Notifier/Listener.__init__(self)
import weakref
@ -19,20 +19,15 @@ class Notifier:
"""
def __init__(self):
#self.listeners = []
self.listeners = weakref.WeakKeyDictionary()
def add_listener(self, listener):
"""add a listener to the list (and self to listers' list)"""
#self.listeners.add(listener)
#listener.notifiers.add(self)
self.listeners[listener] = True
listener.notifiers[self] = True
def rem_listener(self, listener):
"""remove a listener from the list (and self from listers' list)"""
#self.listeners.remove(listener)
#listener.notifiers.remove(self)
del self.listeners[listener]
del listener.notifiers[self]
@ -41,6 +36,16 @@ class Notifier:
for dest in self.listeners:
dest.receive_notify(self, message)
def __getstate__(self):
"""when pickling... do not save self.listeners"""
dict = self.__dict__.copy()
del dict['listeners']
return dict
def __setstate__(self, dict):
"""when unpickling... create new self.listeners"""
self.__dict__ = dict
self.listeners = weakref.WeakKeyDictionary()
class Listener:
"""A listener is notified by one or more Notifiers.
@ -50,26 +55,31 @@ class Listener:
"""
def __init__(self):
# 20090521 - replaced list by weakKeyDict, do when listerner deleted, it is removed from list
#self.notifiers = []
self.notifiers = weakref.WeakKeyDictionary();
def add_notifier(self, notifier):
"""add a notifier to the list (and self to notifiers' list)"""
#self.notifiers.add(notifier)
#notifier.listeners.add(self)
self.notifiers[notifier] = True
notifier.listeners[self] = True
def rem_notifier(self, notifier):
"""remove a notifier from the list (and self from notifiers' list)"""
#self.notifiers.remove(notifier)
#notifier.listeners.remove(self)
del self.notifiers[notifier]
del notifier.listeners[self]
def receive_notify(self, source, message):
"""receive a message from a notifier. Implementing classes should override this."""
print self,"receive_notify",source,message
print "receive_notify", self, source,message
def __getstate__(self):
"""when pickling... do not save self.notifiers"""
dict = self.__dict__.copy()
del dict['notifiers']
return dict
def __setstate__(self, dict):
"""when unpickling... create new self.notifiers"""
self.__dict__ = dict
self.notifiers = weakref.WeakKeyDictionary()

View File

@ -374,6 +374,43 @@ def double_triangle():
problem.add_constraint(DistanceConstraint('v3', 'v4', 10.0))
return problem
def triple_double_triangle():
problem = GeometricProblem(dimension=2)
problem.add_point('QX', vector([0.0, 0.0]))
problem.add_point('QA2', vector([1.0, 0.0]))
problem.add_point('QA3', vector([0.0, 1.0]))
problem.add_point('QY', vector([1.0, 1.0]))
problem.add_constraint(DistanceConstraint('QX', 'QA2', 10.0))
problem.add_constraint(DistanceConstraint('QX', 'QA3', 10.0))
problem.add_constraint(DistanceConstraint('QA2', 'QA3', 10.0))
problem.add_constraint(DistanceConstraint('QA2', 'QY', 10.0))
problem.add_constraint(DistanceConstraint('QA3', 'QY', 10.0))
#problem.add_point('QX', vector([0.0, 0.0]))
problem.add_point('QB2', vector([1.0, 0.0]))
problem.add_point('QZ', vector([0.0, 1.0]))
problem.add_point('QB4', vector([1.0, 1.0]))
problem.add_constraint(DistanceConstraint('QX', 'QB2', 10.0))
problem.add_constraint(DistanceConstraint('QX', 'QZ', 10.0))
problem.add_constraint(DistanceConstraint('QB2', 'QZ', 10.0))
problem.add_constraint(DistanceConstraint('QB2', 'QB4', 10.0))
problem.add_constraint(DistanceConstraint('QZ', 'QB4', 10.0))
#problem.add_point('QY', vector([0.0, 0.0]))
problem.add_point('QC2', vector([1.0, 0.0]))
#problem.add_point('QZ', vector([0.0, 1.0]))
problem.add_point('QC4', vector([1.0, 1.0]))
problem.add_constraint(DistanceConstraint('QY', 'QC2', 10.0))
problem.add_constraint(DistanceConstraint('QY', 'QZ', 10.0))
problem.add_constraint(DistanceConstraint('QC2', 'QZ', 10.0))
problem.add_constraint(DistanceConstraint('QC2', 'QC4', 10.0))
problem.add_constraint(DistanceConstraint('QZ', 'QC4', 10.0))
return problem
def hog1():
# double triangle with inter-angle (needs angle propagation)
problem = GeometricProblem(dimension=2)
@ -717,7 +754,9 @@ def test(problem, use_prototype=True):
print "problem:"
print problem
print "use_prototype=",use_prototype
print "Solving..."
solver = GeometricSolver(problem, use_prototype)
print "...done"
print "drplan:"
print solver.dr
print "top-level rigids:",list(solver.dr.top_level())
@ -883,8 +922,8 @@ def selection_test():
print len(solver.get_solutions()), "solutions"
def runtests():
#diag_select("clsolver3D")
def test3d():
#diag_select("clsolver")
test(double_tetrahedron_problem())
#test(ada_tetrahedron_problem())
#test(double_banana_problem())
@ -899,5 +938,9 @@ def runtests():
#selection_test()
#test(overconstrained_tetra())
def test2d():
#test(ddd_problem())
#test(double_triangle())
test(triple_double_triangle())
if __name__ == "__main__": runtests()
if __name__ == "__main__": test2d()