SketchWave Grid Reference

Understanding and Using SWGrid - Stage

Back to SWGrid Demo

...loading...

SWGrid Class Reference

Overview

The SWGrid class provides a scalable, resizable grid system that transforms between user-defined coordinates and screen (pixel) coordinates. This abstraction allows developers to work in logical, mathematical coordinate systems independent of canvas size or screen resolution.

Key Features:
  • User-defined coordinate system mapping to screen pixels
  • Automatic coordinate transformation (userToScreen and screenToUser)
  • Customizable grid lines with adjustable step sizes
  • Optional axes display with origin marker
  • Dynamic resizing support without distortion
  • Independent x and y scaling
  • Configurable colors for grid lines and axes
  • Toggle visibility of grid and axes
Why Use SWGrid? Working directly with pixel coordinates is limiting and error-prone. SWGrid lets you think in natural units (e.g., -10 to 10 for both axes) while it handles the conversion to screen pixels. This makes mathematical visualizations, simulations, and data plotting far more intuitive.

Understanding Coordinate Systems

Canvas (Screen) Coordinates

By default, p5.js uses a pixel-based coordinate system:

  • Origin (0,0): Top-left corner of the canvas
  • X-axis: Increases from left to right (0 to width)
  • Y-axis: Increases from top to bottom (0 to height)
  • Units: Pixels
Canvas Coordinates (400x400 canvas):

(0,0)────────────────────────────────────▶ x (pixels)
 │                                    (400,0)
 │
 │         Standard p5.js Canvas
 │         Origin at top-left
 │         Y increases downward
 │
 │
 │
 ▼                                  (400,400)
 y (pixels)
                                

User-Defined Coordinates

SWGrid allows you to define your own logical coordinate system:

  • Origin: Can be anywhere within your defined bounds
  • X-axis: Can range from any value to any value (e.g., -10 to 10)
  • Y-axis: Can range from any value to any value (e.g., -10 to 10)
  • Y-direction: Typically increases upward (mathematical convention)
  • Units: Whatever makes sense for your application
User Coordinates (example: -10 to 10 on both axes):

        y (user units)
        ▲
        │                      (10,10)
     10 │─────────────────────────┐
        │                         │
      5 │         User Grid       │
        │         Origin at       │
      0 ├─────────(0,0)───────────┤
        │         center          │
     -5 │                         │
        │                         │
    -10 │─────────────────────────┘
        └─────────┬─────────┬──────▶ x (user units)
               -10    0    10
                                

The Transformation

SWGrid handles the mathematical transformation between these two systems:

User to Screen Conversion:
// Given user coordinates (x, y)
// Upper Left user point: UL
// Lower Right user point: LR

screenX = (x - UL.x) × (canvasWidth / (LR.x - UL.x))
screenY = (UL.y - y) × (canvasHeight / (UL.y - LR.y))

// Note: Y-axis is inverted to match mathematical convention
Screen to User Conversion:
// Given screen coordinates (px, py)

userX = px / xScale + UL.x
userY = UL.y - py / yScale

// Where xScale = canvasWidth / (LR.x - UL.x)
//   and yScale = canvasHeight / (UL.y - LR.y)
Key Insight: The Y-axis inversion is crucial. In canvas coordinates, Y increases downward. In mathematical/user coordinates, Y typically increases upward. SWGrid handles this automatically.

Window Resizing and Distortion

The Problem

When a browser window resizes, the canvas dimensions change. Without proper handling, this causes two major issues:

1. Coordinate Mapping Becomes Invalid
  • The scale factors (xScale, yScale) are calculated based on canvas size
  • When canvas size changes, these scales are outdated
  • Objects appear in wrong positions
  • Coordinates no longer map correctly
2. Visual Distortion Occurs
  • Circles become ellipses if width/height ratio changes
  • Squares become rectangles
  • Aspect ratio of drawings changes
  • Mathematical relationships are visually distorted

The Solution

SWGrid provides the updateScreenBounds() method to recalculate scale factors when the canvas resizes:

function windowResized() {
    resizeCanvas(windowWidth, windowHeight);
    myGrid.updateScreenBounds(); // Recalculate scales
}

// Or with fixed aspect ratio:
function windowResized() {
    let newW = windowWidth;
    let newH = windowWidth; // Keep square
    resizeCanvas(newW, newH);
    myGrid.updateScreenBounds();
}

Aspect Ratio Considerations

