From 260fd59570d9afddd044c307f896f35373a9b2a0 Mon Sep 17 00:00:00 2001 From: Garima Date: Fri, 5 Jul 2024 18:36:29 +0530 Subject: [PATCH 01/16] Solves issue #7059 --- src/core/environment.js | 42 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/core/environment.js b/src/core/environment.js index fb8b56c0f5..2e2c87959c 100644 --- a/src/core/environment.js +++ b/src/core/environment.js @@ -1137,6 +1137,48 @@ function exitFullscreen() { } } + +/** + * Converts 3D world coordinates to 2D screen coordinates. + * + * This function takes a 3D vector and converts its coordinates + * from the world space to screen space. This is useful for placing + * 2D elements in a 3D scene or for determining the screen position + * of 3D objects. + * + * @method worldToScreen + * @param {p5.Vector} worldPosition The 3D coordinates in the world space. + * @return {p5.Vector} A vector containing the 2D screen coordinates. + * @example + *
+ * + * + *
+ * + */ +p5.prototype.worldToScreen = function(worldPosition) { + const renderer = this._renderer; + if (renderer.drawingContext instanceof CanvasRenderingContext2D) { + // Handle 2D context + const transformMatrix = new DOMMatrix() + .scale(1 / pixelDensity()) + .multiply(renderer.drawingContext.getTransform()); + const screenCoordinates = transformMatrix.transformPoint( + new DOMPoint(worldPosition.x, worldPosition.y)); + return createVector(screenCoordinates.x, screenCoordinates.y); + } else{ + // Handle WebGL context + const cameraCoordinates = renderer.uMVMatrix.multiplyPoint(worldPosition); + const normalizedDeviceCoordinates = + renderer.uPMatrix.multiplyAndNormalizePoint(cameraCoordinates); + const screenX = (0.5 + 0.5 * normalizedDeviceCoordinates.x) * this.width; + const screenY = (0.5 - 0.5 * normalizedDeviceCoordinates.y) * this.height; + const screenZ = (0.5 + 0.5 * normalizedDeviceCoordinates.z); + return createVector(screenX, screenY, screenZ); + } +}; + + /** * Returns the sketch's current * URL From 43663346d9d44ddc818e7dbf57f19e22a848b5f4 Mon Sep 17 00:00:00 2001 From: Garima Date: Fri, 5 Jul 2024 18:51:21 +0530 Subject: [PATCH 02/16] Fixed linting --- src/core/environment.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/environment.js b/src/core/environment.js index 2e2c87959c..915da7693c 100644 --- a/src/core/environment.js +++ b/src/core/environment.js @@ -1154,7 +1154,6 @@ function exitFullscreen() { * * * - * */ p5.prototype.worldToScreen = function(worldPosition) { const renderer = this._renderer; From eeed9c9f64514bfc9b12c0f02361801bf6425792 Mon Sep 17 00:00:00 2001 From: Garima Date: Mon, 5 Aug 2024 17:38:29 +0530 Subject: [PATCH 03/16] Added unit tests --- test/unit/core/environment.js | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/test/unit/core/environment.js b/test/unit/core/environment.js index 96cb7ff387..af9ea4ffa3 100644 --- a/test/unit/core/environment.js +++ b/test/unit/core/environment.js @@ -236,4 +236,41 @@ suite('Environment', function() { assert.isNumber(myp5.displayDensity(), pd); }); }); + + suite('2D context test', function() { + beforeEach(function() { + myp5.createCanvas(100, 100); + }); + + test('worldToScreen for 2D context', function() { + let worldPos = myp5.createVector(50, 50); + let screenPos = myp5.worldToScreen(worldPos); + assert.closeTo(screenPos.x, 50, 0.1); + assert.closeTo(screenPos.y, 50, 0.1); + }); + + }); + + suite('3D context test', function() { + beforeEach(function() { + myp5.createCanvas(100, 100, myp5.WEBGL); + }); + + test('worldToScreen for 3D context', function() { + let worldPos = myp5.createVector(0, 0, 0); + let screenPos = myp5.worldToScreen(worldPos); + assert.isTrue(screenPos.x >= 0 && screenPos.x <= 100); + assert.isTrue(screenPos.y >= 0 && screenPos.y <= 100); + }); + + test('worldToScreen with rotation', function() { + myp5.push(); + myp5.rotateY(myp5.PI / 2); + let worldPos = myp5.createVector(50, 0, 0); + let screenPos = myp5.worldToScreen(worldPos); + myp5.pop(); + assert.isTrue(screenPos.x >= 0 && screenPos.x <= 100); + assert.isTrue(screenPos.y >= 0 && screenPos.y <= 100); + }); + }); }); From 420067b182a3f77b564dda4be7b5092e7f3e5970 Mon Sep 17 00:00:00 2001 From: Garima Date: Tue, 6 Aug 2024 23:03:27 +0530 Subject: [PATCH 04/16] Added more tests+ some changes --- test/unit/core/environment.js | 60 ++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/test/unit/core/environment.js b/test/unit/core/environment.js index af9ea4ffa3..026b9735eb 100644 --- a/test/unit/core/environment.js +++ b/test/unit/core/environment.js @@ -249,6 +249,34 @@ suite('Environment', function() { assert.closeTo(screenPos.y, 50, 0.1); }); + test('worldToScreen with rotation in 2D', function() { + myp5.push(); + myp5.translate(50, 50); + myp5.rotate(myp5.PI / 2); + let worldPos = myp5.createVector(10, 0); + let screenPos = myp5.worldToScreen(worldPos); + myp5.pop(); + assert.closeTo(screenPos.x, 50, 0.1); + assert.closeTo(screenPos.y, 60, 0.1); + }); + + test('worldToScreen for a rotating square in 2D', function() { + myp5.push(); + myp5.translate(50, 50); + myp5.rotate(myp5.PI / 4); + let vertices = [ + myp5.createVector(-10, -10), + myp5.createVector(10, -10), + myp5.createVector(10, 10), + myp5.createVector(-10, 10) + ]; + let screenPos = vertices.map(v => myp5.worldToScreen(v)); + myp5.pop(); + screenPos.forEach((pos, i) => { + myp5.text(`(${pos.x.toFixed(1)}, ${pos.y.toFixed(1)})`, pos.x, pos.y); + }); + }); + }); suite('3D context test', function() { @@ -259,18 +287,40 @@ suite('Environment', function() { test('worldToScreen for 3D context', function() { let worldPos = myp5.createVector(0, 0, 0); let screenPos = myp5.worldToScreen(worldPos); - assert.isTrue(screenPos.x >= 0 && screenPos.x <= 100); - assert.isTrue(screenPos.y >= 0 && screenPos.y <= 100); + assert.closeTo(screenPos.x, 50, 0.1); + assert.closeTo(screenPos.y, 50, 0.1); }); - test('worldToScreen with rotation', function() { + test('worldToScreen with rotation in 3D', function() { myp5.push(); myp5.rotateY(myp5.PI / 2); let worldPos = myp5.createVector(50, 0, 0); let screenPos = myp5.worldToScreen(worldPos); myp5.pop(); - assert.isTrue(screenPos.x >= 0 && screenPos.x <= 100); - assert.isTrue(screenPos.y >= 0 && screenPos.y <= 100); + assert.closeTo(screenPos.x, 50, 0.1); + assert.closeTo(screenPos.y, 50, 0.1); + }); + + test('worldToScreen for a rotating cube in 3D', function() { + myp5.push(); + myp5.translate(0, 0, 0); + myp5.rotateX(myp5.PI / 4); + myp5.rotateY(myp5.PI / 4); + let vertices = [ + myp5.createVector(-50, -50, -50), + myp5.createVector(50, -50, -50), + myp5.createVector(50, 50, -50), + myp5.createVector(-50, 50, -50), + myp5.createVector(-50, -50, 50), + myp5.createVector(50, -50, 50), + myp5.createVector(50, 50, 50), + myp5.createVector(-50, 50, 50) + ]; + let screenPos = vertices.map(v => myp5.worldToScreen(v)); + myp5.pop(); + screenPos.forEach((pos, i) => { + myp5.text(`(${pos.x.toFixed(1)}, ${pos.y.toFixed(1)})`, pos.x, pos.y); + }); }); }); }); From d7fc17dd83a5e7263f4a85f5a772bd4d323aa24e Mon Sep 17 00:00:00 2001 From: Garima Date: Tue, 20 Aug 2024 17:03:30 +0530 Subject: [PATCH 05/16] Minor changes with unit tests + examples added --- src/core/environment.js | 63 ++++++++++++++++++++++++++++++++--- test/unit/core/environment.js | 45 ++----------------------- 2 files changed, 62 insertions(+), 46 deletions(-) diff --git a/src/core/environment.js b/src/core/environment.js index 915da7693c..01b30fee3e 100644 --- a/src/core/environment.js +++ b/src/core/environment.js @@ -1152,8 +1152,61 @@ function exitFullscreen() { * @example *
* + * // Example 2: Convert 2D world coordinates of a rotating square to screen coordinates + * function setup() { + * createCanvas(400, 400); + * let vertices = [ + * createVector(-10, -10), + * createVector(10, -10), + * createVector(10, 10), + * createVector(-10, 10) + * ]; + * + * push(); + * translate(200, 200); + * rotate(PI / 4); + * let screenPos = vertices.map(v => worldToScreen(v)); + * pop(); + * + * background(200); + * screenPos.forEach((pos, i) => { + * text(`(${pos.x.toFixed(1)}, ${pos.y.toFixed(1)})`, pos.x, pos.y); + * }); + * } * *
+ * @example + *
+ * + * // Example 1: Convert 3D world coordinates of a rotating cube to 2D screen coordinates + * function setup() { + * createCanvas(400, 400, WEBGL); + * let vertices = [ + * createVector(-50, -50, -50), + * createVector(50, -50, -50), + * createVector(50, 50, -50), + * createVector(-50, 50, -50), + * createVector(-50, -50, 50), + * createVector(50, -50, 50), + * createVector(50, 50, 50), + * createVector(-50, 50, 50) + * ]; + * + * push(); + * translate(0, 0, 0); + * rotateX(PI / 4); + * rotateY(PI / 4); + * let screenPos = vertices.map(v => worldToScreen(v)); + * pop(); + * + * background(200); + * screenPos.forEach((pos, i) => { + * text(`(${pos.x.toFixed(1)}, ${pos.y.toFixed(1)})`, pos.x, pos.y); + * }); + * } + * + *
+ * */ p5.prototype.worldToScreen = function(worldPosition) { const renderer = this._renderer; @@ -1163,21 +1216,23 @@ p5.prototype.worldToScreen = function(worldPosition) { .scale(1 / pixelDensity()) .multiply(renderer.drawingContext.getTransform()); const screenCoordinates = transformMatrix.transformPoint( - new DOMPoint(worldPosition.x, worldPosition.y)); + new DOMPoint(worldPosition.x, worldPosition.y) + ); return createVector(screenCoordinates.x, screenCoordinates.y); - } else{ + } else { // Handle WebGL context const cameraCoordinates = renderer.uMVMatrix.multiplyPoint(worldPosition); const normalizedDeviceCoordinates = - renderer.uPMatrix.multiplyAndNormalizePoint(cameraCoordinates); + renderer.uPMatrix.multiplyAndNormalizePoint(cameraCoordinates); const screenX = (0.5 + 0.5 * normalizedDeviceCoordinates.x) * this.width; const screenY = (0.5 - 0.5 * normalizedDeviceCoordinates.y) * this.height; - const screenZ = (0.5 + 0.5 * normalizedDeviceCoordinates.z); + const screenZ = 0.5 + 0.5 * normalizedDeviceCoordinates.z; return createVector(screenX, screenY, screenZ); } }; + /** * Returns the sketch's current * URL diff --git a/test/unit/core/environment.js b/test/unit/core/environment.js index 026b9735eb..3e4e471f90 100644 --- a/test/unit/core/environment.js +++ b/test/unit/core/environment.js @@ -251,32 +251,14 @@ suite('Environment', function() { test('worldToScreen with rotation in 2D', function() { myp5.push(); - myp5.translate(50, 50); myp5.rotate(myp5.PI / 2); + myp5.translate(50, 50); let worldPos = myp5.createVector(10, 0); let screenPos = myp5.worldToScreen(worldPos); myp5.pop(); assert.closeTo(screenPos.x, 50, 0.1); assert.closeTo(screenPos.y, 60, 0.1); }); - - test('worldToScreen for a rotating square in 2D', function() { - myp5.push(); - myp5.translate(50, 50); - myp5.rotate(myp5.PI / 4); - let vertices = [ - myp5.createVector(-10, -10), - myp5.createVector(10, -10), - myp5.createVector(10, 10), - myp5.createVector(-10, 10) - ]; - let screenPos = vertices.map(v => myp5.worldToScreen(v)); - myp5.pop(); - screenPos.forEach((pos, i) => { - myp5.text(`(${pos.x.toFixed(1)}, ${pos.y.toFixed(1)})`, pos.x, pos.y); - }); - }); - }); suite('3D context test', function() { @@ -294,33 +276,12 @@ suite('Environment', function() { test('worldToScreen with rotation in 3D', function() { myp5.push(); myp5.rotateY(myp5.PI / 2); + myp5.translate(50, 0, 0); let worldPos = myp5.createVector(50, 0, 0); let screenPos = myp5.worldToScreen(worldPos); myp5.pop(); - assert.closeTo(screenPos.x, 50, 0.1); + assert.closeTo(screenPos.x, 100, 0.1); assert.closeTo(screenPos.y, 50, 0.1); }); - - test('worldToScreen for a rotating cube in 3D', function() { - myp5.push(); - myp5.translate(0, 0, 0); - myp5.rotateX(myp5.PI / 4); - myp5.rotateY(myp5.PI / 4); - let vertices = [ - myp5.createVector(-50, -50, -50), - myp5.createVector(50, -50, -50), - myp5.createVector(50, 50, -50), - myp5.createVector(-50, 50, -50), - myp5.createVector(-50, -50, 50), - myp5.createVector(50, -50, 50), - myp5.createVector(50, 50, 50), - myp5.createVector(-50, 50, 50) - ]; - let screenPos = vertices.map(v => myp5.worldToScreen(v)); - myp5.pop(); - screenPos.forEach((pos, i) => { - myp5.text(`(${pos.x.toFixed(1)}, ${pos.y.toFixed(1)})`, pos.x, pos.y); - }); - }); }); }); From c10c4230f3f5856e7aebde4c1ec982a79b4bdc29 Mon Sep 17 00:00:00 2001 From: Garima Date: Wed, 21 Aug 2024 15:52:52 +0530 Subject: [PATCH 06/16] Changes in tests and doc examples --- src/core/environment.js | 69 ++++++++++++++++++++++------------- test/unit/core/environment.js | 17 +++++++-- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/core/environment.js b/src/core/environment.js index 01b30fee3e..4a67088141 100644 --- a/src/core/environment.js +++ b/src/core/environment.js @@ -1152,23 +1152,27 @@ function exitFullscreen() { * @example *
* - * // Example 2: Convert 2D world coordinates of a rotating square to screen coordinates + * // Example 1: Convert 2D world coordinates of a rotating square to screen coordinates * function setup() { - * createCanvas(400, 400); + * createCanvas(100, 100); + * * let vertices = [ - * createVector(-10, -10), - * createVector(10, -10), - * createVector(10, 10), - * createVector(-10, 10) + * createVector(-5, -5), + * createVector(5, -5), + * createVector(5, 5), + * createVector(-5, 5) * ]; * - * push(); - * translate(200, 200); + * push(); // Start a new drawing state + * translate(50, 50); * rotate(PI / 4); + * + * // Convert each vertex to screen coordinates * let screenPos = vertices.map(v => worldToScreen(v)); - * pop(); + * pop(); // Restore original drawing state * * background(200); + * * screenPos.forEach((pos, i) => { * text(`(${pos.x.toFixed(1)}, ${pos.y.toFixed(1)})`, pos.x, pos.y); * }); @@ -1178,29 +1182,42 @@ function exitFullscreen() { * @example *
* - * // Example 1: Convert 3D world coordinates of a rotating cube to 2D screen coordinates + * // Example 2: Convert 3D world coordinates of a rotating cube to 2D screen coordinates + * let vertices; + * * function setup() { - * createCanvas(400, 400, WEBGL); - * let vertices = [ - * createVector(-50, -50, -50), - * createVector(50, -50, -50), - * createVector(50, 50, -50), - * createVector(-50, 50, -50), - * createVector(-50, -50, 50), - * createVector(50, -50, 50), - * createVector(50, 50, 50), - * createVector(-50, 50, 50) + * createCanvas(100, 100, WEBGL); + * vertices = [ + * createVector(-25, -25, -25), + * createVector(25, -25, -25), + * createVector(25, 25, -25), + * createVector(-25, 25, -25), + * createVector(-25, -25, 25), + * createVector(25, -25, 25), + * createVector(25, 25, 25), + * createVector(-25, 25, 25) * ]; + * } * - * push(); - * translate(0, 0, 0); - * rotateX(PI / 4); - * rotateY(PI / 4); + * function draw() { + * background(200); + * + * // Animate rotation + * let rotationX = millis() / 1000; + * let rotationY = millis() / 1200; + * + * rotateX(rotationX); + * rotateY(rotationY); + * + * // Convert world coordinates to screen coordinates * let screenPos = vertices.map(v => worldToScreen(v)); - * pop(); * - * background(200); + * // Display screen coordinates * screenPos.forEach((pos, i) => { + * fill(255); + * noStroke(); + * ellipse(pos.x, pos.y, 3, 3); // Draw points as small ellipses + * fill(0); * text(`(${pos.x.toFixed(1)}, ${pos.y.toFixed(1)})`, pos.x, pos.y); * }); * } diff --git a/test/unit/core/environment.js b/test/unit/core/environment.js index 3e4e471f90..f1f67f3ddc 100644 --- a/test/unit/core/environment.js +++ b/test/unit/core/environment.js @@ -251,8 +251,8 @@ suite('Environment', function() { test('worldToScreen with rotation in 2D', function() { myp5.push(); - myp5.rotate(myp5.PI / 2); myp5.translate(50, 50); + myp5.rotate(myp5.PI / 2); let worldPos = myp5.createVector(10, 0); let screenPos = myp5.worldToScreen(worldPos); myp5.pop(); @@ -273,15 +273,24 @@ suite('Environment', function() { assert.closeTo(screenPos.y, 50, 0.1); }); - test('worldToScreen with rotation in 3D', function() { + test('worldToScreen with rotation in 3D around Y-axis', function() { myp5.push(); myp5.rotateY(myp5.PI / 2); - myp5.translate(50, 0, 0); let worldPos = myp5.createVector(50, 0, 0); let screenPos = myp5.worldToScreen(worldPos); myp5.pop(); - assert.closeTo(screenPos.x, 100, 0.1); + assert.closeTo(screenPos.x, 50, 0.1); assert.closeTo(screenPos.y, 50, 0.1); }); + + test('worldToScreen with rotation in 3D around Z-axis', function() { + myp5.push(); + myp5.rotateZ(myp5.PI / 2); + let worldPos = myp5.createVector(10, 0, 0); + let screenPos = myp5.worldToScreen(worldPos); + myp5.pop(); + assert.closeTo(screenPos.x, 50, 0.1); + assert.closeTo(screenPos.y, 40, 0.1); + }); }); }); From 09343bf8de922f51158c447224259163dff5e9d3 Mon Sep 17 00:00:00 2001 From: Garima Date: Tue, 27 Aug 2024 19:23:59 +0530 Subject: [PATCH 07/16] minor changes in example --- src/core/environment.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/core/environment.js b/src/core/environment.js index 4a67088141..1b2104d971 100644 --- a/src/core/environment.js +++ b/src/core/environment.js @@ -1206,25 +1206,33 @@ function exitFullscreen() { * let rotationX = millis() / 1000; * let rotationY = millis() / 1200; * + * push(); + * * rotateX(rotationX); * rotateY(rotationY); * * // Convert world coordinates to screen coordinates * let screenPos = vertices.map(v => worldToScreen(v)); * + * pop(); + * * // Display screen coordinates * screenPos.forEach((pos, i) => { + * + * let screenX = pos.x - width / 2; + * let screenY = pos.y - height / 2; * fill(255); * noStroke(); - * ellipse(pos.x, pos.y, 3, 3); // Draw points as small ellipses + * ellipse(screenX, screenY, 3, 3); // Draw points as small ellipses * fill(0); - * text(`(${pos.x.toFixed(1)}, ${pos.y.toFixed(1)})`, pos.x, pos.y); + * text(`(${screenX.toFixed(1)}, ${screenY.toFixed(1)})`, screenX, screenY); * }); * } * *
* */ + p5.prototype.worldToScreen = function(worldPosition) { const renderer = this._renderer; if (renderer.drawingContext instanceof CanvasRenderingContext2D) { From c2e87bd79296a656bc957adf8f12268f0a151104 Mon Sep 17 00:00:00 2001 From: Garima Date: Sat, 14 Sep 2024 23:23:21 +0530 Subject: [PATCH 08/16] Minor fixes to pass tests --- src/core/environment.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/environment.js b/src/core/environment.js index 0e392e5ed5..4c452709fa 100644 --- a/src/core/environment.js +++ b/src/core/environment.js @@ -1247,12 +1247,12 @@ p5.prototype.worldToScreen = function(worldPosition) { if (renderer.drawingContext instanceof CanvasRenderingContext2D) { // Handle 2D context const transformMatrix = new DOMMatrix() - .scale(1 / pixelDensity()) + .scale(1 / renderer._pInst.pixelDensity()) .multiply(renderer.drawingContext.getTransform()); const screenCoordinates = transformMatrix.transformPoint( new DOMPoint(worldPosition.x, worldPosition.y) ); - return createVector(screenCoordinates.x, screenCoordinates.y); + return new p5.Vector(screenCoordinates.x, screenCoordinates.y); } else { // Handle WebGL context const cameraCoordinates = renderer.uMVMatrix.multiplyPoint(worldPosition); @@ -1261,7 +1261,7 @@ p5.prototype.worldToScreen = function(worldPosition) { const screenX = (0.5 + 0.5 * normalizedDeviceCoordinates.x) * this.width; const screenY = (0.5 - 0.5 * normalizedDeviceCoordinates.y) * this.height; const screenZ = 0.5 + 0.5 * normalizedDeviceCoordinates.z; - return createVector(screenX, screenY, screenZ); + return new p5.Vector(screenX, screenY, screenZ); } }; From b7436b8751700fde5c6f1ff69a012e2cb448663d Mon Sep 17 00:00:00 2001 From: Garima Date: Sun, 15 Sep 2024 00:21:02 +0530 Subject: [PATCH 09/16] Handled edge cases in worldToScreen to avoid Infinity values --- src/core/environment.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/core/environment.js b/src/core/environment.js index 4c452709fa..c1b1b8602c 100644 --- a/src/core/environment.js +++ b/src/core/environment.js @@ -1258,10 +1258,19 @@ p5.prototype.worldToScreen = function(worldPosition) { const cameraCoordinates = renderer.uMVMatrix.multiplyPoint(worldPosition); const normalizedDeviceCoordinates = renderer.uPMatrix.multiplyAndNormalizePoint(cameraCoordinates); - const screenX = (0.5 + 0.5 * normalizedDeviceCoordinates.x) * this.width; - const screenY = (0.5 - 0.5 * normalizedDeviceCoordinates.y) * this.height; - const screenZ = 0.5 + 0.5 * normalizedDeviceCoordinates.z; - return new p5.Vector(screenX, screenY, screenZ); + // Avoid division by zero or improper transformations + if(isFinite(normalizedDeviceCoordinates.x) + && isFinite(normalizedDeviceCoordinates.y) + && isFinite(normalizedDeviceCoordinates.z)){ + const screenX = (0.5 + 0.5 * normalizedDeviceCoordinates.x) * this.width; + const screenY = (0.5 - 0.5 * normalizedDeviceCoordinates.y) * this.height; + const screenZ = 0.5 + 0.5 * normalizedDeviceCoordinates.z; + return new p5.Vector(screenX, screenY, screenZ); + } + else { + // Handle invalid transformations + return new p5.Vector(NaN, NaN, NaN); + } } }; From 6e9f46c89ed3fa8ba82516d0fffd8dcb0e745bb6 Mon Sep 17 00:00:00 2001 From: Garima Date: Sun, 15 Sep 2024 00:35:31 +0530 Subject: [PATCH 10/16] Minor fixes --- src/core/environment.js | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/core/environment.js b/src/core/environment.js index c1b1b8602c..f997923f2d 100644 --- a/src/core/environment.js +++ b/src/core/environment.js @@ -1254,23 +1254,31 @@ p5.prototype.worldToScreen = function(worldPosition) { ); return new p5.Vector(screenCoordinates.x, screenCoordinates.y); } else { - // Handle WebGL context + // Handle WebGL context (3D) const cameraCoordinates = renderer.uMVMatrix.multiplyPoint(worldPosition); + + // Ensure that we avoid undefined or bad transformations + if (!isFinite(cameraCoordinates.x) || + !isFinite(cameraCoordinates.y) || + !isFinite(cameraCoordinates.z)) { + // Return invalid coordinates if transformation is not valid + return new p5.Vector(NaN, NaN, NaN); + } + const normalizedDeviceCoordinates = renderer.uPMatrix.multiplyAndNormalizePoint(cameraCoordinates); - // Avoid division by zero or improper transformations - if(isFinite(normalizedDeviceCoordinates.x) - && isFinite(normalizedDeviceCoordinates.y) - && isFinite(normalizedDeviceCoordinates.z)){ - const screenX = (0.5 + 0.5 * normalizedDeviceCoordinates.x) * this.width; - const screenY = (0.5 - 0.5 * normalizedDeviceCoordinates.y) * this.height; - const screenZ = 0.5 + 0.5 * normalizedDeviceCoordinates.z; - return new p5.Vector(screenX, screenY, screenZ); - } - else { - // Handle invalid transformations + + // If the z-coordinate of the normalized device coordinates is 0 or very close to it, + // we are too close to or behind the camera, leading to invalid screen coordinates. + if (Math.abs(normalizedDeviceCoordinates.z) < 1e-6) { return new p5.Vector(NaN, NaN, NaN); } + + // Mapping normalizedDeviceCoordinates to screen space + const screenX = (0.5 + 0.5 * normalizedDeviceCoordinates.x) * this.width; + const screenY = (0.5 - 0.5 * normalizedDeviceCoordinates.y) * this.height; + const screenZ = 0.5 + 0.5 * normalizedDeviceCoordinates.z; + return new p5.Vector(screenX, screenY, screenZ); } }; From d8a87a0ecf5a5d4aad8b1949ee45a747a578f537 Mon Sep 17 00:00:00 2001 From: Garima Date: Sun, 15 Sep 2024 19:55:56 +0530 Subject: [PATCH 11/16] Added calculateCombinedMatrix() --- src/core/environment.js | 2 +- src/webgl/p5.RendererGL.js | 9 +++++++++ src/webgl/p5.Shader.js | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/core/environment.js b/src/core/environment.js index f997923f2d..61ba2598ae 100644 --- a/src/core/environment.js +++ b/src/core/environment.js @@ -1255,7 +1255,7 @@ p5.prototype.worldToScreen = function(worldPosition) { return new p5.Vector(screenCoordinates.x, screenCoordinates.y); } else { // Handle WebGL context (3D) - const cameraCoordinates = renderer.uMVMatrix.multiplyPoint(worldPosition); + const cameraCoordinates = renderer.calculateCombinedMatrix().multiplyPoint(worldPosition); // Ensure that we avoid undefined or bad transformations if (!isFinite(cameraCoordinates.x) || diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index 69145241a8..fc4f2c2cb0 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -934,6 +934,15 @@ p5.RendererGL = class RendererGL extends Renderer { this.clear(_r, _g, _b, _a); } + // Combines the model and view matrices to get the uMVMatrix + // This method will be reusable wherever you need to update the combined matrix. + calculateCombinedMatrix() { + const modelMatrix = this.uModelMatrix; + const viewMatrix = this.uViewMatrix; + return modelMatrix.copy().mult(viewMatrix); + } + + ////////////////////////////////////////////// // COLOR ////////////////////////////////////////////// diff --git a/src/webgl/p5.Shader.js b/src/webgl/p5.Shader.js index 8333c92ac5..cf862c02ae 100644 --- a/src/webgl/p5.Shader.js +++ b/src/webgl/p5.Shader.js @@ -574,7 +574,7 @@ p5.Shader = class Shader { const viewMatrix = this._renderer.uViewMatrix; const projectionMatrix = this._renderer.uPMatrix; const modelViewMatrix = (modelMatrix.copy()).mult(viewMatrix); - this._renderer.uMVMatrix = modelViewMatrix; + this._renderer.uMVMatrix = this._renderer.calculateCombinedMatrix(); const modelViewProjectionMatrix = modelViewMatrix.copy(); modelViewProjectionMatrix.mult(projectionMatrix); From bd4a5a3a6d5dcce9c7bd6f92773aa8c505c221ae Mon Sep 17 00:00:00 2001 From: Garima Date: Sun, 15 Sep 2024 20:05:05 +0530 Subject: [PATCH 12/16] Fixes --- src/core/environment.js | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/src/core/environment.js b/src/core/environment.js index 61ba2598ae..83afde0c61 100644 --- a/src/core/environment.js +++ b/src/core/environment.js @@ -1254,31 +1254,15 @@ p5.prototype.worldToScreen = function(worldPosition) { ); return new p5.Vector(screenCoordinates.x, screenCoordinates.y); } else { - // Handle WebGL context (3D) - const cameraCoordinates = renderer.calculateCombinedMatrix().multiplyPoint(worldPosition); - - // Ensure that we avoid undefined or bad transformations - if (!isFinite(cameraCoordinates.x) || - !isFinite(cameraCoordinates.y) || - !isFinite(cameraCoordinates.z)) { - // Return invalid coordinates if transformation is not valid - return new p5.Vector(NaN, NaN, NaN); - } - - const normalizedDeviceCoordinates = - renderer.uPMatrix.multiplyAndNormalizePoint(cameraCoordinates); - - // If the z-coordinate of the normalized device coordinates is 0 or very close to it, - // we are too close to or behind the camera, leading to invalid screen coordinates. - if (Math.abs(normalizedDeviceCoordinates.z) < 1e-6) { - return new p5.Vector(NaN, NaN, NaN); - } - - // Mapping normalizedDeviceCoordinates to screen space - const screenX = (0.5 + 0.5 * normalizedDeviceCoordinates.x) * this.width; - const screenY = (0.5 - 0.5 * normalizedDeviceCoordinates.y) * this.height; - const screenZ = 0.5 + 0.5 * normalizedDeviceCoordinates.z; - return new p5.Vector(screenX, screenY, screenZ); + // Handle WebGL context (3D) + const modelViewMatrix = renderer.calculateCombinedMatrix(); + const cameraCoordinates = modelViewMatrix.multiplyPoint(worldPosition); + const normalizedDeviceCoordinates = + renderer.uPMatrix.multiplyAndNormalizePoint(cameraCoordinates); + const screenX = (0.5 + 0.5 * normalizedDeviceCoordinates.x) * this.width; + const screenY = (0.5 - 0.5 * normalizedDeviceCoordinates.y) * this.height; + const screenZ = 0.5 + 0.5 * normalizedDeviceCoordinates.z; + return new p5.Vector(screenX, screenY, screenZ); } }; From a0ce81d0143f761eb73614b1c87f8aeee3469b1b Mon Sep 17 00:00:00 2001 From: Garima Date: Fri, 18 Oct 2024 11:04:10 +0530 Subject: [PATCH 13/16] Fix --- src/webgl/p5.RendererGL.js | 4 ++-- src/webgl/p5.Shader.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index 77382fa121..f0504d7f91 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -938,8 +938,8 @@ p5.RendererGL = class RendererGL extends Renderer { // Combines the model and view matrices to get the uMVMatrix // This method will be reusable wherever you need to update the combined matrix. calculateCombinedMatrix() { - const modelMatrix = this.uModelMatrix; - const viewMatrix = this.uViewMatrix; + const modelMatrix = this.states.uModelMatrix; + const viewMatrix = this.states.uViewMatrix; return modelMatrix.copy().mult(viewMatrix); } diff --git a/src/webgl/p5.Shader.js b/src/webgl/p5.Shader.js index 2814adb593..cc1568254a 100644 --- a/src/webgl/p5.Shader.js +++ b/src/webgl/p5.Shader.js @@ -937,7 +937,7 @@ function shader(p5, fn){ const viewMatrix = this._renderer.states.uViewMatrix; const projectionMatrix = this._renderer.states.uPMatrix; const modelViewMatrix = (modelMatrix.copy()).mult(viewMatrix); - this._renderer.states.uMVMatrix = this._renderer.states.calculateCombinedMatrix(); + this._renderer.states.uMVMatrix = this._renderer.calculateCombinedMatrix(); const modelViewProjectionMatrix = modelViewMatrix.copy(); modelViewProjectionMatrix.mult(projectionMatrix); From de3c7773d156cee97b0cc3acc141999a0067b1ef Mon Sep 17 00:00:00 2001 From: Garima Date: Sun, 20 Oct 2024 20:59:35 +0530 Subject: [PATCH 14/16] Minor change --- src/core/environment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/environment.js b/src/core/environment.js index f52224dd8b..241bd7d0b8 100644 --- a/src/core/environment.js +++ b/src/core/environment.js @@ -1258,7 +1258,7 @@ p5.prototype.worldToScreen = function(worldPosition) { const modelViewMatrix = renderer.calculateCombinedMatrix(); const cameraCoordinates = modelViewMatrix.multiplyPoint(worldPosition); const normalizedDeviceCoordinates = - renderer.uPMatrix.multiplyAndNormalizePoint(cameraCoordinates); + renderer.states.uPMatrix.multiplyAndNormalizePoint(cameraCoordinates); const screenX = (0.5 + 0.5 * normalizedDeviceCoordinates.x) * this.width; const screenY = (0.5 - 0.5 * normalizedDeviceCoordinates.y) * this.height; const screenZ = 0.5 + 0.5 * normalizedDeviceCoordinates.z; From 85244c0604a556de2c3f0d0c6f14fbf1a3ecce3c Mon Sep 17 00:00:00 2001 From: Garima Date: Sun, 20 Oct 2024 21:34:18 +0530 Subject: [PATCH 15/16] Test fix --- test/unit/core/environment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/core/environment.js b/test/unit/core/environment.js index e4ee1853df..d7d758e1a1 100644 --- a/test/unit/core/environment.js +++ b/test/unit/core/environment.js @@ -290,7 +290,7 @@ suite('Environment', function() { let screenPos = myp5.worldToScreen(worldPos); myp5.pop(); assert.closeTo(screenPos.x, 50, 0.1); - assert.closeTo(screenPos.y, 40, 0.1); + assert.closeTo(screenPos.y, 60, 0.1); }); }); }); From 23accfe9561e081bf0e4a0a50c33546fb4e1d1ba Mon Sep 17 00:00:00 2001 From: Garima Date: Mon, 21 Oct 2024 00:39:19 +0530 Subject: [PATCH 16/16] Docs improved --- src/core/environment.js | 64 +++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/src/core/environment.js b/src/core/environment.js index 241bd7d0b8..f129ff71b2 100644 --- a/src/core/environment.js +++ b/src/core/environment.js @@ -1151,7 +1151,7 @@ function exitFullscreen() { * Converts 3D world coordinates to 2D screen coordinates. * * This function takes a 3D vector and converts its coordinates - * from the world space to screen space. This is useful for placing + * from the world space to screen space. This can be useful for placing * 2D elements in a 3D scene or for determining the screen position * of 3D objects. * @@ -1161,37 +1161,63 @@ function exitFullscreen() { * @example *
* - * // Example 1: Convert 2D world coordinates of a rotating square to screen coordinates - * function setup() { - * createCanvas(100, 100); * + * function setup() { + * createCanvas(150, 150); * let vertices = [ - * createVector(-5, -5), - * createVector(5, -5), - * createVector(5, 5), - * createVector(-5, 5) + * createVector(-20, -20), + * createVector(20, -20), + * createVector(20, 20), + * createVector(-20, 20) * ]; * - * push(); // Start a new drawing state - * translate(50, 50); + * push(); + * translate(75, 55); * rotate(PI / 4); * - * // Convert each vertex to screen coordinates + * // Convert world coordinates to screen coordinates * let screenPos = vertices.map(v => worldToScreen(v)); - * pop(); // Restore original drawing state + * pop(); * * background(200); * + * stroke(0); + * fill(100, 150, 255, 100); + * beginShape(); + * screenPos.forEach(pos => vertex(pos.x, pos.y)); + * endShape(CLOSE); + * + * screenPos.forEach((pos, i) => { + * fill(0); + * textSize(10); + * if (i === 0) { + * text(i + 1, pos.x + 3, pos.y - 7); + * } else if (i === 1) { + * text(i + 1, pos.x + 7, pos.y + 2); + * } else if (i === 2) { + * text(i + 1, pos.x - 2, pos.y + 12); + * } else if (i === 3) { + * text(i + 1, pos.x - 12, pos.y - 2); + * } + * }); + * + * fill(0); + * noStroke(); + * textSize(10); + * let legendY = height - 35; * screenPos.forEach((pos, i) => { - * text(`(${pos.x.toFixed(1)}, ${pos.y.toFixed(1)})`, pos.x, pos.y); + * text(`Vertex ${i + 1}: (${pos.x.toFixed(1)}, ${pos.y.toFixed(1)})`, 5, legendY + i * 10); * }); + * + * describe('A rotating square is transformed and drawn using screen coordinates.'); + * * } * *
+ * * @example *
* - * // Example 2: Convert 3D world coordinates of a rotating cube to 2D screen coordinates * let vertices; * * function setup() { @@ -1206,6 +1232,9 @@ function exitFullscreen() { * createVector(25, 25, 25), * createVector(-25, 25, 25) * ]; + * + * describe('A rotating cube with points mapped to 2D screen space and displayed as ellipses.'); + * * } * * function draw() { @@ -1225,16 +1254,13 @@ function exitFullscreen() { * * pop(); * - * // Display screen coordinates * screenPos.forEach((pos, i) => { * * let screenX = pos.x - width / 2; * let screenY = pos.y - height / 2; - * fill(255); - * noStroke(); - * ellipse(screenX, screenY, 3, 3); // Draw points as small ellipses * fill(0); - * text(`(${screenX.toFixed(1)}, ${screenY.toFixed(1)})`, screenX, screenY); + * noStroke(); + * ellipse(screenX, screenY, 3, 3); * }); * } *