Chapter III.

SVG

ear.svg

SVGs are vector-based, look great at any resolution, and have fantastic interoperability with plotters, cutters, and web browsers.

Introduction

var svg = ear.svg()

This SVG initializes with a 300x150 blank canvas, but doesn't exist on screen. Everything adheres to the HTML DOM standard with methods like appendChild and setAttribute.

Your first task is to append it to the DOM.

var svg = ear.svg()
document.body.appendChild(svg)

Elements made with this library are HTML DOM Elements and work with DOM level 1 methods.

Or, more conveniently:

var svg = ear.svg(document.body)

the svg initializer doesn't require any arguments, but optionally can accept:

The function parameter is an extremely convenient way of creating an SVG.

ear.svg((svg) => {
svg.circle(0, 1, 2);
});

Combining both of these looks like

ear.svg(document.body, (svg) => {
svg.circle(0, 1, 2);
});

This is a very convenient way to wait for page load and encapsulate all your code within a function-scope.

Drawing Shapes

Drawing a shape is a method called on the shape's parent:

var svg = ear.svg();
svg.circle(0, 1, 2);

These are the simple primitives:

svg.circle(x, y, radius)
svg.ellipse(x, y, rx, ry)
svg.line(x1, y1, x2, y2)
svg.rect(x, y, width, height)
svg.text(string, x, y)

The type of all arguments are numbers, except for "string" on text.

All primitives are initially black fill with no stroke, causing a line to be invisible. If you draw a line remember it needs to be styled.

Polygon, Polyline

svg.polygon(points)
svg.polyline(points)

Polygon and polyline take a series of points, which can come in the form of a list of arrays of numbers, or an array of x,y objects.

[ [4, 5], [1, 1], [9, 12] ]
[ {x:5, y:2}, {x:1, y:1} ]

Paths

Paths are the most powerful SVG drawing tool. Think of them like a pen tool; drawing is done by stacking functions one after another.

svg.path()
  .moveTo(50, 50)
  .LineTo(100, 150)
  .curveTo(200, 200, 300, 0)

Path command names follow the spec, including capitalization for absolute/relative.

Groups

So far we have only drawn shapes onto the SVG, the base layer. If you create a group, you can draw shapes into it instead and manage layer order of shapes.

var group = svg.g()
svg.rect(0, 0, 20, 20)
group.circle(10, 10, 20)

In this example, this circle is below the rectangle.

Viewbox

The actual size on screen is managed by the DOM. The viewbox is our tool to set the size and scale of our drawing canvas.

svg.size(-1, -1, 2, 2)  // x, y, width, height

The first two parameters define the top left corner; followed by the width and height.

svg.onMove = function (mouse) { }
{ x: , y: }

We can easily zoom into unit-space where drawing a unit-circle fills the canvas. This makes it especially great playing with math.

Notice the equations being rendered, notice how they appear upside-down.

Y-Axis

Because of a computer standard, the SVG y-axis increases downwards.

It's very easy to invert the y-axis if you want.

svg.scale(1, -1)

Style

Style is applied by chaining methods, each name is an SVG attribute where kebab-case becomes camelCase.

svg.ellipse(40, 30, 20, 10)
  .fill("crimson")
  .stroke("#fcd")
  .strokeDasharray("5 10")
  .strokeWidth(5);

Interactivity

draw

var points = []
var shape = svg.polygon() svg.onMove = function (mouse) { points.push(mouse); if (points.length > 100) { points.shift(); } shape.setPoints(points); };

An SVG created with this library comes with 3 touch methods.

These are basically wrappers around the standard Web API MouseEvent but with viewBox coordinates included.

{
  x: 0.000,
  y: 0.000,
  position: {x: 0.000, y: 0.000},
  pressX: undefined,
  pressY: undefined,
  press: {x: undefined, y: undefined},
  buttons: 0,
}

The event object is the MouseEvent object with additional properties. The buttons key is one of the many standard properties already included.

Controls

svg.controls(4)

A control point is like a touch event that remains in the place where you left it.

svg.controls(4)
  .onChange((point, i, points) => {
    console.log(point) // a coordinate, like [23, 102]
  })

The onChange handler fires every time a point is moved. The three arguments (item, iterator, array) reflect the same found in familiar Javascript methods like map, filter.

svg.controls(4)
  .svg(function () { return ear.svg.circle(svg.getWidth() * 0.05).fill("#e53"); })
  .position(function () { return [random(svg.getWidth()), random(svg.getHeight())]; })
  .parent(back)
  .onChange(function (point, i, points) {
    l1.setPoints(points[0], points[1]);
    l2.setPoints(points[3], points[2]);
    curve.clear().moveTo(points[0]).curveTo(points[1], points[2], points[3]);
  }, true);

Animation

svg.play = function (e) {
// animation code here
}

The play function uses the DOM method requestAnimationFrame and will fire with as little delay as your display allows, typically around 60 fps.

If you come from a background using Processing or openFrameworks, the following code might look fine, but it contains a serious issue in the case of this SVG library.

// bad code!
svg.play = (e) => {
svg.circle(50, 50, 10)
// after a few seconds, your svg
// will contain hundreds of circles
// slowing down the renderer
}

the SVG primitives are actual objects that get appended to the SVG. There are no pixels, and there is no automatic screen clearing. At the very least, call removeChildren somewhere in your animation loop.

svg.removeChildren()

Better yet, consider this approach to animation.

var circle = svg.circle(50, 50, 1)

svg.play = (e) => {
circle.setRadius(e.time)
}

And at any time, you can stop the animation loop.

svg.stop()

Rabbit Ear Integration

This SVG library was written to be fully independent and work alone if needed. (~35kb)

https://robbykraft.github.io/SVG/svg.js

But when integrated with Rabbit Ear additional features become unlocked.

Graph

The SVG library can create renderings of FOLD graphs.

svg.graph(fold)
g.graph(fold)

The graph svg element is a nested layering of g elements that organize vertices, edges, and faces into circles, paths, and polygons respectively. Both svg and g can call this method and it will append as a child.

var drawing = svg.graph(fold)
drawing.edges.mountain.stroke("blue")
drawing.faces.fill("white")

the return value is a g element with some convenient getters that return the inner groups, allowing you to target components for styling.

var graph = ear.graph()
graph.svg().appendTo(svg)

This method is repeated as a method on Rabbit Ear's graph class, which includes cp and origami. It isn't automatically appended, so make sure to append it.

See more: fold-to-svg.

Math Primitives

Rabbit Ear's math primitives gain an svg method that converts them into path elements.

ear.segment(10, 10, 50, 60).svg()
.appendTo(svg)

This returns an SVG path object.

.appendTo(svg)

These elements aren't created as children of any parent. You'll need to append it.

Finally, there are small features here and there, like event handlers return mouse location data as vector objects, making calculations a lot easier.