Important: If you want circles to remain circular and squares to remain square, you must either:
  • Keep the canvas aspect ratio constant (e.g., always square)
  • Use equal ranges for x and y user coordinates (e.g., both -10 to 10)
  • Accept that shapes may stretch if aspect ratios don't match
Maintaining Aspect Ratio
// Force square canvas
function windowResized() {
    let size = min(windowWidth, windowHeight);
    resizeCanvas(size, size);
    grid.updateScreenBounds();
}

// Equal user coordinate ranges
let grid = new SWGrid({
    UL: new SWPoint(-10, 10),
    LR: new SWPoint(10, -10)
    // Same range: 20 units each axis
});
Allowing Distortion
// Flexible canvas
function windowResized() {
    resizeCanvas(windowWidth, windowHeight);
    grid.updateScreenBounds();
    // Shapes may stretch
}

// Different user coordinate ranges
let grid = new SWGrid({
    UL: new SWPoint(-20, 10),
    LR: new SWPoint(20, -10)
    // Different ranges: 40 vs 20
});

Constructor

new SWGrid({UL, LR, showGrid, showAxes, xStep, yStep, gridColor, axisColor})
Configuration Object Parameters:
Parameter Type Default Description
UL SWPoint required Upper left user coordinate (typically has larger y value)
LR SWPoint required Lower right user coordinate (typically has smaller y value)
showGrid boolean true Whether to display grid lines
showAxes boolean true Whether to display x and y axes
xStep number 1 Distance between vertical grid lines in user units
yStep number 1 Distance between horizontal grid lines in user units
gridColor SWColor light gray Color for grid lines
axisColor SWColor black Color for axes and origin marker
Examples:
// Standard mathematical grid: -10 to 10 on both axes
let grid1 = new SWGrid({
    UL: new SWPoint(-10, 10),
    LR: new SWPoint(10, -10)
});

// Custom grid with specific styling
let grid2 = new SWGrid({
    UL: new SWPoint(-5, 8),
    LR: new SWPoint(15, -2),
    xStep: 0.5,
    yStep: 1,
    gridColor: new SWColor(0, 0, 90, 100, "lightGridGray"),
    axisColor: new SWColor(240, 100, 50, 100, "darkBlue"),
    showGrid: true,
    showAxes: true
});

// Minimal grid without grid lines
let grid3 = new SWGrid({
    UL: new SWPoint(0, 100),
    LR: new SWPoint(100, 0),
    showGrid: false,
    showAxes: true
});
Note: The constructor automatically calls updateScreenBounds() to initialize scale factors based on the current canvas size.

Properties

Property Type Description
UL SWPoint Upper left user coordinate point
LR SWPoint Lower right user coordinate point
showGrid boolean Whether grid lines are visible
showAxes boolean Whether axes are visible
xStep number Grid step size for x-axis (user units)
yStep number Grid step size for y-axis (user units)
gridColor SWColor Color for grid lines
axisColor SWColor Color for axes and origin
screenW number Current canvas width in pixels
screenH number Current canvas height in pixels
xScale number Pixels per user unit on x-axis
yScale number Pixels per user unit on y-axis
shouldShowOrigin boolean Whether to draw origin marker (set manually)
Important: The scale properties (xScale, yScale) are recalculated by updateScreenBounds(). Don't modify them directly.

Methods

Coordinate Conversion Methods

userToScreen(x, y)

Converts user coordinates to screen (pixel) coordinates.

Parameters:
  • x (number): User x-coordinate
  • y (number): User y-coordinate
Returns: Object {x, y} - Screen coordinates
let grid = new SWGrid({
    UL: new SWPoint(-10, 10),
    LR: new SWPoint(10, -10)
});

// Convert user point (5, 3) to screen coordinates
let screenPos = grid.userToScreen(5, 3);
console.log(screenPos); // {x: 300, y: 140} (example)

// Use for drawing
fill(0, 100, 100); // HSB red
circle(screenPos.x, screenPos.y, 20);
screenToUser(px, py)

Converts screen (pixel) coordinates to user coordinates.

Parameters:
  • px (number): Screen x-coordinate (pixels)
  • py (number): Screen y-coordinate (pixels)
Returns: Object {x, y} - User coordinates
// Get user coordinates of mouse position
function draw() {
    background(0, 0, 86); // HSB light gray
    grid.draw();
    
    let userPos = grid.screenToUser(mouseX, mouseY);
    
    // Display coordinates
    fill(0, 0, 0); // HSB black
    text(`User: (${userPos.x.toFixed(2)}, ${userPos.y.toFixed(2)})`, 10, 20);
    text(`Screen: (${mouseX}, ${mouseY})`, 10, 40);
}

