Document the public members of the Cassowary package. (#4744)
This commit is contained in:
parent
9850fe1861
commit
ae835f08df
@ -6,6 +6,8 @@ import 'expression.dart';
|
|||||||
import 'equation_member.dart';
|
import 'equation_member.dart';
|
||||||
import 'term.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 {
|
class ConstantMember extends EquationMember {
|
||||||
/// Creates a [ConstantMember] object.
|
/// Creates a [ConstantMember] object.
|
||||||
///
|
///
|
||||||
|
@ -5,21 +5,52 @@
|
|||||||
import 'priority.dart';
|
import 'priority.dart';
|
||||||
import 'expression.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 {
|
enum Relation {
|
||||||
|
/// The relationship between the left and right hand sides of the expression
|
||||||
|
/// is `==`, (lhs == rhs).
|
||||||
equalTo,
|
equalTo,
|
||||||
|
/// The relationship between the left and right hand sides of the expression
|
||||||
|
/// is `<=`, (lhs <= rhs).
|
||||||
lessThanOrEqualTo,
|
lessThanOrEqualTo,
|
||||||
|
/// The relationship between the left and right hand sides of the expression
|
||||||
|
/// is `>=`, (lhs => rhs).
|
||||||
greaterThanOrEqualTo,
|
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 {
|
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);
|
Constraint(this.expression, this.relation);
|
||||||
|
|
||||||
|
/// The [Relation] between a [Constraint] [Expression] and zero.
|
||||||
final Relation relation;
|
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;
|
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;
|
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;
|
Constraint operator |(double p) => this..priority = p;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -5,25 +5,95 @@
|
|||||||
import 'expression.dart';
|
import 'expression.dart';
|
||||||
import 'constraint.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 {
|
abstract class EquationMember {
|
||||||
|
/// The representation of this member after it is hoisted to be an
|
||||||
|
/// expression.
|
||||||
Expression asExpression();
|
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;
|
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;
|
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;
|
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;
|
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);
|
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;
|
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;
|
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;
|
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;
|
Expression operator /(EquationMember m) => asExpression() / m;
|
||||||
}
|
}
|
||||||
|
@ -15,15 +15,24 @@ class _Multiplication {
|
|||||||
final double multiplicand;
|
final double multiplicand;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The representation of a linear [Expression] that can be used to create a
|
||||||
|
/// constraint.
|
||||||
class Expression extends EquationMember {
|
class Expression extends EquationMember {
|
||||||
|
/// Creates a new linear [Expression] using the given terms and constant.
|
||||||
Expression(this.terms, this.constant);
|
Expression(this.terms, this.constant);
|
||||||
|
|
||||||
|
/// Creates a new linear [Expression] by copying the terms and constant of
|
||||||
|
/// another expression.
|
||||||
Expression.fromExpression(Expression expr)
|
Expression.fromExpression(Expression expr)
|
||||||
: this.terms = new List<Term>.from(expr.terms),
|
: this.terms = new List<Term>.from(expr.terms),
|
||||||
this.constant = expr.constant;
|
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;
|
final List<Term> terms;
|
||||||
|
|
||||||
|
/// The constant portion of this linear expression. This is just another
|
||||||
|
/// [Term] with no [Variable].
|
||||||
final double constant;
|
final double constant;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -6,45 +6,70 @@ import 'equation_member.dart';
|
|||||||
import 'expression.dart';
|
import 'expression.dart';
|
||||||
import 'term.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 {
|
class Variable {
|
||||||
static int _total = 0;
|
static int _total = 0;
|
||||||
|
|
||||||
|
/// Creates a new [Variable] with the given constant value.
|
||||||
Variable(this.value) : _tick = _total++;
|
Variable(this.value) : _tick = _total++;
|
||||||
|
|
||||||
final int _tick;
|
final int _tick;
|
||||||
|
|
||||||
|
/// The current value of the variable.
|
||||||
double value;
|
double value;
|
||||||
|
|
||||||
|
/// An optional name given to the variable. This is useful in debugging
|
||||||
|
/// [Solver] state.
|
||||||
String name;
|
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 get owner => _owner;
|
||||||
|
|
||||||
Param _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 applyUpdate(double updated) {
|
||||||
bool res = updated != value;
|
bool res = updated != value;
|
||||||
value = updated;
|
value = updated;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The name used for this [Variable] when debugging the internal state of the
|
||||||
|
/// solver.
|
||||||
String get debugName => name ?? 'variable$_tick';
|
String get debugName => name ?? 'variable$_tick';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => debugName;
|
String toString() => debugName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [Param] wraps a [Variable] and makes it suitable to be used in an
|
||||||
|
/// expression.
|
||||||
class Param extends EquationMember {
|
class Param extends EquationMember {
|
||||||
|
/// Creates a new [Param] with the specified constant value.
|
||||||
Param([double value = 0.0]) : variable = new Variable(value) {
|
Param([double value = 0.0]) : variable = new Variable(value) {
|
||||||
variable._owner = this;
|
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])
|
Param.withContext(dynamic context, [double value = 0.0])
|
||||||
: variable = new Variable(value),
|
: variable = new Variable(value),
|
||||||
context = context {
|
context = context {
|
||||||
variable._owner = this;
|
variable._owner = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The [Variable] associated with this [Param].
|
||||||
final Variable variable;
|
final Variable variable;
|
||||||
|
|
||||||
|
/// Some object outside the [Solver] that is associated with this Param.
|
||||||
dynamic context;
|
dynamic context;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -56,6 +81,9 @@ class Param extends EquationMember {
|
|||||||
@override
|
@override
|
||||||
double get value => variable.value;
|
double get value => variable.value;
|
||||||
|
|
||||||
|
/// The name of the [Variable] associated with this [Param].
|
||||||
String get name => variable.name;
|
String get name => variable.name;
|
||||||
|
|
||||||
|
/// Set the name of the [Variable] associated with this [Param].
|
||||||
set name(String name) { variable.name = name; }
|
set name(String name) { variable.name = name; }
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,26 @@
|
|||||||
|
|
||||||
import 'equation_member.dart';
|
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 {
|
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);
|
ParserException(this.message, this.members);
|
||||||
|
|
||||||
|
/// A detailed message describing the exception.
|
||||||
final String message;
|
final String message;
|
||||||
|
|
||||||
|
/// The members that caused the exception.
|
||||||
List<EquationMember> members;
|
List<EquationMember> members;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -10,19 +10,24 @@ import 'dart:math';
|
|||||||
/// between 0 and 1,000,000,000. These numbers can be created by using the
|
/// between 0 and 1,000,000,000. These numbers can be created by using the
|
||||||
/// [Priority.create] static method.
|
/// [Priority.create] static method.
|
||||||
class Priority {
|
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);
|
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);
|
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);
|
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);
|
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].
|
/// The first number is a multiple of [strong].
|
||||||
///
|
///
|
||||||
@ -30,7 +35,8 @@ class Priority {
|
|||||||
///
|
///
|
||||||
/// The third number is a multiple of [weak].
|
/// 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) {
|
static double create(double a, double b, double c) {
|
||||||
double result = 0.0;
|
double result = 0.0;
|
||||||
result += max(0.0, min(1e3, a)) * 1e6;
|
result += max(0.0, min(1e3, a)) * 1e6;
|
||||||
|
@ -17,24 +17,39 @@ class Result {
|
|||||||
/// Whether this [Result] represents an error (true) or not (false).
|
/// Whether this [Result] represents an error (true) or not (false).
|
||||||
final bool error;
|
final bool error;
|
||||||
|
|
||||||
|
/// The result when the operation was successful.
|
||||||
static const Result success =
|
static const Result success =
|
||||||
const Result._('Success', isError: false);
|
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 =
|
static const Result duplicateConstraint =
|
||||||
const Result._('Duplicate constraint');
|
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 =
|
static const Result unsatisfiableConstraint =
|
||||||
const Result._('Unsatisfiable constraint');
|
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 =
|
static const Result unknownConstraint =
|
||||||
const Result._('Unknown constraint');
|
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 =
|
static const Result duplicateEditVariable =
|
||||||
const Result._('Duplicate edit variable');
|
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 =
|
static const Result badRequiredStrength =
|
||||||
const Result._('Bad required strength');
|
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 =
|
static const Result unknownEditVariable =
|
||||||
const Result._('Unknown edit variable');
|
const Result._('Unknown edit variable');
|
||||||
}
|
}
|
||||||
|
@ -158,6 +158,20 @@ class Solver {
|
|||||||
/// Attempts to add the constraints in the list to the solver. If it cannot
|
/// Attempts to add the constraints in the list to the solver. If it cannot
|
||||||
/// add any for some reason, a cleanup is attempted so that either all
|
/// add any for some reason, a cleanup is attempted so that either all
|
||||||
/// constraints will be added or none.
|
/// constraints will be added or none.
|
||||||
|
///
|
||||||
|
/// Check the [Result] returned to make sure the operation succeeded. Any
|
||||||
|
/// errors will be reported via the `message` property on the [Result].
|
||||||
|
///
|
||||||
|
/// Possible [Result]s:
|
||||||
|
///
|
||||||
|
/// * [Result.success]: All constraints successfully added.
|
||||||
|
/// * [Result.duplicateConstraint]: One of the constraints in the list was
|
||||||
|
/// already in the solver or the same constraint was specified multiple
|
||||||
|
/// times in the argument list. Remove the duplicates and try again.
|
||||||
|
/// * [Result.unsatisfiableConstraint]: One or more constraints were at
|
||||||
|
/// [Priority.required] but could not added because of conflicts with other
|
||||||
|
/// constraints at the same priority. Lower the priority of these
|
||||||
|
/// constraints and try again.
|
||||||
Result addConstraints(List<Constraint> constraints) {
|
Result addConstraints(List<Constraint> constraints) {
|
||||||
_SolverBulkUpdate applier = (Constraint c) => addConstraint(c);
|
_SolverBulkUpdate applier = (Constraint c) => addConstraint(c);
|
||||||
_SolverBulkUpdate undoer = (Constraint c) => removeConstraint(c);
|
_SolverBulkUpdate undoer = (Constraint c) => removeConstraint(c);
|
||||||
@ -165,6 +179,20 @@ class Solver {
|
|||||||
return _bulkEdit(constraints, applier, undoer);
|
return _bulkEdit(constraints, applier, undoer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempts to add an individual [Constraint] to the solver.
|
||||||
|
///
|
||||||
|
/// Check the [Result] returned to make sure the operation succeeded. Any
|
||||||
|
/// errors will be reported via the `message` property on the [Result].
|
||||||
|
///
|
||||||
|
/// Possible [Result]s:
|
||||||
|
///
|
||||||
|
/// * [Result.success]: The constraint was successfully added.
|
||||||
|
/// * [Result.duplicateConstraint]: The constraint was already present in the
|
||||||
|
/// solver.
|
||||||
|
/// * [Result.unsatisfiableConstraint]: The constraint was at
|
||||||
|
/// [Priority.required] but could not be added because of a conflict with
|
||||||
|
/// another constraint at that priority already in the solver. Try lowering
|
||||||
|
/// the priority of the constraint and try again.
|
||||||
Result addConstraint(Constraint constraint) {
|
Result addConstraint(Constraint constraint) {
|
||||||
if (_constraints.containsKey(constraint))
|
if (_constraints.containsKey(constraint))
|
||||||
return Result.duplicateConstraint;
|
return Result.duplicateConstraint;
|
||||||
@ -198,6 +226,21 @@ class Solver {
|
|||||||
return _optimizeObjectiveRow(_objective);
|
return _optimizeObjectiveRow(_objective);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempts to remove a list of constraints from the solver. Either all
|
||||||
|
/// constraints are removed or none. If more fine-grained control over the
|
||||||
|
/// removal is required (for example, not failing on removal of constraints
|
||||||
|
/// not already present in the solver), try removing the each [Constraint]
|
||||||
|
/// individually and check the result on each attempt.
|
||||||
|
///
|
||||||
|
/// Check the [Result] returned to make sure the operation succeeded. Any
|
||||||
|
/// errors will be reported via the `message` property on the [Result].
|
||||||
|
///
|
||||||
|
/// Possible [Result]s:
|
||||||
|
///
|
||||||
|
/// * [Result.success]: The constraints were successfully removed from the
|
||||||
|
/// solver.
|
||||||
|
/// * [Result.unknownConstraint]: One or more constraints in the list were
|
||||||
|
/// not in the solver. So there was nothing to remove.
|
||||||
Result removeConstraints(List<Constraint> constraints) {
|
Result removeConstraints(List<Constraint> constraints) {
|
||||||
_SolverBulkUpdate applier = (Constraint c) => removeConstraint(c);
|
_SolverBulkUpdate applier = (Constraint c) => removeConstraint(c);
|
||||||
_SolverBulkUpdate undoer = (Constraint c) => addConstraint(c);
|
_SolverBulkUpdate undoer = (Constraint c) => addConstraint(c);
|
||||||
@ -205,6 +248,17 @@ class Solver {
|
|||||||
return _bulkEdit(constraints, applier, undoer);
|
return _bulkEdit(constraints, applier, undoer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempt to remove an individual [Constraint] from the solver.
|
||||||
|
///
|
||||||
|
/// Check the [Result] returned to make sure the operation succeeded. Any
|
||||||
|
/// errors will be reported via the `message` property on the [Result].
|
||||||
|
///
|
||||||
|
/// Possible [Result]s:
|
||||||
|
///
|
||||||
|
/// * [Result.success]: The [Constraint] was successfully removed from the
|
||||||
|
/// solver.
|
||||||
|
/// * [Result.unknownConstraint]: The [Constraint] was not in the solver so
|
||||||
|
/// there was nothing to remove.
|
||||||
Result removeConstraint(Constraint constraint) {
|
Result removeConstraint(Constraint constraint) {
|
||||||
_Tag tag = _constraints[constraint];
|
_Tag tag = _constraints[constraint];
|
||||||
if (tag == null)
|
if (tag == null)
|
||||||
@ -231,10 +285,32 @@ class Solver {
|
|||||||
return _optimizeObjectiveRow(_objective);
|
return _optimizeObjectiveRow(_objective);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether the given [Constraint] is present in the solver.
|
||||||
bool hasConstraint(Constraint constraint) {
|
bool hasConstraint(Constraint constraint) {
|
||||||
return _constraints.containsKey(constraint);
|
return _constraints.containsKey(constraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a list of edit [Variable]s to the [Solver] at a given priority.
|
||||||
|
/// Either all edit [Variable] are added or none. No edit variables may be
|
||||||
|
/// added at `Priority.required`.
|
||||||
|
///
|
||||||
|
/// Check the [Result] returned to make sure the operation succeeded. Any
|
||||||
|
/// errors will be reported via the `message` property on the [Result].
|
||||||
|
///
|
||||||
|
/// Possible [Result]s:
|
||||||
|
///
|
||||||
|
/// * [Result.success]: The edit variables were successfully added to [Solver]
|
||||||
|
/// at the specified priority.
|
||||||
|
/// * [Result.duplicateEditVariable]: One of more edit variables were already
|
||||||
|
/// present in the [Solver] or the same edit variables were specified
|
||||||
|
/// multiple times in the list. Remove the duplicates and try again.
|
||||||
|
/// * [Result.badRequiredStrength]: The edit variables were added at
|
||||||
|
/// [Priority.required]. Edit variables are used to
|
||||||
|
/// suggest values to the solver. Since suggestions can't be mandatory,
|
||||||
|
/// priorities cannot be [Priority.required]. If variable values need to be
|
||||||
|
/// fixed at [Priority.required], add that preference as a constraint. This
|
||||||
|
/// allows the solver to check for satisfiability of the constraint (w.r.t
|
||||||
|
/// other constraints at [Priority.required]) and check for duplicates.
|
||||||
Result addEditVariables(List<Variable> variables, double priority) {
|
Result addEditVariables(List<Variable> variables, double priority) {
|
||||||
_SolverBulkUpdate applier = (Variable v) => addEditVariable(v, priority);
|
_SolverBulkUpdate applier = (Variable v) => addEditVariable(v, priority);
|
||||||
_SolverBulkUpdate undoer = (Variable v) => removeEditVariable(v);
|
_SolverBulkUpdate undoer = (Variable v) => removeEditVariable(v);
|
||||||
@ -242,6 +318,26 @@ class Solver {
|
|||||||
return _bulkEdit(variables, applier, undoer);
|
return _bulkEdit(variables, applier, undoer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempt to add a single edit [Variable] to the [Solver] at the given
|
||||||
|
/// priority. No edit variables may be added to the [Solver] at
|
||||||
|
/// `Priority.required`.
|
||||||
|
///
|
||||||
|
/// Check the [Result] returned to make sure the operation succeeded. Any
|
||||||
|
/// errors will be reported via the `message` property on the [Result].
|
||||||
|
///
|
||||||
|
/// Possible [Result]s:
|
||||||
|
///
|
||||||
|
/// * [Result.success]: The edit variable was successfully added to [Solver]
|
||||||
|
/// at the specified priority.
|
||||||
|
/// * [Result.duplicateEditVariable]: The edit variable was already present
|
||||||
|
/// in the [Solver].
|
||||||
|
/// * [Result.badRequiredStrength]: The edit variable was added at
|
||||||
|
/// [Priority.required]. Edit variables are used to
|
||||||
|
/// suggest values to the solver. Since suggestions can't be mandatory,
|
||||||
|
/// priorities cannot be [Priority.required]. If variable values need to be
|
||||||
|
/// fixed at [Priority.required], add that preference as a constraint. This
|
||||||
|
/// allows the solver to check for satisfiability of the constraint (w.r.t
|
||||||
|
/// other constraints at [Priority.required]) and check for duplicates.
|
||||||
Result addEditVariable(Variable variable, double priority) {
|
Result addEditVariable(Variable variable, double priority) {
|
||||||
if (_edits.containsKey(variable))
|
if (_edits.containsKey(variable))
|
||||||
return Result.duplicateEditVariable;
|
return Result.duplicateEditVariable;
|
||||||
@ -267,6 +363,18 @@ class Solver {
|
|||||||
return Result.success;
|
return Result.success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempt the remove the list of edit [Variable] from the solver. Either
|
||||||
|
/// all the specified edit variables are removed or none.
|
||||||
|
///
|
||||||
|
/// Check the [Result] returned to make sure the operation succeeded. Any
|
||||||
|
/// errors will be reported via the `message` property on the [Result].
|
||||||
|
///
|
||||||
|
/// Possible [Result]s:
|
||||||
|
///
|
||||||
|
/// * [Result.success]: The edit variables were successfully removed from the
|
||||||
|
/// [Solver].
|
||||||
|
/// * [Result.unknownEditVariable]: One of more edit variables were not
|
||||||
|
/// already present in the solver.
|
||||||
Result removeEditVariables(List<Variable> variables) {
|
Result removeEditVariables(List<Variable> variables) {
|
||||||
_SolverBulkUpdate applier = (Variable v) => removeEditVariable(v);
|
_SolverBulkUpdate applier = (Variable v) => removeEditVariable(v);
|
||||||
_SolverBulkUpdate undoer = (Variable v) =>
|
_SolverBulkUpdate undoer = (Variable v) =>
|
||||||
@ -275,6 +383,17 @@ class Solver {
|
|||||||
return _bulkEdit(variables, applier, undoer);
|
return _bulkEdit(variables, applier, undoer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempt to remove the specified edit [Variable] from the solver.
|
||||||
|
///
|
||||||
|
/// Check the [Result] returned to make sure the operation succeeded. Any
|
||||||
|
/// errors will be reported via the `message` property on the [Result].
|
||||||
|
///
|
||||||
|
/// Possible [Result]s:
|
||||||
|
///
|
||||||
|
/// * [Result.success]: The edit variable was successfully removed from the
|
||||||
|
/// solver.
|
||||||
|
/// * [Result.unknownEditVariable]: The edit variable was not present in the
|
||||||
|
/// solver. There was nothing to remove.
|
||||||
Result removeEditVariable(Variable variable) {
|
Result removeEditVariable(Variable variable) {
|
||||||
_EditInfo info = _edits[variable];
|
_EditInfo info = _edits[variable];
|
||||||
if (info == null)
|
if (info == null)
|
||||||
@ -286,10 +405,34 @@ class Solver {
|
|||||||
return Result.success;
|
return Result.success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether the given edit [Variable] is present in the solver.
|
||||||
bool hasEditVariable(Variable variable) {
|
bool hasEditVariable(Variable variable) {
|
||||||
return _edits.containsKey(variable);
|
return _edits.containsKey(variable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Suggest an updated value for the edit variable. The edit variable
|
||||||
|
/// must already be added to the solver.
|
||||||
|
///
|
||||||
|
/// Suggestions update values of variables within the [Solver] but take into
|
||||||
|
/// account all the constraints already present in the [Solver]. Depending
|
||||||
|
/// on the constraints, the value of the [Variable] may not actually be the
|
||||||
|
/// value specified. The actual value can be read after the next
|
||||||
|
/// `flushUpdates` call. Since these updates are merely "suggestions", they
|
||||||
|
/// cannot be at `Priority.required`.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// Check the [Result] returned to make sure the operation succeeded. Any
|
||||||
|
/// errors will be reported via the `message` property on the [Result].
|
||||||
|
///
|
||||||
|
/// Possible [Result]s:
|
||||||
|
///
|
||||||
|
/// * [Result.success]: The suggestion was successfully applied to the
|
||||||
|
/// variable within the solver.
|
||||||
|
/// * [Result.unknownEditVariable]: The edit variable was not already present
|
||||||
|
/// in the [Solver]. So the suggestion could not be applied. Add this edit
|
||||||
|
/// variable to the solver and then apply the value again. If you have
|
||||||
|
/// already added the variable to the [Solver], make sure the [Result]
|
||||||
|
/// was `Result.success`.
|
||||||
Result suggestValueForVariable(Variable variable, double value) {
|
Result suggestValueForVariable(Variable variable, double value) {
|
||||||
if (!_edits.containsKey(variable))
|
if (!_edits.containsKey(variable))
|
||||||
return Result.unknownEditVariable;
|
return Result.unknownEditVariable;
|
||||||
@ -299,6 +442,16 @@ class Solver {
|
|||||||
return _dualOptimize();
|
return _dualOptimize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Flush the results of solver. The set of all `context` objects associated
|
||||||
|
/// with variables in the [Solver] is returned. If a [Variable] does not
|
||||||
|
/// contain an associated context, its updates are ignored.
|
||||||
|
///
|
||||||
|
/// The addition and removal of constraints and edit variables to and from the
|
||||||
|
/// [Solver] as well as the application of suggestions to the added edit
|
||||||
|
/// variables leads to the modification of values on a lot of other variables.
|
||||||
|
/// External entities that rely on the values of the variables within the
|
||||||
|
/// [Solver] can read these updates in one shot by "flushing" out these
|
||||||
|
/// updates.
|
||||||
Set<dynamic> flushUpdates() {
|
Set<dynamic> flushUpdates() {
|
||||||
Set<dynamic> updates = new HashSet<dynamic>();
|
Set<dynamic> updates = new HashSet<dynamic>();
|
||||||
|
|
||||||
|
@ -6,11 +6,21 @@ import 'equation_member.dart';
|
|||||||
import 'expression.dart';
|
import 'expression.dart';
|
||||||
import 'param.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 {
|
class Term extends EquationMember {
|
||||||
|
/// Creates term with the given [Variable] and coefficient.
|
||||||
Term(this.variable, this.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;
|
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;
|
final double coefficient;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
Loading…
x
Reference in New Issue
Block a user