npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

d3-ellipse-force

v0.1.1

Published

Force-directed graph where nodes are calculated as ellipses. Offers better layout than circular nodes when nodes are wide or tall, or vary, e.g. with text labels.

Readme

d3-ellipse-force

(Demo)

This plugin provides ellipseForce, an alternative for components manyBodies and collision in d3-force-module. EllipseForce can be used to create force-directed graph layouts where nodes are ellipses or unequal rectangles, e.g. labels or text snippets, which often require wide and low rectangles.

Though ellipseForce calculates elliptic forces, the graphical representation of the node can be a rectangle: Force-directed graphs don't generally work well if nodes are calculated as straight-angled objects -- elliptic shapes allow nodes to nudge and slide past each others in a way that is worth the risk of slight overlap in corners.

Typically force directed graphs treat nodes as circles that 'radiate' their force equally to all directions. EllipseForce changes the shape of this repulsion to be ellipse. Assume that our node is shaped as a wide and low ellipse. Now the repulsion is stronger when the other object is on the same level with the ellipse, on the longer radius and gets weaker as the object rotates towards the shorter radius. Same with the overlaps, if the overlap is happening on the longer radius of the ellipse, then the other object is pushed harder to that direction.

See comments in src/ellipseForce.js for further explanation. The code aims for readability, this is not intended to be a library to be maintained and perfected, this is more to provide a commented solution that you should modify and improve for your use.

In d3 v.4, force calculations are divided into components, where one component can be force to push nodes away from each other (manyBodies), another to make sure that they don't overlap (collision) and one to make links pull nodes together (links). Like many older force layout algorithms, ellipseForce treats pushing apart and avoiding overlaps as same task, thus replacing both manyBodies and collision. You can still use other d3-force components with it.

EllipseForce uses simulation's alpha, and will relax the tension in time like other forces.

A price we have to pay for better layout for labels is performance. EllipseForce doesn't use quadtrees, and as such doesn't scale for hundreds of nodes. It is developed in a hurry and I don't have the time or capacity to think if quadtrees would work at all and how should they be used with elliptic nodes.

Installing

If you use NPM, npm install d3-ellipse-force. But really, it won't install any dependencies, because the single function provided by this plugin doesn't have any strict dependencies. It is assumed to be used as force in d3-force simulation, and to actually draw things with d3, you'll need several other d3-modules. NPM package is mainly there to help bundling ellipseForce into a more complex project.

You can download the latest release.

The easiest way to get started with ellipseForce is to download the latest release, put the d3-ellipse-force.js or its minified version into same folder with your html file, and use minified, external d3-library to provide you with d3-force and the rest:

    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="d3-ellipse-force.js"></script>

API Reference

Use ellipseForce as force in d3's forceSimulation.

# ellipseForce([padding, innerRepulse, outerRepulse])

ellipseForce can take three parameters, padding(=4) innerRepulse(=.5) and outerRepulse(=.5).

  • padding is invisible padding added to radii of ellipsi, a reliable way to add space between nodes.
  • innerRepulse is used to calculate the strength of repulsion when nodes are inside each other, aka. overlap.
  • outerRepulse is the base strength of repulsion towards other, non-overlapping nodes. It dissipates fast.

# force.padding([my_padding]) <>

If padding is specified, sets the current padding to the specified size and returns the force. padding can be given a function that is called with the current node as argument to dynamically create the padding. If padding is not specified, returns the function to receive the current padding size, which defaults to function that returns 4, when called (with any arguments).

# force.innerRepulse([my_innerRepulse]) <>

If innerRepulse is specified, sets the repulsion for overlapping nodes to the specified amount and returns the force. If innerRepulse is not specified, returns the current repulsion, which defaults to 0.5.

# force.outerRepulse([my_outerRepulse]) <>

If outerRepulse is specified, sets the repulsion for non-overlapping nodes to the specified amount and returns the force. If outerRepulse is not specified, returns the current repulsion, which defaults to 0.5.

Example for including ellipseForce as a force (compare to forceSimulation):

    var simulation = d3.forceSimulation(nodes)
        .force("charge", d3.ellipseForce())
        .force("link", d3.forceLink(links))
        .force("center", d3.forceCenter());

Other

I have struggled with rectangular nodes and force-directed graphs several times while developing Kataja, visualisation tool for biolinguistics. It would be useful to have a dynamic algorithm for placing those syntactic elements that don't have one fixed place in a tree. When nodes are snippets of text of various length, common algorithms either reserve too much or too little space for each of them. My many attempts to modify the algorithms to understand rectangles resulted in jumpy graphs that didn't settle nicely. These ellipses are the final, best result. It seems that other people in visualisation have experienced similar problems with force-directed algorithms, so I decided to let the solution fly as a d3 plugin.