Rendering Methods

draw()

Draws the grid lines and axes on the canvas. Call this in your draw() function.

function draw() {
    background(0, 0, 94); // HSB very light gray
    grid.draw(); // Draw grid first, then other elements
    
    // Now draw your shapes, points, etc.
    // ...
}
Note: Grid lines are drawn only if showGrid is true. Axes are drawn only if showAxes is true and they fall within the user coordinate bounds.
drawOrigin()

Draws a small circle at the origin (0, 0) if it's within bounds. Automatically called by draw() if shouldShowOrigin is true.

grid.shouldShowOrigin = true;
grid.draw(); // Will include origin marker

Update Methods

updateScreenBounds()

Recalculates screen dimensions and scale factors. Critical: Call this whenever the canvas is resized.

function windowResized() {
    resizeCanvas(windowWidth, windowHeight);
    grid.updateScreenBounds(); // REQUIRED after resize
}

// Also useful in draw() for dynamic sizing
function draw() {
    background(0, 0, 86); // HSB light gray
    grid.updateScreenBounds(); // Update every frame if canvas size changes
    grid.draw();
}
Critical: Failure to call this method after resizing will result in incorrect coordinate mapping and visual distortion.

Configuration Methods

setBounds(UL, LR)

Changes the user coordinate bounds and updates scale factors.

Parameters:
  • UL (SWPoint): New upper left user coordinate
  • LR (SWPoint): New lower right user coordinate
// Zoom in: smaller range
function zoomIn() {
    grid.setBounds(
        new SWPoint(-5, 5),
        new SWPoint(5, -5)
    );
}

// Zoom out: larger range
function zoomOut() {
    grid.setBounds(
        new SWPoint(-20, 20),
        new SWPoint(20, -20)
    );
}
toggleGrid()

Toggles the visibility of grid lines.

function keyPressed() {
    if (key === 'g' || key === 'G') {
        grid.toggleGrid();
    }
}
toggleAxes()

Toggles the visibility of axes.

function keyPressed() {
    if (key === 'a' || key === 'A') {
        grid.toggleAxes();
    }
}

Usage Examples

Example 1: Basic Grid Setup

let grid;

function setup() {
    createCanvas(400, 400);
    colorMode(HSB, 360, 100, 100, 100);
    
    // Create grid from -10 to 10 on both axes
    grid = new SWGrid({
        UL: new SWPoint(-10, 10),
        LR: new SWPoint(10, -10)
    });
}

function draw() {
    background(0, 0, 94); // HSB very light gray
    grid.draw();
}

function windowResized() {
    resizeCanvas(windowWidth, windowHeight);
    grid.updateScreenBounds();
}

Example 2: Plotting Mathematical Functions

let grid;

function setup() {
    createCanvas(600, 600);
    colorMode(HSB, 360, 100, 100, 100);
    
    grid = new SWGrid({
        UL: new SWPoint(-10, 10),
        LR: new SWPoint(10, -10),
        xStep: 1,
        yStep: 1
    });
}

function draw() {
    background(0, 0, 94); // HSB very light gray
    grid.draw();
    
    // Plot y = sin(x)
    stroke(0, 100, 100); // HSB red
    strokeWeight(2);
    noFill();
    
    beginShape();
    for (let x = -10; x <= 10; x += 0.1) {
        let y = 5 * Math.sin(x); // Scale for visibility
        let screen = grid.userToScreen(x, y);
        vertex(screen.x, screen.y);
    }
    endShape();
}

Example 3: Interactive Coordinate Display

let grid;

function setup() {
    createCanvas(500, 500);
    colorMode(HSB, 360, 100, 100, 100);
    
    grid = new SWGrid({
        UL: new SWPoint(-5, 5),
        LR: new SWPoint(5, -5),
        xStep: 0.5,
        yStep: 0.5
    });
    grid.shouldShowOrigin = true;
}

function draw() {
    background(0, 0, 94); // HSB very light gray
    grid.draw();
    
    // Get user coordinates of mouse
    let userPos = grid.screenToUser(mouseX, mouseY);
    
    // Draw crosshair at mouse position
    stroke(0, 100, 100); // HSB red
    strokeWeight(1);
    
    let mouseScreen = grid.userToScreen(userPos.x, userPos.y);
    line(mouseScreen.x - 10, mouseScreen.y, mouseScreen.x + 10, mouseScreen.y);
    line(mouseScreen.x, mouseScreen.y - 10, mouseScreen.x, mouseScreen.y + 10);
    
    // Display coordinates
    fill(0, 0, 0); // HSB black
    noStroke();
    textSize(14);
    text(`User: (${userPos.x.toFixed(2)}, ${userPos.y.toFixed(2)})`, 10, 20);
    text(`Screen: (${mouseX}, ${mouseY})`, 10, 40);
}

