circlepacker

circle packing in a webworker

Usage no npm install needed!

<script type="module">
  import circlepacker from 'https://cdn.skypack.dev/circlepacker';
</script>

README

circlepacker

downloads

npm install circlepacker

what is it?

circlepacker demo

a circlepacking algorithm that executes in a webworker, so it doesn't clog the ui thread of your browser.

how to use it

the easiest way to use this library is to create a CirclePacker instance and call it`s methods. it is inteded to be used in the browser

please note: circlepacker does not handle the rendering of circles. in merely calculates the circle positions. in the the examples folder, you'll find some demos that show you how to handle rendering.

reference

CirclePacker(options)

addCircles(circles), addCircle(circle), setBounds(bounds), setTarget(position), setCenteringPasses(number), setCollisionPasses(number), setDamping(number), setCenterPull(boolean), update(), dragStart(circleId), drag(circleId, position), dragEnd(circleId), pinCircle(circleId), unpinCircle(circleId), setCircleRadius(circleId, number), setCircleCenterPull(circleId, boolean) destroy()

CirclePacker(options)

returns a new circlepacker instance. it accepts the following options:

    var packerOptions = {
        // the point that the circles should be attracted to
        // REQUIRED
        target: { x: 50, y: 50 },

        // the bounds of the area we want to draw the circles in
        // REQUIRED
        bounds: { width: 100, height: 100 },
    
        // the initial position and sizes of our circles
        // it is possible to add more circles later
        // each circle should have a unique id, a position and a radius
        // REQUIRED
        circles: [
            { id: 'circle1', radius: 34, position: { x: 32, y: 54 }, isPulledToCenter: true, isPinned: false },
            { id: 'circle2', radius: 64, position: { x: 24, y: 42 }, isPulledToCenter: true, isPinned: false },
            { id: 'circle3', radius: 53, position: { x: 23, y: 21 }, isPulledToCenter: true, isPinned: false }
        ],

        // true: continuous animation loop
        // false: one-time animation
        // OPTIONAL. default: true
        continuousMode: true,
        
        // correctness of collision calculations.
        // higher number means means longer time to calculate
        // OPTIONAL
        // default = 3
        collisionPasses: 3,
        
        // number of centering animations per frame.
        // higher number means faster movement and longer time to calculate
        // OPTIONAL
        // default = 1
        centeringPasses: 2,

        // callback function for when movement started
        // can get called multiple times
        // OPTIONAL
        onMoveStart: function () { },

        // callback function for updated circle positions
        onMove: function ( updatedCirclePositions ) {
            // draw logic here...
        },

        // callback function for when movement ended
        // can get called multiple times
        // OPTIONAL
        onMoveEnd: function () { }
    };

    var packer = new CirclePacker( packerOptions );

back to reference

addCircles(circles)

add an array of new circles. each circle should have a unique id, a position and a radius.

    packer.addCircles( [
        { id: 'circle4', radius: 21, position: { x: 12, y: 27 }, isPulledToCenter: false, isPinned: false },
        { id: 'circle5', radius: 64, position: { x: 14, y: 42 }, isPulledToCenter: false, isPinned: false }
    ] );

back to reference

addCircle(circle)

add a single new circle. a circle should have a unique id, a position and a radius.

    packer.addCircles( { id: 'circle6', radius: 21, position: { x: 12, y: 27 } } );

back to reference

setBounds(bounds)

update bounds. a bounds object should have a width and a height.

    packer.setBounds( { width: 200, height: 300 } );

back to reference

setTarget(position)

updates the target position. a position object should have x and y values.

    packer.setTarget( { x: 21, y: 29 } );

back to reference

setCenteringPasses(number)

updates number of centering passed. should an integer >= 1. high values can impact performance.

    packer.setCenteringPasses( 3 );

back to reference

setCollisionPasses(number)

updates number of collision passed. should an integer >= 1. high values can impact performance.

    packer.setCollisionPasses( 3 );

back to reference

setDamping(number)

set damping. this affects the movement speed of the circles. value should be a float between 0 and 1. the default value is 0.025

    packer.setDamping( 0.01 );

back to reference

setCenterPull(boolean)

set center pull. if set to false, circles collide, but are not pulled to the center. the default value is true.

    packer.setCenterPull( false );

back to reference

update()

starts calculation. useful if continuousMode was set to false.

    packer.update();

back to reference

dragStart(circleId)

indicate that we're about to start dragging this circle. this is usually called in a mousedown or a touchstart event handler.

    packer.dragStart( 'circle2' );

back to reference

drag(circleId, position)

update position of dragges circle. a position object should have x and y values. this is usually called in a mousemove or a touchmove event handler.

    packer.drag( 'circle2', { x: 30, y: 45 } );

back to reference

dragEnd(circleId)

indicate that we're done dragging this circle. this is usually called in an mouseup or a touchend event handler.

    packer.dragEnd( 'circle2' );

back to reference

pinCircle(circleId)

pin circle. this means that the circle is static and will not move. other circles will still be bounce off of it.

    packer.pinCircle( 'circle2' );

back to reference

unpinCircle(circleId)

unpin circle. this means that the circle is no longer static and will start colliding with other circles as normal.

    packer.unpinCircle( 'circle2' );

back to reference

setCircleRadius(circleId, number)

change the radius of a circle.

    packer.setCircleRadius( 'circle2', 20 );

back to reference

setCircleCenterPull(circleId, boolean)

change the isPulledToCenter value of a circle. if it is set to false, the circle is not pulled to the center. (it still collides with other circles).

    packer.setCircleCenterPull( 'circle2', true );

back to reference

destroy()

tell circlepacker instance we're done and won't be needing it again. it terminates the webworker. useful for lifecycle hooks in single page web apps.

    packer.destroy();

back to reference

development

the source code is located in the src folder, the built source files are located in the dist folder.

npm run build will run the build script (build.js) that compiles all files in the dist folder.

license

mit

credits

Mario Gonzalez <mariogonzalez@gmail.com> Georg Fischer <snorpey@gmail.com>

large parts of the circle packing algirithm are based on the CirclePackingJS repo by @onedayitwillmake (mit licensed)

missing something?

found a bug? missing a feature? instructions unclear? are you using this library in an interesting project? maybe open an issue or a pull request to let me know. thanks!

most importantly

thank you for taking a look at this repo. and reading the readme file until the end. have a great day :)