-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Description
Increasing access
In normal p5, users have access to functions like rotate()
, scale()
, etc. In p5.strands, if one wants to position particles, currently there is nothing that looks like that, so to do a rotation, one has to do a lot of manual trig. This is a fairly large step in the learning curve that we could possibly smooth out.
Most appropriate sub-area of p5.js?
- AccessibilityColorCore/Environment/RenderingDataDOMEventsImageIOMathTypographyUtilitiesWebGLBuild processUnit testingInternationalizationFriendly errorsOther (specify if possible)To pick up a draggable item, press the space bar. While dragging, use the arrow keys to move the item. Press space again to drop the item in its new position, or press escape to cancel.
Feature enhancement details
I was working on this p5.strands sketch https://openprocessing.org/sketch/2664809 and found myself essentially manually applying a rotation matrix to points. This is fairly hard to do and I wouldn't expect this to be something most users are comfortable with. Maybe there's something we can do there?
My initial thought: we're in the process of making p5.Matrix
public and making regular p5 functions have strands equivalents. Maybe we can make an equivalent of p5.Matrix
that works in strands?
e.g.:
Before | After |
---|---|
const curveCos = cos(theta)
const curveSin = sin(theta)
// ...
inputs.position += [
curveX * curveCos - curveY * curveSin,
curveX * curveSin + curveY * curveCos,
0
] |
const rotation = createTransformMatrix().rotateZ(theta)
inputs.position += rotation * [curveX, curveY, 0] |
Activity
perminder-17 commentedon Jun 1, 2025
This would be a great enhancement, manually applying a rotation matrix to points would really be hard as I can see and I also wouldn't expect this to be something most users are comfortable with as well. Thanks for bringing this up @davepagurek :)
GregStanton commentedon Jun 3, 2025
Hi @davepagurek and @perminder-17! This is great to see, and @davepagurek, thanks for mentioning this use case! I've been working on a new
Transform
class and coincidentally made a relevant demo yesterday, before I saw this issue. It addresses the exact case you mentioned (not p5.strands specifically, but applying transforms to individual points and avoiding manual trig). Great minds think alike! 😄Actually, I've been working on a proposal for several related classes:
Transform
(new)Vector
(enhanced)Matrix
(new)Tensor
(speculative)I suppose I shouldn't hype it up too much, but I feel I've cooked up something special. For each class, I've written up motivation and use cases, a core feature set (with potential roadmap items), an API, and an implementation architecture. I'm entering the phase where I'm writing up all my ideas and finishing the details for a set of public proposals.
I was originally thinking of making these sub-issues of #7754, but to keep things concise and focused, I might start a new umbrella issue since the proposals are all related. I would link to all previous discussions, including this one. How does that sound?
P.S. It looks like you're using operator overloading in your demo code snippet? Is that on purpose? Don't get me wrong. I wish we had operator overloading in JavaScript, but I don't think we have that available unless we do something like paper.js has done. My operating assumption has been that this is probably off the table for p5, based on past discussions, but please let me know if that's changed!
davepagurek commentedon Jun 3, 2025
We actually do have that in p5.strands, as a way to bridge some of the features of GLSL to our shaders-in-js API. Like paper.js, it rewrites operators between vectors into .mult(...) and similar methods, which can also be written out explicitly. Currently it's just there though and not in general.
davepagurek commentedon Jun 3, 2025
Under the hood in strands, variables that look like numbers also aren't actual js numbers, they're objects that keep track of operations applied to them so that we can output matching structures in GLSL. The main benefit of operator overloading is to make those FEEL like they're just numbers so users aren't aware that anything different is happening, unlike some other GLSL builders we looked at. This happens to also allow us to do multiplications on matrices and vectors if we want in strands, but it's still up in the air whether or not we think it makes sense to actively encourage this, or just mention it's a thing you can do so people coming from GLSL don't feel like it's way more verbose in strands.
GregStanton commentedon Jun 4, 2025
Oh, wow. Super interesting. Thanks @davepagurek! I'm glad I asked. (And I should've known you knew what you were doing with the overloading 😅.)
As I mentioned, I'm planning to propose a dedicated
Transform
class, as well as general math classes includingVector
andMatrix
that are decoupled fromTransform
. (The implementations will be different anyway, due to performance requirements, and the APIs will be totally different.)Regarding transforms, my sense is that it'd be best to avoid operator overloading in p5.strands (except possibly for GLSL users who really know what they're doing, although likely not even for them). I think my reasoning applies in this context, but I haven't learned a lot about p5.strands yet, so I'll explain my thinking.
I'll comment briefly about overloads for general matrix operations, within p5.strands, at the end. I'd be curious to hear your thoughts about any of this.
Transform
APIIn the design I'm working on, transform features can be used without any knowledge of matrices or matrix multiplication. I'm treating matrices as essentially an implementation detail. Here's a sample of the current API that includes the general transform features (we'd also have built-in transforms like
rotate()
and some other things):Notes:
applyMatrix()
(alias + deprecation + 3.0 removal). I'll elaborate in the proposal.applyTransform()
andapplyToTransform()
, based on the names.applyToNormal()
feature would obviate the need for a feature likegetNormalMatrix()
and increase cohesion.Abstracting away the matrix operations has a few major benefits in the context of the current conversation:
Since users won't need to know the transforms are implemented with matrices, they may be less likely to think to themselves, "Why is this more verbose than in GLSL?"
Confession
When I first made the
Transform
demo, and when I applied the same transforms using standalone features, I got strange results. It took me a while to figure out what was going on, despite being well versed in linear algebra, including graphics transformations.My mistake was that I had assumed p5 applies transforms by pre-multiplication (as in math), and that p5 applies transforms in the order that they're specified. I only figured out what was going on when I looked carefully at the
glMatrix
source code. I learned that, for reasons I won't get into here, p5 and glMatrix accumulate transforms using post-multiplication by default, but they apply transforms to points using pre-multiplication.A consequence of the conventions used by p5 is that transforms are actually applied in the reverse order to how they're specified by users. That matters because different orders can produce different results. (Unfortunately, I don't think we can do anything about it, since the native drawing context in the 2D canvas and
DOMMatrix
also use these conventions. The existing p5 API for standalone transforms is similar to the native APIs, so having different behavior from those could cause quite a bit of confusion.)Abstracting all of this away definitely seems like the way to go. We just need to be sure to document the fact that the order of transforms is reversed. So, if a user wants to apply a rotation and then a translation, they need to call methods in the opposite order:
translate()
and thenrotate()
.Overloading for pure math operations
If JS ever gets operator overloading, or if we want to implement overloads ourselves (since it's already possible in p5.strands), it'd be good to consider taking advantage of them in the general math classes like
Vector
andMatrix
. This might be really cool! Overloading for vector and matrix math is something I've always wished were possible in JS.However, for p5.strands, I suppose we'd need a matrix backend that runs on the GPU? I think the most viable backends for us are currently designed to run on the CPU. This is assuming we might want a tensor class. I actually came up with a plan for supporting a hardware-accelerated backend in the future, but it seemed like too much of a complication for now.