<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[applyTransforms]]></title><description><![CDATA[<p dir="auto">Hello,</p>
<p dir="auto">In the documentation <a href="https://jscad.app/docs/module-modeling_geometries_geom3.html" rel="nofollow ugc">here</a>, it says:</p>
<p dir="auto">(static) transform(matrix, geometry) → {geom3}<br />
Source:<br />
modeling/src/geometries/geom3/transform.js, line 15<br />
Transform the given geometry using the given matrix. This is a lazy transform of the polygons, as this function only adjusts the transforms. See <strong>applyTransforms()</strong> for the actual application of the transforms to the polygons.</p>
<p dir="auto">I can't find any other reference to applyTransforms. When I add objects to a union, it doesn't seem to apply the transforms. If I return an array of objects from my main function, it transforms them before rendering. That's fine I guess, but shouldn't union() work? And is there an applyTransforms() somewhere?</p>
]]></description><link>https://openjscad.nodebb.com/topic/445/applytransforms</link><generator>RSS for Node</generator><lastBuildDate>Fri, 06 Mar 2026 11:36:55 GMT</lastBuildDate><atom:link href="https://openjscad.nodebb.com/topic/445.rss" rel="self" type="application/rss+xml"/><pubDate>Sat, 06 Sep 2025 22:09:05 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to applyTransforms on Fri, 17 Oct 2025 19:26:16 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="https://openjscad.nodebb.com/uid/1">@z3dev</a> if there is anything I can do to help, I'm willing.</p>
<p dir="auto">I'm happy to share my code as well.</p>
<p dir="auto">Here is PosableGeom3.js:</p>
<pre><code>"use strict";
const jscad = require('@jscad/modeling');
const { Pose } = require('./Pose');
const { mat4, vec3, vec4 } = jscad.maths;
const { geom3 } = jscad.geometries;
const { PI } = Math;
// Base class for geom3 objects with named Poses
class PosableGeom3 {
  constructor(geometry, poses) {
    this._geometry = geometry || geom3.create();
    this._poses = {};
    if (poses) {
      Object.keys(poses).forEach(key =&gt; {
        this._poses[key] = poses[key].clone();
      });
    }
  }
  get polygons() { return this._geometry.polygons; }
  set polygons() { this._geometry.polygons = value; }
  get transforms() { return this._geometry.transforms; }
  set transforms(value) { this._geometry.transforms = value; }
  clone() {
    // Create a new geom3 with deep-copied polygons and transforms
    const clonedGeom = geom3.clone(this._geometry);
    // Clone the poses
    const clonedPoses = {};
    Object.keys(this._poses).forEach(key =&gt; {
      clonedPoses[key] = this._poses[key].clone();
    });
    // Create a new PosableGeom3 with the cloned geometry and poses
    return new PosableGeom3(clonedGeom, clonedPoses);
  }
  transform(matrix) {
    this._geometry = geom3.transform(matrix, this._geometry);
    Object.keys(this._poses).forEach(key =&gt; {
      this._poses[key].transform(matrix);
    });
    return this;
  }
  getPose(name) {
    return this._poses[name] || new Pose();
  }
	applyTransforms(){
		this._geometry = geom3.create(geom3.toPolygons(this._geometry));
		return this;
	}
  alignTo(port, targetPose) {
    const sourcePose = this.getPose(port);
    if (!sourcePose) {
      throw new Error(`Invalid port ${port}`);
    }
    if (!targetPose) {
      throw new Error(`Invalid targetPose`);
    }
    return this.transform(
      sourcePose.getMatrix(targetPose)
    );
  }
}
module.exports = { PosableGeom3 };
</code></pre>
<p dir="auto">And here is Pose.js:</p>
<pre><code>"use strict";
const jscad = require('@jscad/modeling');
const { vec3, vec4, mat4 } = jscad.maths;
const { geom3 } = jscad.geometries;
const { abs } = Math;
const { translate } = jscad.transforms;
const { union } = jscad.booleans;
const { cylinder, sphere, cylinderElliptic } = jscad.primitives;

const x = 0; const y = 1; const z = 2; const w = 3;

