Skip to content

Transform Widget UI

Overview

The Transform Widget UI prefab allows you to quickly attach 3D UI widgets for manipulating position, rotation, and scale to elements in a selection. These prefabs use pointer down and out events and pointer behaviors to perform the interaction. The Mesh based UI and behavior can be customized through the method options, or after creation via selection. We use pointer based interactions as it better supports multiple platforms and one handed interaction for better accessability. These methods are members of the Selection class and will create a UI for each node in a selection. As such, it is recommended to call the prefab functions from root node selections.

Usage

js
let root = anu.bind("cot") //Returns a selection of the single root transform node

root.positionUI(options?: {})
    .rotateUI(options?: {})
    .scaleUI(options?: {})

Options

Position Widget UI

PropertyValueDefault
name(string) name prefix for UI and UI parentnode.name + "PositionUI"
width(number) the width of the capsule meshhalf the width of the parent node bounding box
radius(number) the radius of the capsule mesh5% the width
position(Vector3) the starting position of the capsule meshbottom front position of the parent node bounding box
offset(Vector3) the offset from the starting position2.5 times the radius -y axis
material(Material) the material of the capsule meshStandardMaterial
diffuseColor(Color3Color4) the diffuse color of the material if not defined
visibility(number) the visibility of the capsule 0-11
behavior(SixDofDragBehavior) the behavior attached to the parent nodeSixDofDragBehavior forceCameraOnDragStart=true rotateAroundYOnly=true
billboard(number) the billboard mode setting for the parent mesh0

Rotation Widget UI

PropertyValueDefault
name(string) name prefix for UI and UI parentnode.name + "RotationUI"
axis({xy
diameter(number) the diameter of the torus mesh5% the width of the parent node bounding box
thickness(number) the thickness of the torus meshhalf the diameter
position(Vector3) the starting position of the capsule meshbottom front position of the parent node bounding box
offset(Vector3) the offset from the starting position2.5 times the radius + 2.5 times the diameter -y axis
material(Material) the material of the torus meshStandardMaterial
diffuseColor(Color3Color4) the diffuse color of the material if not defined
visibility(number) the visibility of the torus 0-11
billboard(number) the billboard mode setting for the parent mesh0

Scale Widget UI

PropertyValueDefault
name(string) name prefix for UI and UI parentnode.name + "ScaleUI"
diameter(number) the radius of the sphere mesh5% the width of the parent bounding box
position(Vector3) the starting position of the sphere meshbottom front position of the parent node bounding box
offset(Vector3) the offset from the starting position2.5 times the radius -y axis and the width of the hypothetical positionUI z axis
material(Material) the material of the sphere meshStandardMaterial
diffuseColor(Color3Color4) the diffuse color of the material if not defined
visibility(number) the visibility of the sphere 0-11
behavior(PointerDragBehavior) the behavior attached to the parent nodePointerDragBehavior dragAxis (0,1,0) moveAttached=false
billboard(number) the billboard mode setting for the parent mesh0
minimum(number) minimum scale factorinfinity
maximum(number) maximum scale factorinfinity

Examples

js
// SPDX-License-Identifier: Apache-2.0
// Copyright : J.P. Morgan Chase & Co.

import * as anu from '@jpmorganchase/anu';
import * as BABYLON from '@babylonjs/core';
import * as d3 from 'd3';
import data from './data/iris.json' assert {type: 'json'};

export function transformWidget(engine) {

  //Create an empty Scene
  const scene = new BABYLON.Scene(engine);
  //Add some lighting
  new BABYLON.HemisphericLight('light1', new BABYLON.Vector3(0, 10, 0), scene);
  //Add a camera that rotates around the origin and adjust its properties
  const camera = new BABYLON.ArcRotateCamera('Camera', 0, 0, 0, new BABYLON.Vector3(0, -0.25, 0), scene);
  camera.position = new BABYLON.Vector3(2, 1.5, -4);
  camera.wheelPrecision = 20;
  camera.minZ = 0;
  camera.attachControl(true);

  //Create the D3 functions that we will use to scale our data dimensions to desired output ranges for our visualization
  let scaleX = d3.scaleLinear().domain(d3.extent(d3.map(data, (d) => d.sepalLength))).range([-1,1]).nice();
  let scaleY = d3.scaleLinear().domain(d3.extent(d3.map(data, (d) => d.petalLength))).range([-1,1]).nice();
  let scaleZ = d3.scaleLinear().domain(d3.extent(d3.map(data, (d) => d.sepalWidth))).range([-1,1]).nice();
  //Do the same for color, using Anu helper functions to map values to StandardMaterial objects with colors based on the 'schemecategory10' palette from D3
  let scaleC = d3.scaleOrdinal(anu.ordinalChromatic('d310').toStandardMaterial());

  //Create a Center of Transform TransformNode that serves the parent node for all our meshes that make up our chart
  let CoT = anu.create('cot', 'cot');
  //Select our CoT so that we have it as a Selection object
  let chart = anu.selectName('cot', scene);

  //Create sphere meshes as children of our CoT for each row of our data and set their visual encodings using method chaining
  let spheres = chart.bind('sphere', { diameter: 0.05 }, data)
                     .position((d) => new BABYLON.Vector3(scaleX(d.sepalLength), scaleY(d.petalLength), scaleZ(d.sepalWidth)))
                     .material((d) => scaleC(d.species));

  //Use the Axes prefab with our three D3 scales
  anu.createAxes('myAxes', { scale: { x: scaleX, y: scaleY, z: scaleZ }, parent: chart });

  //Use the Transform Widget prefab to add 3D UI handles to position, rotate, and scale our chart
  chart.positionUI()
       .rotateUI()
       .scaleUI({ minimum: 0.5, maximum: 2 });

  return scene;
};