Skip to content

Position, rotation, and scale

Move, rotate, and scale created, found, or selected BDEngine objects with setPosition, setRotation, and setScale: units, coordinates, and editor updates.

To move a block display you created or an object from the selection, call the methods on that object. They work with built-in editor objects; you do not need to create a custom class.

Which objects can you transform?

BlockDisplay, ItemDisplay, TextDisplay, and regular Collection groups inherit transform methods from Selectable. Get a live object through editorAPI.add, editorAPI.find, or editorAPI.getSelectedObjects and call the method on it. The operation is the same regardless of how you obtained the object.

For example, create a regular block display and set its position, rotation, and scale:

const block = await editorAPI.add(
'stone', 'BlockDisplay', null, true
);
if (block) {
block.setPosition({ x: 2, y: 1, z: 0 });
block.setRotation({ y: 90 });
block.setScale({ x: 0.5, y: 0.5, z: 0.5 });
editorAPI.update(true);
}

Or move one selected block display by one unit along its local Y axis:

const block = editorAPI.getSelectedObjects()
.find(object => object.isBlockDisplay && !object.isStructureBlock);
if (block) {
const position = block.getPosition();
block.setPosition({ y: position.y + 1 });
editorAPI.update(true);
}

These examples assume the editor is ready and in normal editing mode. Real StructureBlock objects have different rules: integer positions and disabled rotation and scaling. Rotation and scale of structure groups depend on the structure preview mode. Having a method does not mean that every utility object supports the same operation.

editorAPI remains the stable public entry point. Methods on a returned object belong to editor classes; obtaining the object through editorAPI does not make all of its properties part of the stable API. See editorAPI and the internal editor for this distinction. Custom classes and geometry are covered separately in Custom objects and dependencies.

Coordinates and general rules

The setters and getters below are synchronous. Position, rotation, and scale are local to the parent. By default, setters mark changed geometry, selection frames, and raycast data for an update, but do not create an Undo command, broadcast the transform to Share Party, or update every panel.

The setters have a skipDirty = false parameter. Keep the default in a typical plugin. Setting it to true skips internal change tracking and requires you to manage updates yourself.

setPosition, setRotation, and setScale accept a partial set of axes: an omitted axis or one set to undefined remains unchanged. Supplied values must be finite numbers. Strings, NaN, and infinities throw a ProjectValidationError; invalid input does not apply a partial set of changes.

With a rotated or scaled parent, local Y may not match world up, and moving by 1 does not necessarily move the object by one block in the world. A setter sets the final local value; to offset the current position, read getPosition() first, as in the example above.

The objPos parameter of editorAPI.add has a different contract: it is an initial offset in world coordinates. Do not treat it as equivalent to setPosition.

Position

setPosition

object.setPosition({ x, y, z }, skipDirty = false) changes the local position. Values use BDEngine scene units; for regular displays, one unit corresponds to one Minecraft block. Values are rounded to object.stepPosition (default 0.000625). Returns undefined.

object.setPosition({ x: 2, z: -1 }); // Y stays unchanged.

getPosition

object.getPosition() returns a new plain { x, y, z } object containing rounded local coordinates. Changing this result does not change the original position. The getter itself does not overwrite the position.

const position = object.getPosition();
object.setPosition({ y: position.y + 1 });

Rotation

setRotation

object.setRotation({ x, y, z }, skipDirty = false) takes degrees, rounds to object.stepRotation (default 0.01), and updates the corresponding Three.js angles. Returns undefined.

object.setRotation({ y: 90 });

By contrast, the standard Three.js object.rotation property contains radians. Do not assign degrees directly to object.rotation.y.

getRotation

object.getRotation() returns { x, y, z } in degrees. If the internal representation has not been created yet, it is calculated from the current rotation.

Unlike getPosition(), this returns a reference to the internal customRotation. Copy it when you need a snapshot. Manually changing this reference does not recalculate the Three.js rotation or notify the editor of a change:

const rotation = { ...object.getRotation() };
object.setRotation({ y: rotation.y + 15 });

Angles may retain accumulated turns. Do not assume that they are always normalized to the range from -180 to 180.

setQuaternionRotation

