From 92316d329d8a0fd115ff7d79982578239adb6e0a Mon Sep 17 00:00:00 2001 From: Cecil Date: Tue, 16 Oct 2018 00:08:27 -0600 Subject: [PATCH] for #410 simple test program for cassowary-ruby gem --- Tests/layout/cs.rb | 69 +++++++++++++++++ lib/cassowary/constraint.rb | 39 ---------- lib/cassowary/simplex_solver.rb | 126 +------------------------------- lib/cassowary/tableau.rb | 84 +-------------------- 4 files changed, 72 insertions(+), 246 deletions(-) create mode 100644 Tests/layout/cs.rb diff --git a/Tests/layout/cs.rb b/Tests/layout/cs.rb new file mode 100644 index 00000000..a4f620eb --- /dev/null +++ b/Tests/layout/cs.rb @@ -0,0 +1,69 @@ +# Not shoes, just ruby +# From https://cassowary.readthedocs.io/en/latest/topics/examples.html#gui-layout +# converted from python to ruby +# +# gem install cassowary-ruby +require 'cassowary' +include Cassowary + +class Button + attr_accessor :left, :width + def initialize(identifier) + @left = Variable.new(name: 'left' + identifier, value: 0) + @width = Variable.new(name: 'width' + identifier, value: 0) + end +end +solver = SimplexSolver.new() +b1 = Button.new('b1') +b2 = Button.new('b2') + +# window horizontal bounds +left_limit = Variable.new(name: 'left') +right_limit = Variable.new(name: 'width') +left_limit.value = 0 +right_limit.value = 0 # CJC: ruby needs a value. + +# left_limit stay is fixed (Required), right can be resized +solver.add_stay(left_limit) +solver.add_stay(right_limit, Strength::WeakStrength) + +# The two buttons are the same width +solver.add_constraint(b1.width.cn_equal b2.width) + +# Button1 starts 50 from the left margin. +solver.add_constraint(b1.left.cn_equal left_limit + 50) + +# Button2 ends 50 from the right margin (???) +solver.add_constraint((left_limit + right_limit).cn_equal b2.left + b2.width + 50) + +# Button2 starts at least 100 from the end of Button1. This is the +# "elastic" constraint in the system that will absorb extra space +# in the layout. +solver.add_constraint(b2.left.cn_equal b1.left + b1.width + 100) + +# Button1 has a minimum width of 87 +solver.add_constraint(b1.width.cn_geq 87) + +# Button1's preferred width is 87 +solver.add_constraint(b1.width.cn_equal 87, Strength::StrongStrength) + +# Button2's minimum width is 113 +solver.add_constraint(b2.width.cn_geq 113) + +# Button2's preferred width is 113 +solver.add_constraint(b2.width.cn_equal 113, Strength::StrongStrength) + +puts "b1: #{b1.inspect}" +puts "b2: #{b2.inspect}" +puts "rl.value #{right_limit.value}" + +puts "RESIZING Window to 500" +right_limit.value = 500 +# example fails - args are wrong for a constraint +#right_limit_stay = solver.add_constraint(right_limit, Strength::RequiredStrength) +right_limit_stay = solver.add_stay(right_limit, Strength::RequiredStrength) +puts "b1: #{b1.inspect}" +puts "b2: #{b2.inspect}" +puts "rl.value #{right_limit.value}" + + diff --git a/lib/cassowary/constraint.rb b/lib/cassowary/constraint.rb index 7fe82134..df6b9f02 100644 --- a/lib/cassowary/constraint.rb +++ b/lib/cassowary/constraint.rb @@ -5,22 +5,6 @@ module Cassowary # Constraints are the restrictions on linear programming; an equality or # inequality between two expressions. ########################################################################### -=begin -class AbstractConstraint(object): - def __init__(self, strength, weight=1.0): - self.strength = strength - self.weight = weight - self.is_edit_constraint = False - self.is_inequality = False - self.is_stay_constraint = False - - @property - def is_required(self): - return self.strength == REQUIRED - - def __repr__(self): - return '%s:{%s}(%s)' % (repr_strength(self.strength), self.weight, self.expression) -=end class AbstractConstraint attr_accessor :strength, :weight, :s_edit_constraint, :is_inequality, :is_stay_contraint @@ -60,29 +44,6 @@ def initialize(variable, strength=STRONG, weight=1.0) end end -=begin -class EditConstraint(AbstractConstraint): - def __init__(self, variable, strength=STRONG, weight=1.0): - super(EditConstraint, self).__init__(strength, weight) - self.variable = variable - self.expression = Expression(variable, -1.0, variable.value) - self.is_edit_constraint = True - - def __repr__(self): - return 'edit:%s' % super(EditConstraint, self).__repr__() - - -class StayConstraint(AbstractConstraint): - def __init__(self, variable, strength=STRONG, weight=1.0): - super(StayConstraint, self).__init__(strength, weight) - self.variable = variable - self.expression = Expression(variable, -1.0, variable.value) - self.is_stay_constraint=True - - def __repr__(self): - return 'stay:%s' % super(StayConstraint, self).__repr__() - -=end class Constraint < AbstractConstraint LEQ = -1 diff --git a/lib/cassowary/simplex_solver.rb b/lib/cassowary/simplex_solver.rb index 26b9a1b9..02f5b4bd 100644 --- a/lib/cassowary/simplex_solver.rb +++ b/lib/cassowary/simplex_solver.rb @@ -46,41 +46,7 @@ def initialize() @rows[self.objective] = Expression() @edit_variable_stack = [0] end -=begin - def __repr__(self): - parts = [] - parts.append('stay_error_vars: %s' % self.stay_error_vars) - parts.append('edit_var_map: %s' % self.edit_var_map) - return super(SimplexSolver, self).__repr__() + '\n' + '\n'.join(parts) - - - def add_constraint(self, cn, strength=None, weight=None): - if strength or weight: - cn = cn.clone() - if strength: - cn.strength = strength - if weight: - cn.weight = weight - - # print('add_constraint', cn) - expr, eplus, eminus, prev_edit_constant = self.new_expression(cn) - - if not self.try_adding_directly(expr): - self.add_with_artificial_variable(expr) - - self.needs_solving = True - - if cn.is_edit_constraint: - i = len(self.edit_var_map) - - self.edit_var_map[cn.variable] = EditInfo(cn, eplus, eminus, prev_edit_constant, i) - if self.auto_solve: - self.optimize(self.objective) - self.set_external_variables() - - return cn -=end def add_constraint(cn, strength=nil, weight=nil) if strength || weight cn = cn.clone @@ -105,23 +71,7 @@ def add_constraint(cn, strength=nil, weight=nil) self.set_external_variables() end end -=begin - def add_edit_var(self, v, strength=STRONG): - # print("add_edit_var", v, strength) - return self.add_constraint(EditConstraint(v, strength)) - def remove_edit_var(self, v): - self.remove_constraint(self.edit_var_map[v].constraint) - - def edit(self): - return SolverEditContext(self) - - def resolve(self): - self.dual_optimize() - self.set_external_variables() - self.infeasible_rows.clear() - self.reset_stay_constants() -=end def add_edit_var(v, strength=STRONG) rreturn self.add_constraint(EditConstraint.new(v, strength)) end @@ -141,12 +91,10 @@ def resolve() self.reset_stay_constants() end -=begin - ####################################################################### # Internals ####################################################################### - +=begin def new_expression(self, cn): # print("* new_expression", cn) # print("cn.is_inequality == ", cn.is_inequality) @@ -221,24 +169,6 @@ def new_expression(self, cn): expr.multiply(-1.0) return expr, eplus, eminus, prev_edit_constant =end - -=begin - def begin_edit(self): - assert len(self.edit_var_map) > 0 - self.infeasible_rows.clear() - self.reset_stay_constants() - self.edit_variable_stack.append(len(self.edit_var_map)) - - def end_edit(self): - assert len(self.edit_var_map) > 0 - self.resolve() - self.edit_variable_stack.pop() - self.remove_edit_vars_to(self.edit_variable_stack[-1]) - - def remove_all_edit_vars(self): - self.remove_edit_vars_to(0) -=end - def begin_edit() #assert len(self.edit_var_map) > 0 self.infeasible_rows.clear() @@ -257,25 +187,7 @@ def remove_all_edit_vars() self.remove_edit_vars_to(0) end -=begin - def remove_edit_vars_to(self, n): - try: - removals = [] - for v, cei in self.edit_var_map.items(): - if cei.index >= n: - removals.append(v) - - for v in removals: - self.remove_edit_var(v) - assert len(self.edit_var_map) == n - - except ConstraintNotFound: - raise InternalError('Constraint not found during internal removal') - - def add_stay(self, v, strength=WEAK, weight=1.0): - return self.add_constraint(StayConstraint(v, strength, weight)) -=end def remove_edit_vars_to(n) begin @@ -287,6 +199,7 @@ def remove_edit_vars_to(n) def add_stay(v, strength=WEAK, weight=1.0) return self.add_constraint(StayConstraint.new(v, strength, weight)) end + =begin def remove_constraint(self, cn): # print("removeConstraint", cn) @@ -409,43 +322,8 @@ def remove_constraint(self, cn): self.optimize(self.objective) self.set_external_variables() =end -=begin - def resolve_array(self, new_edit_constants): - for v, cei in self.edit_var_map.items(): - self.suggest_value(v, new_edit_constants[cei.index]) - - self.resolve() - - def suggest_value(self, v, x): - cei = self.edit_var_map.get(v) - if not cei: - raise InternalError("suggestValue for variable %s, but var is not an edit variable" % v) - # print(cei) - delta = x - cei.prev_edit_constant - cei.prev_edit_constant = x - self.delta_edit_constant(delta, cei.edit_plus, cei.edit_minus) - - def solve(self): - if self.needs_solving: - self.optimize(self.objective) - self.set_external_variables() - - def set_edited_value(self, v, n): - if v not in self.columns or v not in self.rows: - v.value = n - - if not approx_equal(n, v.value): - self.add_edit_var(v) - self.begin_edit() - self.suggest_value(v, n) - self.end_edit() - - def add_var(self, v): - if v not in self.columns or v not in self.rows: - self.add_stay(v) -=end def resolve_array(new_edit_constants) @edit_var_map.each_pair do |v, cei| self.suggest_value(v, new_edit_constants[cei.index]) # TODO class/type of cei is? diff --git a/lib/cassowary/tableau.rb b/lib/cassowary/tableau.rb index ada336b9..670d77a0 100644 --- a/lib/cassowary/tableau.rb +++ b/lib/cassowary/tableau.rb @@ -19,17 +19,7 @@ def initialize # Set of Variables. @external_parametric_vars = Set.new() end -=begin - def __repr__(self): - parts = [] - parts.append('Tableau info:') - parts.append('Rows: %s (= %s constraints)' % (len(self.rows), len(self.rows) - 1)) - parts.append('Columns: %s' % len(self.columns)) - parts.append('Infeasible rows: %s' % len(self.infeasible_rows)) - parts.append('External basic variables: %s' % len(self.external_rows)) - parts.append('External parametric variables: %s' % len(self.external_parametric_vars)) - return '\n'.join(parts) -=end + def note_removed_variable(var, subject) if subject @columns[var].delete(subject) @@ -43,20 +33,6 @@ def note_added_variable(var, subject) @columns[var] || @columns[var] = Set.new(subject) end end - -=begin - def add_row(self, var, expr): - # print('add_row', var, expr) - for clv in expr.terms: - self.columns.setdefault(clv, set()).add(var) - if clv.is_external: - self.external_parametric_vars.add(clv) - - if var.is_external: - self.external_rows.add(var) - - # print(self) -=end def add_row(var, expr) @rows[var] = expr @@ -70,26 +46,7 @@ def add_row(var, expr) @external_rows.add(var) end end -=begin - def remove_column(self, var): - rows = self.columns.pop(var, None) - if rows: - for clv in rows: - expr = self.rows[clv] - expr.remove_variable(var) - - if var.is_external: - try: - self.external_rows.remove(var) - except KeyError: - pass - - try: - self.external_parametric_vars.remove(var) - except KeyError: - pass -=end def remove_column(var) rows = @columns.delete(var) if rows @@ -103,29 +60,7 @@ def remove_column(var) @external_parametric_vars.delete(var) end end -=begin - def remove_row(self, var): - # print("remove_row", var) - expr = self.rows.pop(var) - for clv in expr.terms.keys(): - varset = self.columns[clv] - if varset: - # print("removing from varset", var) - varset.remove(var) - - try: - self.infeasible_rows.remove(var) - except KeyError: - pass - if var.is_external: - try: - self.external_rows.remove(var) - except KeyError: - pass - # print("remove_row returning", expr) - return expr -=end def remove_row(var) expr = @rows.delete(var) expr.terms.each_key do |clv| @@ -140,24 +75,7 @@ def remove_row(var) end return expr end -=begin - def substitute_out(self, oldVar, expr): - varset = self.columns[oldVar] - for v in varset: - row = self.rows[v] - row.substitute_out(oldVar, expr, v, self) - if v.is_restricted and row.constant < 0.0: - self.infeasible_rows.add(v) - - if oldVar.is_external: - self.external_rows.add(oldVar) - try: - self.external_parametric_vars.remove(oldVar) - except KeyError: - pass - del self.columns[oldVar] -=end def substitute_out(oldvar, expr) varset = @columns[oldvar] varset.each_value do |v| # TODO: correct?