Example 4: Using SWPoint with SWGrid

let grid;
let points = [];

function setup() {
    createCanvas(500, 500);
    colorMode(HSB, 360, 100, 100, 100);
    initializeSWColors();
    
    grid = new SWGrid({
        UL: new SWPoint(-10, 10),
        LR: new SWPoint(10, -10)
    });
    
    // Create labeled points in user coordinates
    let labels = ["A", "B", "C", "D", "E"];
    for (let i = 0; i < 5; i++) {
        let x = random(-8, 8);
        let y = random(-8, 8);
        let color = new SWColor(random(360), 80, 90);
        let point = new SWPoint(x, y, undefined, 10, color, labels[i]);
        point.showLabel = true;
        points.push(point);
    }
}

function draw() {
    background(0, 0, 94); // HSB very light gray
    grid.draw();
    
    // Draw points using grid coordinates
    for (let pt of points) {
        pt.drawOnGrid(grid);
    }
}

Example 5: Dynamic Zoom with Mouse Wheel

let grid;
let zoomLevel = 10;

function setup() {
    createCanvas(500, 500);
    colorMode(HSB, 360, 100, 100, 100);
    
    grid = new SWGrid({
        UL: new SWPoint(-zoomLevel, zoomLevel),
        LR: new SWPoint(zoomLevel, -zoomLevel)
    });
}

function draw() {
    background(0, 0, 94); // HSB very light gray
    grid.draw();
    
    // Show zoom level
    fill(0, 0, 0); // HSB black
    noStroke();
    text(`Zoom: ±${zoomLevel}`, 10, 20);
}

function mouseWheel(event) {
    // Zoom in/out
    zoomLevel += event.delta * 0.01;
    zoomLevel = constrain(zoomLevel, 1, 50);
    
    // Update grid bounds
    grid.setBounds(
        new SWPoint(-zoomLevel, zoomLevel),
        new SWPoint(zoomLevel, -zoomLevel)
    );
    
    return false; // Prevent page scroll
}

Example 6: Aspect Ratio Preservation

let grid;

function setup() {
    // Start with square canvas
    let size = min(windowWidth, windowHeight);
    createCanvas(size, size);
    colorMode(HSB, 360, 100, 100, 100);
    
    grid = new SWGrid({
        UL: new SWPoint(-10, 10),
        LR: new SWPoint(10, -10) // Equal ranges
    });
}

function draw() {
    background(0, 0, 94); // HSB very light gray
    grid.draw();
    
    // Draw a perfect circle in user coordinates
    let screenPos = grid.userToScreen(0, 0);
    let radius = grid.xScale * 5; // 5 user units
    
    fill(180, 70, 90); // HSB cyan
    noStroke();
    circle(screenPos.x, screenPos.y, radius * 2);
}

function windowResized() {
    // Maintain square aspect ratio
    let size = min(windowWidth, windowHeight);
    resizeCanvas(size, size);
    grid.updateScreenBounds();
    // Circle remains circular!
}

Example 7: Keyboard Controls

let grid;

function setup() {
    createCanvas(500, 500);
    colorMode(HSB, 360, 100, 100, 100);
    
    grid = new SWGrid({
        UL: new SWPoint(-10, 10),
        LR: new SWPoint(10, -10),
        xStep: 1,
        yStep: 1
    });
}

function draw() {
    background(0, 0, 94); // HSB very light gray
    grid.draw();
    
    // Instructions
    fill(0, 0, 0); // HSB black
    noStroke();
    textSize(12);
    text("Press 'G' to toggle grid", 10, 20);
    text("Press 'A' to toggle axes", 10, 40);
    text("Press 'O' to toggle origin", 10, 60);
    text("Press '+' to increase grid density", 10, 80);
    text("Press '-' to decrease grid density", 10, 100);
}

