Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solves issue #7059 #7113

Merged
merged 18 commits into from
Nov 2, 2024
Merged

Solves issue #7059 #7113

merged 18 commits into from
Nov 2, 2024

Conversation

Garima3110
Copy link
Member

@Garima3110 Garima3110 commented Jul 5, 2024

Resolves #7059

Changes:
Here's a combined sketch that demonstrates the worldToScreen function in both 2D and WebGL contexts.
https://editor.p5js.org/Garima3110/sketches/ilpG7v9Cw

Screenshots of the change:

PR Checklist

@Garima3110
Copy link
Member Author

Please review the code , whenever some maintainer finds the time, Also if you have any idea or some sort of a suggestion, for what example code should i add for the inline documentation of worldToScreen() it would be great!
Thankyou :-)

@davepagurek
Copy link
Contributor

Thanks for taking this on, the code looks good!

  • Do you think we can add a unit test for 2D and a unit test for 3D? Maybe both a simple case (e.g. no transforms have been applied) and also another that we can verify manually (like a 90 degree rotation)?
  • For an example, maybe you could have something like a rotating square (or cube in 3D) where we draw the screen coordinates next to each vertex?

@Garima3110
Copy link
Member Author

Thanks for taking this on, the code looks good!

  • Do you think we can add a unit test for 2D and a unit test for 3D? Maybe both a simple case (e.g. no transforms have been applied) and also another that we can verify manually (like a 90 degree rotation)?
  • For an example, maybe you could have something like a rotating square (or cube in 3D) where we draw the screen coordinates next to each vertex?

Yeah sure! @davepagurek
I'll try doing that by the end of this week.
Thankyou.

@Garima3110
Copy link
Member Author

@davepagurek Just wanted to know , I tried npm run build command for building p5 and run some tests, but for dev2.0 branch the command doesn't work. Is there a different way to build p5 for dev2.0 branch, unlike the main branch which builds p5 just with npm run build.

@limzykenneth
Copy link
Member

@Garima3110 npm run build should work, what error did you get for it?

@Garima3110
Copy link
Member Author

@Garima3110 npm run build should work, what error did you get for it?

image

@limzykenneth
Copy link
Member

Right I see, currently FES is still relying on the parameterData.json file for parameter validation but that file is dynamically generated by the 1.x build via grunt. @Garima3110 Can you switch back to the main branch, run the build, then switch to the dev-2.0 branch to try again? This should generate the parameterData.json file that can be packaged by the rollup build.

@limzykenneth
Copy link
Member

Actually just running npm run docs in the dev-2..0 branch will also generate the parameterData file as @davepagurek have set it up to run the generation script manually.

@Garima3110
Copy link
Member Author

@davepagurek Do the tests seem fine ? Please have a look and let me know if some changes need to be made.
Thankyou!!!

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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we can assert exact coordinates for the 3D tests like we do for the 2D tests instead of having a range like this? e.g. 50, 50 in this case, and I think 50, 100 in the second case?

