Code Monkey home page Code Monkey logo

kiwi.js's Introduction

THIS REPOSITORY IS NO LONGER MAINTAINED

Looking for an alternative? Please have a look at lume/kiwi.

If you are interested in maintaining this repository (and taking ownership of it), please reach out to me here.

kiwi.js

view on npm Build Status codecov

Kiwi.js is a fast TypeScript implementation of the Cassowary constraint solving algorithm, based on the seminal Cassowary paper. Originally created by Chris Colbert, it was redesigned from the ground up to be lightweight, fast and easy to maintain. View the benchmarks to see how it compares to Cassowary.js.

Index

Getting started

Install using NPM:

npm install kiwi.js

The following example creates a solver which automatically calculates the width:

import * as kiwi from 'kiwi.js';
import { equal } from 'assert';

// Create a solver
var solver = new kiwi.Solver();

// Create edit variables
var left = new kiwi.Variable();
var width = new kiwi.Variable();
solver.addEditVariable(left, kiwi.Strength.strong);
solver.addEditVariable(width, kiwi.Strength.strong);
solver.suggestValue(left, 100);
solver.suggestValue(width, 400);

// Create and add a constraint
var right = new kiwi.Variable();
solver.addConstraint(new kiwi.Constraint(new kiwi.Expression([-1, right], left, width), kiwi.Operator.Eq));

// Solve the constraints
solver.updateVariables();
equal(right.value(), 500);

Documentation

Benchmarks

To run the benchmark in the browser, just visit this page.

To run the benchmark locally using nodejs, clone or download this repository and execute the following steps:

npm install
npm run bench

Tests

To run the tests in the browser, just visit this page.

To run the tests locally using nodejs, clone or download this repository and execute the following steps:

npm install
npm run build && npm run test

Contribute

If you like this project and want to support it, show some love and give it a star.

kiwi.js's People

Contributors

adamhaile avatar cacaodev avatar henrikh avatar ijzerenhein avatar joshuahhh avatar jwiggins avatar orentrutner avatar sccolbert avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kiwi.js's Issues

Adding an objective function

Hi! It appears the kiwi.js API doesn't include a function to set an objective function to solve with. Is that right? Would this be an easy change?

(Thanks!)

a proposal for improved ergonomics

kiwi.js is really cool! One thing I've found painful is having to construct all of the constraints in javascript. I've developed a simplistic parser and wrapper interface to make this easier. Here's what I've currently got:

import constraints from './lib/constraints.js'   // the wrapper for kiwi.js


const layout = constraints({
    editableVariables: [
        {
            name: 'mine', 
            strength: 'strong'   // optional parameter
        }
    ],
    constraints: `
        windowWidth  <= (windowHeight - modalHeight) / 25
        windowHeight == 45 / 6
        modalHeight  == mine * 10
        windowWidth  >= 0 required
        windowHeight >= 0 required
    `
})

layout.suggestValue('mine', 102)

layout.updateVariables()

console.log(layout.getValues())
// { mine: 102, windowWidth: 0, windowHeight: 7.5, modalHeight: 1020 }

Would something like this be a welcome addition to kiwi? Open for suggestions on changing the API etc.

thanks for a great module!!

Get the list of constraints

For debugging purposes, I am trying to get the list of all the constrains I have created. Is there a way to get them in a human readable format?

For instance, something like this:

[
    "variable A - variable B >= 10",
    "variable A + variable B <= 20",
    Etc.
]

Or do you have any hints on how I can implement that please apart from maintening a list each time a constraint is added?

Unable to build

I'm pretty sure this issue is purely down to not knowing which version of Typescript you were using to build this.
I've tried building using the version number in the package.json file, (by changing this line of the gruntfile to use node_modules/.bin/tsc, but that throws up the following error:

$ grunt
Running "exec:build" (exec) task
error TS6082: Only 'amd' and 'system' modules are supported alongside --out.
>> Exited with code: 2.
>> Error executing child process: Error: Command failed: node_modules/.bin/tsc --noImplicitAny -m commonjs -d -out lib/kiwi.js src/kiwi.ts
Warning: Task "exec:build" failed. Use --force to continue.

Aborted due to warnings.

So I tried then removing -m commonjs (as that's what the above seems to be complaining about), but then I can't use the project, and there is still an error trying to generate the docs (tmp/kiwi-no-internal-modules.js: Unexpected token } when running jsdoc2md)

Seemingly unused bits

