Skip to content
Kestrel280 edited this page Jun 17, 2025 · 19 revisions

Overview

WebVis is an interactive graphics tool for viewing reconfiguration sequences of modular robots entirely within the browser. The public version of the tool can be accessed here.

Usage and Controls

The 3D environment is navigable using "Orbit" controls:

  • Rotate the scene by left-clicking and dragging.
  • Move around the scene by right-clicking and dragging.
  • Zoom in and out of the scene by scrolling up or down.

Additional controls are provided in the GUI: WEBVIS_CONTROLS

  1. Scenario Loader - Opens a file browser for the user to upload their own Scenario file.
  2. Move Counter - Displays the current move of the active Scenario, out of the total number of moves. (Parallel moves and multi-part moves are treated as a single move.)
  3. Animation Settings - Settings for the speed of animation and whether or not animation should automatically continue after a move is completed.
  4. Graphics Controls - Toggles for camera style (perspective vs. orthographic), background color, and lighting ("Fullbright").
  5. Animation Controls - Buttons to move forward or backwards through the move sequence defined in the Scenario file.
  6. Example Scenario Loaders - Buttons to load example Scenarios to demonstrate the tool's functionality.

Scenarios

"Scenarios" are pre-specified configurations of modular robots and their moves. They are ordinary text files which follow a strict specification.

Scenario File Specification

Scenario files are separated into 4 "blocks". Blocks are separated by a single blank line. Each block has one "entry" per line.

  1. Block 1 contains metadata about the Scenario. It is exactly 3 entries (lines) long. Line 1 and 2 contain, respectively, a name and description for the Scenario; these can be any non-empty string. Line 3 must be either the text CUBE or RHOMBIC_DODECAHEDRON: this declares what type of module will be used for the Scenario.
  2. Block 2 contains "Group" definitions. Each module must be a part of exactly one Group, which defines the module's color and size. A Group definition consists of 5 comma-separated integer values in the following format:

{Group ID}, {Red value 0-255}, {Green value 0-255}, {Blue value 0-255}, {Size 0-100}

  1. Block 3 defines the starting location of each module, as well as the group to which each module belongs. A module entry consists of 5 comma-separated integer values in the following format:

{Module ID}, {Group ID}, {X location}, {Y location}, {Z location}

  1. Block 4 defines the sequence of moves the modules will follow when animating the Scenario. A move entry consists of 5 comma-separated integer values in the following format:

[Checkpoint Flag]{Mover ID}, {Anchor Direction Code}, {Delta X}, {Delta X}, {Delta Z}

'Mover ID' refers to which module should move. 'Anchor Direction Code' is an important specifier which determines how the module will move.

The 'Checkpoint Flag' refers to a single '' character. If absent, this indicates that the move is part of a larger sequence of sub-moves which will collectively be treated as one move. The presence of a '' character indicates the first move in one of these sequences. During animation, all moves within one of the "checkpoint" sequences will be played sequentially. When a new "checkpoint" is reached, animation will automatically pause (unless the "Auto-Animate" setting is enabled).

Block 4 is the final block; empty lines will not begin a new block. Instead, sequential moves should be separated by new lines. Move definitions which are not separated by empty lines will be treated as parallel moves. During animation, parallel moves will animate at the same time.

Anchor Direction Codes

At any point, every module in a valid configuration must be physically connected to at least one other module (via a shared face). Moving a module requires pivoting it about an edge of one of these "anchor" modules. When specifying a move definition in Block 4, the direction from the moving module to an anchor module must be given, in the form of an Anchor Direction Code. (This is necessary because delta-position alone is not enough to disambiguate how a module should move to its target position.)

Anchor Direction Codes are integers which map to direction vectors. Codes '1', '2', and '3' represent the positive x, y, and z directions, respectively; while codes '4', '5', and '6' represent the negative x, y, and z direction.

For example: consider a cube module 'C' at <0, 0, 1>, anchored to a cube module 'C' at <0, 0, 0>. 'M' wishes to pivot about 'A' to reach a final position of <0, 1, 0>. The delta-position is <0, 1, -1>; and the direction from 'C' to 'A' is <0, 0, -1>, which has an anchor direction code of 6 (negative-z). Thus, the move entry in the Scenario file would be:

