Skip to content

Commit a3cb5db

Browse files
committed
Make worldToScreen/screenToWorld accept x,y,[z] coordinates
1 parent 5e53dc3 commit a3cb5db

File tree

4 files changed

+79
-41
lines changed

4 files changed

+79
-41
lines changed

src/core/environment.js

Lines changed: 31 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,7 +1272,9 @@ function environment(p5, fn){
12721272
* of 3D objects.
12731273
*
12741274
* @method worldToScreen
1275-
* @param {p5.Vector} worldPosition The 3D coordinates in the world space.
1275+
* @param {Number|p5.Vector} x The x coordinate in world space. (Or a vector for all three coordinates.)
1276+
* @param {Number} y The y coordinate in world space.
1277+
* @param {Number} [z] The z coordinate in world space.
12761278
* @return {p5.Vector} A vector containing the 2D screen coordinates.
12771279
* @example
12781280
* <div>
@@ -1384,22 +1386,29 @@ function environment(p5, fn){
13841386
*
13851387
*/
13861388
fn.worldToScreen = function(worldPosition) {
1389+
if (typeof worldPosition === "number") {
1390+
// We got passed numbers, convert to vector
1391+
worldPosition = this.createVector(...arguments);
1392+
}
1393+
13871394
const matrix = this._renderer.getWorldToScreenMatrix();
13881395
const screenPosition = matrix.multiplyAndNormalizePoint(worldPosition);
13891396
return screenPosition;
13901397
};
13911398
/**
13921399
* Converts 2D screen coordinates to 3D world coordinates.
13931400
*
1394-
* This function takes a vector and converts its coordinates
1395-
* from the world space to screen space. This can be useful for determining
1396-
* the mouse position relative to a 2D or 3D object.
1401+
* This function takes a vector and converts its coordinates from coordinates
1402+
* on the screen to coordinates in the currently drawn object. This can be
1403+
* useful for determining the mouse position relative to a 2D or 3D object.
13971404
*
1398-
* The Z component of the screen coordinates is treated as "depth", or
1399-
* distance from the camera, when converting to 3D world space.
1405+
* If given, the Z component of the input coordinates is treated as "depth",
1406+
* or distance from the camera.
14001407
*
14011408
* @method screenToWorld
1402-
* @param {p5.Vector} screenPosition The coordinates in screen space.
1409+
* @param {Number|p5.Vector} x The x coordinate in screen space. (Or a vector for all three coordinates.)
1410+
* @param {Number} y The y coordinate in screen space.
1411+
* @param {Number} [z] The z coordinate in screen space.
14031412
* @return {p5.Vector} A vector containing the 3D world space coordinates.
14041413
* @example
14051414
* <div>
@@ -1427,40 +1436,24 @@ function environment(p5, fn){
14271436
*
14281437
* </code>
14291438
* </div>
1430-
* @example
1431-
* <div>
1432-
* <code>
1433-
*
1434-
* function setup() {
1435-
* createCanvas(100, 100, WEBGL);
1436-
* describe('A rotating square with a line passing through the mouse drawn across it.');
1437-
* }
1438-
*
1439-
* function draw() {
1440-
* background(220);
1441-
*
1442-
* // Animate rotation
1443-
* rotateZ(millis() / 1000);
1444-
* rect(-30, -30, 60);
1445-
*
1446-
* // Transform the 0, 0 point to get the depth of the square
1447-
* let center = worldToScreen(createVector(0, 0))
1448-
*
1449-
* // Compute the location of the mouse in the coordinates of the square,
1450-
* // using the depth calculated earlier
1451-
* let localMouse = screenToWorld(createVector(mouseX, mouseY, center.z));
1452-
*
1453-
* // Draw a line parallel to the local Y axis, passing through the mouse
1454-
* line(localMouse.x, -30, localMouse.x, 30);
1455-
* }
1456-
*
1457-
* </code>
1458-
* </div>
14591439
*
14601440
*/
14611441
fn.screenToWorld = function(screenPosition) {
1462-
let matrixInverse = this._renderer.getWorldToScreenMatrix();
1463-
matrixInverse = matrixInverse.invert(matrixInverse);
1442+
if (typeof screenPosition === "number") {
1443+
// We got passed numbers, convert to vector
1444+
screenPosition = this.createVector(...arguments);
1445+
}
1446+
1447+
const matrix = this._renderer.getWorldToScreenMatrix();
1448+
1449+
if (screenPosition.dimensions == 2) {
1450+
// Calculate a sensible Z value for the current camera projection that
1451+
// will result in 0 once converted to world coordinates
1452+
let z = matrix.mat4[14] / matrix.mat4[15];
1453+
screenPosition = this.createVector(screenPosition.x, screenPosition.y, z);
1454+
}
1455+
1456+
const matrixInverse = matrix.invert(matrix);
14641457

14651458
const worldPosition = matrixInverse.multiplyAndNormalizePoint(screenPosition);
14661459
return worldPosition;

src/math/p5.Vector.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,14 @@ class Vector {
3434
// This is how it comes in with createVector()
3535
// This check if the first argument is a function
3636
constructor(...args) {
37-
let dimensions = args.length; // TODO: make default 3 if no arguments
3837
let values = args.map((arg) => arg || 0);
3938
if (typeof args[0] === "function") {
4039
this.isPInst = true;
4140
this._fromRadians = args[0];
4241
this._toRadians = args[1];
4342
values = args.slice(2).map((arg) => arg || 0);
4443
}
44+
let dimensions = values.length; // TODO: make default 3 if no arguments
4545
if (dimensions === 0) {
4646
this.dimensions = 2;
4747
this._values = [0, 0, 0];

test/unit/core/environment.js

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,15 @@ suite('Environment', function() {
249249
assert.closeTo(screenPos.y, 50, 0.1);
250250
});
251251

252+
test('worldToScreen accepts numbers with translation in 2D', function() {
253+
myp5.push();
254+
myp5.translate(50, 50);
255+
let screenPos = myp5.worldToScreen(0, 0);
256+
myp5.pop();
257+
assert.closeTo(screenPos.x, 50, 0.1);
258+
assert.closeTo(screenPos.y, 50, 0.1);
259+
});
260+
252261
test('worldToScreen with rotation in 2D', function() {
253262
myp5.push();
254263
myp5.translate(50, 50);
@@ -267,6 +276,15 @@ suite('Environment', function() {
267276
assert.closeTo(worldPos.y, 50, 0.1);
268277
});
269278

279+
test('screenToWorld accepts numbers with translation in 2D', function() {
280+
myp5.push();
281+
myp5.translate(50, 50);
282+
let worldPos = myp5.screenToWorld(50, 50);
283+
myp5.pop();
284+
assert.closeTo(worldPos.x, 0, 0.1);
285+
assert.closeTo(worldPos.y, 0, 0.1);
286+
});
287+
270288
test('screenToWorld with rotation in 2D', function() {
271289
myp5.push();
272290
myp5.translate(50, 50);
@@ -304,6 +322,15 @@ suite('Environment', function() {
304322
assert.closeTo(screenPos.y, 50, 0.1);
305323
});
306324

325+
test('worldToScreen accepts numbers with translation in 3D', function() {
326+
myp5.push();
327+
myp5.translate(50, 50, 0);
328+
let screenPos = myp5.worldToScreen(0, 0, 0);
329+
myp5.pop();
330+
assert.closeTo(screenPos.x, 100, 0.1);
331+
assert.closeTo(screenPos.y, 100, 0.1);
332+
});
333+
307334
test('worldToScreen with rotation in 3D around Y-axis', function() {
308335
myp5.push();
309336
myp5.rotateY(myp5.PI / 2);
@@ -329,12 +356,22 @@ suite('Environment', function() {
329356
let worldPos = myp5.screenToWorld(screenPos);
330357
assert.closeTo(worldPos.x, 0, 0.1);
331358
assert.closeTo(worldPos.y, 0, 0.1);
359+
assert.closeTo(worldPos.z, 0, 0.1);
360+
});
361+
362+
test('screenToWorld accepts numbers with translation in 3D', function() {
363+
myp5.push();
364+
myp5.translate(50, 50);
365+
let worldPos = myp5.screenToWorld(100, 100);
366+
myp5.pop();
367+
assert.closeTo(worldPos.x, 0, 0.1);
368+
assert.closeTo(worldPos.y, 0, 0.1);
332369
});
333370

334371
test('screenToWorld with rotation in 3D around Y-axis', function() {
335372
myp5.push();
336373
myp5.rotateY(myp5.PI / 2);
337-
let screenPos = myp5.createVector(50, 50, 10/11);
374+
let screenPos = myp5.createVector(50, 50);
338375
let worldPos = myp5.screenToWorld(screenPos);
339376
myp5.pop();
340377
assert.closeTo(worldPos.y, 0, 0.1);
@@ -343,7 +380,7 @@ suite('Environment', function() {
343380
test('screenToWorld with rotation in 3D around Z-axis', function() {
344381
myp5.push();
345382
myp5.rotateZ(myp5.PI / 2);
346-
let screenPos = myp5.createVector(50, 60, 10/11);
383+
let screenPos = myp5.createVector(50, 60);
347384
let worldPos = myp5.screenToWorld(screenPos);
348385
myp5.pop();
349386
assert.closeTo(worldPos.x, 10, 0.1);

test/unit/math/p5.Vector.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ suite("p5.Vector", function () {
5252
assert.equal(v.y, 0);
5353
assert.equal(v.z, 0);
5454
});
55+
56+
test("should have dimensions initialized to 2", function () {
57+
assert.equal(v.dimensions, 2);
58+
});
5559
});
5660

5761
suite.todo("p5.prototype.createVector(1, 2, 3)", function () {
@@ -64,6 +68,10 @@ suite("p5.Vector", function () {
6468
assert.equal(v.y, 2);
6569
assert.equal(v.z, 3);
6670
});
71+
72+
test("should have dimensions initialized to 3", function () {
73+
assert.equal(v.dimensions, 3);
74+
});
6775
});
6876

6977
suite("new p5.Vector()", function () {

0 commit comments

Comments
 (0)