I've been reading the code a bit, and am confused by a few bits that seem not to be used.

  • createMap takes a compare argument, but this isn't used. I don't think the various compare functions (like Constraint.compare) are used anywhere other than calls to createMap, so they are unused too.
  • The code in the thirdparty directory doesn't seem to be used.

Is my interpretation right? If so, I think these ought to be removed, since it's confusing to have them hanging around.

Thanks a bunch for any clarification!

Problem using the kiwi for 3d geometric constraints

Does someone know how to implement constraints on 2d or 3d geometries.

I have a problem on how to implement a distance constraint between two 3d vectors.

Imagine 2 points in the space

D = 10
P1 = [ 0, 0, 0 ]
P2 = [ 5, 0, 0 ]

How I can add a distance constraint between these two points.

Thanks

My code so far

// Create P1
const p1 = new Point3D(0, 0, 0);

// Create edit variables
const x = new kiwi.Variable('x');
const y = new kiwi.Variable('y');
const z = new kiwi.Variable('z');

this.kiwiSolver.addEditVariable(x, kiwi.Strength.strong);
this.kiwiSolver.addEditVariable(y, kiwi.Strength.strong);
this.kiwiSolver.addEditVariable(z, kiwi.Strength.strong);
this.kiwiSolver.suggestValue(x, v.x);
this.kiwiSolver.suggestValue(y, v.y);
this.kiwiSolver.suggestValue(z, v.z);
this.kiwiSolver.updateVariables();
p1.userData['kiwi'] = {x, y, z}

// Create P2
const p2 = new Point3D(100, 0, 0);

// Create edit variables
const x = new kiwi.Variable('x');
const y = new kiwi.Variable('y');
const z = new kiwi.Variable('z');

this.kiwiSolver.addEditVariable(x, kiwi.Strength.strong);
this.kiwiSolver.addEditVariable(y, kiwi.Strength.strong);
this.kiwiSolver.addEditVariable(z, kiwi.Strength.strong);
this.kiwiSolver.suggestValue(x, v.x);
this.kiwiSolver.suggestValue(y, v.y);
this.kiwiSolver.suggestValue(z, v.z);
this.kiwiSolver.updateVariables();
p2.userData['kiwi'] = {x, y, z}


const d = Math.sqrt(
  Math.pow((p1.kiwi.x.value() - p2.kiwi.x.value()), 2) +
  Math.pow((p1.kiwi.y.value() - p2.kiwi.y.value()), 2) +
  Math.pow((p1.kiwi.z.value() - p2.kiwi.z.value()), 2)
);
console.log({d}); // 100


// Here is the problem
const kiwi_distance_constraint = new kiwi.Constraint(
  Math.pow((p1.kiwi.x - p1.kiwi.x), 2) +
  Math.pow((p1.kiwi.y - p1.kiwi.y), 2) +
  Math.pow((p1.kiwi.z - p1.kiwi.z), 2), 
  kiwi.Operator.Eq
);
this.kiwiSolver.addConstraint(kiwi_distance_constraint);
this.kiwiSolver.updateVariables();

Implement updateConstant(constraint:Constraint, constant:number)

Currently, the only way to change the constant of an active constraint is to remove it and add a modified one. There should be a more efficient way to do this.
This function should throw immediately an error if the constraint is required.

Here is an example of a working implementation of this feature (Swift port of Rhea).

"unsatisfiable constraint" error puts solver into an inconsistent state

Hi, thanks for this project. It is small and easy to work with.

I have found what appears to be a bug in the handling of certain state changes.

As far as I can tell, the only way to know whether a new constraint will lead to a feasible solution is to try adding it and trap the "unsatisfiable constraint" error.

In such cases, the new constraint is not added to the solver.

However, the solver no longer behaves as expected at that point. Specifically, if a previously-existing constraint is then removed and re-added to the solver, it is then reported as unsatisfiable, even though it had already been confirmed.

The following script reproduces the issue:

const { Le, Ge } = kiwi.Operator;
const { required, strong } = kiwi.Strength;

const solver = new kiwi.Solver();

console.log(`Add variable x`);
const x = new kiwi.Variable("x");
solver.addEditVariable(x, strong);

console.log(`Add constraint x >= 2 [required]`);
const xge2 = new kiwi.Constraint(x, Ge, 2);
solver.addConstraint(xge2, required);

// Show that you can remove and restore existing constraints with no problem
console.log(`Remove constraint x >= 2 [required]`);
solver.removeConstraint(xge2);
console.log(`Restore constraint x >= 2 [required]`);
solver.addConstraint(xge2);