function keyPressed() {
    if (key === 'g' || key === 'G') {
        grid.toggleGrid();
    }
    if (key === 'a' || key === 'A') {
        grid.toggleAxes();
    }
    if (key === 'o' || key === 'O') {
        grid.shouldShowOrigin = !grid.shouldShowOrigin;
    }
    if (key === '+' || key === '=') {
        grid.xStep = max(0.1, grid.xStep - 0.5);
        grid.yStep = max(0.1, grid.yStep - 0.5);
    }
    if (key === '-' || key === '_') {
        grid.xStep = min(5, grid.xStep + 0.5);
        grid.yStep = min(5, grid.yStep + 0.5);
    }
}

Best Practices

Always Handle Window Resize
function windowResized() {
    resizeCanvas(newWidth, newHeight);
    grid.updateScreenBounds(); // Don't forget this!
}
Maintain Aspect Ratio for Geometric Accuracy
  • Use equal user coordinate ranges (e.g., -10 to 10 on both axes)
  • Keep canvas square or force aspect ratio in windowResized()
  • If different ranges needed, accept that circles become ellipses
Draw Grid First
function draw() {
    background(0, 0, 94); // HSB very light gray
    grid.draw(); // Draw grid BEFORE other elements
    // ... draw shapes, points, etc.
}
Use User Coordinates for Logic
  • Store object positions in user coordinates
  • Use SWPoint.drawOnGrid(grid) for automatic conversion
  • Convert only when necessary for drawing
Choose Appropriate Step Sizes
  • Larger steps (e.g., 5) for coarse grids
  • Smaller steps (e.g., 0.1) for fine grids
  • Match step size to your application's scale
  • Too small steps can clutter the display
Use Meaningful Coordinate Ranges
  • For math: -10 to 10 is intuitive
  • For data: match your data range
  • For physics: use real-world units
  • Avoid very large ranges (precision issues)

Integration with SketchWave Ecosystem

Dependencies

  • p5.js: Required for all drawing operations
  • SWPoint: Required for defining coordinate bounds
  • SWColor: Required for grid and axis colors

Loading Order

Ensure scripts are loaded in this order in your HTML:

<!-- p5js CDN-->
<script src="https://cdn.jsdelivr.net/npm/p5@1.6.0/lib/p5.js"></script>

<!-- SketchWave classes -->
<script src="scripts/swColor.js"></script>
<script src="scripts/swPoint.js"></script>
<script src="scripts/swGrid.js"></script>
<!-- Your sketch script -->
<script src="scripts/mySketch.js"></script>

Working with Other SketchWave Classes

// SWPoint has built-in grid support
// Constructor: new SWPoint(x, y, z, strokeWeight, strokeColor, label)
let myPoint = new SWPoint(5, 3, undefined, 8, swRed, "P");
myPoint.showLabel = true;
myPoint.drawOnGrid(grid); // Automatic conversion

// Manual conversion for custom drawing
let screenPos = grid.userToScreen(myPoint.x, myPoint.y);
circle(screenPos.x, screenPos.y, 20);

Common Patterns

// Pattern 1: Physics simulation with real units
let grid = new SWGrid({
    UL: new SWPoint(0, 100),    // 0-100 meters
    LR: new SWPoint(100, 0),
    xStep: 10,
    yStep: 10
});

// Pattern 2: Data visualization
let grid = new SWGrid({
    UL: new SWPoint(0, maxDataValue),
    LR: new SWPoint(dataPoints.length, 0),
    showGrid: true,
    showAxes: true
});

// Pattern 3: Mathematical graphing
let grid = new SWGrid({
    UL: new SWPoint(-2*Math.PI, 5),
    LR: new SWPoint(2*Math.PI, -5),
    xStep: Math.PI/2,  // Mark multiples of π/2
    yStep: 1
});

Complete Source Code

View the complete, documented source code for the SWGrid class:

Show/Hide Source Code
// File: swGrid.js
// Author: klp
// Purpose: SWGrid class for scalable, resizable grid system in p5.js
// Dependencies: p5.js, SWPoint, SWColor

console.log("[swGrid.js] SWGrid class loaded.");

class SWGrid {
    /**
     * @param {Object} config - Configuration object
     * @param {SWPoint} config.UL - Upper left user coordinate (e.g., new SWPoint(-10, 10))
     * @param {SWPoint} config.LR - Lower right user coordinate (e.g., new SWPoint(10, -10))
     * @param {boolean} [config.showGrid=true] - Show grid lines
     * @param {boolean} [config.showAxes=true] - Show axes
     * @param {number} [config.xStep=1] - Grid step for x axis
     * @param {number} [config.yStep=1] - Grid step for y axis
     * @param {SWColor} [config.gridColor] - Color for grid lines
     * @param {SWColor} [config.axisColor] - Color for axes
     */
    constructor({UL, LR, showGrid=true, showAxes=true, xStep=1, yStep=1, gridColor, axisColor}) {
        this.UL = UL;
        this.LR = LR;
        this.showGrid = showGrid;
        this.showAxes = showAxes;
        this.xStep = xStep;
        this.yStep = yStep;
        this.gridColor = gridColor || new SWColor(0, 0, 85, 100, "gridGray");
        this.axisColor = axisColor || new SWColor(0, 0, 0, 100, "axisBlack");
        this.updateScreenBounds();
        this.shouldShowOrigin = false;
    }//end constructor

