Skip to content

Add Transform3d for homogeneous layer transforms - #590

Open
waywardmonkeys wants to merge 2 commits into
linebender:mainfrom
waywardmonkeys:add-transform3d
Open

Add Transform3d for homogeneous layer transforms#590
waywardmonkeys wants to merge 2 commits into
linebender:mainfrom
waywardmonkeys:add-transform3d

Conversation

@waywardmonkeys

Copy link
Copy Markdown
Contributor

Add Transform3d as a column-major 4 by 4 homogeneous transform in kurbo, with CSS/DOMMatrix coefficient order and multiplication semantics matching Affine.

The initial API covers construction from coefficients and columns, Affine and TranslateScale conversion, exact 2D and 3D-affine classification, CSS-style perspective, homogeneous point transformation, and projection with w > 0 handling.

This intentionally leaves full projective inverse out of the first slice, avoiding an affine-only inverse on a type that can represent perspective.

@waywardmonkeys

Copy link
Copy Markdown
Contributor Author

This PR was done with assistance from Codex (5.5, xhigh).

@DJMcNab DJMcNab left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

As an implementation of this feature, this is unobjectionable. I do have a few nits, but none are especially blocking. I don't feel like I'm especially qualified to decide whether we want this (this is the kind of thing I miss having a strong office hours for). I definitely don't want to make that decision at the end of a workday.

I do agree that as a project, Linebender does need a way to talk about 3d transforms in 2d rendering. I don't want this to sound like I'm trying to blackhole this feature, but part of me wonders if it would actually belong in Peniko (as it's more of a 2d graphics than 2d shapes thing). For the sake of keeping moving forward, I'm definitely not against landing this here.

@tomcur do you have any thoughts on this conceptually?

Comment thread kurbo/src/transform3d.rs Outdated

/// Construct a transform from its four columns.
///
/// Each column is `[x, y, z, w]`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could you expand on this comment? The column is the contribution of their respective element to the final result, right? So when you say x in the doc comment, it's the contribution of the element corresponding to the column number to the resulting x?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Adjusted in next push.

Comment thread kurbo/src/transform3d.rs Outdated
self.0[i]
}

/// Construct the 3D transform with the same effect as a 2D [`Affine`].

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd probably add the "in the xy plane" here, but I think we could get away without it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Adjusted.

Comment thread kurbo/src/transform3d.rs
/// Panics if `i >= 4`.
#[inline]
#[must_use]
pub const fn col(self, i: usize) -> [f64; 4] {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there a use for a row function?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There is, for matrix inspection but also to get the bottom / perspective row. Added in next push.

Comment thread kurbo/src/transform3d.rs
Comment on lines +382 to +398
let c = self.0;
c[0][0] == 1.0
&& c[0][1] == 0.0
&& c[0][2] == 0.0
&& c[0][3] == 0.0
&& c[1][0] == 0.0
&& c[1][1] == 1.0
&& c[1][2] == 0.0
&& c[1][3] == 0.0
&& c[2][0] == 0.0
&& c[2][1] == 0.0
&& c[2][2] == 1.0
&& c[2][3] == 0.0
&& c[3][0] == 0.0
&& c[3][1] == 0.0
&& c[3][2] == 0.0
&& c[3][3] == 1.0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could we use self == Self::IDENTITY?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

const fn in the workspace MSRV, I think.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It might be possible to do self.0 == IDENTITY.0, maybe?

Add `Transform3d` as a column-major 4 by 4 homogeneous transform in `kurbo`, with CSS/`DOMMatrix` coefficient order and multiplication semantics matching `Affine`.

The initial API covers construction from coefficients and columns, `Affine` and `TranslateScale` conversion, exact 2D and 3D-affine classification, CSS-style perspective, homogeneous point transformation, and projection with `w > 0` handling.

This intentionally leaves full projective `inverse` out of the first slice, avoiding an affine-only inverse on a type that can represent perspective.
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.

2 participants