// Now let's add an unsatisfiable constraint
console.log(`Add constraint x <= 1 [required]`);
const xle1 = new kiwi.Constraint(x, Le, 1);
try {
  solver.addConstraint(xle1, required);
} catch (error) {
  if (error.message === "unsatisfiable constraint") {
    console.log(`Constraint was unsatisfiable, as expected`);
  } else throw error;
}

// Some sanity checks, which pass
console.log(
  `Does solver have constraint x >= 2?`,
  solver.hasConstraint(xge2) ? "yes" : "no"
);
console.log(
  `Does solver have constraint x <= 1?`,
  solver.hasConstraint(xle1) ? "yes" : "no"
);
console.log(
  `Does solver have variable x?`,
  solver.hasEditVariable(x) ? "yes" : "no"
);

console.log(`Remove constraint x >= 2 [required]`);
solver.removeConstraint(xge2);
// Solver should have no constraints at this point
console.log(`Restore constraint x >= 2 [required]`);
try {
  solver.addConstraint(xge2);
} catch (error) {
  if (error.message === "unsatisfiable constraint") {
    // This is unexpected, because the constraint was just in the solver...
    // and is now the only constraint, in any case, so must be satisfiable
    console.log(`Constraint was unsatisfiable!!`);
  } else throw error;
}

I have tried other things at this point. For example, you can add effectively the same constraint using a new variable:

console.log(`Create new โ€˜xโ€™ variable instance`);
// Name can be "x" or anything else
const x_b = new kiwi.Variable("x");

// Doesn't seem to matter whether you do this
// solver.addEditVariable(x_b, strong);
console.log(`Create new constraint x >= 2`);
const xge2_b = new kiwi.Constraint(x_b, Ge, 2);

console.log(
  `Does solver have variable new x?`,
  solver.hasEditVariable(x_b) ? "yes" : "no"
);

solver.addConstraint(xge2_b, required);

All in all, the signs point to a possible issue with the internal management of variables in the Solver.addConstraint method, particularly:

        // Creating a row causes symbols to be reserved for the variables
        // in the constraint. If this method exits with an exception,
        // then its possible those variables will linger in the var map.
        // Since its likely that those variables will be used in other
        // constraints and since exceptional conditions are uncommon,
        // i'm not too worried about aggressive cleanup of the var map.
        let data = this._createRow( constraint );
        let row = data.row;
        let tag = data.tag;
        let subject = this._chooseSubject( row, tag );

I hate to file a bug on a holiday, but I thought it might be useful to have for reference. I will dig into this further myself and report back.

Variables are never removed from `_varMap`

Variables are added to _varMap (in Solver) when constraints are added, but they are never removed, even if all the constraints that refer to them are removed. This could cause a memory leak in some situations.

"Unfork" kiwi.js?

This repo is a fork of nucleic/kiwi. Since that's a C++ implementation, it seems unlikely that there will be any PRs going back and forth, so I'm not sure if there's a continued purpose to having this relationship.

Ordinarily I wouldn't care, but apparently GitHub doesn't let you search in forks? So that's annoying. Not a huge deal though.

FWIW, it looks like the way to do this is to contact GitHub support (https://stackoverflow.com/questions/29326767/unfork-a-github-fork-without-deleting).

Thanks!

Compilation failure using library under typescript due to missing `thirdparty` folder

I'm trying to use kiwi.js in a typescript project and can't get it to compile. The issue is that lib/kiwi.d.ts references ../thirdparty/tsu.d.ts, but the whole thirdparty folder isn't included in the npm package, causing a compilation failure.

As a suggestion, the namespace system of code organization the lib uses is largely deprecated for external libraries in favor of the ES6-style module import/export statements. It would be great if the lib could be updated to use the module system. It doesn't look too complicated if you're interested in a PR.

Multiply two variables or expressions

I'd hoped to be able to constrain two lines to be parallel by rewriting the equation for the slope.

m1 == (p1.y - q1.y) / (p1.x - q1.x)
m2 == (p2.y - q2.y) / (p2.x - q2.x)
m1 == m2

algebra-cadabra:

(p1.y - q1.y) * (p2.x - q2.x) == (p2.y - q2.y) * (p1.x - q1.x)

Turns out I can't multiply two expression or variables, is this a fundamental restriction of kiwi or can it be extended in some way to support such things?

Kind Regards

web assembly (wasm) port?

I'd think leveraging web assembly/wasm could see some nice performance gains, since this library is inherently cpu bound.

Integer Variables

Hello,

Is it possible to force variable values to be integers?

For example, I want the value to be either 0 or 1 (nothing in between).

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.