Skip to content

TransformNodeWrapper

public class TransformNodeWrapper : RenderingNodeWrapper, IRetiringWrapper

Handle on a node in the part’s Rendering tree, exposed as Rendering.<Name>. Move it (in the part’s units, millimetres), turn it and scale it relative to its parent, hide or show it with Visible, cast rays from it with Cast and CastTo, and sample environment fields at it with SampleField. Writing an unchanged value costs nothing, so assigning every frame is fine. In PhysicsUpdate, off the main thread, a transform or visibility read returns the last value seen or written and a write is applied on the main thread at its next frame.

public Vector3 Position { get; set; }

The node’s position relative to its parent, in the part’s own units (millimetres on a component).

public Vector3 Rotation { get; set; }

The node’s rotation relative to its parent as Euler angles in RADIANS (X, Y, Z). A half turn is Math.PI (about 3.1416), not 180. For degrees use RotationDegrees.

public Vector3 RotationDegrees { get; set; }

The node’s rotation relative to its parent as Euler angles in DEGREES (X, Y, Z): the same rotation as Rotation, which is in radians. Setting either one changes both.

public Vector3 Scale { get; set; }

The node’s scale relative to its parent: (1, 1, 1) means no scaling, the node at its own size.

public bool Visible { get; set; }

Whether this node is drawn. False hides it and everything under it: it is not drawn, it is not outlined when the part is hovered or selected, and a click where it was no longer picks the part. True shows it again, outlined and clickable as the part is. It hides the drawing only: a collider still includes it, a ray cast from it still casts, and a light or emitter under it still adds to the field a sensor reads (switch those with their own Enabled). Assigning the value it already has costs nothing, so setting it every frame is fine. Set it from Initialize, Update or an interactivity handler; from PhysicsUpdate it is applied at the next frame; drive one node’s visibility from one of the two, since a write from each in the same frame can land in either order. It is never saved and Reset State does not put it back, so set it in Initialize from the part’s own state, a saved property. A node saved hidden in the part is not part of its body: logic can show it, but it is then drawn without an outline or a pick, so save a node visible and hide it from Initialize if it only shows sometimes. The view redraws by itself; no RequireRender is needed.

public SpatialHit LastHit { get; }

The result of the most recent Cast or CastTo - what Update reads when the sensing itself runs in PhysicsUpdate.

public SpatialHit Cast(Vector3 localDirection, double maxMetres);

Cast a ray from this node’s origin along a LOCAL direction (need not be normalised: -Z is “forward” for a camera-like sensor, +Y “up”) out to maxMetres, TRUE SI. Excludes the component’s own bodies. Returns the strike in this node’s frame, or Hit == false for a clean miss. Legal from PhysicsUpdate; anywhere else it returns the last result unchanged. Never throws.

public SpatialHit CastTo(TransformNodeWrapper other);

Is the straight line from this node to another authored node clear, and if not, what blocks it? Both nodes’ own bodies are excluded, so an emitter and its detector on one part read a clear line as Hit == false and an object between them as the hit. Serves any emitter/detector pair, any line-of-sight test, any beam interruption. Same call-site rule as Cast.

public double SampleField(string name, double fallback);

Sample a named environment field AT THIS NODE, omnidirectionally: the enclosing zone’s value plus every FieldEmitter of that name within reach, or fallback when nothing answers. The component-logic sibling of circuit logic’s SampleEnvironment (which samples at the component’s origin): use this when WHERE on the part matters - a photoresistor’s window, a solar cell’s face. Main thread (Update); from the physics thread it returns the last sample.

public double SampleField(string name, double fallback, Vector3 localFacing);

As SampleField, weighting each emitter by this node’s FACING: localFacing is the receiver’s normal in this node’s frame (need not be normalised; zero = omnidirectional). A source square-on contributes fully, edge-on nothing, behind nothing - Lambert’s rule, which is what every photodiode, solar cell and directional microphone obeys. Zone values are direction-free and unaffected.

Generated from the CRUMB source, commit fcec660d, 2026-10-29.