IPA: /sost/, from Amharic: ሶስት "three"
Like the canvas, but in three dimensions
As of right now, Sost is still very much in the alpha stage of development. You can track what needs to be done and what's getting done in the todos. This README will likely be updated as the project progresses to feature the latest developments.
This project, due to its alpha status, has not yet been pushed to the npm registry, so you'll need to build it from the source. Unfortunately, there is no complete documentation for the package as internal structure is still rapidly changing. However, the guide presented here documents most of the common functions.
To start, clone the repo and install the required dependencies:
git clone https://github.com/quantum9innovation/sost
cd sost; yarn install
If you plan on using Sost in native Node.js as opposed to in the browser, you'll need to install canvas
to create the initial 2D rendering context.
Sost is designed to work both in a native Node.js environment (through the canvas
package) and in a browser environment (using Browserify and Terser).
To see some examples of Sost running in Node.js, simply run yarn test
from the project root directory and check artifacts for the results.
For a web demonstration, serve the project root directory (npx live-server
will work fine) and launch index.html in the browser (drag to rotate the view).
If the tests work, you should see an image like the one below appear in the artifacts directory.
Above:
The x, y, and z axes are displayed as red, blue, and green lines, respectively. In the center lies a white point demarcating the origin. The three planes intersecting that point trace out part of the xy, xz, and yz planes.
As the majority of time is being spent on development, this guide is quite brief and focused on the core functionality. More details will be provided when formal documentation is available.
The first thing to do is to generate a 2D canvas. Even though Sost initializes its own canvas, it requires a base canvas to project the 3D objects onto. Sost works with both the canvas
package in Node.js and an actual HTML5 canvas.
If you're using a browser, you'll need to load the browser-ready version of Sost from dist/sost.min.js
, which is generated by yarn build
.
Now, create the canvas and get its 2D rendering context to be passed into Sost:
const canvas = ...
const ctx = canvas.getContext('2d')
In Sost, the canvas initializer is a function that takes a canvas context and three dimensional variables.
At this point, it's important to briefly outline the three main "spaces" in which Sost operates.
The first and most basic of which is the pixel space, which deals with actual 2D coordinates on the ctx
variable.
The second is known as "point space," because it is a user-defined 3D space, which Sost uses to project objects onto 2D pixel space.
Point space is defined by three dimensions: width, height, and depth, which in turn tells Sost how to scale the objects it draws in 3D space onto the 2D canvas.
The middle of the canvas represents the origin of the point space, which is the point
At this point you may be wondering what the third space is. The third space is essentially the same as point space, except that it can be scaled and rotated in three dimensions. This space is known as the Cartesian space. In essence, every point that you tell Sost to draw in Cartesian space goes through two transformations before it is eventually drawn onto the Canvas. The first transformation transforms Cartesian space into point space, and the second transforms point space to pixel space. Point space, in this sense, acts as a static intermediary between Cartesian and pixel space. Objects drawn in point space won't rotate, scale, or otherwise change while those in Cartesian space will. Most high-level objects in Sost are drawn in Cartesian space, while lower-level, more primitive objects are drawn in point space. By default, point space has a scaling factor of 1, making it the same size as Cartesian space without any zoom applied, and a perspective such that rays from a viewer are perpendicular to the XZ plane.
To create a 3D canvas, we need to use the Canvas3D
method and pass in the dimensions of our point space in the order of width (x), height (z), and depth (y).
For example, if we wanted to create a canvas defined by a cube with side length 2 centered at the origin, we would use:
const three = new sost.Canvas3D(ctx, 2, 2, 2)
The eight corners of this cube are given by the vertices
We can set an initial perspective for our Cartesian space by accessing the built-in Camera object.
Perspectives are passed in as an array of two angles, the first representing rotation over the xy plane (zoom
property.
Translating the origin can be achieved by setting the center
property to the new origin coordinates.
Let's set a perspective of
three.Camera.angle = [Math.PI / 4, Math.PI / 4]
Sost currently supports three types of objects: points, lines, and polygons.
Points are drawn as a circle centered at the point's coordinates with a radius equal to three.pointSize
and color three.pointStyle
.
Lines are drawn between a pair of provided coordinates with thickness three.lineWidth
and color three.strokeStyle
.
Polygons are drawn between three or more coordinates with color three.fillStyle
.
Stroke/fill modes can be selectively enabled or disabled for polygons by switching the three.fill
and three.stroke
properties.
Let's start by drawing the three axes of our point space.
// Draw the x axis in red
three.strokeStyle = 'red'
three.line([-1, 0, 0], [1, 0, 0])
// Draw the y axis in blue
three.strokeStyle = 'blue'
three.line([0, -1, 0], [0, 1, 0])
// Draw the z axis in green
three.strokeStyle = 'green'
three.line([0, 0, -1], [0, 0, 1])
We can also plot the origin in white:
three.pointSize = 5
three.pointStyle = 'white'
three.point([0, 0, 0])
Lastly, we'll make use of the polygon
method to draw the xy, xz, and yz planes.
// Turn off stroke
three.stroke = false
// Define plane endpoints as coordinate arrays
const XY = [
[-0.25, -0.25, 0],
[-0.25, 0.25, 0],
[0.25, 0.25, 0],
[0.25, -0.25, 0]
]
const XZ = [
[-0.25, 0, -0.25],
[-0.25, 0, 0.25],
[0.25, 0, 0.25],
[0.25, 0, -0.25]
]
const YZ = [
[0, -0.25, -0.25],
[0, -0.25, 0.25],
[0, 0.25, 0.25],
[0, 0.25, -0.25]
]
// Draw the xy plane in translucent purple
three.fillStyle = 'rgba(255, 0, 255, 0.25)'
three.polygon(XY)
// Draw the xz plane in translucent yellow
three.fillStyle = 'rgba(255, 255, 0, 0.25)'
three.polygon(XZ)
// Draw the yz plane in translucent teal
three.fillStyle = 'rgba(0, 255, 255, 0.25)'
three.polygon(YZ)
Note:
If you are using TypeScript, you may get an error with the above code sample that tells you a type cannot be converted to
vec3d
, which is a special internal type used in the Sost library to store arrays of length3
. When using a variable as a parameter, TypeScript is unable to ensure that the length of that variable is unchanged, and will therefore throw an error stating that the types passed are not compatible withvec3d
. To fix this, you can either pass the arrays directly to thepolygon
method, or copysost/primitives.d.ts
, import thevec3d
type, and assignXY
,XZ
, andYZ
to have typevec3d[]
when defined. You may need to do this whenever you use variables as arguments to Sost drawing functions.
Viola! We get the output generated by our testing suite. For comparison, see the artifacts directory.
As a more advanced sidenote, the point
, line
, and polygon
methods have primitive equivalents (which operate in point space rather than Cartesian space) given by appending a p_
prefix to each of their names: p_point
, p_line
, and p_polygon
.
There's also a clear
method, which completely clears all data drawn to the canvas, and is useful when you want to change perspective and need Sost to redraw the scene.
Sost is very much still a work in progress and there is lots to be done. If you want to help out, it is highly recommended that you fork the repository and view the todos as well as any open issues for work that you can help with. Your contribution is much appreciated!