Table of Contents
Cam Z-Up is a Java-based library for the creative coding environment Processing. Cam Z-Up flips Processing's default projection so that the positive z axis, (0.0, 0.0, 1.0), is the world up axis; the positive y axis, (0.0, 1.0, 0.0), is forward. The world origin, (0.0, 0.0, 0.0), is placed at the center of a sketch.
This library supports two- and three-dimensional graphics. In 2D, if the camera is imagined to be above the sketch looking down, the positive y axis is forward. If the camera is imagined to be looking from a sideview, the y axis is up.
If you can flip the y axis by either
- supplying -1.0 to scale's y parameter or
- supplying (0.0, -1.0, 0.0) to the final parameters of camera
without negative impact to your sketch, chances are you don't need this library.
While Cam Z-Up can help with more complex sketches, it is a general purpose library. It aims to make a number of small tasks easier than in vanilla Processing. It will not be as effective as specialist libraries.
Cam Z-Up is tested with Processing version 4.x. It's been around for a while, so it was overhauled to use the accepted library template on March 19, 2025, this commit.
For more thorough information, please refer to the documentation included within the distribution. For examples, see the examples
directory.
If you're not familiar with git and want to acquire this library from Github,
- Click on the green
Code
button in the upper right corner of this repository. - Select
Download ZIP
to start the download in your browser. - Unzip the download.
- Navigate through the directory
build\libs\
until you find ajar
file. - Drag and drop the
jar
onto an open Processing sketch.
Cam Z-Up is split into two packages: core
and friendly
. The friendly
package contains code compatible with Processing's API. Inside it, you'll find four graphics renderers:
Yup3
, which extendsPGraphicsOpenGL
, similar toP3D
;Zup3
, which also extendsPGraphicsOpenGL
;YupJ2
, which extendsPGraphicsJava2D
, a.k.a.JAVA2D
, the default Processing renderer based on the Java AWT library;Yup2
, which extendsPGraphicsOpenGL
, similar toP2D
, a "2.5D" renderer;
The FX2D
renderer, based on Java FX, is not distributed with Processing, so it's not supported here. The Yup3
renderer treats the positive y axis, (0.0, 1.0, 0.0), as world up.
This library's core
package includes basic utilities that were used to modify the Processing renderer. In this package, you'll find classes such as Vec2
, Vec3
and Quaternion
. The division between friendly
and core
is a protective measure. The aim is to retain the library's usefulness even as bugs in friendly
, or changes to the underlying Processing
library, cause trouble.
With the library installed, you can set up your Processing sketch like so:
// Import the library
import com.behreajj.camzup.friendly.*;
void settings() {
// Supply the renderer's path to size as the
// third argument.
size(128, 128, YupJ2.PATH_STR);
}
More experienced coders may wish to use createGraphics and/or getGraphics
to access the renderers directly.
import com.behreajj.camzup.core.*;
import com.behreajj.camzup.friendly.*;
YupJ2 graphics;
void settings() {
size(128, 128, YupJ2.PATH_STR);
}
void setup() {
// For OpenGL on Macs.
frameRate(60.0);
// Cast from PGraphics to your renderer.
graphics = (YupJ2)getGraphics();
}
Both createGraphics
and getGraphics
return PGraphics
; the result needs to be cast to the specific renderer. The benefit of accessing Cam Z-Up renderers directly, rather than through PApplet
, is that the renderers offer additional conveniences. For example, in the following snippet,
graphics.beginDraw();
graphics.background();
graphics.stroke();
graphics.ellipse(new Vec2(), new Vec2(25.0, 25.0));
graphics.endDraw();
background
and stroke
use default colors, while ellipse
and image
support Vec2
arguments.
Flipping the y axis changes the default rotational direction of a positive angle from clockwise to counter-clockwise.
This can be crucial when working with signed angles, such as those returned from Vec2.headingSigned
or Utils.atan2
, as it's important to understand the relationship between polar coordinates and the four quadrants of the Cartesian coordinate plane.
Signed angles can be converted to unsigned angles with floor modulo, mod
. This should not be confused with truncation modulo, fmod
. In Processing's Java mode, %
is truncation modulo; in Python mode, %
is floor modulo.
Vertex winding is also affected. Below, a piecewise Bezier curve is used to approximate a circle. The central contour uses the opposite (clockwise) vertex winding.
A renderer's vertex winding rule will dictate the fill of contours with the same winding. For more information, see the non-zero and the even-odd rule.
In 3D, the orientation of a spherical coordinate system depends on the up axis. For z-up, the equator of a spherical system rests on the camera's horizon; for y-up, the poles rest on the camera's horizon. The image below shows z-up.
Negative inclinations, in the range [-PI / 2.0
, 0.0
] will fall beneath the equator; positive inclinations, in the range [0.0
, PI / 2.0
], will fall above the equator. An inclination of -PI / 2.0
will return the South pole; an inclination of PI / 2.0
will return the North pole. A positive azimuth will head East from the prime meridian; a negative azimuth will head West.
In OpenGL renderers, texture coordinates default to NORMAL
textureMode. IMAGE
is not supported. This is for three reasons: (1.) the belief that IMAGE
is harder, not easier, to understand; (2.) recognition that NORMAL
is standard; (3.) methods in PGraphicsOpenGL
interfere with textureWrap REPEAT
and cannot be overidden by this library. That aside, as per usual, texture coordinates begin at (0.0, 0.0) in the top-left corner and end at (1.0, 1.0) in the bottom-right corner of the image.
I am not versed in color science, nor do I pretend to be. However, vanilla Processing's approach to color can't not be addressed, so I've introduced enough to allow users to get the job done, namely an Rgb
and Gradient
class. Color is not the central focus of this library and I'm not interested in debating the "correct" way to mix it. Do not use this library for advanced or photorealistic color work.
The Gradient
class allows you to create color ramps, including the following:
Viridis and Magma are color palettes used in data visualizations. Sepia and cyanotype replicate older photographic printing processes.
RYB color is included above because popular tutorials on "Color Harmony" (or "Color Theory") often assume a subtractive red-yellow-blue color model, even in the context of digital media. Processing defaults to additive sRGB, where cyan (#00ffff
) is the complement of red (#ff0000
), not green. This holds regardless of whether you use the HSB
or the RGB
colorMode.
This RYB wheel's limitations should be apparent from the above. Oranges are dilated while blues are compressed. Brighter greens, cyans and magentas are not achievable. Blues and greens are desaturated. This tutorial on harmonies is one of the better I've found.
A quick heuristic to decide if you are blending colors as you prefer is to take two complementary colors - typically red and green - which you predict will yield an ugly blend and sample them.
Here is a brief list of issues with this library and differences which may be unexpected. Some are unresolved bugs, some arise from the design philosophy of the library.
- Support for high density pixel displays may be lost; I cannot test this at the moment, so please report issues with
image
. - The arc implementation has been changed to
mod
the start and stop angles. It no longer responds to ellipseMode;RADIUS
is the default behavior. When given nonuniform scales, the minimum is taken. - The PShape class has numerous problems stemming from both its implementation and its design. I encourage using
CurveEntity
andMeshEntity
objects excepting the case where high poly countPShapeOpenGL
s are more performant. - shapeMode is not supported.
- textMode
SHAPE
is not supported. However you can retrieve glyph outlines from a PFont with theTextShape
class from thepfriendly
package. (Reminder: thePFont
needs to be loaded with createFont). - Color methods no longer promote
int
s in[0, 255]
to gray colors. Usefloat
s orRgb
objects instead. - Aside from the default blend, blendMode is no longer supported. The very concept of blending color in gamma-encoded standard RGB is flawed. See instead the
Img
class, which blends in LAB color space.
- The
image
function forPGraphicsJava2D
is ineffective, both in terms of frame rate and appearance. I recommend that an OpenGL renderer be used instead. Alternatively, rescale images to display size and tint them in a raster image editor. I have made an image function which removes some of the padding around the native renderer's image function in cases where aPImage
can be converted to ajava.awt.Image
insetup
. - As a consequence of how
image
function works above, dynamictint
ing is no longer supported inYupJ2
. - Using
YupJ2
'srotate
orrotateZ
will cause shapes with strokes to jitter. CORNER
is supported for rectMode,ellipseMode
and imageMode. However it is less intuitive with this library. For that reason,CENTER
is the default alignment.- OpenGL renderers do not recognize contours in meshes and curves.
- Neither 3D primitive, sphere and box, are supported; use
MeshEntity3
s instead. - A
Mesh3
material may not have both a fill and a stroke due to flickering in perspective cameras.
Many core Processing functions are marked final
, meaning they cannot be extended and modified by classes in this library; many fields are marked private
meaning they cannot be accessed and/or mutated. This is the one of the reasons for the limitations above.
null
-checks excepted, the goal of this library is not to throw exceptions, but to create. For that reason some liberties have been taken with mathematics.
acos
andasin
clamp the input value to the range-1.0
to1.0
so as to avoid exceptions.- As with Python, JavaScript and OSL,
x != 0
istrue
;true
is1
andfalse
is0
. - Where possible,
Vec2
,Vec3
andVec4
parallel GLSL'sbvec
. Examples include:Vec2 c = Vec2.lt(new Vec2(false, true), new Vec2(true, true));
andboolean d = Vec2.any(c);
. - As with shader languages, I try to protect against divide-by-zero errors when possible. Though mathematically incorrect,
div(x, 0.0) = 0.0
; in consequencefmod(x, 0.0)
andmod(x, 0.0)
returnx
. - Unlike GLSL,
fract
is defined asx - trunc(x)
, notx - floor(x)
. This library refers to the latter asmod1
. - The linear interpolation (
lerp
) method in this library uses the formula(1.0 - t) * a + t * b
, nota + t * (b - a)
. Processing uses the latter. Furthermore, Processing'slerp
is unclamped by default. This library Includes a clamped and unclamped version oflerp
; clamped is assumed to be the default. - The step provided to easing functions is always a scalar (a
float
). There are nostep
,smoothstep
andlinearstep
functions which generate the step to be supplied tomix
.mix
is, however, is defined in relevant classes. - A quaternion's real component is assumed to be its first element,
{ w, x, y, z }
. Its imaginary components are stored in a vector. This is in contrast to other APIs, such as Unity's. - The convention established by Java's
indexOf
function is to return-1
when an array or collection does not contain a query. Some collections in this library, particularlyMesh
s andCurve
s, match Pythonic idiom insofar as they accept negative indices toget
functions. For example,curve.get(-1)
will return the lastKnot
in a curve, provided that it is aclosedLoop
. As a consequence, the reciprocity between Java'sindexOf
andget
is broken. For example:curve.get(curve.knots.indexOf(elmNotInCurve)) != elmNotInCurve
. For this reason,contains
should always be preferred overindexOf
, and no customcontains
method should depend onindexOf
unlessget
s definition is guaranteed. - Between two vectors, the Hadamard product, is the default multiplication associated with the
*
operator.
🇹🇼 🇺🇦