cassowary-ruby 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ OWZkYjdhMWMwYjdkZWM3OWE0NGM2NDVhODFlMjdkZDQ0YzkxN2FjMg==
5
+ data.tar.gz: !binary |-
6
+ NmU1YWEyOTZkNGIyNTM2YmFiZjk3ZjVjMDczNjMyMGJmYWVkNDMxMg==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ YTFhOTI5ZjZjMjAzMGMwN2JkMzJlYWYxZGMyZjg2NjllMWU2NTA1ZmExN2Y5
10
+ NDA1Nzk5ZWI4MDcxYjBjOWY1ZDRhYjNkY2EyNmQ3YWI4ODE1ZjdiMDkyMGFi
11
+ YWRlZjZmN2UwNDQ3NmZmMjNkNTMyNjRjMzlhMmM5OTY3N2FhOTU=
12
+ data.tar.gz: !binary |-
13
+ N2NlZDE5NGYwMWRiZDgwNjViYjFmOWJlZDJmYThiMmI5ZGZhZjNlZDYyNjRk
14
+ N2YxNWEzODliYjZlMzE0MTliZjY3OTk1MjRkOWQ5OGJhMDNkZDEyMjRhODc4
15
+ ZTQ1ZjcyOGIzZDVjMmJlOGRlYzlkZDY0Y2I1MWJkYWJkYjA3ZTE=
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Tim Felgentreff
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,18 @@
1
+ ## Cassowary
2
+
3
+ Cassowary is an incremental constraint solving toolkit that
4
+ efficiently solves systems of linear equalities and
5
+ inequalities. Constraints may be either requirements or
6
+ preferences. Client code specifies the constraints to be maintained,
7
+ and the solver updates the constrained variables to have values that
8
+ satisfy the constraints.
9
+
10
+ This is a Ruby port of the Smalltalk version of Cassowary. The
11
+ original distribution can be found
12
+ [here](http://www.cs.washington.edu/research/constraints/cassowary/).
13
+
14
+ A technical report is included in the original distribution that
15
+ describes the algorithm, interface, and implementation of the
16
+ Cassowary solver. Additionally, the distribution contains toy sample
17
+ applications written in Smalltalk, C++, Java, and Python, and a more
18
+ complex example Java applet, the "Constraint Drawing Application".
data/lib/cassowary.rb ADDED
@@ -0,0 +1,25 @@
1
+ # Copyright (C) 2012 by Tim Felgentreff
2
+
3
+ module Cassowary
4
+ VERSION = "0.5.0"
5
+
6
+ class Error < StandardError; end
7
+ class InternalError < Error; end
8
+ class NonLinearResult < Error; end
9
+ class NotEnoughStays < Error; end
10
+ class RequiredFailure < Error; end
11
+ class TooDifficult < Error; end
12
+ end
13
+
14
+ require "utils/equalities"
15
+
16
+ require "variables"
17
+ require "constraint"
18
+ require "symbolic_weight"
19
+ require "strength"
20
+ require "linear_expression"
21
+ require "simplex_solver"
22
+
23
+ require "ext/object"
24
+ require "ext/float"
25
+ require "ext/numeric"
@@ -0,0 +1,40 @@
1
+ # Copyright (C) 2012 by Tim Felgentreff
2
+
3
+ module Cassowary
4
+ class EditOrStayConstraint < Constraint
5
+ attr_accessor :variable
6
+
7
+ def initialize(hash = {})
8
+ hash = {:weight => 1.0}.merge(hash)
9
+ self.variable = hash[:variable]
10
+ self.strength = hash[:strength]
11
+ self.weight = hash[:weight]
12
+ end
13
+
14
+ def expression
15
+ e = LinearExpression.new
16
+ e.constant = variable.value
17
+ e.terms[variable] = -1.0
18
+ e
19
+ end
20
+ end
21
+
22
+ class EditConstraint < EditOrStayConstraint
23
+ attr_accessor :value
24
+
25
+ def initialize(hash = {})
26
+ super
27
+ self.value = hash[:value]
28
+ end
29
+
30
+ def edit_constraint?
31
+ true
32
+ end
33
+ end
34
+
35
+ class StayConstraint < EditOrStayConstraint
36
+ def stay_constraint?
37
+ true
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,23 @@
1
+ # Copyright (C) 2012 by Tim Felgentreff
2
+
3
+ module Cassowary
4
+ class LinearConstraint < Constraint
5
+ attr_accessor :expression
6
+ end
7
+
8
+ class LinearEquation < LinearConstraint
9
+ def inspect
10
+ "#{strength.inspect}(#{expression.inspect}=0)"
11
+ end
12
+ end
13
+
14
+ class LinearInequality < LinearConstraint
15
+ def inequality?
16
+ true
17
+ end
18
+
19
+ def inspect
20
+ "#{strength.inspect}(#{expression.inspect}>=0)"
21
+ end
22
+ end
23
+ end
data/lib/constraint.rb ADDED
@@ -0,0 +1,30 @@
1
+ # Copyright (C) 2012 by Tim Felgentreff
2
+
3
+ module Cassowary
4
+ class Constraint
5
+ attr_accessor :strength, :weight
6
+
7
+ def expression
8
+ raise NotImplementedError, "my subclass should have implemented #expression"
9
+ end
10
+
11
+ def edit_constraint?
12
+ false
13
+ end
14
+
15
+ def inequality?
16
+ false
17
+ end
18
+
19
+ def required?
20
+ strength.required?
21
+ end
22
+
23
+ def stay_constraint?
24
+ false
25
+ end
26
+ end
27
+ end
28
+
29
+ require "constraint/edit_or_stay_constraint"
30
+ require "constraint/linear_constraint"
data/lib/ext/float.rb ADDED
@@ -0,0 +1,24 @@
1
+ # Copyright (C) 2012 by Tim Felgentreff
2
+
3
+ class Float
4
+ def cl_approx(float)
5
+ # Answer true if I am approximately equal to the argument
6
+ epsilon = Cassowary::SimplexSolver::Epsilon
7
+ if self == 0.0
8
+ float.abs < epsilon
9
+ elsif float == 0.0
10
+ abs < epsilon
11
+ else
12
+ (self - float).abs < (abs * epsilon)
13
+ end
14
+ end
15
+
16
+ def cl_approx_zero
17
+ cl_approx 0.0
18
+ end
19
+
20
+ def definitely_negative
21
+ # return true if I am definitely negative (i.e. smaller than negative epsilon)"
22
+ self < (0.0 - Cassowary::SimplexSolver::Epsilon)
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ # Copyright (C) 2012 by Tim Felgentreff
2
+
3
+ class Numeric
4
+ include Cassowary::Equalities
5
+
6
+ def as_linear_expression
7
+ expr = Cassowary::LinearExpression.new
8
+ expr.constant = self.to_f
9
+ expr
10
+ end
11
+ end
data/lib/ext/object.rb ADDED
@@ -0,0 +1,11 @@
1
+ # Copyright (C) 2012 by Tim Felgentreff
2
+
3
+ class Object
4
+ def cl_approx(x)
5
+ self == x
6
+ end
7
+
8
+ def symbolic_weight?
9
+ false
10
+ end
11
+ end
@@ -0,0 +1,163 @@
1
+ # Copyright (C) 2012 by Tim Felgentreff
2
+
3
+ module Cassowary
4
+ class LinearExpression
5
+ include Equalities
6
+
7
+ attr_accessor :constant, :terms
8
+
9
+ def self.new_with_symbolic_weight
10
+ result = self.new
11
+ result.constant = SymbolicWeight::Zero
12
+ result
13
+ end
14
+
15
+ def initialize
16
+ self.constant = 0.0
17
+ self.terms = {}
18
+ end
19
+
20
+ def any_variable
21
+ if terms.any?
22
+ terms.keys.first
23
+ else
24
+ raise InternalError, "expression is constant"
25
+ end
26
+ end
27
+
28
+ def as_linear_expression
29
+ self
30
+ end
31
+
32
+ def coefficient_for(variable)
33
+ terms[variable] || 0.0
34
+ end
35
+
36
+ def constant?
37
+ terms.empty?
38
+ end
39
+
40
+ def each_variable_and_coefficient(&block)
41
+ terms.each_pair(&block)
42
+ end
43
+
44
+ def add_variable(variable, coefficient, subject = nil, solver = nil)
45
+ if terms.has_key? variable
46
+ new_coeff = coefficient + terms[variable]
47
+ if new_coeff.cl_approx_zero
48
+ terms.delete variable
49
+ solver.note_removed_variable(variable, subject) if solver
50
+ else
51
+ terms[variable] = new_coeff
52
+ end
53
+ else
54
+ terms[variable] = coefficient
55
+ solver.note_added_variable(variable, subject) if solver
56
+ end
57
+ end
58
+
59
+ def add_expression(expr, times, subject = nil, solver = nil)
60
+ increment_constant(times * expr.constant)
61
+ expr.each_variable_and_coefficient do |v, c|
62
+ add_variable(v, times * c, subject, solver)
63
+ end
64
+ end
65
+
66
+ def new_subject(subject)
67
+ nreciprocal = -(1.0 / terms.delete(subject))
68
+ self.constant *= nreciprocal
69
+ terms.each_pair do |v, c|
70
+ terms[v] = c * nreciprocal
71
+ end
72
+ end
73
+
74
+ def change_subject(old, new)
75
+ reciprocal = 1.0 / terms.delete(new)
76
+ nreciprocal = -reciprocal
77
+ self.constant *= nreciprocal
78
+ terms.each_pair do |v, c|
79
+ terms[v] = c * nreciprocal
80
+ end
81
+ terms[old] = reciprocal
82
+ end
83
+
84
+ def increment_constant(num)
85
+ self.constant += num
86
+ end
87
+
88
+ def substitute_variable(var, expr, subject, solver)
89
+ multiplier = terms.delete(var)
90
+ increment_constant(multiplier * expr.constant)
91
+ expr.each_variable_and_coefficient do |v, c|
92
+ if old_coeff = terms[v]
93
+ new_coeff = old_coeff + (multiplier * c)
94
+ if new_coeff.cl_approx_zero
95
+ terms.delete v
96
+ solver.note_removed_variable v, subject
97
+ else
98
+ terms[v] = new_coeff
99
+ end
100
+ else
101
+ terms[v] = multiplier * c
102
+ solver.note_added_variable v, subject
103
+ end
104
+ end
105
+ end
106
+
107
+ def *(x)
108
+ return x * constant if constant?
109
+
110
+ n = if x.is_a? Numeric
111
+ x.to_f
112
+ else
113
+ expr = x.as_linear_expression
114
+ raise NonLinearResult unless expr.constant?
115
+ expr.constant
116
+ end
117
+ result = LinearExpression.new
118
+ result.constant = n * constant
119
+ terms.each_pair do |v, c|
120
+ result.terms[v] = n * c
121
+ end
122
+ result
123
+ end
124
+
125
+ def /(x)
126
+ expr = x.as_linear_expression
127
+ raise NonLinearResult unless expr.constant?
128
+ self * (1.0 / expr.constant)
129
+ end
130
+
131
+ def +(x)
132
+ expr = x.as_linear_expression
133
+ result = LinearExpression.new
134
+ result.constant = constant + expr.constant
135
+ terms.each_pair do |v, c|
136
+ result.terms[v] = c
137
+ end
138
+ expr.each_variable_and_coefficient do |v, c|
139
+ result.add_variable(v, c)
140
+ end
141
+ result
142
+ end
143
+
144
+ def -(x)
145
+ expr = x.as_linear_expression
146
+ result = LinearExpression.new
147
+ result.constant = constant - expr.constant
148
+ terms.each_pair do |v, c|
149
+ result.terms[v] = c
150
+ end
151
+ expr.each_variable_and_coefficient do |v, c|
152
+ result.add_variable(v, -c)
153
+ end
154
+ result
155
+ end
156
+
157
+ def inspect
158
+ terms.keys.inject(constant.inspect) do |str, v|
159
+ "#{str}+#{terms[v].inspect}*#{v.inspect}"
160
+ end
161
+ end
162
+ end
163
+ end