Commit ae835f08 authored by Chinmay Garde's avatar Chinmay Garde Committed by GitHub

Document the public members of the Cassowary package. (#4744)

parent 9850fe18
......@@ -6,6 +6,8 @@ import 'expression.dart';
import 'equation_member.dart';
import 'term.dart';
/// A member of a [Constraint] [Expression] that represent a constant at the
/// time the [Constraint] is added to the solver.
class ConstantMember extends EquationMember {
/// Creates a [ConstantMember] object.
///
......
......@@ -5,21 +5,52 @@
import 'priority.dart';
import 'expression.dart';
/// Relationships between [Constraint] expressions.
///
/// A [Constraint] is created by specifying a relationship between two
/// expressions. The [Solver] tries to satisfy this relationship after the
/// [Constraint] has been added to it at a set priority.
enum Relation {
/// The relationship between the left and right hand sides of the expression
/// is `==`, (lhs == rhs).
equalTo,
/// The relationship between the left and right hand sides of the expression
/// is `<=`, (lhs <= rhs).
lessThanOrEqualTo,
/// The relationship between the left and right hand sides of the expression
/// is `>=`, (lhs => rhs).
greaterThanOrEqualTo,
}
/// A relationship between two expressions (represented by [Expression]) that
/// the [Solver] tries to hold true. In case of ambiguities, the [Solver] will
/// use priorities to determine [Constraint] precedence. Once a [Constraint] is
/// added to the [Solver], this [Priority] cannot be changed.
class Constraint {
/// Creates a new [Constraint] by specifying a single [Expression]. This
/// assumes that the right hand side [Expression] is the constant zero.
/// (`<expression> <relation> <0>`)
Constraint(this.expression, this.relation);
/// The [Relation] between a [Constraint] [Expression] and zero.
final Relation relation;
/// The [Constraint] [Expression]. The [Expression] on the right hand side of
/// constraint must be zero. If the [Expression] on the right is not zero,
/// it must be negated from the left hand [Expression] before a [Constraint]
/// can be created.
final Expression expression;
/// The [Constraint] [Priority]. The [Priority] can only be modified when the
/// [Constraint] is being created. Once it is added to the solver,
/// modifications to the [Constraint] [Priority] will have no effect on the
/// how the solver evaluates the constraint.
double priority = Priority.required;
/// The operator `|` is overloaded as a convenience so that constraint
/// priorities can be specifed along with the [Constraint] expression.
///
/// For example: `ax + by + cx <= 0 | Priority.weak`. See [Priority].
Constraint operator |(double p) => this..priority = p;
@override
......
......@@ -5,25 +5,95 @@
import 'expression.dart';
import 'constraint.dart';
/// Base class for the various parts of cassowary equations.
/// A member that can be used to construct an [Expression] that may be
/// used to create a constraint. This is to facilitate the easy creation of
/// constraints. The use of the operator overloads is completely optional and
/// is only meant as a convenience. The [Constraint] expressions can be created
/// by manually creating instance of [Constraint] variables, then terms and
/// combining those to create expression.
abstract class EquationMember {
/// The representation of this member after it is hoisted to be an
/// expression.
Expression asExpression();
/// Returns if this member is a constant. Constant members can be combined
/// more easily without making the expression non-linear. This makes them
/// easier to use with multiplication and division operators. Constant
/// expression that have zero value may also eliminate other expressions from
/// the solver when used with the multiplication operator.
bool get isConstant;
/// The current constant value of this member. After a [Solver] flush, this is
/// value read by entities outside the [Solver].
double get value;
/// Creates a [Constraint] by using this member as the left hand side
/// expression and the argument as the right hand side [Expression] of a
/// [Constraint] with a [Relation.greaterThanOrEqualTo] relationship between
/// the two.
///
/// For example: `right - left >= cm(200.0)` would read, "the width of the
/// object is at least 200."
Constraint operator >=(EquationMember m) => asExpression() >= m;
/// Creates a [Constraint] by using this member as the left hand side
/// expression and the argument as the right hand side [Expression] of a
/// [Constraint] with a [Relation.lessThanOrEqualTo] relationship between the
/// two.
///
/// For example: `rightEdgeOfA <= leftEdgeOfB` would read, "the entities A and
/// B are stacked left to right."
Constraint operator <=(EquationMember m) => asExpression() <= m;
/// Creates a [Constraint] by using this member as the left hand side
/// expression and the argument as the right hand side [Expression] of a
/// [Constraint] with a [Relation.equalTo] relationship between the two.
///
/// For example: `topEdgeOfBoxA + cm(10.0) == topEdgeOfBoxB` woud read,
/// "the entities A and B have a padding on top of 10."
Constraint equals(EquationMember m) => asExpression().equals(m);
/// Creates a [Expression] by adding this member with the argument. Both
/// members may need to be hoisted to expressions themselves before this can
/// occur.
///
/// For example: `(left + right) / cm(2.0)` can be used as an [Expression]
/// equivalent of the `midPointX` property.
Expression operator +(EquationMember m) => asExpression() + m;
/// Creates a [Expression] by subtracting the argument from this member. Both
/// members may need to be hoisted to expressions themselves before this can
/// occur.
///
/// For example: `right - left` can be used as an [Expression]
/// equivalent of the `width` property.
Expression operator -(EquationMember m) => asExpression() - m;
/// Creates a [Expression] by multiplying this member with the argument. Both
/// members may need to be hoisted to expressions themselves before this can
/// occur.
///
/// Warning: This operation may throw a [ParserException] if the resulting
/// expression is no longer linear. This is because a non-linear [Expression]
/// may not be used to create a constraint. At least one of the [Expression]
/// members must evaluate to a constant.
///
/// For example: `((left + right) >= (cm(2.0) * mid)` declares a `midpoint`
/// constraint. Notice that at least one the members of the right hand
/// `Expression` is a constant.
Expression operator *(EquationMember m) => asExpression() * m;
/// Creates a [Expression] by dividing this member by the argument. Both
/// members may need to be hoisted to expressions themselves before this can
/// occur.
///
/// Warning: This operation may throw a [ParserException] if the resulting
/// expression is no longer linear. This is because a non-linear [Expression]
/// may not be used to create a constraint. The divisor (i.e. the argument)
/// must evaluate to a constant.
///
/// For example: `((left + right) / cm(2.0) >= mid` declares a `midpoint`
/// constraint. Notice that the divisor of the left hand [Expression] is a
/// constant.
Expression operator /(EquationMember m) => asExpression() / m;
}
......@@ -15,15 +15,24 @@ class _Multiplication {
final double multiplicand;
}
/// The representation of a linear [Expression] that can be used to create a
/// constraint.
class Expression extends EquationMember {
/// Creates a new linear [Expression] using the given terms and constant.
Expression(this.terms, this.constant);
/// Creates a new linear [Expression] by copying the terms and constant of
/// another expression.
Expression.fromExpression(Expression expr)
: this.terms = new List<Term>.from(expr.terms),
this.constant = expr.constant;
/// The list of terms in this linear expression. Terms in a an [Expression]
/// must have only one [Variable] (indeterminate) and a degree of 1.
final List<Term> terms;
/// The constant portion of this linear expression. This is just another
/// [Term] with no [Variable].
final double constant;
@override
......
......@@ -6,45 +6,70 @@ import 'equation_member.dart';
import 'expression.dart';
import 'term.dart';
/// A [Variable] inside the layout [Solver]. It represents an indeterminate
/// in the [Expression] that is used to create the [Constraint]. If any entity
/// is interested in watching updates to the value of this indeterminate,
/// it can assign a watcher as the `owner`.
class Variable {
static int _total = 0;
/// Creates a new [Variable] with the given constant value.
Variable(this.value) : _tick = _total++;
final int _tick;
/// The current value of the variable.
double value;
/// An optional name given to the variable. This is useful in debugging
/// [Solver] state.
String name;
/// Variables represent state inside the solver. This state is usually of
/// interest to some entity outside the solver. Such entities can (optionally)
/// associate themselves with these variables. This means that when solver
/// is flushed, it is easy to obtain a reference to the entity the variable
/// is associated with.
Param get owner => _owner;
Param _owner;
/// Used by the [Solver] to apply updates to this variable. Only updated
/// variables show up in [Solver] flush results.
bool applyUpdate(double updated) {
bool res = updated != value;
value = updated;
return res;
}
/// The name used for this [Variable] when debugging the internal state of the
/// solver.
String get debugName => name ?? 'variable$_tick';
@override
String toString() => debugName;
}
/// A [Param] wraps a [Variable] and makes it suitable to be used in an
/// expression.
class Param extends EquationMember {
/// Creates a new [Param] with the specified constant value.
Param([double value = 0.0]) : variable = new Variable(value) {
variable._owner = this;
}
/// Creates a new [Param] with the specified constant value that is tied
/// to some object outside the solver.
Param.withContext(dynamic context, [double value = 0.0])
: variable = new Variable(value),
context = context {
variable._owner = this;
}
/// The [Variable] associated with this [Param].
final Variable variable;
/// Some object outside the [Solver] that is associated with this Param.
dynamic context;
@override
......@@ -56,6 +81,9 @@ class Param extends EquationMember {
@override
double get value => variable.value;
/// The name of the [Variable] associated with this [Param].
String get name => variable.name;
/// Set the name of the [Variable] associated with this [Param].
set name(String name) { variable.name = name; }
}
......@@ -4,11 +4,26 @@
import 'equation_member.dart';
/// Exception thrown when attempting to create a non-linear expression.
///
/// During the creation of constraints or expressions using the overloaded
/// operators, it may be possible to end up with non-linear expressions. Such
/// expressions are not suitable for [Constraint] creation because the [Solver]
/// will reject the same. A [ParserException] is thrown when a developer tries
/// to create such an expression.
///
/// The only cases where this is possible is when trying to multiply two
/// expressions where at least one of them is not a constant expression, or,
/// when trying to divide two expressions where the divisor is not constant.
class ParserException implements Exception {
/// Creates a new [ParserException] with a given message and a list of the
/// offending member for debugging purposes.
ParserException(this.message, this.members);
/// A detailed message describing the exception.
final String message;
/// The members that caused the exception.
List<EquationMember> members;
@override
......
......@@ -10,19 +10,24 @@ import 'dart:math';
/// between 0 and 1,000,000,000. These numbers can be created by using the
/// [Priority.create] static method.
class Priority {
/// The priority level that, by convention, is the highest allowed priority level (1,000,000,000).
/// The [Priority] level that, by convention, is the highest allowed
/// [Priority] level (1,000,000,000).
static final double required = create(1e3, 1e3, 1e3);
/// A priority level that is below the [required] level but still near it (1,000,000).
/// A [Priority] level that is below the [required] level but still near it
/// (1,000,000).
static final double strong = create(1.0, 0.0, 0.0);
/// A priority level logarithmically in the middle of [strong] and [weak] (1,000).
/// A [Priority] level logarithmically in the middle of [strong] and [weak]
/// (1,000).
static final double medium = create(0.0, 1.0, 0.0);
/// A priority level that, by convention, is the lowest allowed priority level (1).
/// A [Priority] level that, by convention, is the lowest allowed [Priority]
/// level (1).
static final double weak = create(0.0, 0.0, 1.0);
/// Computes a priority level by combining three numbers in the range 0..1000.
/// Computes a [Priority] level by combining three numbers in the range
/// 0..1000.
///
/// The first number is a multiple of [strong].
///
......@@ -30,7 +35,8 @@ class Priority {
///
/// The third number is a multiple of [weak].
///
/// By convention, at least one of these numbers should be equal to or greater than 1.
/// By convention, at least one of these numbers should be equal to or greater
/// than 1.
static double create(double a, double b, double c) {
double result = 0.0;
result += max(0.0, min(1e3, a)) * 1e6;
......
......@@ -17,24 +17,39 @@ class Result {
/// Whether this [Result] represents an error (true) or not (false).
final bool error;
/// The result when the operation was successful.
static const Result success =
const Result._('Success', isError: false);
/// The result when the [Constraint] could not be added to the [Solver]
/// because it was already present in the solver.
static const Result duplicateConstraint =
const Result._('Duplicate constraint');
/// The result when the [Constraint] could not be added to the [Solver]
/// because it was unsatisfiable. Try lowering the [Priority] of the
/// [Constraint] and try again.
static const Result unsatisfiableConstraint =
const Result._('Unsatisfiable constraint');
/// The result when the [Constraint] could not be removed from the solver
/// because it was not present in the [Solver] to begin with.
static const Result unknownConstraint =
const Result._('Unknown constraint');
/// The result when could not add the edit [Variable] to the [Solver] because
/// it was already added to the [Solver] previously.
static const Result duplicateEditVariable =
const Result._('Duplicate edit variable');
/// The result when the [Constraint] constraint was added at an invalid
/// priority or an edit [Variable] was added at an invalid or required
/// priority.
static const Result badRequiredStrength =
const Result._('Bad required strength');
/// The result when the edit [Variable] could not be removed from the solver
/// because it was not present in the [Solver] to begin with.
static const Result unknownEditVariable =
const Result._('Unknown edit variable');
}
......@@ -6,11 +6,21 @@ import 'equation_member.dart';
import 'expression.dart';
import 'param.dart';
/// Represents a single term in an expression. This term contains a single
/// indeterminate and has degree 1.
class Term extends EquationMember {
/// Creates term with the given [Variable] and coefficient.
Term(this.variable, this.coefficient);
/// The [Variable] (or indeterminate) portion of this term. Variables are
/// usually tied to an opaque object (via its `context` property). On a
/// [Solver] flush, these context objects of updated variables are returned by
/// the solver. An external entity can then choose to interpret these values
/// in what manner it sees fit.
final Variable variable;
/// The coefficient of this term. Before addition of the [Constraint] to the
/// solver, terms with a zero coefficient are dropped.
final double coefficient;
@override
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment