def test_multiedit1(self): # This test stresses the edit session stack. begin_edit() starts a new # "edit variable group" and "end_edit" closes it, leaving only the # previously opened edit variables still active. x = Variable('x') y = Variable('y') w = Variable('w') h = Variable('h') solver = SimplexSolver() # Add some stays solver.add_stay(x) solver.add_stay(y) solver.add_stay(w) solver.add_stay(h) # start an editing session solver.add_edit_var(x) solver.add_edit_var(y) with solver.edit(): solver.suggest_value(x, 10) solver.suggest_value(y, 20) # Force the system to resolve. solver.resolve() self.assertAlmostEqual(x.value, 10) self.assertAlmostEqual(y.value, 20) self.assertAlmostEqual(w.value, 0) self.assertAlmostEqual(h.value, 0) # Open a second set of variables for editing solver.add_edit_var(w) solver.add_edit_var(h) with solver.edit(): solver.suggest_value(w, 30) solver.suggest_value(h, 40) # Close the second set... self.assertAlmostEqual(x.value, 10) self.assertAlmostEqual(y.value, 20) self.assertAlmostEqual(w.value, 30) self.assertAlmostEqual(h.value, 40) # Now make sure the first set can still be edited solver.suggest_value(x, 50) solver.suggest_value(y, 60) self.assertAlmostEqual(x.value, 50) self.assertAlmostEqual(y.value, 60) self.assertAlmostEqual(w.value, 30) self.assertAlmostEqual(h.value, 40)
def test_multiedit3(self): MIN = 100 MAX = 500 width = Variable('width') height = Variable('height') top = Variable('top') bottom = Variable('bottom') left = Variable('left') right = Variable('right') solver = SimplexSolver() iw = Variable('window_innerWidth', random.randrange(MIN, MAX)) ih = Variable('window_innerHeight', random.randrange(MIN, MAX)) solver.add_constraint(Constraint(width, Constraint.EQ, iw, strength=STRONG, weight=0.0)) solver.add_constraint(Constraint(height, Constraint.EQ, ih, strength=STRONG, weight=0.0)) solver.add_constraint(Constraint(top, Constraint.EQ, 0, strength=WEAK, weight=0.0)) solver.add_constraint(Constraint(left, Constraint.EQ, 0, strength=WEAK, weight=0.0)) solver.add_constraint(Constraint(bottom, Constraint.EQ, top + height, strength=MEDIUM, weight=0.0)) # Right is at least left + width solver.add_constraint(Constraint(right, Constraint.EQ, left + width, strength=MEDIUM, weight=0.0)) solver.add_stay(iw) solver.add_stay(ih) # Propegate viewport size changes. for i in range(0, 30): # Measurement should be cheap here. iwv = random.randrange(MIN, MAX) ihv = random.randrange(MIN, MAX) solver.add_edit_var(iw) solver.add_edit_var(ih) with solver.edit(): solver.suggest_value(iw, iwv) solver.suggest_value(ih, ihv) # solver.resolve() self.assertAlmostEqual(top.value, 0) self.assertAlmostEqual(left.value, 0) self.assertLessEqual(bottom.value, MAX) self.assertGreaterEqual(bottom.value, MIN) self.assertLessEqual(right.value, MAX) self.assertGreaterEqual(right.value, MIN)
class TTSolver: def initialSetup(self, layer): self.solver = SimplexSolver() self.nodehash = {} for p in layer.paths: for n in p.nodes: nid = n.hash() self.nodehash[nid] = { "node": n, "xvar": Variable("x"+str(nid), n.position.x), "yvar": Variable("y"+str(nid), n.position.y), "affected": set() } self.setConstraintsFromHints(layer) def yvar(self,n): if not n: raise ValueError if not (n.hash() in self.nodehash): raise KeyError(n) return self.nodehash[n.hash()]["yvar"] def xvar(self,n): if not n: raise ValueError if not (n.hash() in self.nodehash): raise KeyError(n) return self.nodehash[n.hash()]["xvar"] def setConstraintsFromHints(self, layer): c = [] for h in layer.hints: if h.type == TAG: try: if h.options() == DIAGONAL or (h.options() < 8 and not h.horizontal): v1 = self.yvar(h.originNode) v2 = self.yvar(h.targetNode) yValue = v1.value - v2.value ystem = v1 - v2 c.append(ystem == yValue) if h.options() == DIAGONAL or (h.options() < 8 and h.horizontal): v1 = self.xvar(h.originNode) v2 = self.xvar(h.targetNode) xValue = v1.value - v2.value xstem = v1 - v2 c.append(xstem == xValue) if h.options() == PROPORTIONAL_TRIPLE: if h.horizontal: v1 = self.xvar(h.originNode) v2 = self.xvar(h.targetNode) v3 = self.xvar(h.otherNode1) proportion = safediv(h.targetNode.position.x - h.originNode.position.x , h.otherNode1.position.x - h.targetNode.position.x) else: v1 = self.yvar(h.originNode) v2 = self.yvar(h.targetNode) v3 = self.yvar(h.otherNode1) proportion = safediv(h.targetNode.position.y - h.originNode.position.y, h.otherNode1.position.y - h.targetNode.position.y) d1 = v2 - v1 d2 = v3 - v2 c.append(d2 * proportion == d1) if h.options() == PROPORTIONAL_QUAD: on2 = h.valueForKey_("otherNode2") # Glyphs bug if h.horizontal: v1 = self.xvar(h.originNode) v2 = self.xvar(h.targetNode) v3 = self.xvar(h.otherNode1) v4 = self.xvar(on2) proportion = safediv(h.targetNode.position.x - h.originNode.position.x, on2.position.x - h.otherNode1.position.x) else: v1 = self.yvar(h.originNode) v2 = self.yvar(h.targetNode) v3 = self.yvar(h.otherNode1) v4 = self.yvar(on2) proportion = safediv(h.targetNode.position.y - h.originNode.position.y, on2.position.y - h.otherNode1.position.y) d1 = v2 - v1 d2 = v4 - v3 # print(d1,d2,proportion) c.append(d2 * proportion == d1) except ValueError: print("I found a busted constraint. It'll get cleaned up soon.") for cs in c: self.solver.add_constraint(cs) def _diagonalConstraints(self, c, n1,n2): v1 = self.xvar(n1) v2 = self.xvar(n2) xValue = v1.value - v2.value xstem = v1 - v2 v1 = self.yvar(n1) v2 = self.yvar(n2) yValue = v1.value - v2.value ystem = v1 - v2 c.append(xstem == xValue) c.append(ystem == yValue) def _makeEditable(self,node): n = self.nodehash[node.hash()] self.solver.add_edit_var(n["xvar"]) self.solver.add_edit_var(n["yvar"]) def _suggest(self,node): n = self.nodehash[node.hash()] # print("Suggesting ",n["node"].position.x,n["node"].position.y) self.solver.suggest_value(n["xvar"], n["node"].position.x) self.solver.suggest_value(n["yvar"], n["node"].position.y) def setStayFromNodes(self, layer): nodes = filter(lambda x:x.hash() in self.nodehash, layer.selection) if len(nodes) < 1: return temporaryConstraints = [] c = [] for p in layer.paths: for n in p.nodes: if n.type != GSOFFCURVE: # Constrain left handle and this node if n.prevNode.type == GSOFFCURVE and not n.prevNode.selected: self._diagonalConstraints(c, n, n.prevNode) if n.nextNode.type == GSOFFCURVE and not n.nextNode.selected: self._diagonalConstraints(c, n, n.nextNode) for cs in c: temporaryConstraints.append(self.solver.add_constraint(cs, strength=WEAK)) # print("Putting stuff into solver") for n in nodes: self._makeEditable(n) if n.type != GSOFFCURVE: if n.nextNode.type == GSOFFCURVE: self._makeEditable(n.nextNode) if n.prevNode.type == GSOFFCURVE: self._makeEditable(n.prevNode) else: if n.nextNode.type == CURVE and n.nextNode.smooth: self._makeEditable(n.nextNode) self._makeEditable(n.nextNode.nextNode) elif n.prevNode.type == CURVE and n.prevNode.smooth: self._makeEditable(n.prevNode) self._makeEditable(n.prevNode.prevNode) with self.solver.edit(): for n in nodes: self._suggest(n) if n.type != GSOFFCURVE: if n.nextNode.type == GSOFFCURVE: self._suggest(n.nextNode) if n.prevNode.type == GSOFFCURVE: self._suggest(n.prevNode) else: if n.nextNode.type == CURVE and n.nextNode.smooth: self._suggest(n.nextNode) self._suggest(n.nextNode.nextNode) elif n.prevNode.type == CURVE and n.prevNode.smooth: self._suggest(n.prevNode) self._suggest(n.prevNode.prevNode) for j in nodes: n = self.nodehash[j.hash()] # print("Got: ",n["xvar"],n["yvar"]) for c in temporaryConstraints: self.solver.remove_constraint(c) def updateGlyphWithSolution(self): for i in self.nodehash: n = self.nodehash[i] # print(n["node"], "->", n["xvar"].value, n["yvar"].value) n["node"].position = (n["xvar"].value, n["yvar"].value) def updateSolverFromGlyph(self): for i in self.nodehash: n = self.nodehash[i] n["xvar"].value = n["node"].position.x n["yvar"].value = n["node"].position.y n["xstay"] = self.solver.add_stay(n["xvar"], strength=WEAK) n["ystay"] = self.solver.add_stay(n["yvar"], strength=WEAK) self.solver.add_edit_var(n["xvar"]) self.solver.add_edit_var(n["yvar"]) if len(self.nodehash) > 0: with self.solver.edit(): for i in self.nodehash: n = self.nodehash[i] self.solver.suggest_value(n["xvar"], n["node"].position.x) self.solver.suggest_value(n["yvar"], n["node"].position.y)
def test_multiedit3(self): MIN = 100 MAX = 500 width = Variable('width') height = Variable('height') top = Variable('top') bottom = Variable('bottom') left = Variable('left') right = Variable('right') solver = SimplexSolver() iw = Variable('window_innerWidth', random.randrange(MIN, MAX)) ih = Variable('window_innerHeight', random.randrange(MIN, MAX)) solver.add_constraint( Constraint(width, Constraint.EQ, iw, strength=STRONG, weight=0.0)) solver.add_constraint( Constraint(height, Constraint.EQ, ih, strength=STRONG, weight=0.0)) solver.add_constraint( Constraint(top, Constraint.EQ, 0, strength=WEAK, weight=0.0)) solver.add_constraint( Constraint(left, Constraint.EQ, 0, strength=WEAK, weight=0.0)) solver.add_constraint( Constraint(bottom, Constraint.EQ, top + height, strength=MEDIUM, weight=0.0)) # Right is at least left + width solver.add_constraint( Constraint(right, Constraint.EQ, left + width, strength=MEDIUM, weight=0.0)) solver.add_stay(iw) solver.add_stay(ih) # Propegate viewport size changes. for i in range(0, 30): # Measurement should be cheap here. iwv = random.randrange(MIN, MAX) ihv = random.randrange(MIN, MAX) solver.add_edit_var(iw) solver.add_edit_var(ih) with solver.edit(): solver.suggest_value(iw, iwv) solver.suggest_value(ih, ihv) # solver.resolve() self.assertAlmostEqual(top.value, 0) self.assertAlmostEqual(left.value, 0) self.assertLessEqual(bottom.value, MAX) self.assertGreaterEqual(bottom.value, MIN) self.assertLessEqual(right.value, MAX) self.assertGreaterEqual(right.value, MIN)