object.setQuaternionRotation(quaternion, skipDirty = false) copies the quaternion and updates the rotation representation in degrees using the change relative to the previous quaternion. Returns undefined; an empty argument does not change anything.

Pass a normalized THREE.Quaternion. Validation requires finite x/y/z/w components and a nonzero length, but the method does not automatically normalize the assigned quaternion. An invalid quaternion throws a ProjectValidationError.

const quaternion = new THREE.Quaternion().setFromAxisAngle(
new THREE.Vector3(0, 1, 0),
Math.PI / 2
);
object.setQuaternionRotation(quaternion);

The angle in setFromAxisAngle is in radians, while getRotation() still returns degrees. A quaternion describes orientation, but does not itself store the number of complete turns, so do not use it as a replacement for animation keys with multiple revolutions.

Scale

setScale

object.setScale({ x, y, z }, skipDirty = false) sets scale factors. Values are rounded to stepScale (default 0.0001) and clamped to a minimum of minScale (default 0.0011). Returns undefined. For a particle, it also updates the scale of its gizmo.

object.setScale({ x: 2, y: 2, z: 2 });

A zero or negative scale does not create a zero size or a reflection: it is raised to the minimum positive value. Supported objects use the separate setMirrorX method for reflection.

getScale

object.getScale() returns a new { x, y, z } object. The getter may change the original object: if the current scale does not meet the rounding or minimum constraints, it calls setScale and updates the local and world matrices. It is therefore not always a read without side effects.

Shear, reflection, and matrices

Affine transforms are enabled for objects whose isDisplay or isCollection is true. They are not enabled for a plain custom Selectable subclass. Do not assign these flags just to get the effect: other editor systems expect the corresponding built-in type.

The table describes the base Selectable methods. Specialized types may override them: for example, StructureBlock does not apply shear or reflection, and structure groups disable these operations as well.

Method Contract
supportsAffineTransform() Returns a boolean indicating whether shear and reflection are supported
getShear() Returns a copy of { xy, xz, yz }, with angles in radians
setShear({ xy, xz, yz }, skipDirty = false, updateMatrix = true) Updates the supplied components. Each must be a finite angle strictly between -Math.PI / 2 and Math.PI / 2; an invalid angle returns false without a change, success returns true
getMirrorX() Returns a boolean indicating reflection along local X
setMirrorX(value, skipDirty = false, updateMatrix = true) Converts the value to a boolean and updates the matrix and internal state according to the flags. Returns undefined
setFromAffineMatrix(matrix, options = {}) Decomposes a THREE.Matrix4 into components and returns the object itself
applyMatrix4(matrix) Premultiplies the local matrix by the supplied matrix and returns the object itself
updateMatrix() Rebuilds the local matrix, including supported shear and reflection; returns undefined

setShear accepts a partial set of axes. Values very close to the boundary are additionally clamped to a magnitude of Math.PI / 2 - 0.000001. setFromAffineMatrix accepts referenceRotation = null (a reference THREE.Euler), skipDirty = false, and updateWorld = true. For an object without affine support, the matrix is decomposed into standard position, quaternion, and scale.

if (object.supportsAffineTransform()) {
object.setShear({ xy: THREE.MathUtils.degToRad(10) });
object.setMirrorX(true);
editorAPI.update(true);
}

Updates, history, and collaboration

After direct changes, call editorAPI.update. Pass true if you changed a selected object or the selection itself, so that the editor also rebuilds the transform controller’s temporary selection. For a series of changes, one call after all the setters is enough:

const block = editorAPI.getSelectedObjects()
.find(object => object.isBlockDisplay && !object.isStructureBlock);
if (block) {
block.setPosition({ x: 2 });
block.setRotation({ y: 45 });
block.setScale({ x: 1.5, y: 1.5, z: 1.5 });
editorAPI.update(true);
}

Setters mark data for an update, but do not create a separate Undo/Redo command or broadcast arbitrary changes to Share Party participants. editorAPI.update does not do that either, and does not save a file. Even if you created an object through add with history enabled, subsequent direct transforms do not automatically become separate undoable actions.

If your plugin needs custom history commands or synchronization, implement them through internal editor mechanisms and check them when updating the editor. For programmatic selection, see the selected property.

Put it into practice