A comprehensive guide to understanding the shape rendering architecture
GCanvas is a modular 2D rendering and game framework built on top of the HTML5 Canvas API. At its core, it uses a layered approach to rendering that provides a clean, declarative way to draw and manipulate shapes on the canvas.
The rendering pipeline in GCanvas follows a hierarchical structure that builds from basic positioning to complex visual elements. This hierarchy provides a consistent interface and allows for sophisticated compositions and transformations.
The rendering pipeline consists of a chain of classes that inherit from each other, with each layer adding more functionality. Let's explore each layer in detail:
The Euclidian class is the foundation of all visual elements in GCanvas. It provides the most basic
properties needed for positioning and sizing objects.
// Create a basic Euclidian object
const basicShape = new Euclidian({
x: 100,
y: 100,
width: 50,
height: 30
});
// Position and size can be accessed and modified
console.log(basicShape.x, basicShape.y); // 100, 100
console.log(basicShape.width, basicShape.height); // 50, 30
// The object validates properties to prevent errors
basicShape.width = 80; // Valid
// basicShape.width = "invalid"; // Error: Invalid property value: width invalid
The Geometry2d class extends Euclidian by adding spatial constraints and bounding box
calculations. This allows for controlled positioning and more advanced spatial operations.
// Create a Geometry2d object with constraints
const constrainedShape = new Geometry2d({
x: 100,
y: 100,
width: 50,
height: 30,
minX: 50,
maxX: 150,
minY: 50,
maxY: 150,
crisp: true // Ensures positions and dimensions are whole numbers
});
// Constraints are applied when updating
constrainedShape.x = 40; // Will be constrained to minX (50)
constrainedShape.update();
console.log(constrainedShape.x); // 50
// Get bounding box information
const bounds = constrainedShape.getBounds();
console.log(bounds); // { x: 50, y: 100, width: 50, height: 30 }
// Get local (top-left) position relative to center
const local = constrainedShape.getLocalPosition();
console.log(local); // { x: 25, y: 85 }
The Renderable class extends Geometry2d by adding visual properties and rendering
capabilities. It handles the visibility, opacity, and shadows of visual elements.
// Create a Renderable object
const renderableShape = new Renderable({
x: 100,
y: 100,
width: 50,
height: 30,
visible: true,
opacity: 0.7,
shadowColor: "rgba(0,0,0,0.5)",
shadowBlur: 5,
shadowOffsetX: 3,
shadowOffsetY: 3
});
// Control visibility
renderableShape.visible = false; // Hide the object
renderableShape.visible = true; // Show the object
// Control opacity (0-1)
renderableShape.opacity = 0.5; // 50% opacity
// The render() method will apply these properties
// and then call draw() to render the actual shape
The Transformable class extends Renderable by adding transformation capabilities. It
handles rotation and scaling of visual elements.
// Create a Transformable object
const transformableShape = new Transformable({
x: 100,
y: 100,
width: 50,
height: 30,
rotation: Math.PI / 4, // 45 degrees
scaleX: 1.5,
scaleY: 0.8
});
// Rotation and scaling can be adjusted
transformableShape.rotation = Math.PI / 6; // 30 degrees
transformableShape.scaleX = 2.0;
transformableShape.scaleY = 2.0;
// The applyTransforms() method applies these transformations
// by modifying the canvas context state
The Shape class extends Transformable and serves as the base class for all visual
elements in GCanvas. It adds styling properties and is extended by specific shape implementations like
Circle, Rectangle, etc.
// Create a Shape object (base class for visual elements)
const shape = new Shape({
x: 100,
y: 100,
width: 50,
height: 30,
color: "#ff0000", // Fill color
stroke: "#000000", // Stroke color
lineWidth: 2, // Stroke width
lineJoin: "round", // Line join style
lineCap: "round" // Line cap style
});
// Specific shape classes (Circle, Rectangle, etc.) extend Shape
// and implement their own draw() method
ctx.fill();
ctx.stroke();
ctx.restore();
// Draw triangle
ctx.save();
ctx.translate(triangle.x, triangle.y);
ctx.beginPath();
ctx.moveTo(0, -triangle.size/2);
ctx.lineTo(triangle.size/2, triangle.size/2);
ctx.lineTo(-triangle.size/2, triangle.size/2);
ctx.closePath();
ctx.fillStyle = triangle.color;
ctx.strokeStyle = triangle.stroke;
ctx.lineWidth = triangle.lineWidth;
ctx.fill();
ctx.stroke();
ctx.restore();
GCanvas provides a variety of shape classes that extend the base Shape class. Each specialized
shape implements its own draw() method to render the specific geometry.
Circular shape with radius
Rectangular shape with width and height
Square shape with equal sides
Triangular shape
Star shape with configurable points
Multi-sided shape
Text rendering with styling
Heart-shaped geometry
Simple line segment
Cross shape
Cloud-like shape
3D cube with perspective
3D cylinder with perspective
3D cone with perspective
3D sphere with shading
3D prism with perspective
Ring or donut shape
Diamond shape
SVG path rendering
The Group class is a special container that inherits from Transformable. It allows
multiple shapes to be grouped together and transformed as a single unit.
// Create a Group to hold multiple shapes
const group = new Group({
x: 100,
y: 100
});
// Add shapes to the group
const rect = new Rectangle({
width: 100,
height: 50,
color: "#ff0000"
});
const circle = new Circle(25, {
x: 50,
y: 0,
color: "#0000ff"
});
const text = new TextShape("Hello", {
x: 0,
y: 0,
color: "#ffffff",
align: "center",
baseline: "middle"
});
// Add shapes to the group
group.add(rect);
group.add(circle);
group.add(text);
// Moving the group moves all contained shapes
group.x = 200;
group.y = 150;
// Rotating or scaling the group affects all shapes
group.rotation = Math.PI / 6; // 30 degrees
group.scaleX = 1.5;
group.scaleY = 1.5;
// Changes to the group's opacity affect all children
group.opacity = 0.7;
// Draw the group and all its children
group.draw();