class Pose {
  constructor(point, heading, up) {
    // Default position to origin if not provided
    this._point = point ? vec3.clone(point) : vec3.create();
    // Default heading to Y+ (0, 1, 0) if not provided
    this._heading = vec3.normalize(
      vec3.create(), 
      heading ? vec3.clone(heading) : [0, 1, 0]
    );
    // Default up to Z+ (0, 0, 1) if not provided
    this._up = up ? vec3.clone(up) : [0, 0, 1];

    // Project up onto the plane perpendicular to heading
    const dot = vec3.dot(this._up, this._heading);
    const projectedUp = vec3.subtract(
      vec3.create(),
      this._up,
      vec3.scale(vec3.create(), this._heading, dot)
    );
    // Check if up is parallel to heading 
    // (length of projectedUp near zero)
    if (vec3.length(projectedUp) &lt; 0.00001) {
      // Choose a perpendicular vector based on heading
      if (abs(this._heading[z]) &lt; 0.99999) {
        this._up = vec3.cross(
          vec3.create(), this._heading, [0, 0, 1]
        );
      } else {
        this._up = vec3.cross(
          vec3.create(), this._heading, [1, 0, 0]
        );
      }
    } else {
      this._up = vec3.normalize(vec3.create(), projectedUp);
    }
    // Ensure up is normalized
    vec3.normalize(this._up, this._up);
  }
  get point() {
    return vec3.clone(this._point);
  }
  get heading() {
    return vec3.clone(this._heading);
  }
  get up() {
    return vec3.clone(this._up);
  }
  clone() {
    return new Pose(this._point, this._heading, this._up);
  }
  transform(matrix) {
    // Transform point (w = 1)
    let v = this._point.concat(1); // Convert vec3 to vec4
    v = vec4.transform(vec4.create(), v, matrix);
    this._point = vec3.clone(v); // Convert back to vec3

    // Transform heading (w = 0)
    v = this._heading.concat(0); // Convert vec3 to vec4
    v = vec4.transform(vec4.create(), v, matrix);
    this._heading = vec3.clone(v); // Convert back to vec3

    // Transform up (w = 0)
    v = this._up.concat(0); // Convert vec3 to vec4
    v = vec4.transform(vec4.create(), v, matrix);
    this._up = vec3.clone(v); // Convert back to vec3

    return this;
  }
	getMatrix(targetPose) {
		let t = mat4.create();

		// Step 1: Translate to origin
		t = mat4.multiply(
			mat4.create(),
			mat4.fromTranslation(mat4.create(), vec3.scale(vec3.create(), this._point, -1)),
			t
		);

		// Step 2: Align heading with targetPose
		t = mat4.multiply(
			mat4.create(),
			mat4.fromVectorRotation(mat4.create(), this._heading, targetPose._heading),
			t
		);

		// Step 3: Roll around heading to align up vector
		const p = this.clone().transform(t);
		const dot = vec3.dot(
			vec3.cross(vec3.create(), p._up, targetPose._up),
			p._heading
		);
		let angle = vec3.angle(p._up, targetPose._up);
		if (dot &lt; 0) angle = -angle;

		if (Math.abs(angle) &gt; 1e-6) {
			t = mat4.multiply(
				mat4.create(),
				mat4.fromRotation(mat4.create(), angle, targetPose._heading),
				t
			);
		}

		// Step 4: Translate to targetPose.point
		t = mat4.multiply(
			mat4.create(),
			mat4.fromTranslation(mat4.create(), targetPose._point),
			t
		);

		return t;
	}
  render() {
    const vecGeom = (vector) =&gt; {
      const vectorLength = vec3.length(vector) || 1;;
      const vectorRadius = vectorLength / 10;
      const arrowLength = vectorLength / 5;
      const arrowRadius = vectorLength / 5;
      let out = union(
        cylinder({ // arrow body
          center: [0, 0, vectorLength / 2],
          height: vectorLength,
          radius: vectorRadius
        }),
        cylinderElliptic({ // arrow head
          center: [0, 0, vectorLength],
          startRadius: [arrowRadius, arrowRadius],
          endRadius: [0, 0],
          height: arrowLength
        })
      );
      out = geom3.transform(
        mat4.fromVectorRotation(
          mat4.create(),
          [0, 0, vectorLength],
          vector
        ),
        out
      );
      return out;
    };

    let g = translate(
      this._point,
      union(
        sphere({ radius: 0.2 }),
        vecGeom(this._heading),
        vecGeom(this._up)
      )
    );
    return g;
  }
  roll(angle) {
    if (angle === 0) return this;
    // Create a rotation matrix around the heading vector
    const rotationMatrix = mat4.create();
    mat4.rotate(
      rotationMatrix, rotationMatrix, angle, this._heading
    );
    // Transform the up vector using the rotation matrix (w = 0)
    const transformedUp = vec4.transform(
      vec4.create(), this._up.concat(0), rotationMatrix
    );
    this._up = vec3.clone(transformedUp);

    return this;
  }
  translate(vector) {
    this._point = vec3.add(
      vec3.create(),
      vector,
      this._point
    );
  }
}

