From b558eeec8306216c3da5091066fdc79d98bcc60c Mon Sep 17 00:00:00 2001 From: kwikrick Date: Tue, 21 Aug 2012 17:32:46 +0000 Subject: [PATCH] added 2d method DeriveHH2S & MergeSR --- geosolver/clsolver2D.py | 226 +++++++++++++++++++++++++------------ geosolver/configuration.py | 61 +++++----- test/test.py | 1 + 3 files changed, 185 insertions(+), 103 deletions(-) diff --git a/geosolver/clsolver2D.py b/geosolver/clsolver2D.py index 1596b51..344c811 100644 --- a/geosolver/clsolver2D.py +++ b/geosolver/clsolver2D.py @@ -16,13 +16,46 @@ class ClusterSolver2D(ClusterSolver): def __init__(self): """Instantiate a ClusterSolver2D""" - ClusterSolver.__init__(self, [CheckAR, MergePR, MergeRR, DeriveDDD, DeriveDAD, DeriveADD, DeriveHH2S]) + ClusterSolver.__init__(self, [CheckAR, MergePR, MergeRR, DeriveDDD, DeriveDAD, DeriveADD, DeriveHH2S, MergeSR]) # ---------------------------------------------- # ---------- Methods for 2D solving ------------- # ---------------------------------------------- +class MergeSR(ClusterMethod): + """Merge a Scalabe and a Rigid sharing two points""" + def __init__(self, map): + # check inputs + in1 = map["$r"] + in2 = map["$s"] + # create output + out = Rigid(set(in2.vars)) + # set method parameters + self._inputs = [in1, in2] + self._outputs = [out] + ClusterMethod.__init__(self) + + def _pattern(): + pattern = [["rigid","$r",["$a","$b"]], ["balloon", "$s", ["$a", "$b"]]] + return pattern2graph(pattern) + pattern = staticmethod(_pattern) + patterngraph = _pattern() + + def __str__(self): + s = "MergeSR("+str(self._inputs[0])+"+"+str(self._inputs[1])+"->"+str(self._outputs[0])+")" + s += "[" + self.status_str()+"]" + return s + + def multi_execute(self, inmap): + diag_print("MergeSR.multi_execute called","clmethods") + c1 = self._inputs[0] + c2 = self._inputs[1] + conf1 = inmap[c1] + conf2 = inmap[c2] + return [conf1.merge_scale(conf2)] + + # Merge methods take root cluster in considerations # Derive methods do not take root cluster in consideration @@ -247,7 +280,7 @@ class DeriveDAD(ClusterMethod): return isinstance(dad, DeriveDAD) def triplet2dad(triplet): - print "triplet2dad: start" + #print "triplet2dad: start" hogs = filter(lambda c: isinstance(c, Hedgehog), triplet) rigids= filter(lambda c: isinstance(c, Rigid), triplet) if not(len(hogs)==1 and len(rigids)==2): return None @@ -255,18 +288,18 @@ class DeriveDAD(ClusterMethod): r1 = rigids[0] r2 = rigids[1] b = hog.cvar; - print "triplet2dad: b = ", b + #print "triplet2dad: b = ", b if not(b in r1.vars): return None if not(b in r2.vars): return None - print "triplet2dad: b in rigids" + #print "triplet2dad: b in rigids" p1s = r1.vars.intersection(hog.xvars) p2s = r2.vars.intersection(hog.xvars) if not(len(p1s) == 1): return None if not(len(p2s) == 1): return None a = list(p1s)[0] c = list(p2s)[0] - print "triplet2dad: a = ", a - print "triplet2dad: c = ", c + #print "triplet2dad: a = ", a + #print "triplet2dad: c = ", c if a==c: return None return DeriveDAD( {"$d_ab":r1, "$a_abc":hog, "$d_bc":r2, "$a":a, "$b":b, "$c":c }) # end def @@ -334,16 +367,16 @@ class DeriveADD(ClusterMethod): return isinstance(dad, DeriveADD) def triplet2add(triplet): - print "triplet2add: start" + #print "triplet2add: start" hogs = filter(lambda c: isinstance(c, Hedgehog), triplet) rigids= filter(lambda c: isinstance(c, Rigid), triplet) if not(len(hogs)==1 and len(rigids)==2): return None hog = hogs[0] - print "triplet2add: hog = ",hog + #print "triplet2add: hog = ",hog r1 = rigids[0] r2 = rigids[1] a = hog.cvar; - print "triplet2add: a = ", a + #print "triplet2add: a = ", a if a in r1.vars and not(a in r2.vars): d_ab = r1 d_bc = r2 @@ -352,16 +385,16 @@ class DeriveADD(ClusterMethod): d_bc = r1 else: return None - print "d_ab:",d_ab - print "d_bc:",d_bc + #print "d_ab:",d_ab + #print "d_bc:",d_bc pbs = d_ab.vars.intersection(hog.xvars) if not(len(pbs) == 1): return None b = list(pbs)[0] - print "triplet2add: b = ", b + #print "triplet2add: b = ", b pcs = d_bc.vars.intersection(hog.xvars).difference([b]) if not(len(pcs) == 1): return None c = list(pcs)[0] - print "triplet2add: c = ", c + #print "triplet2add: c = ", c return DeriveADD( {"$a_cab":hog, "$d_ab":d_ab, "$d_bc":d_bc, "$a":a, "$b":b, "$c":c }) # end def triplets = ConnectedTriplets(solver, solver.top_level()) @@ -397,6 +430,81 @@ class DeriveADD(ClusterMethod): constraints.append(SelectionConstraint(fnot(is_acute),[self.a,self.c,self.b])) return constraints +class DeriveHH2S(ClusterMethod): + """Derive a scalable from two angles""" + def __init__(self, map): + # check inputs + self.a_cab = map["$a_cab"] + self.a_abc = map["$a_abc"] + self.a = map["$a"] + self.b = map["$b"] + self.c = map["$c"] + # create output + out = Balloon([self.a,self.b,self.c]) + # set method parameters + self._inputs = [self.a_cab, self.a_abc] + self._outputs = [out] + ClusterMethod.__init__(self) + # do not remove input clusters (because root not considered here) + self.noremove = True + + def __str__(self): + s = "DeriveHH2S("+str(self._inputs[0])+"+"+str(self._inputs[1])+"->"+str(self._outputs[0])+")" + s += "[" + self.status_str()+"]" + return s + + def _incremental_matcher(solver): + def pair_is_hh2s(pair): + method = pair_to_hh2s(pair) + return isinstance(method, DeriveHH2S) + + def pair_to_hh2s(pair): + print "pair_to_hhs2s: start" + assert len(pair)==2 + a_cab = list(pair)[0] + a_abc = list(pair)[1] + a = a_cab.cvar + b = a_abc.cvar + print "a",a + print "b",b + if a == b: + return None + if a not in a_abc.xvars: + return None + if b not in a_cab.xvars: + return None + cs = a_cab.xvars.intersection(a_abc.xvars).difference([a,b]) + print "#cs",len(cs) + if len(cs) != 1: + return None + c = list(cs)[0] + print "c",c + print "hh2s triangle confirmed" + return DeriveHH2S( {"$a_cab":a_cab, "$a_abc":a_abc, "$a":a, "$b":b, "$c":c }) + # end def + + hogs = Hogs(solver) + pairs = ConnectedPairs1(solver, hogs) + matchingpairs = incremental.Filter(lambda pair: pair_is_hh2s(pair), pairs) + matcher = incremental.Map(pair_to_hh2s, matchingpairs) + return matcher + + incremental_matcher = staticmethod(_incremental_matcher) + + + def multi_execute(self, inmap): + diag_print("DeriveHH2S.multi_execute called","clmethods") + c312 = inmap[self.a_cab] + c123 = inmap[self.a_abc] + v1 = self.a + v2 = self.b + v3 = self.c + a312 = angle_3p(c312.get(v3),c312.get(v1),c312.get(v2)) + d12 = 1.0 + a123 = angle_3p(c123.get(v1),c123.get(v2),c123.get(v3)) + solutions = solve_ada(v1,v2,v3,a312,d12,a123) + return solutions + class CheckAR(ClusterMethod): """Represents the overconstrained merging a hedgehog and a rigid that completely overlaps it.""" @@ -454,64 +562,6 @@ class CheckAR(ClusterMethod): # all checks passed, return rigid configuration return [rigid] -class DeriveHH2S(ClusterMethod): - """Derive a scalable from two angles""" - def __init__(self, map): - # check inputs - self.a_cab = map["$a_cab"] - self.a_abc = map["$a_abc"] - self.a = map["$a"] - self.b = map["$b"] - self.c = map["$c"] - # create output - out = Balloon([self.a,self.b,self.c]) - # set method parameters - self._inputs = [self.a_cab, self.a_abc] - self._outputs = [out] - ClusterMethod.__init__(self) - # do not remove input clusters (because root not considered here) - self.noremove = True - - def __str__(self): - s = "DeriveHH2S("+str(self._inputs[0])+"+"+str(self._inputs[1])+"->"+str(self._outputs[0])+")" - s += "[" + self.status_str()+"]" - return s - - def _incremental_matcher(solver): - def pair_is_hh2s(pair): - method = pair_to_hh2s(pair) - return isinstance(method, DeriveHH2S) - - def pair_to_hh2s(pair): - print "pair_to_hhs2s: start" - print "not implemented!" - return None - #return DeriveHH2S( {"$a_cab":a_cab, "$a_abc":a_abc, "$a":a, "$b":b, "$c":c }) - # end def - - hogs = Hogs(solver) - pairs = ConnectedPairs1(solver, hogs) - matchingpairs = incremental.Filter(lambda pair: pair_is_hh2s(pair), pairs) - matcher = incremental.Map(pair_to_hh2s, matchingpairs) - return matcher - - incremental_matcher = staticmethod(_incremental_matcher) - - - def multi_execute(self, inmap): - diag_print("DeriveHH2S.multi_execute called","clmethods") - c312 = inmap[self.a_cab] - c123 = inmap[self.a_abc] - v1 = self.a - v2 = self.b - v3 = self.c - a312 = angle_3p(c312.get(v3),c312.get(v1),c312.get(v2)) - d12 = 1.0 - a123 = angle_3p(c123.get(v1),c123.get(v2),c123.get(v3)) - solutions = solve_ada_3D(v1,v2,v3,a312,d12,a123) - return solutions - - # --------------------------------------------------------- # ------- functions to determine configurations ---------- # --------------------------------------------------------- @@ -562,6 +612,40 @@ def solve_add(a,b,c, a_cab, d_ab, d_bc): rval.append(Configuration(map)) return rval +def solve_ada(a, b, c, a_cab, d_ab, a_abc): + """returns a list of Configurations of v1,v2,v3 such that distance v1-v2=d12 etc. + v: name of point variables + d: numeric distance values + a: numeric angle in radians + """ + diag_print("solve_ada: %s %s %s %f %f %f"%(a,b,c,a_cab,d_ab,a_abc),"clmethods") + p_a = vector.vector([0.0,0.0]) + p_b = vector.vector([d_ab, 0.0]) + dir_ac = vector.vector([math.cos(-a_cab),math.sin(-a_cab)]) + dir_bc = vector.vector([math.cos(math.pi-a_abc),math.sin(math.pi-a_abc)]) + dir_ac[1] = math.fabs(dir_ac[1]) + dir_bc[1] = math.fabs(dir_bc[1]) + if tol_eq(math.sin(a_cab), 0.0) and tol_eq(math.sin(a_abc),0.0): + m = d_ab/2 + math.cos(-a_cab)*d_ab - math.cos(-a_abc)*d_ab + p_c = vector.vector([m,0.0]) + # p_c = (p_a + p_b) / 2 + #p_a.append(0.0) + #p_b.append(0.0) + #p_c.append(0.0) + map = {a:p_a, b:p_b, c:p_c} + cluster = _Configuration(map) + cluster.underconstrained = True + rval = [cluster] + else: + solutions = rr_int(p_a,dir_ac,p_b,dir_bc) + #p_a.append(0.0) + #p_b.append(0.0) + rval = [] + for p_c in solutions: + #p_c.append(0.0) + map = {a:p_a, b:p_b, c:p_c} + rval.append(Configuration(map)) + return rval # ------------------------------------- # --------- incremental sets ---------- diff --git a/geosolver/configuration.py b/geosolver/configuration.py index ab18268..b1e8668 100644 --- a/geosolver/configuration.py +++ b/geosolver/configuration.py @@ -148,38 +148,35 @@ class Configuration: t.underconstrained = underconstrained return t - def merge_scale_2D(self, other, vars=[]): - """returns a new configurations which is this one plus the given other configuration transformed, such that common points will overlap (if possible).""" - if len(vars) == 0: - shared = set(self.vars()).intersection(other.vars()) - else: - shared = vars - underconstrained = self.underconstrained or other.underconstrained - if len(shared) < 2: - raise StandardError, "must have >=2 shared point vars" - - v1 = list(shared)[0] - v2 = list(shared)[1] - p11 = self.map[v1] - p12 = self.map[v2] - if tol_eq(vector.norm(p12-p11),0.0): - underconstrained = True - cs1 = make_hcs_2d_scaled(p11, p11+vector.vector([1.0,0.0])) - else: - cs1 = make_hcs_2d_scaled(p11, p12) - p21 = other.map[v1] - p22 = other.map[v2] - if tol_eq(vector.norm(p22-p21),0.0): - underconstrained = True - cs2 = make_hcs_2d_scaled(p21, p21+vector.vector([1.0,0.0])) - else: - cs2 = make_hcs_2d_scaled(p21, p22) - print cs1, cs2 - t = cs_transform_matrix(cs2, cs1) - othert = other.transform(t) - result = self.add(othert) - result.underconstrained = underconstrained - return result + def _merge_scale_transform_2D(self, other): + """returns a transformation for 'other' to scale, rotate and translate such that its points will overlap with 'this'(if possible).""" + shared = set(self.vars()).intersection(other.vars()) + if len(shared) == 0: + return self._merge_transform_3D(other) + elif len(shared) == 1: + return self._merge_transform_3D(other) + elif len(shared) >= 2: + underconstrained = False + v1 = list(shared)[0] + v2 = list(shared)[1] + p11 = self.map[v1] + p12 = self.map[v2] + if tol_eq(vector.norm(p12-p11),0.0): + underconstrained = True + cs1 = make_hcs_2d_scaled(p11, p11+vector.vector([1.0,0.0])) + else: + cs1 = make_hcs_2d_scaled(p11, p12) + p21 = other.map[v1] + p22 = other.map[v2] + if tol_eq(vector.norm(p22-p21),0.0): + underconstrained = True + cs2 = make_hcs_2d_scaled(p21, p21+vector.vector([1.0,0.0])) + else: + cs2 = make_hcs_2d_scaled(p21, p22) + print cs1, cs2 + t = cs_transform_matrix(cs2, cs1) + t.underconstrained = underconstrained + return t def _merge_transform_3D(self, other): """returns a matrix for a rigid transformation diff --git a/test/test.py b/test/test.py index 1b7a081..4b4ec82 100644 --- a/test/test.py +++ b/test/test.py @@ -967,6 +967,7 @@ def test2d(): #test(triple_double_triangle()) #test(dad_problem()) #test(add_problem()) + #test(ada_problem()) test(aad_problem()) if __name__ == "__main__":