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:
parent
5288253718
commit
fdf7300eef
|
@ -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"]
|
|
@ -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)
|
|
@ -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"]
|
|
@ -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)
|
|
@ -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
|
@ -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)+")"
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
||||
|
||||
|
|
49
test/test.py
49
test/test.py
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue
Block a user