Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions lib/addons/p5.viewport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* @module p5.viewport
* @submodule p5.viewport
* @for p5
* @version 0.0.1
* @author om balgude
* @description This addon provides a setViewport function to remap the canvas coordinate system.
*
*/

(function() {
/**
* The viewportAddon function is automatically called by p5.js to add the addon
* functionality to the p5.js library.
*
* @private
* @param {Object} p5 - The p5.js instance.
* @param {Object} fn - The p5.js prototype object.
* @param {Object} lifecycles - The p5.js lifecycle hooks.
*/
function viewportAddon(p5, fn, lifecycles) {
/**
* Sets the coordinate system to a new viewport.
*
* Calling `setViewport(xmin, xmax, ymin, ymax)` remaps the canvas's
* coordinate system. The top-left corner of the canvas will be `(xmin, ymin)`
* and the bottom-right corner will be `(xmax, ymax)`.
*
* @method setViewport
* @param {Number} xmin The minimum x-value of the viewport.
* @param {Number} xmax The maximum x-value of the viewport.
* @param {Number} ymin The minimum y-value of the viewport.
* @param {Number} ymax The maximum y-value of the viewport.
* @chainable
*/
fn.setViewport = function(xmin, xmax, ymin, ymax) {
this.resetMatrix();
const scaleX = this.width / (xmax - xmin);
const scaleY = this.height / (ymax - ymin);
this.scale(scaleX, scaleY);
this.translate(-xmin, -ymin);
return this;
};
}

// Register the addon with p5.js
if (typeof p5 !== 'undefined') {
p5.registerAddon(viewportAddon);
}
})();
119 changes: 119 additions & 0 deletions src/webgl/p5.Camera.js
Original file line number Diff line number Diff line change
Expand Up @@ -3822,6 +3822,116 @@ function camera(p5, fn){
this._renderer.setCamera(cam);
};

/**
* Returns or sets the current (active) camera of a 3D sketch.
*
* `activeCamera()` provides a standard way to access and modify the active
* camera. When called with no arguments, it returns the current active camera.
* When called with a camera argument, it sets that camera as the active camera.
*
* This function works alongside <a href="#/p5/setCamera">setCamera()</a> and
* provides a joint getter/setter pattern that's consistent with other p5.js
* functions.
*
* Note: `activeCamera()` can only be used in WebGL mode.
*
* @method activeCamera
* @param {p5.Camera} [cam] camera that should be made active. If omitted, returns the current active camera.
* @return {p5.Camera|p5} the current active camera (when called with no arguments) or the p5 instance (for chaining when setting).
* @for p5
*
* @example
* <div>
* <code>
* // Get the current active camera.
*
* let cam1;
* let cam2;
*
* function setup() {
* createCanvas(100, 100, WEBGL);
*
* // Create two cameras.
* cam1 = createCamera();
* cam2 = createCamera();
* cam2.setPosition(400, -400, 800);
* cam2.lookAt(0, 0, 0);
*
* // Set cam1 as active.
* activeCamera(cam1);
*
* describe('A white cube on a gray background.');
* }
*
* function draw() {
* background(200);
*
* // Get the current active camera.
* let currentCam = activeCamera();
*
* // Draw the box.
* box();
* }
* </code>
* </div>
*
* @example
* <div>
* <code>
* // Double-click to toggle between cameras.
*
* let cam1;
* let cam2;
*
* function setup() {
* createCanvas(100, 100, WEBGL);
*
* // Create the first camera.
* cam1 = createCamera();
*
* // Create the second camera.
* cam2 = createCamera();
* cam2.setPosition(400, -400, 800);
* cam2.lookAt(0, 0, 0);
*
* // Set the current camera to cam1.
* activeCamera(cam1);
*
* describe('A white cube on a gray background. The camera toggles between frontal and aerial views when the user double-clicks.');
* }
*
* function draw() {
* background(200);
*
* // Draw the box.
* box();
* }
*
* // Toggle the current camera when the user double-clicks.
* function doubleClicked() {
* // Get the current camera.
* let currentCam = activeCamera();
*
* // Switch to the other camera.
* if (currentCam === cam1) {
* activeCamera(cam2);
* } else {
* activeCamera(cam1);
* }
* }
* </code>
* </div>
*/
fn.activeCamera = function (cam) {
this._assert3d('activeCamera');
if (cam !== undefined) {
this._renderer.setCamera(cam);
return this;
} else {
return this._renderer.states.curCamera;
}
};

/**
* A class to describe a camera for viewing a 3D sketch.
*
Expand Down Expand Up @@ -3989,6 +4099,15 @@ function camera(p5, fn){
this.states.setValue('uViewMatrix', this.states.uViewMatrix.clone());
this.states.uViewMatrix.set(cam.cameraMatrix);
};

RendererGL.prototype.activeCamera = function(cam) {
if (cam !== undefined) {
this.setCamera(cam);
return this;
} else {
return this.states.curCamera;
}
};
}

export default camera;
Expand Down
46 changes: 46 additions & 0 deletions test/unit/webgl/p5.Camera.js
Original file line number Diff line number Diff line change
Expand Up @@ -1134,6 +1134,52 @@ suite('p5.Camera', function() {
});
});

suite('activeCamera() getter/setter', function() {
test('activeCamera() returns current camera when called with no arguments', function() {
var currentCam = myp5.activeCamera();
assert.deepCloseTo(currentCam, myp5._renderer.states.curCamera);
assert.deepCloseTo(currentCam, myCam);
});

test('activeCamera() sets camera when called with camera argument', function() {
var myCam2 = myp5.createCamera();
myp5.activeCamera(myCam2);
assert.deepCloseTo(myCam2, myp5._renderer.states.curCamera);
var retrievedCam = myp5.activeCamera();
assert.deepCloseTo(retrievedCam, myCam2);
});

test('activeCamera() works correctly with multiple cameras', function() {
var myCam2 = myp5.createCamera();
var myCam3 = myp5.createCamera();

// Set cam2
myp5.activeCamera(myCam2);
assert.deepCloseTo(myCam2, myp5.activeCamera());

// Set cam3
myp5.activeCamera(myCam3);
assert.deepCloseTo(myCam3, myp5.activeCamera());

// Set back to original
myp5.activeCamera(myCam);
assert.deepCloseTo(myCam, myp5.activeCamera());
});

test('activeCamera() returns p5 instance for chaining when setting', function() {
var myCam2 = myp5.createCamera();
var result = myp5.activeCamera(myCam2);
assert.strictEqual(result, myp5);
});

test('activeCamera() returns the same camera object that was set', function() {
var myCam2 = myp5.createCamera();
myp5.activeCamera(myCam2);
var retrievedCam = myp5.activeCamera();
assert.strictEqual(retrievedCam, myCam2);
});
});

suite('Camera attributes after resizing', function() {
test('Camera position is the same', function() {
myp5.createCanvas(1, 1, myp5.WEBGL);
Expand Down