Home

Awesome

Cirque

Utilities for negotiating between circles and paths in SVG. See demo.

Cirque

npm install --save cirque (or yarn add cirque)

Functions

<a href="#interpolatePath" name="interpolatePath">#</a> interpolatePath(a, b)

Interpolates between two SVG path description strings.

Expects a circle path (which gets sampled) and a polygonal chain path (which gets oversampled when necessary), although two circles work just by ordinary interpolation, and two chains should work in many cases as well.

<a href="#interpolatePaths" name="interpolatePaths">#</a> interpolatePaths(a, b)

Individually interpolates (using interpolatePath) between corresponding items in two parallel arrays of SVG path strings.

<a href="#circlePath" name="circlePath">#</a> circlePath(circle)

Converts the passed circle object to an SVG path string (i.e. a circle path consisting of two arc commands).

<a href="#geoToCircle" name="geoToCircle">#</a> geoToCircle(geometry, [path], [radius], [object])

Converts the geometry object to a circle object sharing its centroid.

Circle objects take the form: { x, y, r }

<a href="#polygonToCircle" name="polygonToCircle">#</a> polygonToCircle(polygon)

Converts a polygon to a circle object sharing its centroid.

<a href="#avoidOverlap" name="avoidOverlap">#</a> avoidOverlap(objects, [margin])

Pass an array of circle objects to separate colliding circles so that no overlaps remain. Mutates objects in place. Margin (minimum gap, or maximum overlap if negative) defaults to 0. (Uses d3.forceCollide.)

<a href="#radiusScale" name="radiusScale">#</a> radiusScale(area, value)

Receives total area and total value as arguments, and returns a D3 scale in which the area of a circle with the given radius corresponds to a linearly-scaled value.

Examples

let render  // Given a function that renders SVG paths
let path    // Given a geo path generator

Example: geometry

import { geoToCircle, circlePath, interpolatePath } from 'cirque'

let geometry  // Given a GeoJSON Polygon or MultiPolygon geometry

const interpolator = interpolatePath(
  path(geometry),
  circlePath( geoToCircle(geometry, path) )
)

d3.transition().tween('shape', () => t => { render( interpolator(t) ) })

Example: features

import * as cirque from 'cirque'

let features  // Given an array of GeoJSON Polygon or MultiPolygon features

const scale = cirque.radiusScale( path.area(mergedFeatures), 7.5e9 )
const circles = features.map(feature =>
  circle.geoToCircle(feature, path, scale(feature.properties['population']))
)

const separatedCircles = cirque.avoidOverlap(circles)
const circlePaths = separatedCircles.map(cirque.circlePath)
const interpolator = cirque.interpolatePaths(features.map(path), circlePaths)

d3.transition().tween('shapes', () => t => { render( interpolator(t) ) })

Approach

Limitations

The chain path is a SVG path description of a polygonal chain (i.e. polyline, composite Bézier, etc.) containing any SVG path commands except arcs, support for which is planned.

The circle path is a SVG path description containing an M command followed by at least one A command (but typically two). circlePath is a utility for generating simple, compatible circle paths. Some more flexiblility in the format may come in the future, including (optionally) adhering to winding order.

Rationale

Just as a lack of color is physically considered black (though artistically often considered white), a lack of shape can in a certain sense be called a circle (or a n-sphere generally): no discrete segmentation, and no starting point any better than another.

This shapelessness is desirable for comparing values in a controlled way (say, in a bubble map) to minimize distortion and distraction.

The tools in this package amount to a method for going between precise forms such as geographic areas, and corresponding value-sized bubbles, while maintaining constancy.

Shape morphing alternatives

Discussion and contribution

Open an issue or pull request with either high-level use cases or practical support tickets, or contact us on twitter. We intend to keep this package focused on its stated mission, but advice, critique, and experiences are very welcome.