cassowary.js

repository·master·Indexed 23 days ago

https://github.com/slightlyoff/cassowary.js

A high-performance JavaScript implementation of the Cassowary hierarchical linear constraint solver. It enables the creation of flexible, responsive layouts by defining relationships between variables using Equations and Inequalities with assignable strengths (required, strong, medium, weak). The library provides a SimplexSolver to manage variables and constraints, supporting interactive updates via setEditedValue and batch updates through beginEdit and endEdit.

Tokens
2.4K
Snippets
2
Records
14
Agent score
67%

What's inside cassowary.js

  1. Use Cassowary in a Node.js project

    master

    To use Cassowary in Node.js, require the cassowary module. The entire API is exported by the c object. You can instantiate a SimplexSolver, create Variable objects with initial values, and define relationships using Equation and Expression objects.

    // The entire API is exported by the cassowary object
    var c = require("cassowary");
    
    var solver = new c.SimplexSolver();
    var x = new c.Variable({ value: 167 });
    var y = new c.Variable({ value: 2 });
    var eq = new c.Equation(x, new c.Expression(y));
    solver.addConstraint(eq);
    // ...
  2. Build Cassowary from source

    master
    To build the project from source, you must first initialize submodules and install dependencies. To generate a minified binary (bin/c.js) from the src/ directory, use make build (requires Python). To create an NPM package from source, use make dist.
  3. Run Cassowary tests

    master

    Tests can be run in a browser or via the command line using Node.js.

    Browser: Open tests/unittests.html?config=tests/intern in a modern browser and check the console.

    Command Line: Run npm test or use the make test target if you have make installed.

  4. Create variables with SimplexSolver

    master

    Variables are the fundamental building blocks of the constraint system. You can create different types of variables depending on your needs:

    • Variable: A standard variable that can be part of constraints.
    • DummyVariable: Used internally for equality constraints; they are restricted and not pivotable.
    • ObjectiveVariable: A special variable (usually named Z) that represents the value to be optimized.
    • SlackVariable: Used to convert inequalities into equalities.

    When adding a variable to a SimplexSolver via addVar(variable), the solver automatically adds a StayConstraint to ensure the variable's value is maintained unless a constraint forces it to change.

  5. Core Abstractions in Cassowary JS

    master

    Cassowary JS is a constraint solver based on the Simplex algorithm. It uses several key abstractions to model mathematical problems:

    • Variables: The fundamental units of the solver. They can be Variable (external/user-defined), DummyVariable (internal/restricted), ObjectiveVariable (used for the optimization target), or SlackVariable (used to convert inequalities to equations).
    • Expressions: Linear combinations of variables and constants (e.g., 3*x + 2*y + 5).
    • Constraints: Rules applied to expressions. They can be Inequality (e.g., expr >= 0), Equation (e.g., expr = 0), EditConstraint (used for smooth transitions when values change), or StayConstraint (used to keep variables at specific values).
    • Strengths: Defines how strictly a constraint must be satisfied. Available levels are required, strong, medium, and weak.
  6. How the SimplexSolver works

    master

    The SimplexSolver is the core engine of Cassowary JS. It uses the Simplex algorithm to find an optimal solution for a set of linear constraints.

    Key behaviors:

    • Constraints: You can add constraints such as Inequality (e.g., <=, >=, <, >) and Equality (equations) to the solver.
    • Optimization: The optimize() method performs the actual solving process. If the objective function is unbounded, it throws an InternalError with the message "Objective function is unbounded in optimize".
    • Callbacks: You can register a callback function using _addCallback(fn) which will be executed when the solver finds a solution (via onsolved).
    • Expressions: The solver operates on Expression objects, which can be composed of Variable objects, NumericLiterals, and arithmetic operations like plus, minus, and times.
  7. Configure Cassowary logging and debugging

    master

    You can control the verbosity of the solver's output by setting configuration properties on the exported c object. These are useful for debugging constraint solving behavior.

    // Log general debugging information
    c.debug = [ false || true ]; // default false
    // Detailed logging
    c.trace = [ false || true ]; // default false
    // Verbose logging
    c.verbose = [ false || true ]; // default false
    // Logging of tableau additions
    c.traceAdded = [ false || true ]; // default false
    // Logging of ...?
    c.GC = [ false || true ]; // default false
  8. Use SimplexSolver to solve linear constraints

    master

    The SimplexSolver is the primary class used to manage and solve a system of linear constraints. It extends Tableau and provides methods to add variables, constraints, and perform optimizations.

    Core Workflow

    1. Initialize: Create a new SimplexSolver instance.
    2. Add Variables: Use addVar(variable) to introduce variables into the system. This automatically adds a 'stay' constraint to the variable.
    3. Add Constraints: Use addConstraint(constraint) to add inequalities (Inequality) or equations (Equation).
    4. Solve: Call solve() to optimize the objective function based on the added constraints.
    5. Access Values: After solving, the current values of the variables are updated within the variable objects themselves.

    Key Methods

    • addVar(variable): Adds a variable and an associated stay constraint.
    • addConstraint(constraint): Adds a constraint (e.g., Inequality or Equation) to the solver.
    • solve(): Triggers the optimization process.
    • setEditedValue(variable, value): A specialized method for interactive use. It treats the change as an EditConstraint, allowing the solver to adjust other variables to accommodate the new value while minimizing the impact on the objective function.
    • beginEdit() / endEdit(): Wraps a series of suggestValue calls to perform a batch update of variable values as part of an edit cycle.
  9. Manage Variables and Expressions

    master

    The library provides utilities to build linear expressions from variables and constants.

    Variables

    • new Variable({ name, value, prefix }): Creates a user-defined variable.
    • new DummyVariable({ name, value, prefix }): Creates a restricted internal variable.
    • new SlackVariable({ name, value, prefix }): Creates a variable used for inequality slack.

    Expressions

    Expressions are built using arithmetic methods:

    • Expression.fromConstant(n)
    • Expression.fromVariable(v)
    • .plus(other)
    • .minus(other)
    • .times(other)
    • .divide(other)
  10. Use SimplexSolver to solve constraints

    master

    The SimplexSolver is the primary class used to manage variables and constraints. You can add constraints, update variable values, and solve the system to find optimal values.

    Key Methods:

    • addConstraint(constraint): Adds a new constraint to the solver.
    • add(constraint1, constraint2, ...): Convenience method to add multiple constraints.
    • setEditedValue(variable, value): Updates a variable's value while attempting to minimize the impact on existing constraints (using EditConstraint internally).
    • solve(): Triggers the optimization process.
    • addStay(variable, strength, weight): Adds a constraint to keep a variable at a specific value.
  11. Handle interactive updates with setEditedValue

    master

    If you are building an interactive UI where a user drags a component to change a value, use setEditedValue(variable, value) instead of manually updating the variable's value.

    setEditedValue performs the following:

    1. Checks if the variable is already part of the solver.
    2. If the value has changed, it adds an EditConstraint to the solver.
    3. It calls beginEdit() to prepare the solver for a batch update.
    4. It uses suggestValue to calculate the necessary delta to reach the new value.
    5. It calls endEdit() to resolve the constraints and re-optimize the system.

    This ensures that the change is treated as a 'soft' requirement that the solver tries to satisfy while respecting other constraints.