{C's id}, 6, 0, 1, -1

Rhombic dodecahedrons follow the same pattern, but use 2-digit Anchor Direction Codes, as the direction from any Rhombic Dodecahedron module to an anchor must be have components in exactly 2 axes.

For example: a rhombic dodecahedron module 'R' at <0, -1, 1> is anchored to another rhombic dodecahedron module 'A' at <0, 0, 0>. 'R' wishes to pivot about 'A' to reach some target location. The direction from 'R' to 'A' is <0, 1, -1>, which is in the positive-y, negative-z direction; so, its anchor direction code is 26 ('2' for positive-y, '6' for negative-z).

Anchor Direction Code Table

| Anchor Direction Code |  Anchor Direction  |    Comments   |
|-----------------------|--------------------|---------------|
|          -3           |         N/A        | z-first Slide |
|          -2           |         N/A        | y-first Slide |
|          -1           |         N/A        | x-first Slide |
|          0            |         N/A        |  Direct Slide |
|          1            |    < 1,  0,  0>    |   +x (Cube)   |
|          2            |    < 0,  1,  0>    |   +y (Cube)   |
|          3            |    < 0,  0,  1>    |   +z (Cube)   |
|          4            |    <-1,  0,  0>    |   -x (Cube)   |
|          5            |    < 0, -1,  0>    |   -y (Cube)   |
|          6            |    < 0,  0, -1>    |   -z (Cube)   |
|          12           |    < 1,  1,  0>    |  +x, +y (RD)  |
|          15           |    < 1, -1,  0>    |  +x, -y (RD)  |
|          42           |    <-1,  1,  0>    |  -x, +y (RD)  |
|          45           |    <-1, -1,  0>    |  -x, -y (RD)  |
|          13           |    < 1,  0,  1>    |  +x, +z (RD)  |
|          16           |    < 1,  0, -1>    |  +x, -z (RD)  |
|          43           |    <-1,  0,  1>    |  +x, -z (RD)  |
|          46           |    <-1,  0, -1>    |  -x, -z (RD)  |
|          23           |    < 0,  1,  1>    |  +y, +z (RD)  |
|          26           |    < 0,  1, -1>    |  +y, -z (RD)  |
|          53           |    < 0, -1,  1>    |  -y, +z (RD)  |
|          56           |    < 0, -1, -1>    |  -y, -z (RD)  |

Example Scenario File

This is a simple Rhombic Dodecahedron scenario file with only 2 modules and 2 style-groups. Note that in-line comments, beginning with '//', are acceptable in blocks 2-4. This scenario does not contain any parallel moves.

Scenario Name
Scenario Description
RHOMBIC_DODECAHEDRON

40, 255,255,255, 100    // Block 2: Group Definitions
50, 255,  0,  0,  95    // Group 50 is red, with size 95%

1, 40,  0,  0,   0   	// Block 3: Module Definitions
2, 50,  0,  1,   1	// Module ID #2 starts at <0, 1, 1> and belongs to group 50

*2, 56,   1,  0, -1 	// Block 4: Move definitions

 2, 45,  -1,  0, -1	// Module ID #2 moves by <-1, 0, -1> using anchor code 45 (-y, -z)

 2, 53,  -1,  0,  1

 2, 15,   1,  0,  1

*2, 56,   1, -1,  0	// New "checkpoint"

 2, 46,   0,  1, -1

 2, 45,   0, -1, -1

 2, 43,  -1,  1,  0

 2, 53,  -1, -1,  0

 2, 13,   0,  1,  1

 2, 15,   0, -1,  1

 2, 16,   1,  1,  0

*2, 56,  -1, -1,  0	// New "checkpoint"

 2, 16,   1, -1,  0

 2, 26,   1,  1,  0

 2, 46,   0, -1, -1

 2, 42,   0,  1, -1

 2, 43,  -1, -1,  0

 2, 23,  -1,  1,  0

 2, 13,   0, -1,  1

 2, 12,   0,  1,  1

 2, 16,   1, -1,  0

 2, 26,   1,  0, -1	// New "checkpoint"

 2, 42,  -1,  0, -1

 2, 23,  -1,  0,  1

 2, 12,   1,  0,  1

Exporting SVGs

The program supports rendering to SVG. Pressing Shift+M will toggle between WebGL and SVG rendering modes. Pressing Shift+P will log the object to the console (a canvas object if in WebGL mode, or an SVG object if in SVG mode).

The Three.js SVGRenderer used internally is a proof-of-concept renderer with a number of quirks and bugs. Manual post-processing may be required to clean up the SVG.

Recommended Workflow

  1. Open a scenario file and navigate to the desired camera position and angle.
  2. Press Shift+M to enter SVG rendering mode. (In some cases, this may cause the colors to render incorrectly. Toggling the 'Fullbright' setting may help alleviate this.)
  3. Press Shift+P to log the SVG object to the console window.
  4. Open the console window (the shortcut for this is typically F12, but it may depend on your browser).
  5. Right click the SVG output and click "Copy Object" (exact verbiage may depend on browser).
  6. Open a text editor and paste the SVG output into the editor. Save the file as a .SVG file.
  7. Load the SVG file into an editing program such as Inkscape (free and open-source).
  8. Perform any desired edits.

Useful tools and tips for cleanup in Inkscape:

  1. Edit->'Resize Page to Selection' -- The SVG canvas, by default, will typically be much larger than required. Select all elements and use this option to resize the canvas.
  2. 'Layers and Objects' window. This window allows you to group different paths, or temporarily hide paths so they don't get in the way of the paths you're trying to work on.
  3. Path->'Union'. for combining multiple paths into one. The SVG renderer cannot render quads, so quads are rendered as triangles; selecting the two triangles and using this tool will combine them into a quad.
  4. 'Shape Builder Tool'. Selecting a number of paths and then clicking the 'Shape Builder Tool' will allow you to selectively combine the paths into the desired configuration; so you can trim undesired edges, combine triangles, and fix occlusion issues.

Click here for a reference video on how to export and cleanup an SVG.

Technical Details

WebVis uses Three.js, a 3D graphics library for Javascript built on top of WebGL.

main.js is the script file which is actually included on the webpage. It sets up the Three.js scene, initializes several global variables, and starts the main animation loop animate().

Modules

Module.js contains a class definition for Modules. The Module constructor loads the module geometry and texture into a Three.js mesh (using the helper function _createModuleMesh), stores the module in a global list of Modules (gModules), and adds the Module to the Three.js scene (gScene). Note that the Module mesh is installed as the child of an invisible, non-rotating parent mesh. The parent mesh stores the actual location of the Module in world space, so that the child mesh remains in a convenient local space for performing rotations and pivots.

The geometry data (vertices and texture uv mappings) for the different module types is stored in ModuleGeometries.js; this script uses this data to construct Three.js BufferGeometry objects for the different module types, which are used when constructing the Module meshes. Also contained in this file is a helper struct, ModuleData, which just contains quick-reference geometric properties of each module type, used for performing animations.

Lastly, ModuleMaterials.js contains Three.js Material constructors for use with the Module meshes.

Moves & Animations

Move.js contains a class definition for Moves. "Moves" are defined in block 4 of Scenario files. When a Scenario file is loaded, all of the move definitions from block 4 are used to construct a single global MoveSetSequence, which contains the ordered list of Moves that the Scenario encodes. (Actually, it contains MoveSet's -- a MoveSet nominally contains just a single move, but MAY contain several moves which execute in parallel).

The full hierarchy looks like so:

  1. A single MoveSetSequence, which contains an ordered list of...
  2. MoveSet objects, which contain 1 or more...
  3. Moves, which specify how the object should animate by giving it several "keyframes" in the form of...
  4. "Steps", which give low-level animation details (rotation axis and pre-/post-translation vectors) for the motion.

The steps are constructed automatically when Moves are loaded from a Scenario file, using Move.constructStandardStep() for cube and RD moves; Move.constructCornerSlideSteps() for special-case cube sliding movements; and Move.constructCatomSteps() for catom moves. The code contains detailed comments on how these constructions are performed.

Animating a Module involves 3 steps: a pre-translation, rotation, and post-translation. In modular robotics, our animations typically involve pivoting the module about one of its edges. In the pre-translation step, the module is translated (in local space) so that the pivoting edge intersects the local origin. In the rotation step, the module is rotated about the axis specified by the edge. Finally, the post-translation step simply "un-does" the pre-translation step. All of these are matrix operations on the Module mesh: Move.animateMove() -> Move._pivotAnimate() contains the core logic for performing these operations and calculating a final transformation matrix for the mesh on each animation frame.

Putting it all together:

  1. main.js -> animate() is called on each frame.
  2. A variable, currentAnimProgress, stores the progress through the CURRENT MoveSet being executed (from 0.0 to 1.0).*
  3. For each Move in the current MoveSet, Module.animateMove() is called to update the respective module's mesh.
  4. The scene is rendered.

*Note: when currentAnimProgress exceeds 1.0, Module.finishMove() is called, which updates the position of the Module's parent mesh and updates the Module's cumulative rotation matrix (a pre-transform for the mesh which helps keep the module's textures properly aligned). In addition, the variable is reset to 0.0 and the next MoveSet is loaded from the global MoveSetSequence.

User & GUI

User.js, GUI.js

Clone this wiki locally