Deeper paths
With Zdog you are able to draw pseudo-3D graphics with several ad-hoc classes, such as Ellipse or Cone. Behind these constructs, however, there lies a Shape class, built with several properties and a prominent path key.
new Zdog.Shape({
// ...,
path: [
// ...
]
}); The class works similarly to the <path> element in SVG, with a sequence of instructions. But there are considerable differences, as well as challenges and creative solutions.
Where the element describes a shape in the d attribute, with a string.
<path d="M -4 3 0 -3 4 3 Z" /> A Zdog Shape creates the same figure with an array and a set of coordinates.
new Zdog.Shape({
// ...,
path: [
{ x: -4, y: 3 },
{ x: 0, y: -3 },
{ x: 4, y: 3 }
]
}); Consider the triangle you’ve just seen in the two snippets. In the body of an <svg> node the shape is drawn point by point, marking the position of the vertices.
Behind the scenes I’ve styled the polygon with a fill, but also a stroke, making sure to round the edges. Not only to please your eyesight with a softer visual, but also to match another Zdog feature you’re about to appreciate.
In the context of an illustration, the Shape class introduces the triangle with the same vertices. These appear in the array, and each with its own object. You detail the coordinates with the x and y properties, positioning the points on the corresponding axes.
For its part, Zdog adds a stroke on the basis of the input color, but does not paint between the points. For this, you have to set the fill property to true.
new Zdog.Shape({
// ...,
color: 'hsl(0, 0%, 70%)',
fill: true,
path: [
{ x: -4, y: 3 },
{ x: 0, y: -3 },
{ x: 4, y: 3 }
]
}); But why is that? Consider the result, tilted on the x axis and then the y axis.
Zdog uses the stroke, a rounded stroke, to emulate depth. As you rotate the shape the library projects the points so that, even in 2D, your eyes are convinced. The triangle lives and breathes in three dimensions.
The effect is convincing, but subtle as well. If you want a thicker shape, not just an outline, but a wedge, you have to build it yourself side after side. Luckily, you can automate the process with a few lines of code.
First, we need a reference to the triangle, to the instance of the Shape class.
const shape = new Zdog.Shape({
// ...,
}); In the variable you find an object, and in this object, the array of points. With this information, thickness is a matter of choosing how deep should the wedge be.
const depth = 5; With the value, the easiest step is to repeat the triangle. Zdog offers the copy method on the instance of every class.
shape.copy(); But copying the shape as-is would be silly — the face would overlap with the original. In line with our goal of adding depth, you can offset the copy on the z axis with the translate property. Pay attention, though. Higher z values have the shapes move closer to your point of view. If you want the opposite change, you would need to move the copy in the opposite direction.
const z = depth * -1;
shape.copy({
translate: { z }
}); With one side complete we can move on to the more complex portions, the sections gluing the two parts together. But in the path array we have everything we need.
const { path } = shape;
for (let i = 0; i < path.length; i++) {
// ...
} Looping through the collection we are able to evaluate the vertices in pairs.
const { x: x1, y: y1 } = path[i];
const { x: x2, y: y2 } = path[(i + 1) % path.length]; In so doing, we gain access to the different segments. Extracting the starting pair is an easy feat — refer to the object at the incrementing index. Be warned about the ending couple, however; this one refers to the point at the index next to the current one, and you have to be mindful of the array’s length. Connecting the last vertice to the first, you need to go back to the very first item, to the beginning of the array.
With the coordinates we finally draw the sides. And for this we can repeat the Shape class. Not the instance, but the initial construct. Here, use the coordinates with the chosen depth value, drawing the quadrilateral over the z axis.
new Zdog.Shape({
// ...,
path: [
{ x: x1, y: y1, z: 0 },
{ x: x1, y: y1, z: z },
{ x: x2, y: y2, z: z },
{ x: x2, y: y2, z: 0 }
]
}); You are taking a detour, the long way round on the additional axis. And eventually, realize the shape.
I’ve used a distinct color for the copy and the sides, a darker version of the same hue, but that is up to your preference, how you choose to further stress the presence of the deeper picture.
Zdog and the path property support more types of instructions, to draw arcs and bezier curves, but even with straight lines, the code helps you achieve a lot. Especially with more elaborate shapes.