module.exports = { Pose }; // end of file
</code></pre>
]]></description><link>https://openjscad.nodebb.com/post/1483</link><guid isPermaLink="true">https://openjscad.nodebb.com/post/1483</guid><dc:creator><![CDATA[sopatt]]></dc:creator><pubDate>Fri, 17 Oct 2025 19:26:16 GMT</pubDate></item><item><title><![CDATA[Reply to applyTransforms on Thu, 16 Oct 2025 11:32:46 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="https://openjscad.nodebb.com/uid/859"><a class="plugin-mentions-user plugin-mentions-a" href="https://openjscad.nodebb.com/uid/859">@sopatt</a></a> <a class="plugin-mentions-user plugin-mentions-a" href="https://openjscad.nodebb.com/uid/859"><a class="plugin-mentions-user plugin-mentions-a" href="https://openjscad.nodebb.com/uid/859">@sopatt</a></a> That's cool stuff. In the early versions there was a 'connector' concept but that was left behind. We really want to bring that back in V3, so I'm wondering if your efforts could contribute to a better version.</p>
]]></description><link>https://openjscad.nodebb.com/post/1482</link><guid isPermaLink="true">https://openjscad.nodebb.com/post/1482</guid><dc:creator><![CDATA[z3dev]]></dc:creator><pubDate>Thu, 16 Oct 2025 11:32:46 GMT</pubDate></item><item><title><![CDATA[Reply to applyTransforms on Thu, 16 Oct 2025 07:06:06 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="https://openjscad.nodebb.com/uid/1">@z3dev</a> hello and thank you for the reply.</p>
<p dir="auto">I am extending jscad with some custom classes Pose, PosableGeom3 which wraps geom3, and derivative what-not, that allows me to snap different pieces of a composition to each other based on Poses, which is an object consisting of two vectors and a point which represent a position and orientation. So for example I create tubes with different types of bends (curve, straight, U, dogleg) each having a start pose and an end pose. I do something like myGeom1. alignTo('start', myGeom2.getPose('end')); to snap one's start to another's end using a transform. I can then snap the next piece to the end of that one, etc. It's a heck of a lot easier to do this than calculate every object's individual 3D position, orientation, and rotation separately. It's very powerful and solves problems I was struggling with in OpenSCAD.</p>
<p dir="auto">Anyway alignTo calls this.<em>geometry = geom3.transform(matrix, this</em>. geometry) but I was finding my geometry was rendering as if the transforms were never applied. Incidentally PosableGeom3 exposes the underlying geom3's polygons and transforms properties through getters as well as transforms through a getter and a setter so I think that implies a PosableGeom3 "Is_a" geom3. Certainly when I pass it to library functions expecting a geom3 it "just works". I'm not sure why that's not enough but I guess this is an unconventional usage of the library hence why I was needing to apply transforms at times other than what the library expects. Again, that's a guess. In any case I was able to work around it by adding an applyTransforms method to my class which contains this one line: this._geometry = geom3.create(geom3.toPolygons(this._geometry));</p>
<p dir="auto">Thanks again for all your effort on this. It's been such a fun, useful, and powerful tool.</p>
]]></description><link>https://openjscad.nodebb.com/post/1481</link><guid isPermaLink="true">https://openjscad.nodebb.com/post/1481</guid><dc:creator><![CDATA[sopatt]]></dc:creator><pubDate>Thu, 16 Oct 2025 07:06:06 GMT</pubDate></item><item><title><![CDATA[Reply to applyTransforms on Sat, 20 Sep 2025 10:49:27 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="https://openjscad.nodebb.com/uid/859">@sopatt</a> Welcome</p>
<p dir="auto">You don't have to worry about applying the transforms. That's done automatically before other operations like booleans, etc</p>
<p dir="auto">You just have to position the shapes properly.</p>
<p dir="auto">If you provide an example then we can help out. Also see some of the more complex examples.</p>
]]></description><link>https://openjscad.nodebb.com/post/1478</link><guid isPermaLink="true">https://openjscad.nodebb.com/post/1478</guid><dc:creator><![CDATA[z3dev]]></dc:creator><pubDate>Sat, 20 Sep 2025 10:49:27 GMT</pubDate></item></channel></rss>