1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
//! This crate contains an implementation of the Cassowary constraint solving algorithm, based upon
//! the work by G.J. Badros et al. in 2001. This algorithm is designed primarily for use
//! constraining elements in user interfaces. Constraints are linear combinations of the problem
//! variables. The notable features of Cassowary that make it ideal for user interfaces are that it
//! is incremental (i.e. you can add and remove constraints at runtime and it will perform the
//! minimum work to update the result) and that the constraints can be violated if necessary, with
//! the order in which they are violated specified by setting a "strength" for each constraint. This
//! allows the solution to gracefully degrade, which is useful for when a user interface needs to
//! compromise on its constraints in order to still be able to display something.
//!
//! ## Constraint syntax
//!
//! This crate aims to provide syntax for describing linear constraints as naturally as possible,
//! within the limitations of Rust's type system. Generally you can write constraints as you would
//! naturally, however the operator symbol (for greater-than, less-than, equals) is replaced with an
//! instance of the `WeightedRelation` enum wrapped in "pipe brackets".
//!
//! For example, for the constraint `(a + b) * 2 + c >= d + 1` with strength `s`, the code to use is
//!
//! ```ignore
//! (a + b) * 2.0 + c |GE(s)| d + 1.0
//! ```
//!
//! # A simple example
//!
//! Imagine a layout consisting of two elements laid out horizontally. For small window widths the
//! elements should compress to fit, but if there is enough space they should display at their
//! preferred widths. The first element will align to the left, and the second to the right. For
//! this example we will ignore vertical layout.
//!
//! First we need to include the relevant parts of `cassowary`:
//!
//! ```
//! use kasuari::WeightedRelation::*;
//! use kasuari::{Solver, Variable};
//! ```
//!
//! And we'll construct some conveniences for pretty printing (which should hopefully be
//! self-explanatory):
//!
//! ```ignore
//! use hashbrown::HashMap;
//! let mut names = HashMap::new();
//! fn print_changes(names: &HashMap<Variable, &'static str>, changes: &[(Variable, f64)]) {
//! println!("Changes:");
//! for &(ref var, ref val) in changes {
//! println!("{}: {}", names[var], val);
//! }
//! }
//! ```
//!
//! Let's define the variables required - the left and right edges of the elements, and the width of
//! the window.
//!
//! ```ignore
//! let window_width = Variable::new();
//! names.insert(window_width, "window_width");
//!
//! struct Element {
//! left: Variable,
//! right: Variable
//! }
//! let box1 = Element {
//! left: Variable::new(),
//! right: Variable::new()
//! };
//! names.insert(box1.left, "box1.left");
//! names.insert(box1.right, "box1.right");
//!
//! let box2 = Element {
//! left: Variable::new(),
//! right: Variable::new()
//! };
//! names.insert(box2.left, "box2.left");
//! names.insert(box2.right, "box2.right");
//! ```
//!
//! Now to set up the solver and constraints.
//!
//! ```ignore
//! let mut solver = Solver::new();
//! solver.add_constraints(&[
//! window_width |GE(REQUIRED)| 0.0, // positive window width
//! box1.left |EQ(REQUIRED)| 0.0, // left align
//! box2.right |EQ(REQUIRED)| window_width, // right align
//! box2.left |GE(REQUIRED)| box1.right, // no overlap
//! // positive widths
//! box1.left |LE(REQUIRED)| box1.right,
//! box2.left |LE(REQUIRED)| box2.right,
//! // preferred widths:
//! box1.right - box1.left |EQ(WEAK)| 50.0,
//! box2.right - box2.left |EQ(WEAK)| 100.0
//! ])?;
//! # Ok::<(), kasuari::InternalSolverError>(())
//! ```
//!
//! The window width is currently free to take any positive value. Let's constrain it to a
//! particular value. Since for this example we will repeatedly change the window width, it is most
//! efficient to use an "edit variable", instead of repeatedly removing and adding constraints (note
//! that for efficiency reasons we cannot edit a normal constraint that has been added to the
//! solver).
//!
//! ```ignore
//! solver.add_edit_variable(window_width, STRONG).unwrap();
//! solver.suggest_value(window_width, 300.0).unwrap();
//! ```
//!
//! This value of 300 is enough to fit both boxes in with room to spare, so let's check that this is
//! the case. We can fetch a list of changes to the values of variables in the solver. Using the
//! pretty printer defined earlier we can see what values our variables now hold.
//!
//! ```ignore
//! print_changes(&names, solver.fetch_changes());
//! ```
//!
//! This should print (in a possibly different order):
//!
//! ```ignore
//! Changes:
//! window_width: 300
//! box1.right: 50
//! box2.left: 200
//! box2.right: 300
//! ```
//!
//! Note that the value of `box1.left` is not mentioned. This is because `solver.fetch_changes` only
//! lists *changes* to variables, and since each variable starts in the solver with a value of zero,
//! any values that have not changed from zero will not be reported.
//!
//! Now let's try compressing the window so that the boxes can't take up their preferred widths.
//!
//! ```ignore
//! solver.suggest_value(window_width, 75.0);
//! print_changes(&names, solver.fetch_changes);
//! ```
//!
//! Now the solver can't satisfy all of the constraints. It will pick at least one of the weakest
//! constraints to violate. In this case it will be one or both of the preferred widths. For
//! efficiency reasons this is picked nondeterministically, so there are two possible results. This
//! could be
//!
//! ```ignore
//! Changes:
//! window_width: 75
//! box1.right: 0
//! box2.left: 0
//! box2.right: 75
//! ```
//!
//! or
//!
//! ```ignore
//! Changes:
//! window_width: 75
//! box2.left: 50
//! box2.right: 75
//! ```
//!
//! Due to the nature of the algorithm, "in-between" solutions, although just as valid, are not
//! picked.
//!
//! In a user interface this is not likely a result we would prefer. The solution is to add another
//! constraint to control the behaviour when the preferred widths cannot both be satisfied. In this
//! example we are going to constrain the boxes to try to maintain a ratio between their widths.
//!
//! ```
//! # use kasuari::{ Solver, Variable, Strength };
//! # use kasuari::WeightedRelation::*;
//! #
//! # use hashbrown::HashMap;
//! # let mut names = HashMap::new();
//! # fn print_changes(names: &HashMap<Variable, &'static str>, changes: &[(Variable, f64)]) {
//! # println!("Changes:");
//! # for &(ref var, ref val) in changes {
//! # println!("{}: {}", names[var], val);
//! # }
//! # }
//! #
//! # let window_width = Variable::new();
//! # names.insert(window_width, "window_width");
//! # struct Element {
//! # left: Variable,
//! # right: Variable
//! # }
//! # let box1 = Element {
//! # left: Variable::new(),
//! # right: Variable::new()
//! # };
//! # names.insert(box1.left, "box1.left");
//! # names.insert(box1.right, "box1.right");
//! # let box2 = Element {
//! # left: Variable::new(),
//! # right: Variable::new()
//! # };
//! # names.insert(box2.left, "box2.left");
//! # names.insert(box2.right, "box2.right");
//! # let mut solver = Solver::new();
//! # solver.add_constraints([
//! # window_width |GE(Strength::REQUIRED)| 0.0, // positive window width
//! # box1.left |EQ(Strength::REQUIRED)| 0.0, // left align
//! # box2.right |EQ(Strength::REQUIRED)| window_width, // right align
//! # box2.left |GE(Strength::REQUIRED)| box1.right, // no overlap
//! # // positive widths
//! # box1.left |LE(Strength::REQUIRED)| box1.right,
//! # box2.left |LE(Strength::REQUIRED)| box2.right,
//! # // preferred widths:
//! # box1.right - box1.left |EQ(Strength::WEAK)| 50.0,
//! # box2.right - box2.left |EQ(Strength::WEAK)| 100.0]).unwrap();
//! # solver.add_edit_variable(window_width, Strength::STRONG).unwrap();
//! # solver.suggest_value(window_width, 300.0).unwrap();
//! # print_changes(&names, solver.fetch_changes());
//! # solver.suggest_value(window_width, 75.0);
//! # print_changes(&names, solver.fetch_changes());
//! solver.add_constraint(
//! (box1.right - box1.left) / 50.0 |EQ(Strength::MEDIUM)| (box2.right - box2.left) / 100.0
//! ).unwrap();
//! print_changes(&names, solver.fetch_changes());
//! ```
//!
//! Now the result gives values that maintain the ratio between the sizes of the two boxes:
//!
//! ```ignore
//! Changes:
//! box1.right: 25
//! box2.left: 25
//! ```
//!
//! This example may have appeared somewhat contrived, but hopefully it shows the power of the
//! cassowary algorithm for laying out user interfaces.
//!
//! One thing that this example exposes is that this crate is a rather low level library. It does
//! not have any inherent knowledge of user interfaces, directions or boxes. Thus for use in a user
//! interface this crate should ideally be wrapped by a higher level API, which is outside the scope
//! of this crate.
extern crate alloc;
pub use ;
pub use ;
pub use Expression;
pub use ;
pub use ;
pub use Strength;
pub use Term;
pub use Variable;