    // Call this in windowResized or after canvas resize
    updateScreenBounds() {
        this.screenW = width;
        this.screenH = height;
        this.xScale = this.screenW / (this.LR.x - this.UL.x);
        this.yScale = this.screenH / (this.UL.y - this.LR.y);
    }//end updateScreenBounds

    // Convert user (x, y) to screen (px, py)
    userToScreen(x, y) {
        const px = (x - this.UL.x) * this.xScale;
        const py = (this.UL.y - y) * this.yScale;
        return {x: px, y: py};
    }//end userToScreen

    // Convert screen (px, py) to user (x, y)
    screenToUser(px, py) {
        const x = px / this.xScale + this.UL.x;
        const y = this.UL.y - py / this.yScale;
        return {x, y};
    }//end screenToUser

    // Draw the grid, axes, and origin
    draw() {
        // Draw grid lines
        if (this.showGrid) {
            stroke(this.gridColor.col);
            strokeWeight(1);
            // Vertical grid lines
            for (let x = Math.ceil(this.UL.x/this.xStep)*this.xStep; x <= this.LR.x; x += this.xStep) {
                const {x: px1, y: py1} = this.userToScreen(x, this.UL.y);
                const {x: px2, y: py2} = this.userToScreen(x, this.LR.y);
                line(px1, py1, px2, py2);
            }
            // Horizontal grid lines
            for (let y = Math.floor(this.LR.y/this.yStep)*this.yStep; y <= this.UL.y; y += this.yStep) {
                const {x: px1, y: py1} = this.userToScreen(this.UL.x, y);
                const {x: px2, y: py2} = this.userToScreen(this.LR.x, y);
                line(px1, py1, px2, py2);
            }
        }
        // Draw axes
        if (this.showAxes) {
            stroke(this.axisColor.col);
            strokeWeight(2);
            // y-axis (x=0)
            if (this.UL.x < 0 && this.LR.x > 0) {
                const {x: px1, y: py1} = this.userToScreen(0, this.UL.y);
                const {x: px2, y: py2} = this.userToScreen(0, this.LR.y);
                line(px1, py1, px2, py2);
            }
            // x-axis (y=0)
            if (this.LR.y < 0 && this.UL.y > 0) {
                const {x: px1, y: py1} = this.userToScreen(this.UL.x, 0);
                const {x: px2, y: py2} = this.userToScreen(this.LR.x, 0);
                line(px1, py1, px2, py2);
            }

            if(this.shouldShowOrigin) {
                this.drawOrigin();
            }
            
        }
    }//end draw

    drawOrigin() {
        // Draw origin if in bounds
        if (this.UL.x < 0 && this.LR.x > 0 && this.LR.y < 0 && this.UL.y > 0) {
            const {x: px, y: py} = this.userToScreen(0, 0);
            fill(this.axisColor.col);
            noStroke();
            ellipse(px, py, 6, 6);
        }
    }//end drawOrigin

    // Toggle grid visibility
    toggleGrid() {
        this.showGrid = !this.showGrid;
    }//end toggleGrid

    // Toggle axes visibility
    toggleAxes() {
        this.showAxes = !this.showAxes;
    }//end toggleAxes

    // Set new user bounds
    setBounds(UL, LR) {
        this.UL = UL;
        this.LR = LR;
        this.updateScreenBounds();
    }//end setBounds
}//end SWGrid class

// Usage example (in your sketch):
// let grid;
// function setup() {
//   createCanvas(400, 400);
//   grid = new SWGrid({UL: new SWPoint(-10, 10), LR: new SWPoint(10, -10)});
// }
// function draw() {
//   background(240);
//   grid.updateScreenBounds();
//   grid.draw();
// }
Developer Notes: This reference guide is part of the SketchWave ecosystem. SWGrid is fundamental for creating mathematical visualizations, data plots, and simulations with proper coordinate systems. For questions or contributions, please refer to the SketchWave documentation or contact the development team.