test('worldToScreen for 2D context', function() {
let worldPos = myp5.createVector(50, 50);
let screenPos = myp5.worldToScreen(worldPos);
assert.closeTo(screenPos.x, 50, 0.1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is great! Can we add a test of rotation in 2D too?

@Garima3110
Copy link
Member Author

I have added more tests , and made some changes according to the suggestions please have a look!

Copy link
Contributor

@davepagurek davepagurek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making changes, and sorry for the delay! Got sick and then recovered in the mean time, but we're back now!

});

test('worldToScreen for a rotating cube in 3D', function() {
myp5.push();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one doesn't assert anything so it might not work super well as a unit test, but this would be a cool example to have in the reference for this item! Maybe we could move it into the reference comments instead? (Same for the 2D case, having those as two examples would be great!)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@limzykenneth in the 2.0 branch, is there an easy way to get a preview of how the docs will look, similar to grunt yui:dev on the main branch?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not at the moment. The offline reference (which I also forked out here) can potentially help but it is not setup to work automatically yet.

test('worldToScreen with rotation in 2D', function() {
myp5.push();
myp5.translate(50, 50);
myp5.rotate(myp5.PI / 2);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think since we translate before we rotate, the rotation doesn't end up actually affecting the position. If we rotate first, we should see an effect (I would expect (x, y) to become (y, -x) I think?)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, actually, ignore this -- it's only true if you're using (0,0) as your local coordinate. I see the offset by 10 does actually switch axes here!


test('worldToScreen with rotation in 3D', function() {
myp5.push();
myp5.rotateY(myp5.PI / 2);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comment here: just rotating won't affect the coordinates, so could we do a translation after this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, ignore that, as mentioned above, it should be ok as long as you're converting a nonzero coordinate. But it feels odd that the resulting coordinate would still be 50,50 since the untransformed example above also ends up at that. should this value be something else?

Copy link
Member Author

@Garima3110 Garima3110 Aug 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, ignore that, as mentioned above, it should be ok as long as you're converting a nonzero coordinate. But it feels odd that the resulting coordinate would still be 50,50 since the untransformed example above also ends up at that. should this value be something else?

Maybe it should be 200,200
The default camera position in p5.js is at (0, 0, 800), looking towards the origin (0, 0, 0) along the negative Z-axis. The camera's view is centered on the Z-axis, so any point on this axis is projected to the center of the screen.
rotateY(myp5.PI / 2) rotates the point (50, 0, 0) 90 degrees around the Y-axis.
This rotation transforms the point (50, 0, 0) into (0, 0, -50). After rotation, the point is 50 units in front of the origin along the negative Z-axis. After the rotation, the point (0, 0, -50) is at a distance of 850 units from the camera (800 - (-50)). The screen coordinates are calculated based on the projection of this point into the 2D view of the camera.
Since the rotated point lies directly on the Z-axis, its X and Y screen coordinates should map to the center of the canvas, so --> 200,200

What are your thoughts on this @davepagurek please let me know?!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, I definitely didn't catch that this was a rotateY -- I read it as just rotate, which would have been around the z axis. That now explains why the local offset doesn't update the resulting screen x and y values. The 50,50 result now makes sense, since I think the canvas is only 100x100:

myp5.createCanvas(100, 100);

So I think this result does actually make sense, and maybe we should just add one more test of rotation about the Z axis so that we can confirm that an example like the one you use in the 2D tests also works in WebGL.

* createVector(-50, 50, 50)
* ];
*
* push();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you feel about moving this to draw() and making the rotation change slowly over time?

Copy link
Member Author

@Garima3110 Garima3110 Aug 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A great idea , will do this in a while.

* let rotationX = millis() / 1000;
* let rotationY = millis() / 1200;
*
* rotateX(rotationX);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to make sure the text itself isn't rotated, I think we might need to wrap the rotation + worldToScreen calls in a push/pop like you've got in the 2D mode example.

* 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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the screen coordinates relative to the top left? if so we may need to subtract width/2 and height/2 to keep these centered when drawing them in WebGL mode

* background(200);
*
* screenPos.forEach((pos, i) => {
* text(`(${pos.x.toFixed(1)}, ${pos.y.toFixed(1)})`, pos.x, pos.y);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

@davepagurek
Copy link
Contributor

Sorry for the delay on this! I recently got the tests passing on the dev-2.0 branch, so if you pull that branch in your fork and merge it into this branch again, we can double-check that we get a green checkmark from the test runner and then merge if it looks good.

@Garima3110
Copy link
Member Author

While I continue troubleshooting the failed tests, I'm open to any suggestions you might have, @davepagurek!

return new p5.Vector(screenCoordinates.x, screenCoordinates.y);
} else {
// Handle WebGL context (3D)
const cameraCoordinates = renderer.uMVMatrix.multiplyPoint(worldPosition);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, maybe it's this -- we split up uMVMatrix into uModelMatrix and uViewMatrix, and only update uMVMatrix right before we draw a shape here:

_setMatrixUniforms() {
const modelMatrix = this._renderer.uModelMatrix;
const viewMatrix = this._renderer.uViewMatrix;
const projectionMatrix = this._renderer.uPMatrix;
const modelViewMatrix = (modelMatrix.copy()).mult(viewMatrix);
this._renderer.uMVMatrix = modelViewMatrix;

Maybe we can refactor the bit that sets uMVMatrix into a function on the renderer, like calculateCombinedMatrix(), which gets called in _setMatrixUniforms above, and then you can also call it here right before you make cameraCoordinates?

@Garima3110
Copy link
Member Author

Strange, all tests pass as I try this for the main branch but not here in the 2.0 branch.

Copy link
Contributor

@davepagurek davepagurek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed a few places where it either wasn't accessing states where it should, or was accessing it where it shouldn't, we can see if it works after this. The states thing is new in the 2.0 branch so that's probably where the discrepancy is coming from, hopefully once that's fixed itll work again!

@@ -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 = modelViewMatrix;
this._renderer.states.uMVMatrix = this._renderer.states.calculateCombinedMatrix();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this method needs to be on the renderer and not the states:

Suggested change
this._renderer.states.uMVMatrix = this._renderer.states.calculateCombinedMatrix();
this._renderer.states.uMVMatrix = this._renderer.calculateCombinedMatrix();

// 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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this and the line below would need to access states now:

Suggested change
const modelMatrix = this.uModelMatrix;
const modelMatrix = this.states.uModelMatrix;

// This method will be reusable wherever you need to update the combined matrix.
calculateCombinedMatrix() {
const modelMatrix = this.uModelMatrix;
const viewMatrix = this.uViewMatrix;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const viewMatrix = this.uViewMatrix;
const viewMatrix = this.states.uViewMatrix;

@Garima3110
Copy link
Member Author

Thanks for the clarification @davepagurek
I’ll work on these suggestions and get back to you soon!

const modelViewMatrix = renderer.calculateCombinedMatrix();
const cameraCoordinates = modelViewMatrix.multiplyPoint(worldPosition);
const normalizedDeviceCoordinates =
renderer.uPMatrix.multiplyAndNormalizePoint(cameraCoordinates);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this one is causing the test failures now:
image

I think this one should be in states too:

Suggested change
renderer.uPMatrix.multiplyAndNormalizePoint(cameraCoordinates);
renderer.states.uPMatrix.multiplyAndNormalizePoint(cameraCoordinates);

@Garima3110
Copy link
Member Author

@davepagurek I have made some changes in the doc examples too, everything seems to work fine, please let me know if any other change needs to be done.
Thankyou!

Copy link
Contributor

@davepagurek davepagurek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry this took a while to review! In the process I finally figured out a way to preview the docs live in the p5.js-website repo 😅

@davepagurek davepagurek merged commit 68ae795 into processing:dev-2.0 Nov 2, 2024
2 checks passed
@Garima3110 Garima3110 deleted the worldToScreen branch November 2, 2024 07:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants