Transformation Matrix for p5.Strands - #8984
Conversation
|
|
||
| const originalScale = fn.scale; | ||
| augmentFn(fn, p5, 'scale', function (...args) { | ||
| const t = args[0]; | ||
| if (!isStrandsTransform(t)) return originalScale.apply(this, args); | ||
| const scales = args.slice(1); | ||
| const uniform = scales.length === 1; // scale(t, s) scales every axis by s | ||
| const x = scales[0] ?? 1; | ||
| const y = scales[1] ?? (uniform ? x : 1); | ||
| const z = scales[2] ?? (uniform ? x : 1); | ||
| return transformStep(t, | ||
| [x, 0, 0, 0, y, 0, 0, 0, 1], | ||
| [x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1]); | ||
| }); | ||
|
|
||
| const originalRotate = fn.rotate; | ||
| augmentFn(fn, p5, 'rotate', function (...args) { | ||
| const t = args[0]; | ||
| if (!isStrandsTransform(t)) return originalRotate.apply(this, args); | ||
| const angle = args[1]; // 2D: rotate in-plane; 3D: rotate about the Z axis | ||
| const c = this.cos(angle); | ||
| const s = this.sin(angle); | ||
| const ns = s.mult(-1); // -sin | ||
| return transformStep(t, | ||
| [c, s, 0, ns, c, 0, 0, 0, 1], | ||
| [c, s, 0, 0, ns, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); | ||
| }); | ||
|
|
||
| augmentFn(fn, p5, 'skewX', function (...args) { | ||
| if (!strandsContext.active) { | ||
| p5._friendlyError(`It looks like you've called skewX outside of a shader's modify() function.`); | ||
| return; | ||
| } | ||
| const [t, angle] = args; | ||
| const k = this.tan(angle); // x' = x + tan(angle) * y | ||
| return transformStep(t, | ||
| [1, 0, 0, k, 1, 0, 0, 0, 1], | ||
| [1, 0, 0, 0, k, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); | ||
| }); | ||
|
|
||
| augmentFn(fn, p5, 'skewY', function (...args) { | ||
| if (!strandsContext.active) { | ||
| p5._friendlyError(`It looks like you've called skewY outside of a shader's modify() function.`); | ||
| return; | ||
| } | ||
| const [t, angle] = args; | ||
| const k = this.tan(angle); // y' = y + tan(angle) * x | ||
| return transformStep(t, | ||
| [1, k, 0, 0, 1, 0, 0, 0, 1], | ||
| [1, k, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); | ||
| }); | ||
|
|
||
| augmentFn(fn, p5, 'rotateAxisAngle', function (...args) { | ||
| if (!strandsContext.active) { | ||
| p5._friendlyError(`It looks like you've called rotateAxisAngle outside of a shader's modify() function.`); | ||
| return; | ||
| } | ||
| const [t, axis, angle] = args; | ||
| if (!t?.isStrandsNode || t.typeInfo().baseType !== BaseType.MAT || t.dimension !== 4) { | ||
| FES.userError('type error', | ||
| 'rotateAxisAngle() only works on a 3D transform created with transform3D().'); | ||
| } | ||
| const n = this.normalize(axis); | ||
| const x = n.x, y = n.y, z = n.z; | ||
| const c = this.cos(angle); | ||
| const s = this.sin(angle); | ||
| const omc = p5.strandsNode(1).sub(c); // 1 - cos |
There was a problem hiding this comment.
Hi @davepagurek ,
Just thinking out loud. In our non-strands modules, each area of functionality lives in its own file, for example, transform.js keeps the JSDoc, examples, and implementation together in one place.
I was thinking of following that same convention for the strands transforms: moving the matrix and transform helpers out of strands_api.js into a dedicated strands_transform.js (something better name for the file), with each function's docs kept alongside its implementation.
One thing to check: right now the strands functions are documented centrally in p5.strands.js rather than inline. Would you prefer keeping that, or colocating the transform docs with the code in the new file?
|
Just starte looking at this one, really exciting! I will work on some more complex sketches to test it out, but I appreciate the docs you've included out of the box. Would it be a good idea to add some bench and/or unit and/or visual tests here? I think visual and unit at minimum, given the scope of the PR, would be good... |
Ah, correct. I'm also getting feedback and testing it with more complex examples myself. For now, I'm trying to find ways to break the feature and catch any issues. Once I'm confident with the code, I'll add tests for them. |
Resolves #[Add issue number here]
Changes:
Screenshots of the change:
PR Checklist
npm run lintpasses