blob: 5caba75393ba0a4792ce1892404161aaf9912df2 [file] [log] [blame]
<meta charset="utf-8" lang="en"><style class="fallback">
body{visibility:hidden;}
table {
border-collapse: collapse;
}
.interpolation td {
border: 0px solid #888;
margin: 0;
padding: 0.5em;
}
td {
border: 1px solid #888;
margin: 0;
padding: 0.5em;
}
th {
background-color: #AAA;
color: #FFF;
border: 1px solid #888;
margin: 0;
padding: 0.5em;
text-align: left;
}
tr:nth-child(even) {
background-color: #EEE;
}
.md h1, .md .nonumberh1 {page-break-before:always}
@media screen and (min-width: 110em) {
.md .longTOC, .md .mediumTOC, .md .shortTOC {
max-width: 50%;
width: 25%;
max-height: 100%;
float: left;
position: fixed;
left: 20px;
top: 20px;
overflow-y: auto;
}
.md table {
margin-left: 0 !important;
margin-right: auto !important;
}
.md canvas {
display: block !important;
margin-left: auto !important;
margin-right: auto !important;
padding-bottom: 20px; /* Optional: adds some breathing room */
}
}
</style>
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true });
</script>
**RemoteCompose Wire Format v1.1.0**
February 09, 2026
*Current number of operations: 141 (0 not fully documented)*
!!! ERROR
This is work in progress and subject to modifications.
RemoteCompose is a standalone content format for Android. It consists
of a list of operations providing rendering, layout, animation, expression evaluation and bindings.
Wire Format overview
======
The RemoteCompose wire format is a simple flat list of Operations,
serialized in a binary format (see next sections for a detailed description of each operation).
!!! WARNING
This wire format is intended to be used for fast generation and transport. For display purposes,
a more runtime-appropriate representation (i.e. a tree of operations and containers) should be created and used instead.
Each list of operations contains as first element a Header (see section [Header]).
|Operations|
|:----:|
| **Header** |
| *Operation 1* |
| *Operation 2* |
| *Operation 3* |
| ... |
| *Operation n* |
Encoding
--------
Operations are encoded in binary, using the following types:
| Type | Size (in bytes) |
|:----:|:----:|
| BYTE | 1 |
| BOOLEAN | 1 |
| SHORT | 2 |
| INT | 4 |
| FLOAT | 4 |
| LONG | 8 |
| DOUBLE | 8 |
| BUFFER | 4 + Size |
| UTF8 | BUFFER |
A Buffer is simply encoded as the size of the buffer in bytes (encoded as an INT, so 4 bytes)
followed by the byte buffer itself. UTF8 payload is simply encoded as a byte Buffer using encodeToByteArray().
Components & Layout
-------------------
Operations can further be grouped in components, surrounded by ComponentStart (section [ComponentStart])
and ComponentEnd (section [ComponentEnd]).
|Operations|
|:----:|
| ... |
| **ComponentStart** |
| *Operation 1* |
| *Operation 2* |
| *Operation 3* |
| ... |
| *Operation n* |
| **ComponentEnd** |
| ... |
More specialized components can be created by using alternative layout operations instead of ComponentStart:
- RootLayoutComponent
- BoxLayout
- RowLayout
- ColumnLayout
Layout managers such as BoxLayout, RowLayout, ColumnLayout provide a way to layout Components.
Such layoutable/resizable components are structured slightly differently:
|Operations| | |
|:----: |:----:| :----: |
| **Header** | ||
| ... | ... | ... |
| **RootLayoutComponent** | | |
| | **RowLayout** | |
| ... | ... | ... |
| | **RowLayout** | |
| | *Modifier 1* | |
| | *Modifier 2* | |
| ... | ... | ... |
| | *Modifier n* | |
| | | **LayoutComponentContent** |
| | | *Component 1* |
| | | *Component 2* |
| | | ... |
| | | *Component n* |
| | | **ComponentEnd** |
| | **ComponentEnd** | |
| **ComponentEnd** | | |
In the example above, you can see that RowLayout is comprised of two sections -- a list of Modifier operations followed by a LayoutComponentContent component,
containing the components to be laid out.
# Layout overview
## Overview
The layout system work with **LayoutManager** that can layout **LayoutComponent** and **Component**.
LayoutComponents are able to be laid out as well as resized, while Components can only be laid out.
The following diagram shows the runtime class hiearchy:
<pre>
<div class="mermaid">
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
Component <|-- RootLayoutComponent
Component <|-- LayoutComponent
LayoutComponent <|-- LayoutManager
LayoutManager <|-- BoxLayout
LayoutManager <|-- ColumnLayout
LayoutManager <|-- RowLayout
LayoutManager <|-- FitBoxLayout
LayoutManager <|-- CoreText
BoxLayout <|-- CanvasLayout
ColumnLayout <|-- CollapsibleColumnLayout
RowLayout <|-- CollapsibleRowLayout
RowLayout <|-- FlowLayout
LayoutManager <|-- ImageLayout
LayoutManager <|-- StateLayout
</div>
</pre>
## Components
**Component** is the base class representing Origami Components.
We create components when loading the document, out of the **ComponentStart**
and **ComponentEnd** operations in the original operations stream, grouping a list of operations:
<pre>
<div class="mermaid">
graph LR
operations["`ComponentStart
Operation 1
...
Operation n
ComponentEnd 3`"]
operations --> Result
Result
subgraph Result [Runtime Component]
Component --> OP1[Operation 1]
Component --> OP2[...]
Component --> OP3[Operation n]
end
</div>
</pre>
## LayoutComponents
**LayoutComponent** represents layoutable components, which can be measured and may layout sub-components.
It keeps track of a list of modifiers as well as a list of child components.
<pre>
<div class="mermaid">
graph TD
LC[LayoutComponent]
LC --> Mods[Modifiers]
Mods --> M1[modifier 1]
Mods --> M2[modifier 2]
Mods --> M3[...]
Mods --> MN[modifier n]
LC --> Children[children]
Children --> C1[component 1]
Children --> C2[component 2]
Children --> C3[...]
Children --> CN[component n]
</div>
</pre>
### Modifiers
Modifiers are a list of operations that change the way a component can be displayed.
We have several modifiers that are related to the layout itself:
| Modifiers | Role |
|--------------------------|---------------------------|
| WidthModifierOperation | width as fixed dimension |
| HeightModifierOperation | height as fixed dimension |
| PaddingModifierOperation | padding information |
As well as modifiers that are able to change the way the component paint itself:
| Modifiers | Role |
|----------------------------------|------------------------------------|
| BackgroundModifierOperation | Draw a plain color background |
| BorderModifierOperation | Draw a border around the component |
| ClipRectModifierOperation | Add a clipping rect |
| RoundedClipRectModifierOperation | Add a clipping round rect |
Modifiers are being applied in order, with Size and Padding impacting the draw-related
modifiers. Modifiers are doing a simple version of the layout pass (i.e. we don't ask modifiers
to measure and then layout them, we only layout them).
Padding modifiers can be used before or after the content modifiers, which means that we do not
need a "margin" concept as in other UI systems. Instead we can set up something like:
<pre>
<div class="mermaid">
graph LR
P1[Padding 4] --> BG[Background]
BG --> P2[Padding 8]
P2 --> Content[/Content/]
</div>
</pre>
Which would result in a "margin" of 4 followed by painting the background, with a "padding" of 8
for the actual content.
#### Sizing
If a component is set to compute its own size (WRAP), Padding will be added to the computed size.
But to note, if a component uses a modifier for indicating a fixed dimension, such as:
<pre>
<div class="mermaid">
graph LR
P1[Padding 4] --> S[Size 10x10]
S --> P2[Padding 2]
</div>
</pre>
The computed size of the component for its parent layout manager will be 14x14.
#### Borders
Borders are always applied on top of the content, so putting the border before a background or after would result in the same visuals:
<pre>
<div class="mermaid">
graph LR
B1[Border] --> BG1[Background]
BG1 == "visual equivalent" ==> BG2[Background]
BG2 --> B2[Border]
</div>
</pre>
Borders are purely a visual decoration, they do not impact sizing/padding, but they are impacted
by them, so have to be evaluated in order.
## Layout Managers
The actual layout managers are implemented as subclass of LayoutManager:
| Layout Manager | Role |
|-------------------------|---------------------------------------------------|
| BoxLayout | Layout elements in the same place |
| RowLayout | Layout elements in a row |
| ColumnLayout | Layout elements in a column |
| CollapsibleRowLayout | as RowLayout, but skip elements that don't fit |
| CollapsibleColumnLayout | as ColumnLayout, but skip elements that don't fit |
| FitBoxLayout | pick the first child that fits |
| FlowLayout | as RowLayout, but wrap elements if they don't fit |
### Scroll areas
Scroll areas are handled as subclasses of LayoutContent:
<pre>
<div class="mermaid">
graph TD
SCH[ScrollContentHost]
SCH --> SC[ScrollContent]
SCH --> LC[LayoutContent]
</div>
</pre>
## Layout / Measure cycle
For a runtime document, we end up with the following structure for the layout operations:
<pre>
<div class="mermaid">
graph TD
RLC[RootLayoutComponent]
RLC --> LM1[LayoutManager]
LM1 --> LM2[LayoutManager]
LM2 --> C1[Component]
LM2 --> C2[Component]
LM1 --> C3[Component]
</div>
</pre>
The general layout / measure cycle works as follow:
- for each LayoutComponent, measure their children recursively
- capture that measure in a MeasurePass object
- then recursively layout the components by using the MeasurePass information.
<pre>
<div class="mermaid">
graph TD
M[Measure]
M -- "ideally single pass, using intrinsic sizes (multi-measure is discouraged, but allowed)" --> M
M --> MP[MeasurePass]
MP -- "contains position+size of all the components" --> L[Layout]
L -- "we can animate upon receiving a new layout(measurePass) request " --> A[Animate]
</div>
</pre>
1. RootLayoutComponent.layout(width, height) will measure all its children (Components), and gather
the result of the measure in a MeasurePass object. The MeasurePass is then used to layout
the components. The measure on its own never change the components, it's only role is to
gather the resulting dimensions / positions.
2. Components implements the Measurable interface.
3. The measure pass is ideally done as a single pass, asking each child to measure itself
given min/max constraints for width and height: the contract for the child is to enforce
those min/max constraints and set a size that is within those.
4. By default, when a component receive a new layout with a measure information, we animate
the change.
## Measure
Measure is done by asking components to add a ComponentMeasure to MeasurePass;
ComponentMeasure contains the position, dimension and visibility of the component.
Each component has an implicit ID that we use to map a given ComponentMeasure to a component.
The measure itself consists in passing a set of (min, max) for width and height to the component.
As part of the measurement, the list of modifiers may need to be evaluated (in order), to take in account
dimension modifiers (size, padding..).
### Multi-measure
In order to limit the needs for multi-measures, Measurable components have the concept
of intrinsic sizes (min & max) -- they should be handled as equivalent to a measure query,
but should be cached (or even pre-cached) by the component.
Layout managers then can ask each child to measure themselves given min/max constraints,
or if they need to, ask the child for their intrinsic sizes, then measure them.
Layout managers can call measure multiple times on children (i.e. there is
nothing preventing multi-measure) but this should be avoided as possible, given the
potential performance impact (re-measuring a layout high in the hierarchy has the potential
for a large performance hit).
A component can indicate that its size/content changed and thus a layout pass is needed
by calling invalidateMeasure(); this will flag the chain of component until the root
and trigger the layout pass.
## Request layout / Request measure
Generally, the layout system will react to change in dimensions, propagated from the root.
Another important mechanism is the ability for components to invalidate themselves and trigger
a relayout/remeasure pass.
Components can do that simply by calling `invalidateMeasure()` -- this will mark themselves
as needing both a repaint and a remeasure, and will walk the tree up until the root component,
invalidating them as the tree is traversed. The root component then uses this to trigger a measure pass.
# Scrolling Architecture in RemoteCompose
RemoteCompose provides a physics-based, hierarchical scrolling system that supports orthogonal nesting (e.g., horizontal rows inside vertical columns) and dynamic state synchronization.
## Core Concepts
### 1. Coordinate Spaces
To handle deep nesting and scrolling reliably, the interaction engine uses three distinct coordinate spaces:
| Space | Reference | Usage |
| :--- | :--- | :--- |
| **Root-Relative** | Document origin (0,0) | Used for top-level hit testing (`contains(x, y)`). |
| **Viewport-Relative** (`lx, ly`) | Component top-left on screen | Used by Modifiers (ripples, scroll logic). |
| **Content-Relative** (`cx, cy`) | Component's scrollable origin | Passed to children so they can ignore parent scroll. |
### 2. The Scroll Delegate
Scrolling is not hardcoded into layouts. Instead, `LayoutComponent` uses a `ScrollDelegate` interface. If a component has a `ScrollModifierOperation` in its modifier list, it registers itself as the delegate for horizontal or vertical axes.
- **Measure Phase**: The layout manager queries the delegate for the "content dimension" (total scrollable area).
- **Layout Phase**: The delegate updates the component's internal scroll offsets based on user interaction or bound variables.
- **Paint Phase**: `LayoutComponent` applies a translation transformation (`getScrollX()`, `getScrollY()`) before painting its children.
## The Interaction Pipeline
### 1. Event Entry (`CoreDocument`)
Touch events enter via `CoreDocument.touchDown/Drag/Up`. The document tracks "applied touch operations"—components currently being interacted with—to bypass hit-testing during a drag.
### 2. Recursive Dispatch (`Component`)
Events propagate down the tree in **reverse drawing order** (top-most component first).
- A component first performs a **Root-Relative** hit test.
- If it contains the point, it calculates the **Viewport** and **Content** offsets.
- It dispatches to children using the **Content** space.
- It then dispatches to its own modifiers using the **Viewport** space.
### 3. Event Consumption
Interaction handlers return a `boolean`.
- If a child component consumes an event (e.g., a button click), sibling components are ignored.
- **Crucially**, parent modifiers (like a `ScrollModifier`) are still notified even if a child consumed the event. This allows a nested list to scroll even if the user started the drag on a button.
## Physics and State (`TouchExpression`)
The `ScrollModifierOperation` typically hosts a `TouchExpression`. This engine handles:
- **Velocity Tracking**: Calculates pixels-per-second during a drag.
- **Fling/Easing**: When the user releases, it uses a `VelocityEasing` curve to continue the scroll.
- **Notches**: Supports snapping to specific positions (even spacing, absolute points, or percentages).
## Nested Scrolling Logic
RemoteCompose handles nested scrolls by utilizing the "Orthogonal Capture" rule:
1. A vertical scroll only consumes the **Y** component of a drag.
2. A horizontal scroll only consumes the **X** component.
3. Because propagation continues up the parent chain for modifiers, a vertical swipe inside a horizontal row will be ignored by the row's scroll logic but captured by the parent column's scroll logic.
## Layout Integration
Layout managers like `ColumnLayout` and `RowLayout` are "scroll-aware":
- During `internalLayoutMeasure`, they check `mComponentModifiers.hasVerticalScroll()`.
- If true, they allow the content to exceed the host's viewport height.
- They then call `setVerticalScrollDimension()` to inform the modifier of the total scrollable range, which the `TouchExpression` uses for boundary clamping.
# Click Handling and Actions in RemoteCompose
RemoteCompose provides a flexible click system based on modifiers that can trigger both internal state changes and host-side callbacks.
## Click Propagation Process
Click events follow a similar propagation path to touch events but are specifically triggered on the "up" phase of a gesture if the movement threshold hasn't been exceeded.
### 1. Root Dispatch (`CoreDocument`)
When a user clicks, `CoreDocument.onClick(x, y)` is called with root-relative coordinates.
### 2. Backward Search (`Component`)
The document iterates through its component tree in **reverse drawing order** (top-to-bottom). For each component:
- It checks if the point `(x, y)` is within the component's bounds via `contains(x, y)`.
- If contained, it recursively calls `onClick` on its children.
- If no child handles the click, it dispatches to its own `onClick` handlers (usually via `ComponentModifiers`).
### 3. Local Transformation
Before reaching a `ClickModifierOperation`, the coordinates are transformed:
- **Root Space**: Used for initial hit detection.
- **Content Space**: Passed to children and modifiers, accounting for parent scrolling and padding.
## Click Modifiers (`ClickModifierOperation`)
The primary way to make a component interactive is by adding a click modifier.
### Interaction Features
- **Ripple Animation**: Triggers a visual feedback ripple at the exact touch location. The ripple uses `Easing.CUBIC_STANDARD` and animates both color and radius over 1000ms.
- **Haptic Feedback**: Automatically triggers a host haptic effect (type 3) upon a successful click.
- **Role & Semantics**: Identifies the component as a `Role.BUTTON` for accessibility services (e.g., TalkBack).
## Actions
A `ClickModifierOperation` acts as a container for one or more `ActionOperation`s.
| Action Type | Description |
| :--- | :--- |
| **ValueChange** | Updates a RemoteCompose variable (Float, String, or Boolean). Useful for toggling states like `isExpanded` or `clickCount`. |
| **HostAction** | Sends a metadata string back to the host application (Android/iOS). This is used for navigation or triggering native logic. |
| **Custom Actions** | Can be implemented to perform complex sequences, such as multiple variable updates or conditional logic. |
## Interaction with Scrolling
The system ensures that clicks and scrolls don't conflict:
1. **Threshold**: If a touch move exceeds a small distance threshold (e.g., 5-10 pixels), the gesture is "captured" by a scroll parent.
2. **Cancellation**: Once captured by a scroll, any pending click animations or actions on child components are cancelled.
3. **Consumption**: If a click is handled by a child, the event returns `true`, stopping further propagation to parent click handlers.
# Document Protocol Operations
Core document metadata, versioning, themes, and high-level behaviors.
Operations in this category:
| ID | Name | Version | Size (bytes)
| ---- | ---- | ---- | ---- |
| 0 | Header | v6 | 29
| 63 | Theme | v6 | 5
| 185 | Rem | v7 | 1
| 214 | ContainerEnd | v6 | 1
## Header
Document metadata, containing the version, original size & density, capabilities mask
6 Fields, total size 29 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>Header</td><td>Value: 0</td></tr>
<tr><td>INT</td><td>majorVersion</td><td>Major version</td></tr><tr><td>INT</td><td>minorVersion</td><td>Minor version</td></tr><tr><td>INT</td><td>patchVersion</td><td>Patch version</td></tr><tr><td>INT</td><td>width</td><td>Document width in pixels</td></tr><tr><td>INT</td><td>height</td><td>Document height in pixels</td></tr><tr><td>LONG</td><td>capabilities</td><td>Capabilities mask</td></tr></table>
## Theme
Set a theme
1 Fields, total size 5 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>Theme</td><td>Value: 63</td></tr>
<tr><td>INT</td><td>THEME</td><td>theme id</td></tr></table>
### THEME
| Name | Value |
| ---- | ---- |
| UNSPECIFIED | -1
| DARK | -2
| LIGHT | -3
### Theme Illustration
The `Theme` operation allows the document to adapt its visual appearance (colors, styles) based on the active theme (e.g., Light or Dark mode).
<div id="themeContainer">
<canvas id="themeCanvas_v1" width="500" height="200" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
var themeTimer = 0;
function draw() {
var canvas = document.getElementById('themeCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var isDark = (Math.floor(themeTimer / 100) % 2 === 1);
themeTimer++;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444';
var bg = isDark ? '#333' : '#fff';
var textCol = isDark ? '#eee' : '#444';
var primaryCol = isDark ? '#8ab4f8' : '#0047AB';
ctx.fillStyle = bg;
ctx.fillRect(0, 0, 500, 200);
// Component Representation
ctx.fillStyle = primaryCol;
ctx.fillRect(150, 50, 200, 100);
ctx.strokeStyle = textCol;
ctx.lineWidth = 2;
ctx.strokeRect(150, 50, 200, 100);
ctx.fillStyle = textCol;
ctx.font = 'bold 24px Arial';
ctx.textAlign = 'center';
ctx.fillText(isDark ? 'DARK MODE' : 'LIGHT MODE', 250, 110);
ctx.font = 'bold 18px Arial';
ctx.fillStyle = GRAY;
ctx.fillText('Theme switching automatically updates linked colors', 250, 180);
requestAnimationFrame(draw);
}
draw();
})();
</script>
## Rem (added in v7)
Embed a remark or comment string in the document
1 Fields, total size 1 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>Rem</td><td>Value: 185</td></tr>
<tr><td>UTF8</td><td>text</td><td>The comment string</td></tr></table>
## ContainerEnd
End tag for a container component (Row, Column, etc.)
0 Fields, total size 1 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ContainerEnd</td><td>Value: 214</td></tr>
</table>
# Data Operations
Definitions of static constants, named variables, collections (lists/maps), and resource data (Bitmaps, Fonts).
Operations in this category:
| ID | Name | Version | Size (bytes)
| ---- | ---- | ---- | ---- |
| 80 | FloatConstant | v6 | 9
| 101 | BitmapData | v6 | 13
| 137 | NamedVariable | v6 | 9
| 140 | IntegerConstant | v6 | 9
| 143 | BooleanConstant | v6 | 6
| 145 | DataMapIds | v6 | 9
| 146 | IdListData | v6 | 9 + null x 4
| 147 | IdListData | v6 | 9 + null x 4
| 148 | LongConstant | v6 | 13
| 154 | DataMapLookup | v6 | 13
| 189 | FontData | v7 | 9
| 197 | DataDynamicListFloat | v7 | 9
| 198 | UpdateDynamicFloatList | v7 | 13
## FloatConstant
A float and its associated id
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>FloatConstant</td><td>Value: 80</td></tr>
<tr><td>INT</td><td>id</td><td>id of the Float constant</td></tr><tr><td>FLOAT</td><td>value</td><td>32-bit float value</td></tr></table>
## BitmapData
Embed or reference bitmap image data
4 Fields, total size 13 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>BitmapData</td><td>Value: 101</td></tr>
<tr><td>INT</td><td>imageId</td><td>The ID of the bitmap</td></tr><tr><td>INT</td><td>widthAndType</td><td>Encoded width and image type</td></tr><tr><td>INT</td><td>heightAndEncoding</td><td>Encoded height and data encoding</td></tr><tr><td>BYTE[]</td><td>bitmap</td><td>The raw or encoded bitmap data</td></tr></table>
## NamedVariable
Add a string name for an ID
3 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>NamedVariable</td><td>Value: 137</td></tr>
<tr><td>INT</td><td>varId</td><td>id to label</td></tr><tr><td>INT</td><td>varType</td><td>The type of variable</td></tr><tr><td>UTF8</td><td>name</td><td>String</td></tr></table>
## IntegerConstant
A integer and its associated id
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>IntegerConstant</td><td>Value: 140</td></tr>
<tr><td>INT</td><td>id</td><td>id of the Int constant</td></tr><tr><td>INT</td><td>value</td><td>32-bit int value</td></tr></table>
## BooleanConstant
A boolean and its associated id
2 Fields, total size 6 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>BooleanConstant</td><td>Value: 143</td></tr>
<tr><td>INT</td><td>id</td><td>id of Int</td></tr><tr><td>BYTE</td><td>value</td><td>8-bit 0 or 1</td></tr></table>
## DataMapIds
Encode a collection of named variable IDs
3 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DataMapIds</td><td>Value: 145</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the map</td></tr><tr><td>INT</td><td>length</td><td>Number of entries</td></tr><tr><td>REPEATED DATA</td><td colspan="2"><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>UTF8</td><td>name</td><td>The name of the entry</td></tr><tr><td>INT</td><td>type</td><td>The type of the entry</td></tr><tr><td>INT</td><td>id</td><td>The ID of the variable</td></tr></table></td></tr></table>
## IdListData
A list of IDs
3 Fields, total size 9 + null x 4 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>IdListData</td><td>Value: 146</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the list</td></tr><tr><td>INT</td><td>length</td><td>Number of IDs</td></tr><tr><td>INT[]</td><td>ids</td><td>The array of IDs</td></tr></table>
## IdListData
A list of floats
3 Fields, total size 9 + null x 4 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>IdListData</td><td>Value: 147</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the list</td></tr><tr><td>INT</td><td>length</td><td>Number of floats</td></tr><tr><td>FLOAT[]</td><td>values</td><td>The array of floats</td></tr></table>
## LongConstant
A long and its associated id
2 Fields, total size 13 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>LongConstant</td><td>Value: 148</td></tr>
<tr><td>INT</td><td>id</td><td>id of the Long constant</td></tr><tr><td>LONG</td><td>value</td><td>The long Value</td></tr></table>
## DataMapLookup
Look up a value in a data map
3 Fields, total size 13 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DataMapLookup</td><td>Value: 154</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the output value</td></tr><tr><td>INT</td><td>dataMapId</td><td>The ID of the data map</td></tr><tr><td>INT</td><td>stringId</td><td>The ID of the string to look up</td></tr></table>
## FontData (added in v7)
Embed raw font data in the document
3 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>FontData</td><td>Value: 189</td></tr>
<tr><td>INT</td><td>fontId</td><td>The ID of the font</td></tr><tr><td>INT</td><td>type</td><td>The type of the font (unused)</td></tr><tr><td>BYTE[]</td><td>fontData</td><td>The raw font file data</td></tr></table>
## DataDynamicListFloat (added in v7)
A dynamic list of floats
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DataDynamicListFloat</td><td>Value: 197</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the list</td></tr><tr><td>FLOAT</td><td>length</td><td>The length of the list</td></tr></table>
## UpdateDynamicFloatList (added in v7)
Update a value in a dynamic float list
3 Fields, total size 13 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>UpdateDynamicFloatList</td><td>Value: 198</td></tr>
<tr><td>INT</td><td>arrayId</td><td>The ID of the array</td></tr><tr><td>FLOAT</td><td>index</td><td>The index to update</td></tr><tr><td>FLOAT</td><td>value</td><td>The new value</td></tr></table>
# Paint & Styles Operations
Management of paint properties, shaders, and complex color definitions (themes, expressions).
Operations in this category:
| ID | Name | Version | Size (bytes)
| ---- | ---- | ---- | ---- |
| 40 | PaintData | v6 | 1 + null x 4
| 45 | ShaderData | v6 | 25 + null x 4 + null x 4
| 134 | ColorExpression | v6 | 25
| 138 | ColorConstant | v6 | 9
| 180 | ColorAttribute | v6 | 11
| 196 | ColorTheme | v7 | 21
## PaintData
Encode a Paint object with various properties
1 Fields, total size 1 + null x 4 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>PaintData</td><td>Value: 40</td></tr>
<tr><td>INT[]</td><td>paintBundle</td><td>The encoded paint properties</td></tr></table>
### PaintData Illustration
The `PaintData` operation encodes properties like style (Fill/Stroke), color, and stroke width.
#### Fill vs Stroke
<div id="paintStylesContainer">
<canvas id="paintStylesCanvas_v1" width="500" height="250" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('paintStylesCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', DARK_GRAY = '#444444';
ctx.clearRect(0, 0, 500, 250);
// STYLE_FILL
ctx.beginPath();
ctx.arc(125, 125, 65, 0, 2 * Math.PI);
ctx.fillStyle = BLUE;
ctx.fill();
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 22px Arial';
ctx.fillText('STYLE_FILL', 65, 220);
// STYLE_STROKE
ctx.beginPath();
ctx.arc(375, 125, 65, 0, 2 * Math.PI);
ctx.lineWidth = 7;
ctx.strokeStyle = BLUE;
ctx.stroke();
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 22px Arial';
ctx.fillText('STYLE_STROKE', 300, 220);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## ShaderData
Define a shader with associated uniforms
11 Fields, total size 25 + null x 4 + null x 4 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ShaderData</td><td>Value: 45</td></tr>
<tr><td>INT</td><td>shaderID</td><td>The ID of the shader</td></tr><tr><td>INT</td><td>shaderTextId</td><td>The ID of the shader source text</td></tr><tr><td>INT</td><td>sizes</td><td>Encoded sizes of uniform maps (float, int, bitmap)</td></tr><tr><td>UTF8</td><td>floatUniformName[0..n]</td><td>Name of float uniform</td></tr><tr><td>INT</td><td>floatUniformLength[0..n]</td><td>Length of float uniform</td></tr><tr><td>FLOAT[]</td><td>floatUniformValues[0..n]</td><td>Values of float uniform</td></tr><tr><td>UTF8</td><td>intUniformName[0..n]</td><td>Name of int uniform</td></tr><tr><td>INT</td><td>intUniformLength[0..n]</td><td>Length of int uniform</td></tr><tr><td>INT[]</td><td>intUniformValues[0..n]</td><td>Values of int uniform</td></tr><tr><td>UTF8</td><td>bitmapUniformName[0..n]</td><td>Name of bitmap uniform</td></tr><tr><td>INT</td><td>bitmapUniformValue[0..n]</td><td>ID of bitmap uniform</td></tr></table>
### ShaderData Illustration
The `ShaderData` operation defines complex color effects like gradients or image-based patterns.
#### Linear Gradient Example
<div id="shaderGradientContainer">
<canvas id="shaderGradientCanvas_v1" width="500" height="160" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('shaderGradientCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444';
ctx.clearRect(0, 0, 500, 160);
var gradient = ctx.createLinearGradient(80, 0, 420, 0);
gradient.addColorStop(0, BLUE);
gradient.addColorStop(1, GRAY);
ctx.fillStyle = gradient;
ctx.fillRect(80, 30, 340, 100);
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 20px Arial';
ctx.fillText('Start Color (Blue)', 60, 150);
ctx.fillText('End Color (Gray)', 320, 150);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## ColorExpression
Define a color via dynamic expression (HSV, ARGB, or Interpolation)
6 Fields, total size 25 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ColorExpression</td><td>Value: 134</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the resulting color</td></tr><tr><td>INT</td><td>mode</td><td>The color calculation mode</td></tr><tr><td>INT</td><td>param1</td><td>First parameter (color1, hue, or alpha depending on mode)</td></tr><tr><td>INT</td><td>param2</td><td>Second parameter (color2, saturation, or red depending on mode)</td></tr><tr><td>INT</td><td>param3</td><td>Third parameter (tween, value, or green depending on mode)</td></tr><tr><td>INT</td><td>param4</td><td>Fourth parameter (blue, only used in ARGB modes)</td></tr></table>
### mode
| Name | Value |
| ---- | ---- |
| COLOR_COLOR_INTERPOLATE | 0
| ID_COLOR_INTERPOLATE | 1
| COLOR_ID_INTERPOLATE | 2
| ID_ID_INTERPOLATE | 3
| HSV_MODE | 4
| ARGB_MODE | 5
| IDARGB_MODE | 6
## ColorConstant
Define a static color and associate it with an ID
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ColorConstant</td><td>Value: 138</td></tr>
<tr><td>INT</td><td>colorId</td><td>The ID of the color</td></tr><tr><td>INT</td><td>color</td><td>32-bit ARGB color value</td></tr></table>
## ColorAttribute
Extract components (Hue, RGB, Alpha) from a color
3 Fields, total size 11 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ColorAttribute</td><td>Value: 180</td></tr>
<tr><td>INT</td><td>id</td><td>The ID to output the result to</td></tr><tr><td>INT</td><td>colorId</td><td>The ID of the source color</td></tr><tr><td>SHORT</td><td>type</td><td>The component to extract</td></tr></table>
### type
| Name | Value |
| ---- | ---- |
| COLOR_HUE | 0
| COLOR_SATURATION | 1
| COLOR_BRIGHTNESS | 2
| COLOR_RED | 3
| COLOR_GREEN | 4
| COLOR_BLUE | 5
| COLOR_ALPHA | 6
## ColorTheme [EXPERIMENTAL] (added in v7)
!!! WARNING
Experimental operation
Define a color that adapts to the current theme (light/dark)
6 Fields, total size 21 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ColorTheme</td><td>Value: 196</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the color</td></tr><tr><td>INT</td><td>groupId</td><td>The ID of the color group name string</td></tr><tr><td>SHORT</td><td>lightModeIndex</td><td>The ID of the color in the light group</td></tr><tr><td>SHORT</td><td>darkModeIndex</td><td>The ID of the color in the dark group</td></tr><tr><td>INT</td><td>lightModeFallback</td><td>32-bit ARGB fallback color for light mode</td></tr><tr><td>INT</td><td>darkModeFallback</td><td>32-bit ARGB fallback color for dark mode</td></tr></table>
# Canvas Operations
Low-level drawing primitives for shapes, paths, text, and bitmaps, as well as clipping and layer management.
Operations in this category:
| ID | Name | Version | Size (bytes)
| ---- | ---- | ---- | ---- |
| 42 | DrawRect | v6 | 17
| 44 | DrawBitmap | v6 | 25
| 46 | DrawCircle | v6 | 13
| 47 | DrawLine | v6 | 17
| 51 | DrawRoundRect | v6 | 25
| 52 | DrawSector | v6 | 25
| 56 | DrawOval | v6 | 17
| 66 | DrawBitmapInt | v6 | 41
| 124 | DrawPath | v6 | 5
| 125 | DrawTweenPath | v6 | 21
| 139 | DrawContent | v6 | 1
| 149 | DrawBitmapScaled | v6 | 49
| 152 | DrawArc | v6 | 25
| 158 | PathTween | v6 | 17
| 159 | PathCreate | v6 | 13
| 160 | PathAppend | v6 | 9 + null x 4
| 173 | CanvasOperations | v6 | 1
| 175 | PathCombine | v6 | 14
| 190 | DrawToBitmap | v7 | 13
## DrawRect
Draw the specified rectangle
4 Fields, total size 17 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawRect</td><td>Value: 42</td></tr>
<tr><td>FLOAT</td><td>left</td><td>The left side of the rectangle</td></tr><tr><td>FLOAT</td><td>top</td><td>The top of the rectangle</td></tr><tr><td>FLOAT</td><td>right</td><td>The right side of the rectangle</td></tr><tr><td>FLOAT</td><td>bottom</td><td>The bottom of the rectangle</td></tr></table>
### DrawRect Illustration
The `DrawRect` operation renders a rectangle defined by its `left`, `top`, `right`, and `bottom` coordinates.
<div id="drawRectContainer">
<canvas id="drawRectCanvas_v1" width="500" height="500" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('drawRectCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
ctx.clearRect(0, 0, 500, 500);
var left = 100, top = 100, right = 400, bottom = 350;
var width = right - left;
var height = bottom - top;
ctx.fillStyle = BLUE_TRANS;
ctx.fillRect(left, top, width, height);
ctx.lineWidth = 5;
ctx.strokeStyle = BLUE;
ctx.strokeRect(left, top, width, height);
ctx.font = 'bold 20px Arial';
ctx.fillStyle = DARK_GRAY;
ctx.fillText('left, top (' + left + ', ' + top + ')', left - 80, top - 25);
ctx.fillText('right, bottom (' + right + ', ' + bottom + ')', right - 180, bottom + 50);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## DrawBitmap
Draw a bitmap
6 Fields, total size 25 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawBitmap</td><td>Value: 44</td></tr>
<tr><td>INT</td><td>imageId</td><td>The ID of the bitmap</td></tr><tr><td>FLOAT</td><td>left</td><td>The left side of the image</td></tr><tr><td>FLOAT</td><td>top</td><td>The top of the image</td></tr><tr><td>FLOAT</td><td>right</td><td>The right side of the image</td></tr><tr><td>FLOAT</td><td>bottom</td><td>The bottom of the image</td></tr><tr><td>INT</td><td>descriptionId</td><td>The ID of the content description string</td></tr></table>
## DrawCircle
Draw a Circle
3 Fields, total size 13 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawCircle</td><td>Value: 46</td></tr>
<tr><td>FLOAT</td><td>centerX</td><td>The x-coordinate of the center of the circle to be drawn</td></tr><tr><td>FLOAT</td><td>centerY</td><td>The y-coordinate of the center of the circle to be drawn</td></tr><tr><td>FLOAT</td><td>radius</td><td>The radius of the circle to be drawn</td></tr></table>
### DrawCircle Illustration
The `DrawCircle` operation renders a circle centered at `(centerX, centerY)` with a given `radius`.
<div id="drawCircleContainer">
<canvas id="drawCircleCanvas_v1" width="500" height="500" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('drawCircleCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
ctx.clearRect(0, 0, 500, 500);
var cx = 250, cy = 250, radius = 125;
// Main Circle
ctx.beginPath();
ctx.arc(cx, cy, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = BLUE_TRANS;
ctx.fill();
ctx.lineWidth = 5;
ctx.strokeStyle = BLUE;
ctx.stroke();
// Center Anchor
ctx.beginPath();
ctx.arc(cx, cy, 7, 0, 2 * Math.PI, false);
ctx.fillStyle = DARK_GRAY;
ctx.fill();
// Construction Line
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.lineTo(cx + radius, cy);
ctx.strokeStyle = GRAY;
ctx.setLineDash([12, 8]);
ctx.lineWidth = 2;
ctx.stroke();
ctx.setLineDash([]);
// Text
ctx.font = 'bold 22px Arial';
ctx.fillStyle = DARK_GRAY;
ctx.fillText('Center (cx, cy)', cx + 15, cy - 15);
ctx.font = '20px Arial';
ctx.fillText('radius: ' + radius, cx + 20, cy + 35);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## DrawLine
Draw a line segment
4 Fields, total size 17 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawLine</td><td>Value: 47</td></tr>
<tr><td>FLOAT</td><td>startX</td><td>The x-coordinate of the start point of the line</td></tr><tr><td>FLOAT</td><td>startY</td><td>The y-coordinate of the start point of the line</td></tr><tr><td>FLOAT</td><td>endX</td><td>The x-coordinate of the end point of the line</td></tr><tr><td>FLOAT</td><td>endY</td><td>The y-coordinate of the end point of the line</td></tr></table>
### DrawLine Illustration
The `DrawLine` operation renders a single straight line segment between `(startX, startY)` and `(endX, endY)`.
<div id="drawLineContainer">
<canvas id="drawLineCanvas_v1" width="500" height="500" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('drawLineCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444';
ctx.clearRect(0, 0, 500, 500);
var x1 = 100, y1 = 100, x2 = 400, y2 = 400;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineWidth = 10;
ctx.strokeStyle = BLUE;
ctx.lineCap = 'round';
ctx.stroke();
ctx.font = 'bold 22px Arial';
ctx.fillStyle = DARK_GRAY;
ctx.fillText('Start (' + x1 + ', ' + y1 + ')', x1 - 40, y1 - 30);
ctx.fillText('End (' + x2 + ', ' + y2 + ')', x2 - 120, y2 + 50);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## DrawRoundRect
Draw the specified round-rect
6 Fields, total size 25 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawRoundRect</td><td>Value: 51</td></tr>
<tr><td>FLOAT</td><td>left</td><td>The left side of the rect</td></tr><tr><td>FLOAT</td><td>top</td><td>The top of the rect</td></tr><tr><td>FLOAT</td><td>right</td><td>The right side of the rect</td></tr><tr><td>FLOAT</td><td>bottom</td><td>The bottom of the rect</td></tr><tr><td>FLOAT</td><td>rx</td><td>The x-radius of the oval used to round the corners</td></tr><tr><td>FLOAT</td><td>ry</td><td>The y-radius of the oval used to round the corners</td></tr></table>
### DrawRoundRect Illustration
The `DrawRoundRect` operation renders a rectangle with rounded corners, defined by its bounds and the radii `rx` and `ry`.
<div id="drawRoundRectContainer">
<canvas id="drawRoundRectCanvas_v1" width="600" height="400" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('drawRoundRectCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
ctx.clearRect(0, 0, 600, 400);
var x = 100, y = 60, w = 300, h = 220, r = 50;
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.quadraticCurveTo(x + w, y, x + w, y + r);
ctx.lineTo(x + w, y + h - r);
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
ctx.lineTo(x + r, y + h);
ctx.quadraticCurveTo(x, y + h, x, y + h - r);
ctx.lineTo(x, y + r);
ctx.quadraticCurveTo(x, y, x + r, y);
ctx.closePath();
ctx.fillStyle = BLUE_TRANS;
ctx.fill();
ctx.lineWidth = 5;
ctx.strokeStyle = BLUE;
ctx.stroke();
ctx.font = 'bold 22px Arial';
ctx.fillStyle = DARK_GRAY;
ctx.textAlign = 'left';
ctx.fillText('rx, ry = ' + r, x + w + 15, y + r);
ctx.font = '18px Arial';
ctx.fillStyle = GRAY;
ctx.textAlign = 'center';
ctx.fillText('bounds (left, top, right, bottom)', x + w/2, y + h + 45);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## DrawSector
Draw the specified sector (pie shape)which will be scaled to fit inside the specified oval
6 Fields, total size 25 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawSector</td><td>Value: 52</td></tr>
<tr><td>FLOAT</td><td>left</td><td>The left side of the Oval</td></tr><tr><td>FLOAT</td><td>top</td><td>The top of the Oval</td></tr><tr><td>FLOAT</td><td>right</td><td>The right side of the Oval</td></tr><tr><td>FLOAT</td><td>bottom</td><td>The bottom of the Oval</td></tr><tr><td>FLOAT</td><td>startAngle</td><td>Starting angle (in degrees) where the arc begins</td></tr><tr><td>FLOAT</td><td>sweepAngle</td><td>Sweep angle (in degrees) measured clockwise</td></tr></table>
### DrawSector Illustration
The `DrawSector` operation renders a "pie" shape within a specified oval bounding box, starting from `startAngle` and sweeping through `sweepAngle`.
<div id="drawSectorContainer">
<canvas id="drawSectorCanvas_v1" width="600" height="500" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('drawSectorCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
ctx.clearRect(0, 0, 600, 500);
var cx = 300, cy = 250, rx = 175, ry = 125;
var startAngle = 45;
var sweepAngle = 110;
// Bounding Oval
ctx.beginPath();
ctx.ellipse(cx, cy, rx, ry, 0, 0, 2 * Math.PI);
ctx.strokeStyle = '#eee';
ctx.setLineDash([10, 10]);
ctx.lineWidth = 2;
ctx.stroke();
ctx.setLineDash([]);
// The Sector
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.ellipse(cx, cy, rx, ry, 0, (startAngle * Math.PI) / 180, ((startAngle + sweepAngle) * Math.PI) / 180, false);
ctx.closePath();
ctx.fillStyle = BLUE_TRANS;
ctx.fill();
ctx.lineWidth = 4;
ctx.strokeStyle = BLUE;
ctx.stroke();
// Start Angle Indicator
var sx = cx + rx * Math.cos(startAngle * Math.PI / 180);
var sy = cy + ry * Math.sin(startAngle * Math.PI / 180);
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.lineTo(sx, sy);
ctx.strokeStyle = BLUE;
ctx.lineWidth = 2;
ctx.stroke();
// End Angle Indicator
var ex = cx + rx * Math.cos((startAngle + sweepAngle) * Math.PI / 180);
var ey = cy + ry * Math.sin((startAngle + sweepAngle) * Math.PI / 180);
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.lineTo(ex, ey);
ctx.strokeStyle = GRAY;
ctx.stroke();
// Center point
ctx.beginPath();
ctx.arc(cx, cy, 5, 0, 2 * Math.PI);
ctx.fillStyle = DARK_GRAY;
ctx.fill();
// Labels
ctx.font = 'bold 18px Arial';
ctx.textAlign = 'left';
ctx.fillStyle = BLUE;
ctx.fillText('startAngle: ' + startAngle + '°', sx + 10, sy + 15);
ctx.textAlign = 'left';
ctx.fillStyle = DARK_GRAY;
ctx.fillText('sweepAngle: ' + sweepAngle + '°', ex - 120, ey + 65);
ctx.textAlign = 'center';
ctx.font = '14px Arial';
ctx.fillStyle = GRAY;
ctx.fillText('Center (cx, cy)', cx, cy - 15);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## DrawOval
Draw the specified oval
4 Fields, total size 17 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawOval</td><td>Value: 56</td></tr>
<tr><td>FLOAT</td><td>left</td><td>The left side of the oval</td></tr><tr><td>FLOAT</td><td>top</td><td>The top of the oval</td></tr><tr><td>FLOAT</td><td>right</td><td>The right side of the oval</td></tr><tr><td>FLOAT</td><td>bottom</td><td>The bottom of the oval</td></tr></table>
### DrawOval Illustration
The `DrawOval` operation renders an oval that fits within the specified bounding box.
<div id="drawOvalContainer">
<canvas id="drawOvalCanvas_v1" width="500" height="500" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('drawOvalCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
ctx.clearRect(0, 0, 500, 500);
var cx = 250, cy = 250, rx = 175, ry = 100;
// Main Oval
ctx.beginPath();
ctx.ellipse(cx, cy, rx, ry, 0, 0, 2 * Math.PI);
ctx.fillStyle = BLUE_TRANS;
ctx.fill();
ctx.lineWidth = 5;
ctx.strokeStyle = BLUE;
ctx.stroke();
// Bounding Box
ctx.setLineDash([12, 12]);
ctx.strokeStyle = GRAY;
ctx.lineWidth = 2;
ctx.strokeRect(cx - rx, cy - ry, rx * 2, ry * 2);
ctx.setLineDash([]);
// Labels
ctx.font = 'bold 22px Arial';
ctx.fillStyle = DARK_GRAY;
ctx.fillText('left, top', cx - rx, cy - ry - 15);
ctx.fillText('right, bottom', cx + rx - 140, cy + ry + 40);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## DrawBitmapInt
Draw a bitmap using integer coordinates
10 Fields, total size 41 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawBitmapInt</td><td>Value: 66</td></tr>
<tr><td>INT</td><td>imageId</td><td>The ID of the bitmap</td></tr><tr><td>INT</td><td>srcLeft</td><td>The left side of the source image</td></tr><tr><td>INT</td><td>srcTop</td><td>The top of the source image</td></tr><tr><td>INT</td><td>srcRight</td><td>The right side of the source image</td></tr><tr><td>INT</td><td>srcBottom</td><td>The bottom of the source image</td></tr><tr><td>INT</td><td>dstLeft</td><td>The left side of the destination</td></tr><tr><td>INT</td><td>dstTop</td><td>The top of the destination</td></tr><tr><td>INT</td><td>dstRight</td><td>The right side of the destination</td></tr><tr><td>INT</td><td>dstBottom</td><td>The bottom of the destination</td></tr><tr><td>INT</td><td>cdId</td><td>The ID of the content description string</td></tr></table>
## DrawPath
Draw a path
1 Fields, total size 5 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawPath</td><td>Value: 124</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the path to draw</td></tr></table>
## DrawTweenPath
Draw an interpolated path between two paths
5 Fields, total size 21 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawTweenPath</td><td>Value: 125</td></tr>
<tr><td>INT</td><td>path1Id</td><td>The ID of the first path</td></tr><tr><td>INT</td><td>path2Id</td><td>The ID of the second path</td></tr><tr><td>FLOAT</td><td>tween</td><td>Interpolation value [0..1]</td></tr><tr><td>FLOAT</td><td>start</td><td>Trim the start of the path [0..1]</td></tr><tr><td>FLOAT</td><td>stop</td><td>Trim the end of the path [0..1]</td></tr></table>
## DrawContent
Draw the component content
0 Fields, total size 1 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawContent</td><td>Value: 139</td></tr>
</table>
## DrawBitmapScaled
Draw a bitmap with scaling and alignment options
12 Fields, total size 49 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawBitmapScaled</td><td>Value: 149</td></tr>
<tr><td>INT</td><td>imageId</td><td>The ID of the bitmap</td></tr><tr><td>FLOAT</td><td>srcLeft</td><td>The left side of the source image</td></tr><tr><td>FLOAT</td><td>srcTop</td><td>The top of the source image</td></tr><tr><td>FLOAT</td><td>srcRight</td><td>The right side of the source image</td></tr><tr><td>FLOAT</td><td>srcBottom</td><td>The bottom of the source image</td></tr><tr><td>FLOAT</td><td>dstLeft</td><td>The left side of the destination</td></tr><tr><td>FLOAT</td><td>dstTop</td><td>The top of the destination</td></tr><tr><td>FLOAT</td><td>dstRight</td><td>The right side of the destination</td></tr><tr><td>FLOAT</td><td>dstBottom</td><td>The bottom of the destination</td></tr><tr><td>INT</td><td>scaleType</td><td>Type of scaling to apply</td></tr><tr><td>FLOAT</td><td>scaleFactor</td><td>Factor for fixed scale</td></tr><tr><td>INT</td><td>cdId</td><td>The ID of the content description string</td></tr></table>
### scaleType
| Name | Value |
| ---- | ---- |
| SCALE_NONE | 0
| SCALE_INSIDE | 1
| SCALE_FILL_WIDTH | 2
| SCALE_FILL_HEIGHT | 3
| SCALE_FIT | 4
| SCALE_CROP | 5
| SCALE_FILL_BOUNDS | 6
| SCALE_FIXED_SCALE | 7
### DrawBitmapScaled Illustration
The `DrawBitmapScaled` operation handles rendering images within a destination area using different scaling algorithms. To illustrate this, a "House" image with a 1:2 aspect ratio (tall) is used.
<div id="drawBitmapScaledContainer">
<canvas id="drawBitmapScaledCanvas_v1" width="500" height="300" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('drawBitmapScaledCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#BBBBBB', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
ctx.clearRect(0, 0, 500, 300);
function drawHouse(ctx, w, h) {
ctx.beginPath();
ctx.rect(0, h * 0.4, w, h * 0.6);
ctx.moveTo(0, h * 0.4);
ctx.lineTo(w / 2, 0);
ctx.lineTo(w, h * 0.4);
ctx.rect(w * 0.35, h * 0.7, w * 0.3, h * 0.3);
ctx.fillStyle = BLUE_TRANS;
ctx.fill();
ctx.lineWidth = Math.max(2, w/20);
ctx.strokeStyle = BLUE;
ctx.stroke();
}
function drawScaleExample(x, label, mode) {
var destW = 120, destH = 180;
var imgW = 100, imgH = 200;
ctx.save();
ctx.translate(x, 40);
ctx.strokeStyle = '#eee';
ctx.setLineDash([5, 5]);
ctx.lineWidth = 2;
ctx.strokeRect(0, 0, destW, destH);
ctx.setLineDash([]);
ctx.save();
ctx.beginPath(); ctx.rect(0, 0, destW, destH); ctx.clip();
if (mode === 'FIT') {
var s = Math.min(destW / imgW, destH / imgH);
ctx.translate((destW - imgW * s) / 2, (destH - imgH * s) / 2);
drawHouse(ctx, imgW * s, imgH * s);
} else if (mode === 'CROP') {
var s = Math.max(destW / imgW, destH / imgH);
ctx.translate((destW - imgW * s) / 2, (destH - imgH * s) / 2);
drawHouse(ctx, imgW * s, imgH * s);
} else if (mode === 'FILL') {
drawHouse(ctx, destW, destH);
}
ctx.restore();
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 18px Arial';
ctx.textAlign = 'center';
ctx.fillText(label, destW / 2, destH + 35);
ctx.restore();
}
drawScaleExample(40, 'SCALE_FIT', 'FIT');
drawScaleExample(190, 'SCALE_CROP', 'CROP');
drawScaleExample(340, 'SCALE_FILL', 'FILL');
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## DrawArc
Draw the specified arcwhich will be scaled to fit inside the specified oval
6 Fields, total size 25 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawArc</td><td>Value: 152</td></tr>
<tr><td>FLOAT</td><td>left</td><td>The left side of the Oval</td></tr><tr><td>FLOAT</td><td>top</td><td>The top of the Oval</td></tr><tr><td>FLOAT</td><td>right</td><td>The right side of the Oval</td></tr><tr><td>FLOAT</td><td>bottom</td><td>The bottom of the Oval</td></tr><tr><td>FLOAT</td><td>startAngle</td><td>Starting angle (in degrees) where the arc begins</td></tr><tr><td>FLOAT</td><td>sweepAngle</td><td>Sweep angle (in degrees) measured clockwise</td></tr></table>
### DrawArc Illustration
The `DrawArc` operation renders an arc within a specified oval bounding box, starting from `startAngle` and sweeping through `sweepAngle` (degrees).
<div id="drawArcContainer">
<canvas id="drawArcCanvas_v1" width="600" height="500" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('drawArcCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444';
ctx.clearRect(0, 0, 600, 500);
var cx = 300, cy = 250, rx = 175, ry = 125;
var startAngle = 30;
var sweepAngle = 240;
// Bounding Oval
ctx.beginPath();
ctx.ellipse(cx, cy, rx, ry, 0, 0, 2 * Math.PI);
ctx.strokeStyle = '#eee';
ctx.setLineDash([10, 10]);
ctx.lineWidth = 2;
ctx.stroke();
ctx.setLineDash([]);
// Axes
ctx.beginPath();
ctx.moveTo(cx - rx - 20, cy);
ctx.lineTo(cx + rx + 20, cy);
ctx.moveTo(cx, cy - ry - 20);
ctx.lineTo(cx, cy + ry + 20);
ctx.strokeStyle = '#f8f8f8';
ctx.stroke();
// The Arc
ctx.beginPath();
ctx.ellipse(cx, cy, rx, ry, 0, (startAngle * Math.PI) / 180, ((startAngle + sweepAngle) * Math.PI) / 180, false);
ctx.lineWidth = 8;
ctx.strokeStyle = BLUE;
ctx.stroke();
// Start Angle Line
ctx.beginPath();
ctx.moveTo(cx, cy);
var sx = cx + rx * Math.cos(startAngle * Math.PI / 180);
var sy = cy + ry * Math.sin(startAngle * Math.PI / 180);
ctx.lineTo(sx, sy);
ctx.strokeStyle = BLUE;
ctx.lineWidth = 2;
ctx.stroke();
// End Angle Line
ctx.beginPath();
ctx.moveTo(cx, cy);
var ex = cx + rx * Math.cos((startAngle + sweepAngle) * Math.PI / 180);
var ey = cy + ry * Math.sin((startAngle + sweepAngle) * Math.PI / 180);
ctx.lineTo(ex, ey);
ctx.strokeStyle = GRAY;
ctx.stroke();
// Labels
ctx.font = 'bold 18px Arial';
ctx.fillStyle = BLUE;
ctx.textAlign = 'left';
ctx.fillText('startAngle: ' + startAngle + '°', sx + (startAngle > 90 ? -150 : 10), sy + (sy > cy ? 20 : -10));
ctx.fillStyle = DARK_GRAY;
ctx.fillText('sweepAngle: ' + sweepAngle + '°', ex + (ex < cx ? -160 : 10), ey + (ey > cy ? 25 : -10));
ctx.font = '14px Arial';
ctx.fillStyle = GRAY;
ctx.textAlign = 'right';
ctx.fillText('0° (3 o\'clock)', cx + rx - 5, cy - 10);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## PathTween
Interpolate between two paths and store the result in a new path ID
4 Fields, total size 17 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>PathTween</td><td>Value: 158</td></tr>
<tr><td>INT</td><td>outId</td><td>The ID of the resulting interpolated path</td></tr><tr><td>INT</td><td>pathId1</td><td>The ID of the first source path</td></tr><tr><td>INT</td><td>pathId2</td><td>The ID of the second source path</td></tr><tr><td>FLOAT</td><td>tween</td><td>The interpolation factor [0..1]</td></tr></table>
### PathTween Illustration
The `PathTween` operation interpolates between two source paths (Path 1 and Path 2) based on a `tween` factor (0.0 to 1.0).
<div id="pathTweenContainer">
<canvas id="pathTweenCanvas_v1" width="500" height="300" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('pathTweenCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#BBBBBB', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.4)';
ctx.clearRect(0, 0, 500, 300);
var tween = 0.5;
function getPoints(t) {
// Circle-ish
var p1 = [ {x:100,y:150}, {x:150,y:50}, {x:250,y:50}, {x:300,y:150} ];
// Line-ish
var p2 = [ {x:100,y:250}, {x:150,y:250}, {x:250,y:250}, {x:300,y:250} ];
return p1.map((p, i) => ({
x: p.x + (p2[i].x - p.x) * t,
y: p.y + (p2[i].y - p.y) * t
}));
}
function drawPath(pts, color, width, dash) {
ctx.beginPath();
ctx.moveTo(pts[0].x, pts[0].y);
ctx.bezierCurveTo(pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
ctx.strokeStyle = color;
ctx.lineWidth = width;
if (dash) ctx.setLineDash(dash);
ctx.stroke();
ctx.setLineDash([]);
}
// Source Paths
drawPath(getPoints(0), GRAY, 2, [5, 5]);
drawPath(getPoints(1), GRAY, 2, [5, 5]);
// Tweened Path
drawPath(getPoints(tween), BLUE, 6);
ctx.font = 'bold 20px Arial';
ctx.fillStyle = GRAY;
ctx.fillText('Path 1 (tween: 0.0)', 100, 40);
ctx.fillText('Path 2 (tween: 1.0)', 100, 280);
ctx.fillStyle = BLUE;
ctx.fillText('Interpolated (tween: 0.5)', 120, 140);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## PathCreate
Start the creation of a dynamic path
3 Fields, total size 13 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>PathCreate</td><td>Value: 159</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the path to create</td></tr><tr><td>FLOAT</td><td>startX</td><td>The X coordinate of the starting point</td></tr><tr><td>FLOAT</td><td>startY</td><td>The Y coordinate of the starting point</td></tr></table>
### PathCreate Illustration
The `PathCreate` operation begins the definition of a path, followed by `PathAppend` operations to add segments like lines or curves.
<div id="pathCreateContainer">
<canvas id="pathCreateCanvas_v1" width="500" height="300" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('pathCreateCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444';
ctx.clearRect(0, 0, 500, 300);
var startX = 100, startY = 200;
var p1x = 200, p1y = 50;
var p2x = 300, p2y = 250;
var endX = 400, endY = 100;
// The Path
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(p1x, p1y);
ctx.bezierCurveTo(p2x, p2y, p2x, p2y, endX, endY);
ctx.lineWidth = 5;
ctx.strokeStyle = BLUE;
ctx.stroke();
// Start point (PathCreate)
ctx.beginPath();
ctx.arc(startX, startY, 8, 0, 2 * Math.PI);
ctx.fillStyle = DARK_GRAY;
ctx.fill();
ctx.font = 'bold 20px Arial';
ctx.fillStyle = DARK_GRAY;
ctx.fillText('PathCreate (startX, startY)', startX - 20, startY + 35);
ctx.font = '18px Arial';
ctx.fillStyle = GRAY;
ctx.fillText('followed by PathAppend segments...', 150, 280);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## PathAppend
Append segments to an existing dynamic path
3 Fields, total size 9 + null x 4 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>PathAppend</td><td>Value: 160</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the path to append to</td></tr><tr><td>INT</td><td>length</td><td>The number of elements in the path data</td></tr><tr><td>FLOAT[]</td><td>pathData</td><td>The sequence of commands and coordinates</td></tr></table>
### PathAppend Illustration
The `PathAppend` operation adds segments to an existing path.
#### LineTo
Simple straight line segment.
<div id="pathAppendLineContainer">
<canvas id="pathAppendLineCanvas" width="500" height="120" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
#### QuadraticTo
A curve with a single control point. The dashed lines show the tangents from the endpoints.
<div id="pathAppendQuadContainer">
<canvas id="pathAppendQuadCanvas" width="500" height="200" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
#### CubicTo
A curve with two control points, allowing for more complex shapes like "S" curves.
<div id="pathAppendCubicContainer">
<canvas id="pathAppendCubicCanvas" width="500" height="250" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444';
function drawPoint(ctx, x, y, label, align) {
ctx.beginPath();
ctx.arc(x, y, 5, 0, Math.PI * 2);
ctx.fillStyle = DARK_GRAY;
ctx.fill();
ctx.font = '14px Arial';
if (align === 'right') {
ctx.textAlign = 'right';
ctx.fillText(label, x - 10, y + 5);
} else {
ctx.textAlign = 'left';
ctx.fillText(label, x + 10, y + 5);
}
}
function setupLine() {
var canvas = document.getElementById('pathAppendLineCanvas');
if (!canvas) { setTimeout(setupLine, 100); return; }
var ctx = canvas.getContext('2d');
var x1 = 100, y1 = 60, x2 = 400, y2 = 60;
ctx.clearRect(0, 0, 500, 120);
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineWidth = 5;
ctx.strokeStyle = BLUE;
ctx.stroke();
drawPoint(ctx, x1, y1, 'Start', 'right');
drawPoint(ctx, x2, y2, 'End');
}
function setupQuad() {
var canvas = document.getElementById('pathAppendQuadCanvas');
if (!canvas) { setTimeout(setupQuad, 100); return; }
var ctx = canvas.getContext('2d');
var x1 = 100, y1 = 150, cx = 250, cy = 30, x2 = 400, y2 = 150;
ctx.clearRect(0, 0, 500, 200);
// Tangents
ctx.setLineDash([5, 5]);
ctx.strokeStyle = GRAY;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(x1, y1); ctx.lineTo(cx, cy); ctx.lineTo(x2, y2);
ctx.stroke();
ctx.setLineDash([]);
// Curve
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.quadraticCurveTo(cx, cy, x2, y2);
ctx.lineWidth = 5;
ctx.strokeStyle = BLUE;
ctx.stroke();
drawPoint(ctx, x1, y1, 'Start', 'right');
drawPoint(ctx, x2, y2, 'End');
drawPoint(ctx, cx, cy, 'Control Point');
}
function setupCubic() {
var canvas = document.getElementById('pathAppendCubicCanvas');
if (!canvas) { setTimeout(setupCubic, 100); return; }
var ctx = canvas.getContext('2d');
var x1 = 80, y1 = 180, c1x = 120, c1y = 20, c2x = 380, c2y = 230, x2 = 420, y2 = 70;
ctx.clearRect(0, 0, 500, 250);
// Tangents
ctx.setLineDash([5, 5]);
ctx.strokeStyle = GRAY;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(x1, y1); ctx.lineTo(c1x, c1y);
ctx.moveTo(x2, y2); ctx.lineTo(c2x, c2y);
ctx.stroke();
ctx.setLineDash([]);
// Curve
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.bezierCurveTo(c1x, c1y, c2x, c2y, x2, y2);
ctx.lineWidth = 5;
ctx.strokeStyle = BLUE;
ctx.stroke();
drawPoint(ctx, x1, y1, 'Start', 'right');
drawPoint(ctx, x2, y2, 'End');
drawPoint(ctx, c1x, c1y, 'CP1');
drawPoint(ctx, c2x, c2y, 'CP2');
}
function run() { setupLine(); setupQuad(); setupCubic(); }
if (document.readyState === 'complete') { run(); }
else { window.addEventListener('load', run); setTimeout(run, 500); }
})();
</script>
## CanvasOperations
A collection of canvas operations
0 Fields, total size 1 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>CanvasOperations</td><td>Value: 173</td></tr>
</table>
## PathCombine
Combine two paths using a boolean operation (Union, Intersect, etc.)
4 Fields, total size 14 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>PathCombine</td><td>Value: 175</td></tr>
<tr><td>INT</td><td>outId</td><td>The ID of the resulting path</td></tr><tr><td>INT</td><td>pathId1</td><td>The ID of the first source path</td></tr><tr><td>INT</td><td>pathId2</td><td>The ID of the second source path</td></tr><tr><td>BYTE</td><td>operation</td><td>The boolean operation to perform</td></tr></table>
### operation
| Name | Value |
| ---- | ---- |
| OP_DIFFERENCE | 0
| OP_INTERSECT | 1
| OP_REVERSE_DIFFERENCE | 2
| OP_UNION | 3
| OP_XOR | 4
### PathCombine Illustration
The `PathCombine` operation performs boolean logic between two paths to create a new complex shape.
<div id="pathCombineContainer">
<canvas id="pathCombineCanvas_v1" width="500" height="220" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
var phase = 0;
function draw() {
var canvas = document.getElementById('pathCombineCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#BBBBBB', DARK_GRAY = '#444444';
ctx.clearRect(0, 0, 500, 220);
// Offset between circles oscillates
var circleOffset = 20 + (Math.sin(phase) + 1) / 2 * 40;
phase += 0.03;
function drawCircles(x, label, mode) {
ctx.save();
ctx.translate(x, 60);
var r = 40;
var c1x = 40 - circleOffset/2;
var c2x = 40 + circleOffset/2;
// Combined Result (Logic)
ctx.save();
if (mode === 'INTERSECT') {
ctx.beginPath(); ctx.arc(c1x, 40, r, 0, Math.PI*2); ctx.clip();
ctx.beginPath(); ctx.arc(c2x, 40, r, 0, Math.PI*2);
ctx.fillStyle = BLUE; ctx.fill();
} else if (mode === 'UNION') {
ctx.beginPath(); ctx.arc(c1x, 40, r, 0, Math.PI*2);
ctx.arc(c2x, 40, r, 0, Math.PI*2);
ctx.fillStyle = BLUE; ctx.fill();
} else if (mode === 'DIFFERENCE') {
ctx.beginPath(); ctx.arc(c1x, 40, r, 0, Math.PI*2);
ctx.fillStyle = BLUE; ctx.fill();
ctx.globalCompositeOperation = 'destination-out';
ctx.beginPath(); ctx.arc(c2x, 40, r, 0, Math.PI*2); ctx.fill();
}
ctx.restore();
// Overlays (Original Outlines)
ctx.strokeStyle = '#eee';
ctx.lineWidth = 1;
ctx.setLineDash([2, 2]);
ctx.beginPath(); ctx.arc(c1x, 40, r, 0, Math.PI*2); ctx.stroke();
ctx.beginPath(); ctx.arc(c2x, 40, r, 0, Math.PI*2); ctx.stroke();
ctx.setLineDash([]);
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 18px Arial';
ctx.textAlign = 'center';
ctx.fillText(label, 40, 130);
ctx.restore();
}
drawCircles(40, 'UNION', 'UNION');
drawCircles(190, 'INTERSECT', 'INTERSECT');
drawCircles(340, 'DIFFERENCE', 'DIFFERENCE');
requestAnimationFrame(draw);
}
draw();
})();
</script>
## DrawToBitmap (added in v7)
Draw to a bitmap
3 Fields, total size 13 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawToBitmap</td><td>Value: 190</td></tr>
<tr><td>INT</td><td>bitmapId</td><td>The bitmap to draw to or 0 to draw to the canvas</td></tr><tr><td>INT</td><td>mode</td><td>Flags to support configuration of bitmap</td></tr><tr><td>INT</td><td>color</td><td>set the initial of the bitmap</td></tr></table>
# Text Operations
Operations for defining, manipulating, measuring, and rendering text and font data. This includes support for static strings, dynamic formatting, text-based expressions, spatial measurement of text
bounds, and high-fidelity rendering using both standard and bitmap fonts across various canvas geometries.
Operations in this category:
| ID | Name | Version | Size (bytes)
| ---- | ---- | ---- | ---- |
| 43 | DrawText | v6 | 29
| 48 | DrawBitmapFontText | v6 | 25
| 49 | DrawBitmapFontTextOnPath | v7 | 25
| 53 | DrawTextOnPath | v6 | 17
| 57 | DrawTextOnCircle | v6 | 33
| 102 | TextData | v6 | 5
| 133 | DrawTextAnchored | v6 | 25
| 135 | TextFromFloat | v6 | 17
| 136 | TextMerge | v6 | 13
| 151 | TextFromFloat | v6 | 13
| 153 | TextLookupInt | v6 | 13
| 155 | TextMeasure | v6 | 13
| 156 | TextLength | v6 | 9
| 167 | BitmapFontData | v6 | 29
| 170 | TextMeasure | v6 | 13
| 182 | TextSubtext | v7 | 17
| 183 | BitmapTextMeasure | v7 | 21
| 184 | DrawBitmapTextAnchored | v7 | 33
| 199 | TextTransform | v7 | 21
| 208 | TextLayout | v6 | 45
## DrawText
Draw a run of text, all in a single direction
8 Fields, total size 29 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawText</td><td>Value: 43</td></tr>
<tr><td>INT</td><td>textId</td><td>The ID of the text to render</td></tr><tr><td>INT</td><td>start</td><td>The start index of the text to render</td></tr><tr><td>INT</td><td>end</td><td>The end index of the text to render</td></tr><tr><td>INT</td><td>contextStart</td><td>The index of the start of the shaping context</td></tr><tr><td>INT</td><td>contextEnd</td><td>The index of the end of the shaping context</td></tr><tr><td>FLOAT</td><td>x</td><td>The x position at which to draw the text</td></tr><tr><td>FLOAT</td><td>y</td><td>The y position at which to draw the text</td></tr><tr><td>BOOLEAN</td><td>rtl</td><td>Whether the run is in RTL direction</td></tr></table>
### DrawText Illustration
The `DrawText` operation renders a run of text at a specific `(x, y)` coordinate.
<div id="drawTextContainer">
<canvas id="drawTextCanvas_v1" width="500" height="250" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('drawTextCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444';
ctx.clearRect(0, 0, 500, 250);
var x = 50, y = 150;
ctx.font = '60px Arial';
ctx.fillStyle = BLUE;
ctx.fillText('Hello World', x, y);
// Anchor
ctx.beginPath();
ctx.arc(x, y, 7, 0, 2 * Math.PI);
ctx.fillStyle = DARK_GRAY;
ctx.fill();
ctx.font = 'bold 22px Arial';
ctx.fillStyle = DARK_GRAY;
ctx.fillText('Origin (x, y)', x, y + 50);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## DrawBitmapFontText
Draw text using a bitmap font
6 Fields, total size 25 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawBitmapFontText</td><td>Value: 48</td></tr>
<tr><td>INT</td><td>textId</td><td>The ID of the text to render</td></tr><tr><td>INT</td><td>bitmapFontId</td><td>The ID of the bitmap font</td></tr><tr><td>INT</td><td>start</td><td>The start index of the text to render</td></tr><tr><td>INT</td><td>end</td><td>The end index of the text to render</td></tr><tr><td>FLOAT</td><td>x</td><td>The x position at which to draw the text</td></tr><tr><td>FLOAT</td><td>y</td><td>The y position at which to draw the text</td></tr></table>
## DrawBitmapFontTextOnPath (added in v7)
Draw text using a bitmap font along a path
6 Fields, total size 25 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawBitmapFontTextOnPath</td><td>Value: 49</td></tr>
<tr><td>INT</td><td>textId</td><td>The ID of the text to render</td></tr><tr><td>INT</td><td>bitmapFontId</td><td>The ID of the bitmap font</td></tr><tr><td>INT</td><td>pathId</td><td>The ID of the path to follow</td></tr><tr><td>INT</td><td>start</td><td>The start index of the text to render</td></tr><tr><td>INT</td><td>end</td><td>The end index of the text to render</td></tr><tr><td>FLOAT</td><td>yAdj</td><td>Vertical adjustment relative to the path</td></tr></table>
## DrawTextOnPath
Draw text along a path
4 Fields, total size 17 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawTextOnPath</td><td>Value: 53</td></tr>
<tr><td>INT</td><td>textId</td><td>The ID of the text</td></tr><tr><td>INT</td><td>pathId</td><td>The ID of the path</td></tr><tr><td>FLOAT</td><td>hOffset</td><td>Horizontal offset along the path</td></tr><tr><td>FLOAT</td><td>vOffset</td><td>Vertical offset relative to the path</td></tr></table>
### DrawTextOnPath Illustration
The `DrawTextOnPath` operation renders text along a specified path. The text follows the curve, maintaining its orientation relative to the path's tangent.
<div id="drawTextOnPathContainer">
<canvas id="drawTextOnPathCanvas_v1" width="500" height="250" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('drawTextOnPathCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444';
ctx.clearRect(0, 0, 500, 250);
var p0 = {x: 60, y: 180};
var p1 = {x: 120, y: 60};
var p2 = {x: 380, y: 300};
var p3 = {x: 440, y: 180};
// Reference Path
ctx.beginPath();
ctx.moveTo(p0.x, p0.y);
ctx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
ctx.strokeStyle = '#eee';
ctx.lineWidth = 2;
ctx.setLineDash([12, 12]);
ctx.stroke();
ctx.setLineDash([]);
var text = "RemoteCompose Text Following Curve";
ctx.font = '24px Arial';
ctx.fillStyle = BLUE;
ctx.textAlign = 'center';
function getBezierPoint(t) {
var cx = 3 * (p1.x - p0.x);
var bx = 3 * (p2.x - p1.x) - cx;
var ax = p3.x - p0.x - cx - bx;
var cy = 3 * (p1.y - p0.y);
var by = 3 * (p2.y - p1.y) - cy;
var ay = p3.y - p0.y - cy - by;
var x = (ax * Math.pow(t, 3)) + (bx * Math.pow(t, 2)) + (cx * t) + p0.x;
var y = (ay * Math.pow(t, 3)) + (by * Math.pow(t, 2)) + (cy * t) + p0.y;
return {x: x, y: y};
}
function getBezierTangent(t) {
var cx = 3 * (p1.x - p0.x);
var bx = 3 * (p2.x - p1.x) - cx;
var ax = p3.x - p0.x - cx - bx;
var cy = 3 * (p1.y - p0.y);
var by = 3 * (p2.y - p1.y) - cy;
var ay = p3.y - p0.y - cy - by;
var dx = (3 * ax * Math.pow(t, 2)) + (2 * bx * t) + cx;
var dy = (3 * ay * Math.pow(t, 2)) + (2 * by * t) + cy;
return Math.atan2(dy, dx);
}
var chars = text.split("");
var step = 0.8 / chars.length;
for (var i = 0; i < chars.length; i++) {
var t = 0.1 + (i * step);
var pt = getBezierPoint(t);
var angle = getBezierTangent(t);
ctx.save();
ctx.translate(pt.x, pt.y);
ctx.rotate(angle);
ctx.fillText(chars[i], 0, -8);
ctx.restore();
}
ctx.font = 'bold 20px Arial';
ctx.fillStyle = DARK_GRAY;
ctx.fillText('Path Tangent Alignment', 120, 30);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## DrawTextOnCircle
Draw text along a circle
8 Fields, total size 33 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawTextOnCircle</td><td>Value: 57</td></tr>
<tr><td>INT</td><td>textId</td><td>The ID of the text</td></tr><tr><td>FLOAT</td><td>centerX</td><td>The x coordinate of the center</td></tr><tr><td>FLOAT</td><td>centerY</td><td>The y coordinate of the center</td></tr><tr><td>FLOAT</td><td>radius</td><td>The radius of the circle</td></tr><tr><td>FLOAT</td><td>startAngle</td><td>The start angle in degrees</td></tr><tr><td>FLOAT</td><td>warpRadiusOffset</td><td>The warp radius offset</td></tr><tr><td>INT</td><td>alignment</td><td>The alignment of the text</td></tr><tr><td>INT</td><td>placement</td><td>The placement of the text</td></tr></table>
### alignment
| Name | Value |
| ---- | ---- |
| START | 0
| CENTER | 1
| END | 2
### placement
| Name | Value |
| ---- | ---- |
| OUTSIDE | 0
| INSIDE | 1
### DrawTextOnCircle Illustration
The `DrawTextOnCircle` operation renders curved text along the circumference of a circle.
<div id="drawTextOnCircleContainer">
<canvas id="drawTextOnCircleCanvas_v1" width="500" height="500" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('drawTextOnCircleCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444';
ctx.clearRect(0, 0, 500, 500);
var cx = 250, cy = 250, r = 160;
var text = "RemoteCompose Curved Text Illustration";
// Circle Guide
ctx.strokeStyle = '#eee';
ctx.setLineDash([12, 12]);
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(cx, cy, r, 0, 2 * Math.PI);
ctx.stroke();
ctx.setLineDash([]);
ctx.font = '32px Arial';
ctx.fillStyle = BLUE;
ctx.textAlign = 'center';
var characters = text.split("");
var anglePerChar = (Math.PI * 1.5) / characters.length;
var startAngle = -Math.PI * 0.75;
for (var i = 0; i < characters.length; i++) {
ctx.save();
ctx.translate(cx, cy);
var currentAngle = startAngle + i * anglePerChar;
ctx.rotate(currentAngle);
ctx.fillText(characters[i], 0, -r);
ctx.restore();
}
// Center Anchor
ctx.beginPath();
ctx.arc(cx, cy, 6, 0, 2 * Math.PI);
ctx.fillStyle = DARK_GRAY;
ctx.fill();
ctx.font = 'bold 22px Arial';
ctx.fillStyle = DARK_GRAY;
ctx.fillText('Center (cx, cy)', cx + 15, cy - 15);
// Radius indicator
ctx.strokeStyle = BLUE;
ctx.setLineDash([5, 5]);
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.lineTo(cx + r, cy);
ctx.stroke();
ctx.fillStyle = BLUE;
ctx.fillText('Radius', cx + r/2, cy + 25);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## TextData
Define a static string and associate it with an ID
2 Fields, total size 5 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>TextData</td><td>Value: 102</td></tr>
<tr><td>INT</td><td>textId</td><td>The ID of the text</td></tr><tr><td>UTF8</td><td>text</td><td>The string value</td></tr></table>
## DrawTextAnchored
Draw text centered about an anchor point
6 Fields, total size 25 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawTextAnchored</td><td>Value: 133</td></tr>
<tr><td>INT</td><td>textId</td><td>The ID of the text to render</td></tr><tr><td>FLOAT</td><td>x</td><td>The x-position of the anchor point</td></tr><tr><td>FLOAT</td><td>y</td><td>The y-position of the anchor point</td></tr><tr><td>FLOAT</td><td>panX</td><td>The horizontal pan from left(-1) to right(1), 0 being centered</td></tr><tr><td>FLOAT</td><td>panY</td><td>The vertical pan from top(-1) to bottom(1), 0 being centered</td></tr><tr><td>INT</td><td>flags</td><td>Behavior flags</td></tr></table>
### flags
| Name | Value |
| ---- | ---- |
| ANCHOR_TEXT_RTL | 1
| ANCHOR_MONOSPACE_MEASURE | 2
| MEASURE_EVERY_TIME | 4
| BASELINE_RELATIVE | 8
### DrawTextAnchored Illustration
The `DrawTextAnchored` operation renders text relative to an anchor point `(x, y)` using horizontal (`panX`) and vertical (`panY`) values ranging from -1.0 to 1.0.
#### Center Aligned (`panX: 0, panY: 0`)
<div id="drawTextAnchoredContainer_center">
<canvas id="drawTextAnchoredCanvas_center" width="500" height="160" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
#### Top-Left Aligned (`panX: -1, panY: -1`)
<div id="drawTextAnchoredContainer_tl">
<canvas id="drawTextAnchoredCanvas_tl" width="500" height="160" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
#### Bottom-Right Aligned (`panX: 1, panY: 1`)
<div id="drawTextAnchoredContainer_br">
<canvas id="drawTextAnchoredCanvas_br" width="500" height="160" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function setupCanvas(canvasId, panX, panY, anchorLabelYOffset) {
function draw() {
var canvas = document.getElementById(canvasId);
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#BBBBBB', DARK_GRAY = '#444444';
var ax = 250, ay = 80;
var text = "Anchored Text";
ctx.font = '32px Arial';
ctx.textBaseline = 'top';
var metrics = ctx.measureText(text);
var tw = metrics.width;
var th = 32;
ctx.clearRect(0, 0, 500, 160);
var dx = (-(panX + 1) / 2) * tw;
var dy = (-(panY + 1) / 2) * th;
ctx.strokeStyle = '#ddd';
ctx.lineWidth = 1;
ctx.strokeRect(ax + dx, ay + dy, tw, th);
ctx.fillStyle = BLUE;
ctx.fillText(text, ax + dx, ay + dy);
ctx.beginPath();
ctx.arc(ax, ay, 6, 0, 2 * Math.PI);
ctx.fillStyle = DARK_GRAY;
ctx.fill();
ctx.font = 'bold 18px Arial';
ctx.fillStyle = DARK_GRAY;
ctx.textAlign = 'left';
var ayOff = anchorLabelYOffset || -15;
ctx.fillText('Anchor (x, y)', ax + 15, ay + ayOff);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
}
setupCanvas('drawTextAnchoredCanvas_center', 0, 0, -35);
setupCanvas('drawTextAnchoredCanvas_tl', -1, -1, -25);
setupCanvas('drawTextAnchoredCanvas_br', 1, 1, -15);
})();
</script>
## TextFromFloat
Convert a float value into a formatted string
5 Fields, total size 17 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>TextFromFloat</td><td>Value: 135</td></tr>
<tr><td>INT</td><td>textId</td><td>The ID of the resulting text</td></tr><tr><td>FLOAT</td><td>value</td><td>The float value to convert</td></tr><tr><td>SHORT</td><td>digitsBefore</td><td>Number of digits before the decimal point</td></tr><tr><td>SHORT</td><td>digitsAfter</td><td>Number of digits after the decimal point</td></tr><tr><td>INT</td><td>flags</td><td>Formatting and padding flags</td></tr></table>
## TextMerge
Merge two strings into one
3 Fields, total size 13 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>TextMerge</td><td>Value: 136</td></tr>
<tr><td>INT</td><td>textId</td><td>The ID of the resulting text</td></tr><tr><td>INT</td><td>srcId1</td><td>The ID of the first source string</td></tr><tr><td>INT</td><td>srcId2</td><td>The ID of the second source string</td></tr></table>
## TextFromFloat
Look up a string from a collection via index
3 Fields, total size 13 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>TextFromFloat</td><td>Value: 151</td></tr>
<tr><td>INT</td><td>textId</td><td>The ID of the resulting text</td></tr><tr><td>INT</td><td>dataSetId</td><td>The ID of the string collection</td></tr><tr><td>FLOAT</td><td>index</td><td>The index of the string to retrieve</td></tr></table>
## TextLookupInt
Look up a string from a collection via an integer index variable
3 Fields, total size 13 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>TextLookupInt</td><td>Value: 153</td></tr>
<tr><td>INT</td><td>textId</td><td>The ID of the resulting text</td></tr><tr><td>INT</td><td>dataSetId</td><td>The ID of the string collection</td></tr><tr><td>INT</td><td>indexId</td><td>The ID of the integer index variable</td></tr></table>
## TextMeasure
Measure text dimensions and store the result in a float variable
3 Fields, total size 13 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>TextMeasure</td><td>Value: 155</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the float variable to store the result</td></tr><tr><td>INT</td><td>textId</td><td>The ID of the text to measure</td></tr><tr><td>INT</td><td>type</td><td>The type of measurement (WIDTH, HEIGHT, etc.)</td></tr></table>
### type
| Name | Value |
| ---- | ---- |
| MEASURE_WIDTH | 0
| MEASURE_HEIGHT | 1
| MEASURE_LEFT | 2
| MEASURE_RIGHT | 3
| MEASURE_TOP | 4
| MEASURE_BOTTOM | 5
### TextMeasure Illustration
The `TextMeasure` operation calculates dimensions of a text string, such as its width, height, or relative positions like the baseline.
<div id="textMeasureContainer">
<canvas id="textMeasureCanvas_v1" width="600" height="220" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('textMeasureCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#BBBBBB', DARK_GRAY = '#444444';
ctx.clearRect(0, 0, 600, 220);
var x=60, y=110;
var text = "Typography";
ctx.font = '80px serif';
ctx.textAlign = 'left';
ctx.textBaseline = 'alphabetic';
// Draw Text
ctx.fillStyle = BLUE;
ctx.fillText(text, x, y);
// Metrics lines
var metrics = ctx.measureText(text);
var w = metrics.width;
var ascent = 60; // approx
var descent = 20; // approx
// Baseline
ctx.strokeStyle = DARK_GRAY;
ctx.lineWidth = 2;
ctx.beginPath(); ctx.moveTo(x - 20, y); ctx.lineTo(x + w + 20, y); ctx.stroke();
// Ascent
ctx.strokeStyle = GRAY;
ctx.setLineDash([5, 5]);
ctx.beginPath(); ctx.moveTo(x - 20, y - ascent); ctx.lineTo(x + w + 20, y - ascent); ctx.stroke();
// Descent
ctx.beginPath(); ctx.moveTo(x - 20, y + descent); ctx.lineTo(x + w + 20, y + descent); ctx.stroke();
ctx.setLineDash([]);
// Labels
ctx.font = 'bold 18px Arial';
ctx.textAlign = 'left';
ctx.fillStyle = DARK_GRAY;
ctx.fillText('Baseline', x + w + 25, y + 5);
ctx.fillStyle = GRAY;
ctx.fillText('Ascent', x + w + 25, y - ascent + 5);
ctx.fillText('Descent', x + w + 25, y + descent + 5);
// Width dimension
var dimY = y + 50;
ctx.strokeStyle = DARK_GRAY;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(x, dimY); ctx.lineTo(x + w, dimY);
ctx.moveTo(x, dimY - 5); ctx.lineTo(x, dimY + 5);
ctx.moveTo(x + w, dimY - 5); ctx.lineTo(x + w, dimY + 5);
ctx.stroke();
ctx.textAlign = 'center';
ctx.fillStyle = DARK_GRAY;
ctx.fillText('Width: ' + Math.round(w) + 'px', x + w/2, dimY + 25);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## TextLength
Get the length of a string and store it in a float variable
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>TextLength</td><td>Value: 156</td></tr>
<tr><td>INT</td><td>lengthId</td><td>The ID of the float variable to store the length</td></tr><tr><td>INT</td><td>textId</td><td>The ID of the text to measure</td></tr></table>
## BitmapFontData
Define a bitmap font with glyph metadata and optional kerning
13 Fields, total size 29 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>BitmapFontData</td><td>Value: 167</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the bitmap font</td></tr><tr><td>INT</td><td>versionAndNumGlyphs</td><td>Encoded version and number of glyphs</td></tr><tr><td>UTF8</td><td>chars[0..n]</td><td>The characters for each glyph</td></tr><tr><td>INT</td><td>bitmapId[0..n]</td><td>The bitmap ID for each glyph</td></tr><tr><td>SHORT</td><td>marginLeft[0..n]</td><td>Left margin for each glyph</td></tr><tr><td>SHORT</td><td>marginTop[0..n]</td><td>Top margin for each glyph</td></tr><tr><td>SHORT</td><td>marginRight[0..n]</td><td>Right margin for each glyph</td></tr><tr><td>SHORT</td><td>marginBottom[0..n]</td><td>Bottom margin for each glyph</td></tr><tr><td>SHORT</td><td>width[0..n]</td><td>Width for each glyph</td></tr><tr><td>SHORT</td><td>height[0..n]</td><td>Height for each glyph</td></tr><tr><td>SHORT</td><td>kerningSize</td><td>Number of entries in the kerning table</td></tr><tr><td>UTF8</td><td>glyphPair[0..n]</td><td>Glyph pair for kerning</td></tr><tr><td>SHORT</td><td>adjustment[0..n]</td><td>Horizontal adjustment for kerning pair</td></tr></table>
## TextMeasure
Extract text-related properties (width, length, etc.)
4 Fields, total size 13 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>TextMeasure</td><td>Value: 170</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the float variable to store the result</td></tr><tr><td>INT</td><td>textId</td><td>The ID of the text variable to measure</td></tr><tr><td>SHORT</td><td>type</td><td>The type of property to extract</td></tr><tr><td>SHORT</td><td>unused</td><td>unused field</td></tr></table>
## TextSubtext (added in v7)
Extract a substring from a source string
4 Fields, total size 17 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>TextSubtext</td><td>Value: 182</td></tr>
<tr><td>INT</td><td>textId</td><td>The ID of the resulting substring</td></tr><tr><td>INT</td><td>srcId1</td><td>The ID of the source string</td></tr><tr><td>FLOAT</td><td>start</td><td>The start index of the substring</td></tr><tr><td>FLOAT</td><td>len</td><td>The length of the substring (or -1 for remainder)</td></tr></table>
## BitmapTextMeasure (added in v7)
Measure text dimensions specifically for bitmap fonts
5 Fields, total size 21 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>BitmapTextMeasure</td><td>Value: 183</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the float variable to store the result</td></tr><tr><td>INT</td><td>textId</td><td>The ID of the text to measure</td></tr><tr><td>INT</td><td>bitmapFontId</td><td>The ID of the bitmap font</td></tr><tr><td>INT</td><td>type</td><td>The type of measurement (WIDTH, HEIGHT, etc.)</td></tr><tr><td>FLOAT</td><td>glyphSpacing</td><td>Horizontal spacing adjustment between glyphs in pixels</td></tr></table>
## DrawBitmapTextAnchored (added in v7)
Draw bitmap font text anchored to a point with alignment (pan)
8 Fields, total size 33 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawBitmapTextAnchored</td><td>Value: 184</td></tr>
<tr><td>INT</td><td>textId</td><td>The ID of the text to render</td></tr><tr><td>INT</td><td>bitmapFontId</td><td>The ID of the bitmap font</td></tr><tr><td>FLOAT</td><td>start</td><td>The start index of the text to render</td></tr><tr><td>FLOAT</td><td>end</td><td>The end index of the text to render</td></tr><tr><td>FLOAT</td><td>x</td><td>The x-position of the anchor point</td></tr><tr><td>FLOAT</td><td>y</td><td>The y-position of the anchor point</td></tr><tr><td>FLOAT</td><td>panX</td><td>The horizontal pan from left(-1) to right(1), 0 being centered</td></tr><tr><td>FLOAT</td><td>panY</td><td>The vertical pan from top(-1) to bottom(1), 0 being centered</td></tr></table>
## TextTransform [EXPERIMENTAL] (added in v7)
!!! WARNING
Experimental operation
Transform a string (case conversion, trimming, etc.)
5 Fields, total size 21 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>TextTransform</td><td>Value: 199</td></tr>
<tr><td>INT</td><td>textId</td><td>The ID of the resulting transformed text</td></tr><tr><td>INT</td><td>srcId1</td><td>The ID of the source string</td></tr><tr><td>FLOAT</td><td>start</td><td>The start index of the transformation range</td></tr><tr><td>FLOAT</td><td>len</td><td>The length of the transformation range</td></tr><tr><td>INT</td><td>operation</td><td>The type of transformation to apply</td></tr></table>
### operation
| Name | Value |
| ---- | ---- |
| TEXT_TO_LOWERCASE | 1
| TEXT_TO_UPPERCASE | 2
| TEXT_TRIM | 3
| TEXT_CAPITALIZE | 4
| TEXT_UPPERCASE_FIRST_CHAR | 5
## TextLayout
Text layout implementation
11 Fields, total size 45 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>TextLayout</td><td>Value: 208</td></tr>
<tr><td>INT</td><td>componentId</td><td>Unique ID for this component</td></tr><tr><td>INT</td><td>animationId</td><td>ID used to match components for animation purposes</td></tr><tr><td>INT</td><td>textId</td><td>The ID of the text to display</td></tr><tr><td>INT</td><td>color</td><td>The text color (ARGB)</td></tr><tr><td>FLOAT</td><td>fontSize</td><td>The font size in pixels</td></tr><tr><td>INT</td><td>fontStyle</td><td>The font style (0=normal, 1=italic)</td></tr><tr><td>FLOAT</td><td>fontWeight</td><td>The font weight [1..1000]</td></tr><tr><td>INT</td><td>fontFamilyId</td><td>The ID of the font family name string</td></tr><tr><td>INT</td><td>textAlign</td><td>The text alignment and flags</td></tr><tr><td>INT</td><td>overflow</td><td>The overflow strategy</td></tr><tr><td>INT</td><td>maxLines</td><td>The maximum number of lines</td></tr></table>
# Layout Operations
High-level structural components (Box, Row, Column, etc.) used to build the UI hierarchy.
Operations in this category:
| ID | Name | Version | Size (bytes)
| ---- | ---- | ---- | ---- |
| 2 | ComponentStart | v6 | 17
| 200 | RootLayout | v6 | 5
| 201 | LayoutContent | v6 | 5
| 209 | HostAction | v6 | 5
| 210 | HostNamedAction | v6 | 9
| 216 | HostActionMetadata | v6 | 9
| 217 | StateLayout | v6 | 21
## ComponentStart
Basic component encapsulating draw commands. This is not resizable.
4 Fields, total size 17 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ComponentStart</td><td>Value: 2</td></tr>
<tr><td>INT</td><td>type</td><td>Type of component</td></tr><tr><td>INT</td><td>componentId</td><td>Unique ID for this component</td></tr><tr><td>FLOAT</td><td>width</td><td>Width of the component</td></tr><tr><td>FLOAT</td><td>height</td><td>Height of the component</td></tr></table>
## RootLayout
Root element for a document. Other components / layout managers are children in the component tree starting from this Root component.
1 Fields, total size 5 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>RootLayout</td><td>Value: 200</td></tr>
<tr><td>INT</td><td>componentId</td><td>Unique ID for this component</td></tr></table>
## LayoutContent
Container for child components. BoxLayout, RowLayout and ColumnLayout expect a LayoutComponentContent as a child, encapsulating the components that need to be laid out.
1 Fields, total size 5 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>LayoutContent</td><td>Value: 201</td></tr>
<tr><td>INT</td><td>componentId</td><td>Unique ID for this component</td></tr></table>
## HostAction
Host action. This operation represents a host action
1 Fields, total size 5 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>HostAction</td><td>Value: 209</td></tr>
<tr><td>INT</td><td>ACTION_ID</td><td>Host Action ID</td></tr></table>
## HostNamedAction
Host Named action. This operation represents a host action
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>HostNamedAction</td><td>Value: 210</td></tr>
<tr><td>INT</td><td>TEXT_ID</td><td>Named Host Action Text ID</td></tr><tr><td>INT</td><td>VALUE_ID</td><td>Named Host Action Value ID</td></tr></table>
## HostActionMetadata
Host action + metadata. This operation represents a host action that can also provides some metadata
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>HostActionMetadata</td><td>Value: 216</td></tr>
<tr><td>INT</td><td>ACTION_ID</td><td>Host Action ID</td></tr><tr><td>INT</td><td>METADATA</td><td>Host Action Text Metadata ID</td></tr></table>
## StateLayout
A layout that switches between child layouts based on an index
5 Fields, total size 21 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>StateLayout</td><td>Value: 217</td></tr>
<tr><td>INT</td><td>componentId</td><td>Unique ID for this component</td></tr><tr><td>INT</td><td>animationId</td><td>ID for animation purposes</td></tr><tr><td>INT</td><td>horizontalPositioning</td><td>Horizontal positioning value</td></tr><tr><td>INT</td><td>verticalPositioning</td><td>Vertical positioning value</td></tr><tr><td>INT</td><td>indexId</td><td>The ID of the variable providing the current state index</td></tr></table>
# Layout Managers
Higher-level components that manage the positioning and sizing of their children according to specific algorithms (Row, Column, Box, etc.).
Operations in this category:
| ID | Name | Version | Size (bytes)
| ---- | ---- | ---- | ---- |
| 176 | FitBoxLayout | v6 | 17
| 202 | BoxLayout | v6 | 17
| 203 | RowLayout | v6 | 21
| 204 | ColumnLayout | v6 | 21
| 205 | CanvasLayout | v6 | 9
| 230 | CollapsibleRow | v6 | 21
| 233 | CollapsibleColumn | v6 | 21
| 234 | ImageLayout | v6 | 21
| 239 | CoreText | v7 | 85 + null x 4 + null x 4
| 240 | FlowLayout | v7 | 21
## FitBoxLayout
FitBox layout implementation.
Only displays the first child component that fits in the available space.
4 Fields, total size 17 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>FitBoxLayout</td><td>Value: 176</td></tr>
<tr><td>INT</td><td>componentId</td><td>Unique ID for this component</td></tr><tr><td>INT</td><td>animationId</td><td>ID used to match components for animation purposes</td></tr><tr><td>INT</td><td>horizontalPositioning</td><td>Horizontal positioning value</td></tr><tr><td>INT</td><td>verticalPositioning</td><td>Vertical positioning value</td></tr></table>
### horizontalPositioning
| Name | Value |
| ---- | ---- |
| START | 1
| CENTER | 2
| END | 3
### verticalPositioning
| Name | Value |
| ---- | ---- |
| TOP | 4
| CENTER | 2
| BOTTOM | 5
### FitBox Layout Algorithm
FitBox is a specialized container that only renders the **first** child component that completely fits within the available space.
#### Visual Illustration (Animated Fitting Logic)
<div id="fitboxLayoutContainer">
<canvas id="fitboxLayoutCanvas_v1" width="500" height="300" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
var phase = 0;
function draw() {
var canvas = document.getElementById('fitboxLayoutCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#BBBBBB', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
ctx.clearRect(0, 0, 500, 300);
// Container size oscillates
var baseW = 350, baseH = 180;
var minW = 80, minH = 40;
var containerW = minW + (Math.sin(phase) + 1) / 2 * (baseW - minW);
var containerH = minH + (Math.sin(phase) + 1) / 2 * (baseH - minH);
phase += 0.015;
var startX = 25, startY = 80;
// Available Space
ctx.strokeStyle = DARK_GRAY;
ctx.setLineDash([5, 5]);
ctx.strokeRect(startX, startY, containerW, containerH);
ctx.setLineDash([]);
ctx.font = 'bold 16px Arial';
ctx.fillStyle = DARK_GRAY;
ctx.fillText('FitBox Size: ' + Math.round(containerW) + 'x' + Math.round(containerH), startX, startY - 15);
// Define 3 potential children with different sizes
var children = [
{ id: 1, w: 250, h: 120, label: 'Child 1 (Large)' },
{ id: 2, w: 150, h: 80, label: 'Child 2 (Medium)' },
{ id: 3, w: 60, h: 30, label: 'Child 3 (Small)' }
];
var selectedIdx = -1;
for (var i = 0; i < children.length; i++) {
if (children[i].w <= containerW && children[i].h <= containerH) {
selectedIdx = i;
break;
}
}
// Draw the selection indicator
ctx.fillStyle = BLUE;
ctx.font = 'bold 18px Arial';
ctx.textAlign = 'left';
if (selectedIdx !== -1) {
ctx.fillText('Selected: ' + children[selectedIdx].label, startX + 250, 40);
// Draw the selected child inside the container
ctx.fillStyle = 'rgba(0, 71, 171, 0.5)';
ctx.fillRect(startX, startY, children[selectedIdx].w, children[selectedIdx].h);
ctx.strokeStyle = BLUE;
ctx.lineWidth = 3;
ctx.strokeRect(startX, startY, children[selectedIdx].w, children[selectedIdx].h);
// Draw big white number in center
ctx.fillStyle = 'white';
ctx.font = 'bold 50px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(children[selectedIdx].id, startX + children[selectedIdx].w / 2, startY + children[selectedIdx].h / 2);
} else {
ctx.fillStyle = 'red';
ctx.fillText('Selected: NONE (Too small)', startX + 250, 40);
}
// Draw the list of candidates
ctx.textAlign = 'left';
ctx.font = '14px Arial';
for (var i = 0; i < children.length; i++) {
var active = (i === selectedIdx);
var tooBig = (children[i].w > containerW || children[i].h > containerH);
ctx.fillStyle = active ? BLUE : (tooBig ? '#ccc' : DARK_GRAY);
var prefix = active ? 'â–¶ ' : ' ';
ctx.fillText(prefix + children[i].label + ' [' + children[i].w + 'x' + children[i].h + ']', 30, 230 + i * 20);
}
requestAnimationFrame(draw);
}
draw();
})();
</script>
#### Measurement and Selection
1. **Fitting Logic**:
- The layout iterates through its children in order.
- For each child, it checks its minimum required dimensions (defined by `WidthIn` or `HeightIn` modifiers).
- The first child whose minimum width and height are less than or equal to the FitBox's available constraints is selected.
2. **Visibility Control**:
- The selected child is marked as `VISIBLE`.
- All other children are marked as `GONE`.
- If no child fits, the FitBox itself may become `GONE`.
3. **Sizing**:
- The FitBox takes the measured size of the single selected child.
## BoxLayout
Box layout implementation.
Child components are laid out independently from one another,
and painted in their hierarchy order (first children drawnbefore the latter). Horizontal and Vertical positioningare supported.
4 Fields, total size 17 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>BoxLayout</td><td>Value: 202</td></tr>
<tr><td>INT</td><td>COMPONENT_ID</td><td>unique id for this component</td></tr><tr><td>INT</td><td>ANIMATION_ID</td><td>id used to match components, for animation purposes</td></tr><tr><td>INT</td><td>HORIZONTAL_POSITIONING</td><td>horizontal positioning value</td></tr><tr><td>INT</td><td>VERTICAL_POSITIONING</td><td>vertical positioning value</td></tr></table>
### HORIZONTAL_POSITIONING
| Name | Value |
| ---- | ---- |
| START | 1
| CENTER | 2
| END | 3
### VERTICAL_POSITIONING
| Name | Value |
| ---- | ---- |
| TOP | 4
| CENTER | 2
| BOTTOM | 5
### Box Layout Algorithm
The Box Layout manager positions child components independently within its own bounds. It is the most basic container, allowing components to be stacked or positioned relative to the Box's edges.
#### Visual Illustration (Alignment)
<div id="boxLayoutContainer">
<canvas id="boxLayoutCanvas_v1" width="500" height="300" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('boxLayoutCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
ctx.clearRect(0, 0, 500, 300);
var W=400, H=200, startX=50, startY=50;
// Box Bounds
ctx.strokeStyle = GRAY;
ctx.setLineDash([5, 5]);
ctx.strokeRect(startX, startY, W, H);
ctx.setLineDash([]);
function drawItem(offsetX, offsetY, label, color) {
var iw=100, ih=60;
ctx.fillStyle = color;
ctx.fillRect(startX + offsetX, startY + offsetY, iw, ih);
ctx.strokeStyle = BLUE;
ctx.lineWidth = 2;
ctx.strokeRect(startX + offsetX, startY + offsetY, iw, ih);
ctx.fillStyle = 'white';
ctx.font = 'bold 14px Arial';
ctx.textAlign = 'center';
ctx.fillText(label, startX + offsetX + iw/2, startY + offsetY + ih/2 + 5);
}
// Stacked/Aligned Items
drawItem(0, 0, 'Top-Start', 'rgba(0, 71, 171, 0.4)');
drawItem(W-100, H-60, 'Bottom-End', 'rgba(0, 71, 171, 0.7)');
drawItem((W-100)/2, (H-60)/2, 'Center', BLUE);
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 18px Arial';
ctx.textAlign = 'left';
ctx.fillText('Independent Child Alignment & Stacking', startX, startY - 20);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
#### Measurement Phase
The measurement process is handled in `computeWrapSize` and `computeSize`.
1. **Independent Measurement**:
- Each child component is measured independently with the available width and height of the Box.
- If a child has a dynamic `LayoutCompute` modifier, its size is calculated based on the provided expressions before final measurement.
2. **Intrinsic Size (Wrap Content)**:
- If the Box is set to wrap its content, its `width` will be the maximum width among all its visible children.
- Similarly, its `height` will be the maximum height among all its visible children.
#### Layout Phase
In the `internalLayoutMeasure` method, children are positioned based on the Box's alignment rules.
1. **Alignment**:
- **Horizontal Positioning**: Children can be aligned to the `START`, `CENTER`, or `END` of the Box.
- **Vertical Positioning**: Children can be aligned to the `TOP`, `CENTER`, or `BOTTOM` of the Box.
- Each child is positioned using these global rules relative to the Box's internal area (after padding).
2. **Stacking**:
- Components are painted in their hierarchy order. The first child in the list is drawn first, and subsequent children are drawn on top if they overlap.
### Examples
<table class="interpolation">
<tr>
<tr>
<td width="100">Top</td>
<td>
<img src='images/layout-BoxLayout-start-top.png' width="150">
</td>
</tr>
<tr>
<td width="100">Center</td>
<td>
<img src='images/layout-BoxLayout-center-center.png' width="150">
</td>
</tr>
<tr>
<td width="100">Bottom</td>
<td>
<img src='images/layout-BoxLayout-end-bottom.png' width="150">
</td>
</tr>
</tr>
</table>
## RowLayout
Row layout implementation, positioning components one after the other horizontally.
It supports weight and horizontal/vertical positioning.
5 Fields, total size 21 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>RowLayout</td><td>Value: 203</td></tr>
<tr><td>INT</td><td>componentId</td><td>Unique ID for this component</td></tr><tr><td>INT</td><td>animationId</td><td>ID used to match components for animation purposes</td></tr><tr><td>INT</td><td>horizontalPositioning</td><td>Horizontal positioning value</td></tr><tr><td>INT</td><td>verticalPositioning</td><td>Vertical positioning value</td></tr><tr><td>FLOAT</td><td>spacedBy</td><td>Horizontal spacing between components</td></tr></table>
### horizontalPositioning
| Name | Value |
| ---- | ---- |
| START | 1
| CENTER | 2
| END | 3
| SPACE_BETWEEN | 6
| SPACE_EVENLY | 7
| SPACE_AROUND | 8
### verticalPositioning
| Name | Value |
| ---- | ---- |
| TOP | 4
| CENTER | 2
| BOTTOM | 5
### Row Layout Algorithm
The Row Layout manager positions child components horizontally one after another. It supports intrinsic sizing, weight-based distribution, and various horizontal and vertical alignment strategies.
#### Visual Illustration (Weights)
<div id="rowLayoutContainer">
<canvas id="rowLayoutCanvas_v1" width="500" height="200" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
var phase = 0;
function draw() {
var canvas = document.getElementById('rowLayoutCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
ctx.clearRect(0, 0, 500, 200);
var startX = 25, startY = 60, rowH = 80;
var minW = 150, maxW = 450;
var totalW = minW + (Math.sin(phase) + 1) / 2 * (maxW - minW);
phase += 0.02;
// Row Bounds
ctx.strokeStyle = GRAY;
ctx.setLineDash([5, 5]);
ctx.strokeRect(startX, startY, totalW, rowH);
ctx.setLineDash([]);
// Items
// Item 1: Fixed
var w1 = 80;
ctx.fillStyle = BLUE_TRANS;
ctx.fillRect(startX, startY, w1, rowH);
ctx.strokeStyle = BLUE;
ctx.lineWidth = 2;
ctx.strokeRect(startX, startY, w1, rowH);
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 14px Arial';
ctx.textAlign = 'center';
ctx.fillText('Fixed (80px)', startX + w1/2, startY + rowH/2 + 5);
// Remaining width distribution
var remainingW = Math.max(0, totalW - w1);
// Item 2: Weight 1
var w2 = remainingW * (1/3);
ctx.fillStyle = BLUE_TRANS;
ctx.fillRect(startX + w1, startY, w2, rowH);
ctx.strokeRect(startX + w1, startY, w2, rowH);
ctx.fillStyle = BLUE;
if (w2 > 50) {
var p2 = Math.round(w2 / totalW * 100);
ctx.fillText('Weight 1 (' + p2 + '%)', startX + w1 + w2/2, startY + rowH/2 + 5);
}
// Item 3: Weight 2
var w3 = remainingW * (2/3);
ctx.fillStyle = BLUE;
ctx.fillRect(startX + w1 + w2, startY, w3, rowH);
ctx.strokeRect(startX + w1 + w2, startY, w3, rowH);
ctx.fillStyle = 'white';
if (w3 > 50) {
var p3 = Math.round(w3 / totalW * 100);
ctx.fillText('Weight 2 (' + p3 + '%)', startX + w1 + w2 + w3/2, startY + rowH/2 + 5);
}
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 18px Arial';
ctx.textAlign = 'left';
ctx.fillText('Horizontal Stacking with Weights', startX, startY - 20);
requestAnimationFrame(draw);
}
draw();
})();
</script>
#### Measurement Phase
The measurement process is handled in `computeWrapSize` and `computeSize`.
1. **Weight Distribution**:
- The layout first identifies children with horizontal weights (`WidthModifier.weight`).
- Children **without** weights are measured first with the available width. The accumulated width of these children is subtracted from the total available space.
- The remaining space is then distributed among the weighted children proportional to their weight value.
- Each weighted child is measured with its calculated fixed width.
2. **Intrinsic Size**:
- The `width` of the Row is the sum of all children's measured widths plus the `spacedBy` gaps between them.
- The `height` of the Row is the maximum height among all measured children.
#### Layout Phase
Once children are measured, the `internalLayoutMeasure` method determines their final `(x, y)` coordinates.
1. **Horizontal Positioning**:
- **START**: Children are packed at the beginning of the row.
- **CENTER**: The entire block of children is centered horizontally.
- **END**: Children are packed at the end of the row.
- **SPACE_BETWEEN**: The first child is at the start, the last at the end, and the remaining space is distributed evenly between children.
- **SPACE_EVENLY**: Space is distributed such that the gap between any two items and the edges is equal.
- **SPACE_AROUND**: Space is distributed such that the gap between items is equal, and the gap at the ends is half of the internal gap.
2. **Vertical Positioning**:
- Applied individually to each child within the calculated Row height.
- **TOP**: Child is at the top.
- **CENTER**: Child is vertically centered.
- **BOTTOM**: Child is at the bottom.
- **Baseline Alignment**: If children have `AlignBy` modifiers, they are aligned based on their specified baseline or anchor.
3. **Spacing**:
- The `spacedBy` value is added between each child in all positioning modes.
### Examples
<table class="interpolation">
<tr>
<tr>
<td width="100">Start</td>
<td>
<img src='images/layout-RowLayout-start-top.png' width="400">
</td>
</tr>
<tr>
<td width="100">Center</td>
<td>
<img src='images/layout-RowLayout-center-top.png' width="400">
</td>
</tr>
<tr>
<td width="100">End</td>
<td>
<img src='images/layout-RowLayout-end-top.png' width="400">
</td>
</tr>
<tr>
<td width="100">SpaceEvenly</td>
<td>
<img src='images/layout-RowLayout-space-evenly-top.png' width="400">
</td>
</tr>
<tr>
<td width="100">SpaceAround</td>
<td>
<img src='images/layout-RowLayout-space-around-top.png' width="400">
</td>
</tr>
<tr>
<td width="100">SpaceBetween</td>
<td>
<img src='images/layout-RowLayout-space-between-top.png' width="400">
</td>
</tr>
</tr>
</table>
## ColumnLayout
Column layout implementation, positioning components one after the other vertically.
It supports weight and horizontal/vertical positioning.
5 Fields, total size 21 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ColumnLayout</td><td>Value: 204</td></tr>
<tr><td>INT</td><td>componentId</td><td>Unique ID for this component</td></tr><tr><td>INT</td><td>animationId</td><td>ID used to match components for animation purposes</td></tr><tr><td>INT</td><td>horizontalPositioning</td><td>Horizontal positioning value</td></tr><tr><td>INT</td><td>verticalPositioning</td><td>Vertical positioning value</td></tr><tr><td>FLOAT</td><td>spacedBy</td><td>Horizontal spacing between components</td></tr></table>
### horizontalPositioning
| Name | Value |
| ---- | ---- |
| START | 1
| CENTER | 2
| END | 3
### verticalPositioning
| Name | Value |
| ---- | ---- |
| TOP | 4
| CENTER | 2
| BOTTOM | 5
| SPACE_BETWEEN | 6
| SPACE_EVENLY | 7
| SPACE_AROUND | 8
### Column Layout Algorithm
The Column Layout manager positions child components vertically one after another. It is the vertical counterpart to the Row Layout.
#### Visual Illustration (Weights)
<div id="columnLayoutContainer">
<canvas id="columnLayoutCanvas_v1" width="500" height="400" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
var phase = 0;
function draw() {
var canvas = document.getElementById('columnLayoutCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
ctx.clearRect(0, 0, 500, 400);
var startX = 150, startY = 50, colW = 200;
var minH = 100, maxH = 320;
var totalH = minH + (Math.sin(phase) + 1) / 2 * (maxH - minH);
phase += 0.02;
// Column Bounds
ctx.strokeStyle = GRAY;
ctx.setLineDash([5, 5]);
ctx.strokeRect(startX, startY, colW, totalH);
ctx.setLineDash([]);
// Items
// Item 1: Fixed
var h1 = 60;
ctx.fillStyle = BLUE_TRANS;
ctx.fillRect(startX, startY, colW, h1);
ctx.strokeStyle = BLUE;
ctx.lineWidth = 2;
ctx.strokeRect(startX, startY, colW, h1);
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 14px Arial';
ctx.textAlign = 'center';
ctx.fillText('Fixed (60px)', startX + colW/2, startY + h1/2 + 5);
// Remaining height distribution
var remainingH = Math.max(0, totalH - h1);
// Item 2: Weight 1
var h2 = remainingH * (1/3);
ctx.fillStyle = BLUE_TRANS;
ctx.fillRect(startX, startY + h1, colW, h2);
ctx.strokeRect(startX, startY + h1, colW, h2);
ctx.fillStyle = BLUE;
if (h2 > 25) {
var p2 = Math.round(h2 / totalH * 100);
ctx.fillText('Weight 1 (' + p2 + '%)', startX + colW/2, startY + h1 + h2/2 + 5);
}
// Item 3: Weight 2
var h3 = remainingH * (2/3);
ctx.fillStyle = BLUE;
ctx.fillRect(startX, startY + h1 + h2, colW, h3);
ctx.strokeRect(startX, startY + h1 + h2, colW, h3);
ctx.fillStyle = 'white';
if (h3 > 25) {
var p3 = Math.round(h3 / totalH * 100);
ctx.fillText('Weight 2 (' + p3 + '%)', startX + colW/2, startY + h1 + h2 + h3/2 + 5);
}
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 18px Arial';
ctx.textAlign = 'left';
ctx.fillText('Vertical Stacking with Weights', 140, 30);
requestAnimationFrame(draw);
}
draw();
})();
</script>
#### Measurement Phase
1. **Weight Distribution**:
- The layout identifies children with vertical weights (`HeightModifier.weight`).
- Children **without** weights are measured first. Their combined height is subtracted from the total available height.
- The remaining vertical space is distributed among weighted children proportional to their weights.
2. **Intrinsic Size**:
- The `height` of the Column is the sum of all children's measured heights plus the `spacedBy` vertical gaps.
- The `width` of the Column is the maximum width among all measured children.
#### Layout Phase
1. **Vertical Positioning**:
- **TOP**: Children are packed at the top.
- **CENTER**: The block of children is centered vertically.
- **BOTTOM**: Children are packed at the bottom.
- **SPACE_BETWEEN**, **SPACE_EVENLY**, **SPACE_AROUND**: Similar to Row Layout, but applied vertically to distribute children across the available height.
2. **Horizontal Positioning**:
- Applied individually to each child within the Column's width.
- **START**, **CENTER**, **END**: Aligns children horizontally within the column.
3. **Spacing**:
- The `spacedBy` value is added between each child vertically.
### Examples
<table class="interpolation">
<tr>
<td>
<table class="interpolation" width="100" height="400">
<tr><td><img src='images/layout-ColumnLayout-start-top.png' height="400"></td></tr>
<tr><td width="100">Top</td></tr>
</table>
</td>
<td>
<table class="interpolation" width="100" height="400">
<tr><td><img src='images/layout-ColumnLayout-start-center.png' height="400"></td></tr>
<tr><td width="100">Center</td></tr>
</table>
</td>
<td>
<table class="interpolation" width="100" height="400">
<tr><td><img src='images/layout-ColumnLayout-start-bottom.png' height="400"></td></tr>
<tr><td width="100">Bottom</td></tr>
</table>
</td>
<td>
<table class="interpolation" width="100" height="400">
<tr><td><img src='images/layout-ColumnLayout-start-space-evenly.png' height="400"></td></tr>
<tr><td width="100">SpaceEvenly</td></tr>
</table>
</td>
<td>
<table class="interpolation" width="100" height="400">
<tr><td><img src='images/layout-ColumnLayout-start-space-around.png' height="400"></td></tr>
<tr><td width="100">SpaceAround</td></tr>
</table>
</td>
<td>
<table class="interpolation" width="100" height="400">
<tr><td><img src='images/layout-ColumnLayout-start-space-between.png' height="400"></td></tr>
<tr><td width="100">SpaceBetween</td></tr>
</table>
</td>
</tr>
</table>
## CanvasLayout
Canvas implementation. Encapsulates drawing operations.
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>CanvasLayout</td><td>Value: 205</td></tr>
<tr><td>INT</td><td>componentId</td><td>Unique ID for this component</td></tr><tr><td>INT</td><td>animationId</td><td>ID used to match components for animation purposes</td></tr></table>
### Canvas Layout Algorithm
Canvas Layout is a container optimized for custom drawing operations while still supporting child components.
#### Visual Illustration
<div id="canvasLayoutContainer">
<canvas id="canvasLayoutCanvas_v1" width="500" height="300" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('canvasLayoutCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', DARK_BLUE = '#002366', GRAY = '#888888', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
ctx.clearRect(0, 0, 500, 300);
var cx = 50, cy = 50, cw = 400, ch = 200;
// Canvas Bounds
ctx.strokeStyle = DARK_GRAY;
ctx.lineWidth = 2;
ctx.strokeRect(cx, cy, cw, ch);
ctx.fillStyle = '#fdfdfd';
ctx.fillRect(cx, cy, cw, ch);
// Custom Drawing (relative to canvas top-left)
ctx.save();
ctx.translate(cx, cy);
// Drawing 1: Grid
ctx.strokeStyle = '#eee';
ctx.lineWidth = 1;
for (var i = 0; i < cw; i += 40) { ctx.beginPath(); ctx.moveTo(i, 0); ctx.lineTo(i, ch); ctx.stroke(); }
for (var j = 0; j < ch; j += 40) { ctx.beginPath(); ctx.moveTo(0, j); ctx.lineTo(cw, j); ctx.stroke(); }
// Drawing 2: Shapes
ctx.fillStyle = BLUE_TRANS;
ctx.beginPath();
ctx.arc(100, 100, 60, 0, Math.PI*2);
ctx.fill();
ctx.strokeStyle = BLUE;
ctx.stroke();
// Drawing 3: Child Component (e.g., a button or text block)
var childX = 220, childY = 80, childW = 120, childH = 40;
ctx.fillStyle = DARK_BLUE;
ctx.fillRect(childX, childY, childW, childH);
ctx.fillStyle = 'white';
ctx.font = 'bold 14px Arial';
ctx.textAlign = 'center';
ctx.fillText('Child Component', childX + childW/2, childY + 25);
// Annotation for child positioning
ctx.strokeStyle = DARK_GRAY;
ctx.setLineDash([2, 2]);
ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(childX, childY); ctx.stroke();
ctx.setLineDash([]);
ctx.fillStyle = DARK_GRAY;
ctx.font = '12px Arial';
ctx.fillText('LayoutCompute(x, y)', childX/2, childY/2);
ctx.restore();
ctx.font = 'bold 20px Arial';
ctx.fillStyle = DARK_GRAY;
ctx.textAlign = 'left';
ctx.fillText('Canvas Layout', cx, cy - 15);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
#### Layout Logic
1. **Drawing Encapsulation**:
- Unlike other layouts, CanvasLayout primarily serves as a scope for `Draw` operations (DrawRect, DrawCircle, etc.).
- All drawing instructions contained within the Canvas are executed relative to the Canvas's top-left corner.
2. **Child Positioning**:
- Children of a CanvasLayout are by default positioned at `(0, 0)` and sized to fill the entire Canvas area if standard `LayoutComponentContent` is used.
- However, if `LayoutCompute` modifiers are used, children can be positioned anywhere on top of the custom drawing.
## CollapsibleRow
A row layout that can hide children if space is insufficient
5 Fields, total size 21 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>CollapsibleRow</td><td>Value: 230</td></tr>
<tr><td>INT</td><td>componentId</td><td>Unique ID for this component</td></tr><tr><td>INT</td><td>animationId</td><td>ID for animation purposes</td></tr><tr><td>INT</td><td>horizontalPositioning</td><td>Horizontal positioning value</td></tr><tr><td>INT</td><td>verticalPositioning</td><td>Vertical positioning value</td></tr><tr><td>FLOAT</td><td>spacedBy</td><td>Horizontal spacing between components</td></tr></table>
### Collapsible Row Layout Algorithm
Collapsible Row extends Row Layout with the ability to automatically hide children that do not fit within the available horizontal space.
#### Basic Overflow Animation
<div id="collapsibleRowContainer">
<canvas id="collapsibleRowCanvas_v1" width="500" height="180" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
#### Priority-Based Hiding Animation
In this example, **Item 2** has a higher priority value (lower importance), so it is hidden **first**. When it disappears, the layout collapses and moves Item 3 into its place.
<div id="collapsibleRowPriorityContainer">
<canvas id="collapsibleRowPriorityCanvas_v1" width="500" height="180" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
var itemW = 120, itemH = 60, spacing = 10, startX = 25, startY = 70;
function drawItem(ctx, idx, x, y, fits, label) {
if (!fits) return; // In a real layout, GONE items aren't drawn at all
ctx.fillStyle = BLUE_TRANS;
ctx.fillRect(x, y, itemW, itemH);
ctx.strokeStyle = BLUE;
ctx.lineWidth = 2;
ctx.strokeRect(x, y, itemW, itemH);
ctx.fillStyle = BLUE;
ctx.textAlign = 'center';
ctx.font = 'bold 14px Arial';
ctx.fillText(label, x + itemW/2, y + itemH/2 + 5);
}
var phase1 = 0, phase2 = 0;
function animate() {
var c1 = document.getElementById('collapsibleRowCanvas_v1');
var c2 = document.getElementById('collapsibleRowPriorityCanvas_v1');
if (c1) {
var ctx = c1.getContext('2d');
ctx.clearRect(0, 0, 500, 180);
var w = 100 + (Math.sin(phase1) + 1) / 2 * 350;
phase1 += 0.015;
ctx.strokeStyle = GRAY; ctx.setLineDash([5, 5]); ctx.strokeRect(startX, startY, w, itemH); ctx.setLineDash([]);
ctx.fillStyle = GRAY; ctx.font = 'bold 14px Arial'; ctx.textAlign='left'; ctx.fillText('Width: ' + Math.round(w) + 'px', startX, startY - 10);
var currentX = startX;
for (var i=0; i<3; i++) {
var fits = ( (currentX + itemW - startX) <= w );
if (fits) {
drawItem(ctx, i+1, currentX, startY, true, 'ITEM ' + (i+1));
currentX += itemW + spacing;
}
}
}
if (c2) {
var ctx = c2.getContext('2d');
ctx.clearRect(0, 0, 500, 180);
var w = 100 + (Math.sin(phase2) + 1) / 2 * 350;
phase2 += 0.015;
ctx.strokeStyle = GRAY; ctx.setLineDash([5, 5]); ctx.strokeRect(startX, startY, w, itemH); ctx.setLineDash([]);
ctx.fillStyle = GRAY; ctx.font = 'bold 14px Arial'; ctx.textAlign='left'; ctx.fillText('Width: ' + Math.round(w) + 'px', startX, startY - 10);
var items = [
{id: 1, p: 10},
{id: 2, p: 100},
{id: 3, p: 20}
];
var visibility = {};
var accumulated = 0;
// Process in priority order (lowest P first)
var priorityProcess = [...items].sort((a,b) => a.p - b.p);
for (var itm of priorityProcess) {
if (accumulated + itemW <= w) {
visibility[itm.id] = true;
accumulated += itemW + spacing;
} else {
visibility[itm.id] = false;
}
}
var currentX = startX;
for (var itm of items) {
if (visibility[itm.id]) {
drawItem(ctx, itm.id, currentX, startY, true, 'ITEM ' + itm.id + ' (P:' + itm.p + ')');
currentX += itemW + spacing;
}
}
}
requestAnimationFrame(animate);
}
animate();
})();
</script>
#### Measurement Phase
1. **Priority Sorting**:
- If children have `CollapsiblePriority` modifiers, they are sorted by priority (lowest values are kept first).
- If priorities are equal, the original hierarchy order is used.
2. **Overflow Detection**:
- Children are measured one by one.
- The layout keeps track of the accumulated width.
- As soon as a child's width would cause the total to exceed the available width, that child and all subsequent children (in priority order) are marked as `GONE`.
#### Layout Phase
- The remaining `VISIBLE` children are laid out exactly like a standard Row Layout, using the specified horizontal and vertical positioning.
## CollapsibleColumn
A column layout that can hide children if space is insufficient
5 Fields, total size 21 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>CollapsibleColumn</td><td>Value: 233</td></tr>
<tr><td>INT</td><td>componentId</td><td>Unique ID for this component</td></tr><tr><td>INT</td><td>animationId</td><td>ID for animation purposes</td></tr><tr><td>INT</td><td>horizontalPositioning</td><td>Horizontal positioning value</td></tr><tr><td>INT</td><td>verticalPositioning</td><td>Vertical positioning value</td></tr><tr><td>FLOAT</td><td>spacedBy</td><td>Vertical spacing between components</td></tr></table>
### Collapsible Column Layout Algorithm
Collapsible Column extends Column Layout by hiding children that exceed the available vertical space.
#### Basic Overflow Animation
<div id="collapsibleColumnContainer">
<canvas id="collapsibleColumnCanvas_v1" width="500" height="400" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
#### Priority-Based Hiding Animation
In this example, **Item 2** has a higher priority value (lower importance), so it is hidden **first**. Notice how Item 3 shifts up to fill the gap when Item 2 is removed.
<div id="collapsibleColumnPriorityContainer">
<canvas id="collapsibleColumnPriorityCanvas_v1" width="500" height="400" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
var itemW = 200, itemH = 80, spacing = 10, startX = 150, startY = 50;
function drawItem(ctx, idx, x, y, fits, label) {
if (!fits) return;
ctx.fillStyle = BLUE_TRANS;
ctx.fillRect(x, y, itemW, itemH);
ctx.strokeStyle = BLUE;
ctx.lineWidth = 2;
ctx.strokeRect(x, y, itemW, itemH);
ctx.fillStyle = BLUE;
ctx.textAlign = 'center';
ctx.font = 'bold 14px Arial';
ctx.fillText(label, x + itemW/2, y + itemH/2 + 5);
}
var phase1 = 0, phase2 = 0;
function animate() {
var c1 = document.getElementById('collapsibleColumnCanvas_v1');
var c2 = document.getElementById('collapsibleColumnPriorityCanvas_v1');
if (c1) {
var ctx = c1.getContext('2d');
ctx.clearRect(0, 0, 500, 400);
var h = 50 + (Math.sin(phase1) + 1) / 2 * 300;
phase1 += 0.015;
ctx.strokeStyle = GRAY; ctx.setLineDash([5, 5]); ctx.strokeRect(startX, startY, itemW, h); ctx.setLineDash([]);
ctx.fillStyle = GRAY; ctx.font = 'bold 14px Arial'; ctx.textAlign='left'; ctx.fillText('Height: ' + Math.round(h) + 'px', startX, startY - 10);
var currentY = startY;
for (var i=0; i<3; i++) {
var fits = ( (currentY + itemH - startY) <= h );
if (fits) {
drawItem(ctx, i+1, startX, currentY, true, 'ITEM ' + (i+1));
currentY += itemH + spacing;
}
}
}
if (c2) {
var ctx = c2.getContext('2d');
ctx.clearRect(0, 0, 500, 400);
var h = 50 + (Math.sin(phase2) + 1) / 2 * 300;
phase2 += 0.015;
ctx.strokeStyle = GRAY; ctx.setLineDash([5, 5]); ctx.strokeRect(startX, startY, itemW, h); ctx.setLineDash([]);
ctx.fillStyle = GRAY; ctx.font = 'bold 14px Arial'; ctx.textAlign='left'; ctx.fillText('Height: ' + Math.round(h) + 'px', startX, startY - 10);
var items = [
{id: 1, p: 10},
{id: 2, p: 100},
{id: 3, p: 20}
];
var visibility = {};
var accumulated = 0;
var priorityProcess = [...items].sort((a,b) => a.p - b.p);
for (var itm of priorityProcess) {
if (accumulated + itemH <= h) {
visibility[itm.id] = true;
accumulated += itemH + spacing;
} else {
visibility[itm.id] = false;
}
}
var currentY = startY;
for (var itm of items) {
if (visibility[itm.id]) {
drawItem(ctx, itm.id, startX, currentY, true, 'ITEM ' + itm.id + ' (P:' + itm.p + ')');
currentY += itemH + spacing;
}
}
}
requestAnimationFrame(animate);
}
animate();
})();
</script>
#### Measurement Phase
1. **Priority Sorting**:
- Children are sorted based on their `CollapsiblePriority` modifier.
2. **Overflow Detection**:
- The layout measures children and accumulates their heights.
- Any child that would cause the total height to exceed the Column's maximum height is marked as `GONE`.
#### Layout Phase
- Visible children are positioned vertically according to standard Column Layout rules (Top, Center, Bottom, etc.).
## ImageLayout
Image layout implementation
5 Fields, total size 21 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ImageLayout</td><td>Value: 234</td></tr>
<tr><td>INT</td><td>componentId</td><td>Unique ID for this component</td></tr><tr><td>INT</td><td>animationId</td><td>ID used to match components for animation purposes</td></tr><tr><td>INT</td><td>bitmapId</td><td>The ID of the bitmap to display</td></tr><tr><td>INT</td><td>scaleType</td><td>The scale type to apply</td></tr><tr><td>FLOAT</td><td>alpha</td><td>The alpha transparency [0..1]</td></tr></table>
### Image Layout Algorithm
Image Layout handles the display and scaling of bitmap resources.
#### Visual Illustration (Scale Modes)
<div id="imageLayoutContainer">
<canvas id="imageLayoutCanvas_v1" width="500" height="280" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('imageLayoutCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444';
ctx.clearRect(0, 0, 500, 280);
function drawScaleDemo(x, y, label, mode) {
var vw = 120, vh = 120; // Viewport
var iw = 160, ih = 80; // Image (wider than high)
ctx.save();
ctx.translate(x, y);
// Label
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 16px Arial';
ctx.textAlign = 'center';
ctx.fillText(label, vw/2, -15);
// Viewport bounds
ctx.strokeStyle = '#eee';
ctx.setLineDash([4, 4]);
ctx.strokeRect(0, 0, vw, vh);
ctx.setLineDash([]);
ctx.save();
ctx.beginPath();
ctx.rect(0, 0, vw, vh);
ctx.clip();
var dx, dy, dw, dh;
if (mode === 'FIT') {
var scale = Math.min(vw / iw, vh / ih);
dw = iw * scale;
dh = ih * scale;
dx = (vw - dw) / 2;
dy = (vh - dh) / 2;
} else if (mode === 'FILL') {
dx = 0; dy = 0; dw = vw; dh = vh;
} else if (mode === 'CENTER_CROP') {
var scale = Math.max(vw / iw, vh / ih);
dw = iw * scale;
dh = ih * scale;
dx = (vw - dw) / 2;
dy = (vh - dh) / 2;
}
// Draw Placeholder Image
ctx.fillStyle = BLUE;
ctx.fillRect(dx, dy, dw, dh);
ctx.strokeStyle = 'white';
ctx.lineWidth = 2;
ctx.strokeRect(dx + 5, dy + 5, dw - 10, dh - 10);
ctx.beginPath();
ctx.moveTo(dx, dy); ctx.lineTo(dx + dw, dy + dh);
ctx.moveTo(dx + dw, dy); ctx.lineTo(dx, dy + dh);
ctx.stroke();
ctx.restore();
// Border
ctx.strokeStyle = DARK_GRAY;
ctx.lineWidth = 2;
ctx.strokeRect(0, 0, vw, vh);
ctx.restore();
}
drawScaleDemo(40, 60, 'FIT', 'FIT');
drawScaleDemo(190, 60, 'CENTER_CROP', 'CENTER_CROP');
drawScaleDemo(340, 60, 'FILL', 'FILL');
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
#### Sizing
1. **Wrap Content**:
- If sizing is not constrained, the component takes the intrinsic width and height of the source bitmap.
2. **Scaling**:
- Calculates a destination rectangle within the component's bounds based on the `scaleType`.
- Supports typical scaling modes like `Fit`, `CenterCrop`, and `Fill`.
#### Rendering
- Applies alpha transparency.
- Clips the bitmap to the component's bounds if the scaled image exceeds them.
## CoreText [EXPERIMENTAL] (added in v7)
!!! WARNING
Experimental operation
Core text layout implementation with advanced styling
26 Fields, total size 85 + null x 4 + null x 4 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>CoreText</td><td>Value: 239</td></tr>
<tr><td>INT</td><td>textId</td><td>The ID of the text to display</td></tr><tr><td>INT</td><td>componentId</td><td>Unique ID for this component</td></tr><tr><td>INT</td><td>animationId</td><td>ID for animation purposes</td></tr><tr><td>INT</td><td>color</td><td>The text color (ARGB)</td></tr><tr><td>INT</td><td>colorId</td><td>The ID of the color variable</td></tr><tr><td>FLOAT</td><td>fontSize</td><td>The font size</td></tr><tr><td>FLOAT</td><td>minFontSize</td><td>Minimum font size for autosize</td></tr><tr><td>FLOAT</td><td>maxFontSize</td><td>Maximum font size for autosize</td></tr><tr><td>INT</td><td>fontStyle</td><td>The font style</td></tr><tr><td>FLOAT</td><td>fontWeight</td><td>The font weight</td></tr><tr><td>INT</td><td>fontFamily</td><td>The ID of the font family</td></tr><tr><td>INT</td><td>textAlign</td><td>Text alignment</td></tr><tr><td>INT</td><td>overflow</td><td>Overflow behavior</td></tr><tr><td>INT</td><td>maxLines</td><td>Maximum number of lines</td></tr><tr><td>FLOAT</td><td>letterSpacing</td><td>Letter spacing</td></tr><tr><td>FLOAT</td><td>lineHeightAdd</td><td>Line height addition</td></tr><tr><td>FLOAT</td><td>lineHeightMultiplier</td><td>Line height multiplier</td></tr><tr><td>INT</td><td>lineBreakStrategy</td><td>Line break strategy</td></tr><tr><td>INT</td><td>hyphenationFrequency</td><td>Hyphenation frequency</td></tr><tr><td>INT</td><td>justificationMode</td><td>Justification mode</td></tr><tr><td>BOOLEAN</td><td>underline</td><td>Whether to underline</td></tr><tr><td>BOOLEAN</td><td>strikethrough</td><td>Whether to strikethrough</td></tr><tr><td>INT[]</td><td>fontAxis</td><td>Font axis tags</td></tr><tr><td>FLOAT[]</td><td>fontAxisValues</td><td>Font axis values</td></tr><tr><td>BOOLEAN</td><td>autosize</td><td>Whether to enable autosize</td></tr><tr><td>INT</td><td>flags</td><td>Behavior flags</td></tr></table>
### Core Text Layout Algorithm
Core Text is a leaf layout component responsible for high-fidelity text rendering and measurement.
#### Visual Illustration (Autosize and Alignment)
<div id="coreTextContainer">
<canvas id="coreTextCanvas_v1" width="500" height="280" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
var phase = 0;
function draw() {
var canvas = document.getElementById('coreTextCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444';
ctx.clearRect(0, 0, 500, 280);
// Box size oscillates for Autosize demo
var boxW = 150 + Math.sin(phase) * 50;
var boxH = 100 + Math.cos(phase) * 30;
phase += 0.02;
var text = "DYNAMIC TEXT";
// 1. Autosize Demo
ctx.save();
ctx.translate(50, 60);
ctx.strokeStyle = GRAY;
ctx.setLineDash([4, 4]);
ctx.strokeRect(0, 0, boxW, boxH);
ctx.setLineDash([]);
// Simple heuristic for autosize illustration
var fontSize = Math.min(boxW / 7, boxH / 1.5);
ctx.font = 'bold ' + fontSize + 'px Arial';
ctx.fillStyle = BLUE;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(text, boxW/2, boxH/2);
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 16px Arial';
ctx.textAlign = 'left';
ctx.fillText('Autosize: ' + Math.round(fontSize) + 'px', 0, -15);
ctx.restore();
// 2. Alignment Demo
ctx.save();
ctx.translate(300, 60);
var alignBoxW = 160, alignBoxH = 150;
ctx.strokeStyle = '#eee';
ctx.strokeRect(0, 0, alignBoxW, alignBoxH);
ctx.font = '18px Arial';
ctx.fillStyle = DARK_GRAY;
// Left
ctx.textAlign = 'left';
ctx.fillText('Align: Left', 10, 30);
ctx.fillStyle = BLUE;
ctx.fillRect(10, 35, 100, 4);
// Center
ctx.fillStyle = DARK_GRAY;
ctx.textAlign = 'center';
ctx.fillText('Align: Center', alignBoxW/2, 80);
ctx.fillStyle = BLUE;
ctx.fillRect(alignBoxW/2 - 50, 85, 100, 4);
// Right
ctx.fillStyle = DARK_GRAY;
ctx.textAlign = 'right';
ctx.fillText('Align: Right', alignBoxW - 10, 130);
ctx.fillStyle = BLUE;
ctx.fillRect(alignBoxW - 110, 135, 100, 4);
ctx.restore();
requestAnimationFrame(draw);
}
draw();
})();
</script>
#### Measurement Phase
1. **Complex Text Analysis**:
- The algorithm checks for features requiring complex layout: line breaks (\n), tabs (\t), letter spacing, varied line heights, or overflow ellipsis.
- If these are present, it uses a complex layout engine to calculate line breaks and word wrapping.
2. **Autosize**:
- If `autosize` is enabled, the layout performs a binary search between `minFontSize` and `maxFontSize`.
- It finds the largest font size that allows the text to fit within the provided `maxWidth` and `maxHeight` without causing unexpected hyphenation or overflow.
3. **Intrinsic Size**:
- The measured size is the bounding box of the rendered text glyphs.
#### Styling and Alignment
- Supports ARGB colors (static or dynamic via ID).
- Supports font weights, styles (italic), and variable font axes.
- Horizontal alignment (`Left`, `Center`, `Right`, `Justify`) is applied during the text layout process.
## FlowLayout [EXPERIMENTAL] (added in v7)
!!! WARNING
Experimental operation
Flow layout implementation. Positions components one after the other horizontally and wraps to the next line if space is exhausted.
5 Fields, total size 21 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>FlowLayout</td><td>Value: 240</td></tr>
<tr><td>INT</td><td>componentId</td><td>Unique ID for this component</td></tr><tr><td>INT</td><td>animationId</td><td>ID for animation purposes</td></tr><tr><td>INT</td><td>horizontalPositioning</td><td>Horizontal positioning value</td></tr><tr><td>INT</td><td>verticalPositioning</td><td>Vertical positioning value</td></tr><tr><td>FLOAT</td><td>spacedBy</td><td>Horizontal spacing between components</td></tr></table>
### horizontalPositioning
| Name | Value |
| ---- | ---- |
| START | 1
| CENTER | 2
| END | 3
| SPACE_BETWEEN | 6
| SPACE_EVENLY | 7
| SPACE_AROUND | 8
### verticalPositioning
| Name | Value |
| ---- | ---- |
| TOP | 4
| CENTER | 2
| BOTTOM | 5
### Flow Layout Algorithm
The Flow Layout manager positions child components horizontally and automatically wraps them to a new "row" when the available width is exhausted.
#### Visual Illustration
<div id="flowLayoutContainer">
<canvas id="flowLayoutCanvas_v1" width="500" height="300" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
var widthPhase = 0;
function draw() {
var canvas = document.getElementById('flowLayoutCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
ctx.clearRect(0, 0, 500, 300);
// Dynamic width for animation
var baseWidth = 460;
var minWidth = 150;
var currentFlowWidth = minWidth + (Math.sin(widthPhase) + 1) / 2 * (baseWidth - minWidth);
widthPhase += 0.02;
var startX = 20, startY = 60;
var itemW = 60, itemH = 40, spacing = 10;
var items = 8;
// Draw Container Boundary
ctx.strokeStyle = GRAY;
ctx.setLineDash([5, 5]);
ctx.strokeRect(startX, startY, currentFlowWidth, 200);
ctx.setLineDash([]);
ctx.font = 'bold 18px Arial';
ctx.fillStyle = GRAY;
ctx.fillText('Available Width: ' + Math.round(currentFlowWidth) + 'px', startX, startY - 15);
// Flow Logic
var tx = 0, ty = 0;
for (var i = 0; i < items; i++) {
if (tx + itemW > currentFlowWidth) {
tx = 0;
ty += itemH + spacing;
}
ctx.fillStyle = BLUE_TRANS;
ctx.fillRect(startX + tx, startY + ty, itemW, itemH);
ctx.strokeStyle = BLUE;
ctx.lineWidth = 2;
ctx.strokeRect(startX + tx, startY + ty, itemW, itemH);
ctx.fillStyle = BLUE;
ctx.font = 'bold 14px Arial';
ctx.textAlign = 'center';
ctx.fillText(i + 1, startX + tx + itemW/2, startY + ty + itemH/2 + 5);
tx += itemW + spacing;
}
requestAnimationFrame(draw);
}
draw();
})();
</script>
#### Measurement Phase
1. **Segmentation**:
- The algorithm first iterates through children to determine which ones fit on the current line.
- If a child (including its minimum width if weighted) exceeds the remaining width in the current line, a new line is started.
- This results in a list of "rows," where each row is a collection of components.
2. **Wrap Sizing**:
- The total `width` is the maximum width of any individual row.
- The total `height` is the sum of the heights of all rows.
#### Layout Phase
1. **Row Processing**:
- Each segment (row) identified in the measurement phase is treated like an individual Row Layout.
- Horizontal alignment (`START`, `CENTER`, `END`, etc.) is applied to components within each row.
- Vertical alignment (`TOP`, `CENTER`, `BOTTOM`) determines how the entire block of rows is positioned within the Flow Layout's total height.
2. **Spacing**:
- Horizontal spacing between components in a row is controlled by `spacedBy`.
# Modifier Operations
Augmentations applied to components to change their dimensions, appearance, or behavior.
Operations in this category:
| ID | Name | Version | Size (bytes)
| ---- | ---- | ---- | ---- |
| 16 | WidthModifierOperation | v6 | 9
| 54 | RoundedClipRectModifierOperation | v6 | 17
| 55 | BackgroundModifierOperation | v6 | 37
| 58 | PaddingModifierOperation | v6 | 17
| 59 | ClickModifier | v6 | 1
| 67 | HeightModifierOperation | v6 | 9
| 107 | BorderModifierOperation | v6 | 45
| 108 | ClipRectModifierOperation | v6 | 1
| 174 | DrawContentOperation | v6 | 1
| 211 | ComponentVisibilityOperation | v6 | 5
| 219 | TouchModifier | v6 | 1
| 220 | TouchUpModifier | v6 | 1
| 221 | OffsetModifierOperation | v6 | 9
| 223 | ZIndexModifierOperation | v6 | 5
| 224 | GraphicsLayerModifierOperation | v6 | 5
| 225 | TouchCancelModifier | v6 | 1
| 226 | ScrollModifierOperation | v6 | 17
| 228 | MarqueeModifierOperation | v6 | 25
| 229 | RippleModifier | v6 | 1
| 231 | WidthInModifierOperation | v6 | 9
| 232 | HeightInModifierOperation | v6 | 9
| 235 | CollapsiblePriorityModifierOperation | v6 | 9
| 237 | AlignByModifierOperation | v7 | 9
| 238 | LayoutCompute | v7 | 9
## WidthModifierOperation
Set the width dimension on a component
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>WidthModifierOperation</td><td>Value: 16</td></tr>
<tr><td>INT</td><td>type</td><td>The type of dimension rule (0=FIXED, 1=WRAP, etc.)</td></tr><tr><td>FLOAT</td><td>value</td><td>The width value</td></tr></table>
## RoundedClipRectModifierOperation
Clip the component's content to its rounded rectangular bounds
4 Fields, total size 17 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>RoundedClipRectModifierOperation</td><td>Value: 54</td></tr>
<tr><td>FLOAT</td><td>topStart</td><td>The topStart radius of the rectangle</td></tr><tr><td>FLOAT</td><td>topEnd</td><td>The topEnd radius of the rectangle</td></tr><tr><td>FLOAT</td><td>bottomStart</td><td>The bottomStart radius of the rectangle</td></tr><tr><td>FLOAT</td><td>bottomEnd</td><td>The bottomEnd radius of the rectangle</td></tr></table>
### RoundedClipRect Illustration
The `RoundedClipRect` modifier restricts the drawing area of a component to its own bounds with rounded corners.
<div id="roundedClipContainer">
<canvas id="roundedClipCanvas_v1" width="500" height="300" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('roundedClipCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444';
ctx.clearRect(0, 0, 500, 300);
var x=100, y=50, w=300, h=200, r=40;
// Background content (dimmed)
ctx.globalAlpha = 0.1;
ctx.fillStyle = BLUE;
ctx.fillRect(50, 20, 400, 260);
ctx.globalAlpha = 1.0;
// Guide
ctx.strokeStyle = GRAY;
ctx.lineWidth = 2;
ctx.setLineDash([8, 8]);
ctx.strokeRect(x, y, w, h);
ctx.setLineDash([]);
// Clip
ctx.save();
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.quadraticCurveTo(x + w, y, x + w, y + r);
ctx.lineTo(x + w, y + h - r);
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
ctx.lineTo(x + r, y + h);
ctx.quadraticCurveTo(x, y + h, x, y + h - r);
ctx.lineTo(x, y + r);
ctx.quadraticCurveTo(x, y, x + r, y);
ctx.clip();
ctx.fillStyle = BLUE;
ctx.fillRect(50, 20, 400, 260);
ctx.fillStyle = 'white';
ctx.font = 'bold 24px Arial';
ctx.textAlign = 'center';
ctx.fillText('Rounded Clip Area', x + w/2, y + h/2 + 10);
ctx.restore();
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 18px Arial';
ctx.fillText('Clip Bounds (left, top, right, bottom)', x, y + h + 30);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## BackgroundModifierOperation
Define a background color or shape for a component
9 Fields, total size 37 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>BackgroundModifierOperation</td><td>Value: 55</td></tr>
<tr><td>INT</td><td>flags</td><td>Behavior flags</td></tr><tr><td>INT</td><td>colorId</td><td>The ID of the color if flags include COLOR_REF</td></tr><tr><td>INT</td><td>reserve1</td><td>Reserved for future use</td></tr><tr><td>INT</td><td>reserve2</td><td>Reserved for future use</td></tr><tr><td>FLOAT</td><td>r</td><td>Red component [0..1]</td></tr><tr><td>FLOAT</td><td>g</td><td>Green component [0..1]</td></tr><tr><td>FLOAT</td><td>b</td><td>Blue component [0..1]</td></tr><tr><td>FLOAT</td><td>a</td><td>Alpha component [0..1]</td></tr><tr><td>INT</td><td>shapeType</td><td>The shape type (0=RECTANGLE, 1=CIRCLE)</td></tr></table>
### Background Modifier Illustration
The `Background` modifier applies a color and shape behind a component.
#### Rectangle Shape
<div id="bgRectContainer">
<canvas id="bgRectCanvas_v1" width="240" height="240" style="border:1px solid #ccc; background: #fff; display: inline-block; margin: 10px;"></canvas>
</div>
#### Circle Shape
<div id="bgCircleContainer">
<canvas id="bgCircleCanvas_v1" width="240" height="240" style="border:1px solid #ccc; background: #fff; display: inline-block; margin: 10px;"></canvas>
</div>
<script>
(function() {
function setup(id, type) {
function draw() {
var canvas = document.getElementById(id);
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444';
ctx.clearRect(0, 0, 240, 240);
ctx.fillStyle = BLUE;
if (type === 'rect') {
ctx.fillRect(30, 30, 180, 180);
} else {
ctx.beginPath();
ctx.arc(120, 120, 90, 0, 2 * Math.PI);
ctx.fill();
}
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 22px Arial';
ctx.textAlign = 'center';
ctx.fillText(type.toUpperCase(), 120, 230);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
}
setup('bgRectCanvas_v1', 'rect');
setup('bgCircleCanvas_v1', 'circle');
})();
</script>
## PaddingModifierOperation
Define padding around a component
4 Fields, total size 17 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>PaddingModifierOperation</td><td>Value: 58</td></tr>
<tr><td>FLOAT</td><td>left</td><td>Left padding</td></tr><tr><td>FLOAT</td><td>top</td><td>Top padding</td></tr><tr><td>FLOAT</td><td>right</td><td>Right padding</td></tr><tr><td>FLOAT</td><td>bottom</td><td>Bottom padding</td></tr></table>
### Padding Modifier Illustration
The `Padding` modifier adds space around a component's content.
<div id="paddingModifierContainer">
<canvas id="paddingModifierCanvas_v1" width="500" height="330" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('paddingModifierCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.1)';
var L=50, T=65, R=80, B=35;
var W=500, H=330;
ctx.clearRect(0, 0, W, H);
// Component Bounds
ctx.strokeStyle = '#eee';
ctx.lineWidth = 2;
ctx.setLineDash([4, 4]);
ctx.strokeRect(15, 15, W-30, H-30);
ctx.setLineDash([]);
// Padding Area
ctx.fillStyle = BLUE_TRANS;
ctx.fillRect(15, 15, W-30, H-30);
// Content Area
ctx.fillStyle = BLUE;
ctx.fillRect(15+L, 15+T, W-30-L-R, H-30-T-B);
// Labels
ctx.font = 'bold 20px Arial';
ctx.fillStyle = GRAY;
ctx.textAlign = 'center';
ctx.fillText('Top: ' + T, W/2, 15+T/2 + 8);
ctx.fillText('Bottom: ' + B, W/2, H-15-B/2 + 8);
ctx.textAlign = 'left';
ctx.fillText('Left: ' + L, 15+8, H/2);
ctx.textAlign = 'right';
ctx.fillText('Right: ' + R, W-15-8, H/2);
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
ctx.fillText('Content', W/2, H/2);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## ClickModifier
Click modifier. This operation contains a list of action operations executed on click
0 Fields, total size 1 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ClickModifier</td><td>Value: 59</td></tr>
</table>
## HeightModifierOperation
Set the height dimension on a component
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>HeightModifierOperation</td><td>Value: 67</td></tr>
<tr><td>INT</td><td>type</td><td>The type of dimension rule (0=FIXED, 1=WRAP, etc.)</td></tr><tr><td>FLOAT</td><td>value</td><td>The height value</td></tr></table>
## BorderModifierOperation
Define a border for a component
11 Fields, total size 45 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>BorderModifierOperation</td><td>Value: 107</td></tr>
<tr><td>INT</td><td>flags</td><td>Behavior flags</td></tr><tr><td>INT</td><td>colorId</td><td>The ID of the color if flags include COLOR_REF</td></tr><tr><td>INT</td><td>reserve1</td><td>Reserved for future use</td></tr><tr><td>INT</td><td>reserve2</td><td>Reserved for future use</td></tr><tr><td>FLOAT</td><td>borderWidth</td><td>Width of the border</td></tr><tr><td>FLOAT</td><td>roundedCorner</td><td>Radius for rounded corners</td></tr><tr><td>FLOAT</td><td>r</td><td>Red component [0..1]</td></tr><tr><td>FLOAT</td><td>g</td><td>Green component [0..1]</td></tr><tr><td>FLOAT</td><td>b</td><td>Blue component [0..1]</td></tr><tr><td>FLOAT</td><td>a</td><td>Alpha component [0..1]</td></tr><tr><td>INT</td><td>shapeType</td><td>The shape type (0=RECTANGLE, 1=CIRCLE)</td></tr></table>
### Border Modifier Illustration
The `Border` modifier draws a stroke around the component's edge with a specific width and corner radius.
<div id="borderModifierContainer">
<canvas id="borderModifierCanvas_v1" width="500" height="250" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('borderModifierCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444';
var x=80, y=50, w=340, h=150, r=35, bw=8;
ctx.clearRect(0, 0, 500, 250);
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.quadraticCurveTo(x + w, y, x + w, y + r);
ctx.lineTo(x + w, y + h - r);
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
ctx.lineTo(x + r, y + h);
ctx.quadraticCurveTo(x, y + h, x, y + h - r);
ctx.lineTo(x, y + r);
ctx.quadraticCurveTo(x, y, x + r, y);
ctx.closePath();
ctx.lineWidth = bw;
ctx.strokeStyle = BLUE;
ctx.stroke();
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 22px Arial';
ctx.fillText('borderWidth: ' + bw, x + w/2 - 75, y + h/2);
ctx.fillText('roundedCorner: ' + r, x + w - 50, y + 15);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## ClipRectModifierOperation
Clip the component's content to its rectangular bounds
0 Fields, total size 1 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ClipRectModifierOperation</td><td>Value: 108</td></tr>
</table>
### ClipRect Illustration
The `ClipRect` modifier restricts the drawing area of a component to its own rectangular bounds.
<div id="clipRectContainer">
<canvas id="clipRectCanvas_v1" width="500" height="330" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('clipRectCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
ctx.clearRect(0, 0, 500, 330);
var clipX=80, clipY=80, clipW=340, clipH=170;
// Draw what's outside the clip (dimmed Blue)
ctx.globalAlpha = 0.2;
ctx.fillStyle = BLUE;
ctx.beginPath();
ctx.arc(115, 115, 100, 0, 2 * Math.PI);
ctx.fill();
ctx.globalAlpha = 1.0;
// Clip Boundary
ctx.strokeStyle = GRAY;
ctx.lineWidth = 2;
ctx.setLineDash([8, 8]);
ctx.strokeRect(clipX, clipY, clipW, clipH);
ctx.setLineDash([]);
ctx.font = 'bold 22px Arial';
ctx.fillStyle = DARK_GRAY;
ctx.fillText('Clip Bounds', clipX, clipY - 15);
// Apply clip and draw content
ctx.save();
ctx.beginPath();
ctx.rect(clipX, clipY, clipW, clipH);
ctx.clip();
ctx.fillStyle = BLUE;
ctx.beginPath();
ctx.arc(115, 115, 100, 0, 2 * Math.PI);
ctx.fill();
ctx.fillStyle = BLUE;
ctx.globalAlpha = 0.6;
ctx.fillRect(300, 130, 170, 170);
ctx.restore();
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## DrawContentOperation
A modifier that triggers drawing of the component's content
0 Fields, total size 1 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>DrawContentOperation</td><td>Value: 174</td></tr>
</table>
## ComponentVisibilityOperation
Set component visibility from a provided integer variable
1 Fields, total size 5 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ComponentVisibilityOperation</td><td>Value: 211</td></tr>
<tr><td>INT</td><td>visibilityId</td><td>The ID of the integer variable representing visibility</td></tr></table>
### ComponentVisibility Illustration
The `ComponentVisibility` modifier controls whether a component is visible, hidden but still taking up space, or completely removed from the layout.
<div id="visibilityModifierContainer">
<canvas id="visibilityModifierCanvas_v1" width="500" height="250" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('visibilityModifierCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#BBBBBB', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
ctx.clearRect(0, 0, 500, 250);
function drawState(x, y, label, sublabel, vis) {
ctx.strokeStyle = GRAY;
ctx.setLineDash([5, 5]);
ctx.strokeRect(x, y, 120, 100);
ctx.setLineDash([]);
if (vis === 'VISIBLE') {
ctx.fillStyle = BLUE;
ctx.fillRect(x + 10, y + 10, 100, 80);
} else if (vis === 'INVISIBLE') {
ctx.fillStyle = BLUE_TRANS;
ctx.fillRect(x + 10, y + 10, 100, 80);
ctx.setLineDash([2, 2]);
ctx.strokeStyle = BLUE;
ctx.strokeRect(x + 10, y + 10, 100, 80);
ctx.setLineDash([]);
}
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 18px Arial';
ctx.textAlign = 'center';
ctx.fillText(label, x + 60, y + 130);
ctx.font = '14px Arial';
ctx.fillStyle = GRAY;
ctx.fillText(sublabel, x + 60, y + 150);
}
drawState(50, 40, 'VISIBLE', 'Drawn & Sized', 'VISIBLE');
drawState(190, 40, 'INVISIBLE', 'Sized only', 'INVISIBLE');
drawState(330, 40, 'GONE', 'Not Sized', 'GONE');
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## TouchModifier
Touch down modifier. This operation contains a list of action operations executed on touch down
0 Fields, total size 1 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>TouchModifier</td><td>Value: 219</td></tr>
</table>
## TouchUpModifier
Touch up modifier. This operation contains a list of action operations executed on touch up
0 Fields, total size 1 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>TouchUpModifier</td><td>Value: 220</td></tr>
</table>
## OffsetModifierOperation
Shift the component's position
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>OffsetModifierOperation</td><td>Value: 221</td></tr>
<tr><td>FLOAT</td><td>x</td><td>X offset</td></tr><tr><td>FLOAT</td><td>y</td><td>Y offset</td></tr></table>
### Offset Modifier Illustration
The `Offset` modifier shifts the position of a component by a specific amount in X and Y without affecting its layout in the parent.
<div id="offsetModifierContainer">
<canvas id="offsetModifierCanvas_v1" width="500" height="250" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('offsetModifierCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
var ox=100, oy=50;
ctx.clearRect(0, 0, 500, 250);
// Original (dashed)
ctx.setLineDash([8, 8]);
ctx.strokeStyle = '#bbb';
ctx.lineWidth = 2;
ctx.strokeRect(65, 65, 135, 100);
ctx.setLineDash([]);
ctx.font = 'bold 18px Arial';
ctx.fillStyle = '#888';
ctx.fillText('Original', 65, 55);
// Offset
ctx.fillStyle = BLUE_TRANS;
ctx.fillRect(65 + ox, 65 + oy, 135, 100);
ctx.strokeStyle = BLUE;
ctx.lineWidth = 3;
ctx.strokeRect(65 + ox, 65 + oy, 135, 100);
// Arrow
ctx.beginPath();
ctx.moveTo(130, 115);
ctx.lineTo(130 + ox, 115 + oy);
ctx.strokeStyle = GRAY;
ctx.lineWidth = 2;
ctx.stroke();
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 22px Arial';
ctx.fillText('x: ' + ox + ', y: ' + oy, 130 + ox/2 - 40, 115 + oy/2 - 10);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## ZIndexModifierOperation
Define the Z-Index of a component
1 Fields, total size 5 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ZIndexModifierOperation</td><td>Value: 223</td></tr>
<tr><td>FLOAT</td><td>value</td><td>The Z-Index value</td></tr></table>
### ZIndex Illustration
The `ZIndex` modifier determines the stacking order of overlapping components. Components with a higher Z-Index are drawn on top of those with a lower value.
<div id="zIndexContainer">
<canvas id="zIndexCanvas_v1" width="500" height="300" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('zIndexCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#BBBBBB', DARK_GRAY = '#444444', BLUE_LIGHT = 'rgba(0, 71, 171, 0.6)';
ctx.clearRect(0, 0, 500, 300);
function drawBox(x, y, label, color, z) {
ctx.fillStyle = color;
ctx.fillRect(x, y, 150, 100);
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.strokeRect(x, y, 150, 100);
ctx.fillStyle = 'white';
ctx.font = 'bold 20px Arial';
ctx.fillText(label, x + 20, y + 40);
ctx.font = '16px Arial';
ctx.fillText('Z-Index: ' + z, x + 20, y + 70);
}
// Stacked boxes illustration
drawBox(100, 100, 'Box A', '#888', 1);
drawBox(150, 130, 'Box B', BLUE_LIGHT, 2);
drawBox(200, 160, 'Box C', BLUE, 3);
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 22px Arial';
ctx.fillText('Higher Z drawn last (on top)', 120, 50);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## GraphicsLayerModifierOperation
Define transformations (scale, rotation, alpha) for a component
3 Fields, total size 5 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>GraphicsLayerModifierOperation</td><td>Value: 224</td></tr>
<tr><td>INT</td><td>length</td><td>Number of attributes</td></tr><tr><td>REPEATED DATA</td><td colspan="2"><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>INT</td><td>attributeId</td><td>The ID and type of the attribute</td></tr><tr><td>FLOAT</td><td>attributeValue</td><td>The value of the attribute</td></tr></table></td></tr><tr><td>REPEATED DATA</td><td colspan="2"><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>INT</td><td>attributeId</td><td>The ID and type of the attribute</td></tr><tr><td>INT</td><td>attributeValue</td><td>The value of the attribute</td></tr></table></td></tr></table>
### GraphicsLayer Illustration
The `GraphicsLayer` modifier applies advanced transformations and effects like rotation, scaling, and transparency to a component.
<div id="graphicsLayerContainer">
<canvas id="graphicsLayerCanvas_v1" width="500" height="330" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('graphicsLayerCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', DARK_GRAY = '#444444';
ctx.clearRect(0, 0, 500, 330);
var rotation = 15;
var scale = 1.2;
var alpha = 0.7;
ctx.save();
ctx.translate(250, 165);
ctx.rotate(rotation * Math.PI / 180);
ctx.scale(scale, scale);
ctx.globalAlpha = alpha;
ctx.fillStyle = BLUE;
ctx.fillRect(-85, -85, 170, 170);
ctx.strokeStyle = BLUE;
ctx.lineWidth = 3;
ctx.strokeRect(-85, -85, 170, 170);
ctx.restore();
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 22px Arial';
ctx.fillText('rotationZ: ' + rotation + '°', 15, 35);
ctx.fillText('scaleX/Y: ' + scale, 15, 70);
ctx.fillText('alpha: ' + alpha, 15, 105);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## TouchCancelModifier
Touch cancel modifier. This operation contains a list of action operations executed on touch cancel
0 Fields, total size 1 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>TouchCancelModifier</td><td>Value: 225</td></tr>
</table>
## ScrollModifierOperation
Define a scrolling behavior for a component
4 Fields, total size 17 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ScrollModifierOperation</td><td>Value: 226</td></tr>
<tr><td>INT</td><td>direction</td><td>Direction of the scroll (0=VERTICAL, 1=HORIZONTAL)</td></tr><tr><td>FLOAT</td><td>position</td><td>The current scroll position (expression)</td></tr><tr><td>FLOAT</td><td>max</td><td>The maximum scroll position</td></tr><tr><td>FLOAT</td><td>notchMax</td><td>The maximum notch position</td></tr></table>
### Scroll Modifier Illustration
The `Scroll` modifier allows a component to have a larger internal content area than its physical viewport bounds.
<div id="scrollModifierContainer">
<canvas id="scrollModifierCanvas_v1" width="500" height="300" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
var phase = 0;
function draw() {
var canvas = document.getElementById('scrollModifierCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.1)';
ctx.clearRect(0, 0, 500, 300);
var viewportX=150, viewportY=80, viewportW=200, viewportH=180;
var contentW=200, contentH=500;
// Animate scroll position
var maxScroll = contentH - viewportH;
var scrollY = (Math.sin(phase) + 1) / 2 * maxScroll;
phase += 0.02;
// Content Area (Full) - Drawn behind/around
ctx.strokeStyle = GRAY;
ctx.setLineDash([5, 5]);
ctx.strokeRect(viewportX, viewportY - scrollY, contentW, contentH);
ctx.setLineDash([]);
ctx.font = '16px Arial';
ctx.fillStyle = GRAY;
ctx.textAlign = 'left';
ctx.fillText('Content Area (500px)', viewportX + 10, viewportY - scrollY + 25);
// Some content items
for(var i=0; i<7; i++) {
ctx.fillStyle = BLUE_TRANS;
ctx.fillRect(viewportX + 20, viewportY - scrollY + 50 + i*70, contentW - 40, 50);
}
// Viewport (Clip bounds)
ctx.lineWidth = 4;
ctx.strokeStyle = DARK_GRAY;
ctx.strokeRect(viewportX, viewportY, viewportW, viewportH);
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 22px Arial';
ctx.fillText('Viewport (180px)', viewportX, viewportY - 15);
// Scroll Indicator
ctx.fillStyle = BLUE;
var thumbH = (viewportH / contentH) * viewportH;
var thumbY = viewportY + (scrollY / contentH) * viewportH;
ctx.fillRect(viewportX + viewportW + 5, thumbY, 6, thumbH);
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 16px Arial';
ctx.fillText('scroll position', viewportX + viewportW + 15, thumbY + thumbH/2 + 5);
requestAnimationFrame(draw);
}
draw();
})();
</script>
## MarqueeModifierOperation
Define a scrolling marquee effect for a component
6 Fields, total size 25 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>MarqueeModifierOperation</td><td>Value: 228</td></tr>
<tr><td>INT</td><td>iterations</td><td>Number of iterations</td></tr><tr><td>INT</td><td>animationMode</td><td>Animation mode</td></tr><tr><td>FLOAT</td><td>repeatDelayMillis</td><td>Repeat delay in ms</td></tr><tr><td>FLOAT</td><td>initialDelayMillis</td><td>Initial delay in ms</td></tr><tr><td>FLOAT</td><td>spacing</td><td>Spacing between marquee iterations</td></tr><tr><td>FLOAT</td><td>velocity</td><td>Velocity of the marquee animation</td></tr></table>
### Marquee Illustration
The `Marquee` modifier creates a scrolling animation for text that is too long to fit within its container.
<div id="marqueeModifierContainer">
<canvas id="marqueeModifierCanvas_v1" width="500" height="150" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
var offset = 0;
function draw() {
var canvas = document.getElementById('marqueeModifierCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.1)';
ctx.clearRect(0, 0, 500, 150);
var vx=100, vy=50, vw=300, vh=50;
var text = "This is a long marquee text that scrolls continuously...";
ctx.font = '24px Arial';
var tw = ctx.measureText(text).width;
// Viewport
ctx.strokeStyle = DARK_GRAY;
ctx.lineWidth = 2;
ctx.strokeRect(vx, vy, vw, vh);
// Scrolling Content
ctx.save();
ctx.beginPath();
ctx.rect(vx, vy, vw, vh);
ctx.clip();
ctx.fillStyle = BLUE;
ctx.fillText(text, vx + 20 - offset, vy + 35);
// Draw repeat if needed
if (offset > 20) {
ctx.fillText(text, vx + 20 - offset + tw + 50, vy + 35);
}
ctx.restore();
// Labels
ctx.fillStyle = GRAY;
ctx.font = 'bold 18px Arial';
ctx.fillText('Viewport (Static)', vx, vy - 15);
ctx.fillText('Velocity Direction', 180, vy + vh + 35);
// Arrow
ctx.beginPath();
ctx.moveTo(150, 130);
ctx.lineTo(100, 130);
ctx.strokeStyle = BLUE;
ctx.stroke();
offset = (offset + 1) % (tw + 50);
requestAnimationFrame(draw);
}
draw();
})();
</script>
## RippleModifier
Ripple modifier. This modifier will do a ripple animation on touch down
0 Fields, total size 1 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>RippleModifier</td><td>Value: 229</td></tr>
</table>
## WidthInModifierOperation
Add additional constraints to the width
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>WidthInModifierOperation</td><td>Value: 231</td></tr>
<tr><td>FLOAT</td><td>min</td><td>The minimum width, -1 if not applied</td></tr><tr><td>FLOAT</td><td>max</td><td>The maximum width, -1 if not applied</td></tr></table>
## HeightInModifierOperation
Add additional constraints to the height
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>HeightInModifierOperation</td><td>Value: 232</td></tr>
<tr><td>FLOAT</td><td>min</td><td>The minimum height, -1 if not applied</td></tr><tr><td>FLOAT</td><td>max</td><td>The maximum height, -1 if not applied</td></tr></table>
## CollapsiblePriorityModifierOperation
Add additional priority to children of collapsible layouts
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>CollapsiblePriorityModifierOperation</td><td>Value: 235</td></tr>
<tr><td>INT</td><td>orientation</td><td>Horizontal(0) or Vertical (1)</td></tr><tr><td>FLOAT</td><td>priority</td><td>The associated priority</td></tr></table>
## AlignByModifierOperation [EXPERIMENTAL] (added in v7)
!!! WARNING
Experimental operation
Align a component based on a specific baseline or anchor
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>AlignByModifierOperation</td><td>Value: 237</td></tr>
<tr><td>FLOAT</td><td>line</td><td>The ID of the float variable or baseline ID to align by</td></tr><tr><td>INT</td><td>flags</td><td>Alignment flags</td></tr></table>
## LayoutCompute [EXPERIMENTAL] (added in v7)
!!! WARNING
Experimental operation
Compute component position and measure via dynamic expressions
3 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>LayoutCompute</td><td>Value: 238</td></tr>
<tr><td>INT</td><td>type</td><td>Type of computation (0=MEASURE, 1=POSITION)</td></tr><tr><td>INT</td><td>boundsId</td><td>The ID of the float list variable to store the bounds</td></tr><tr><td>BOOLEAN</td><td>animateChanges</td><td>Whether to animate layout changes</td></tr></table>
# Actions & Events Operations
Interactive side effects, including host-side actions and dynamic state updates.
Operations in this category:
| ID | Name | Version | Size (bytes)
| ---- | ---- | ---- | ---- |
| 212 | ValueIntegerChangeActionOperation | v6 | 9
| 213 | ValueStringChangeActionOperation | v6 | 9
| 218 | ValueIntegerExpressionChangeActionOperation | v6 | 17
| 222 | ValueFloatChangeActionOperation | v6 | 9
| 227 | ValueFloatExpressionChangeActionOperation | v6 | 9
## ValueIntegerChangeActionOperation
Action that sets a new value for an integer variable
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ValueIntegerChangeActionOperation</td><td>Value: 212</td></tr>
<tr><td>INT</td><td>targetValueId</td><td>The ID of the integer variable to update</td></tr><tr><td>INT</td><td>value</td><td>The new integer value to assign</td></tr></table>
## ValueStringChangeActionOperation
Action that sets a new value for a string variable
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ValueStringChangeActionOperation</td><td>Value: 213</td></tr>
<tr><td>INT</td><td>targetValueId</td><td>The ID of the string variable to update</td></tr><tr><td>INT</td><td>valueId</td><td>The ID of the new string value to assign</td></tr></table>
## ValueIntegerExpressionChangeActionOperation
Action that updates an integer variable via a dynamic expression
2 Fields, total size 17 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ValueIntegerExpressionChangeActionOperation</td><td>Value: 218</td></tr>
<tr><td>LONG</td><td>targetValueId</td><td>The ID of the integer variable to update</td></tr><tr><td>LONG</td><td>valueExpressionId</td><td>The ID of the expression to evaluate</td></tr></table>
## ValueFloatChangeActionOperation
Action that sets a new value for a float variable
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ValueFloatChangeActionOperation</td><td>Value: 222</td></tr>
<tr><td>INT</td><td>targetValueId</td><td>The ID of the float variable to update</td></tr><tr><td>FLOAT</td><td>value</td><td>The new float value to assign</td></tr></table>
## ValueFloatExpressionChangeActionOperation
Action that updates a float variable via a dynamic expression
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ValueFloatExpressionChangeActionOperation</td><td>Value: 227</td></tr>
<tr><td>INT</td><td>targetValueId</td><td>The ID of the float variable to update</td></tr><tr><td>INT</td><td>valueExpressionId</td><td>The ID of the expression to evaluate</td></tr></table>
# Animation & Particles Operations
Specialized systems for time-based transitions, impulses, and complex particle effects.
Operations in this category:
| ID | Name | Version | Size (bytes)
| ---- | ---- | ---- | ---- |
| 14 | AnimationSpec | v6 | 29
| 161 | ParticlesCreate | v6 | 21 + null x 4
| 163 | ParticlesLoop | v6 | 17 + null x 4 + null x 4
| 164 | ImpulseOperation | v6 | 9
| 165 | ImpulseProcess | v6 | 1
| 194 | ParticlesCompare | v7 | 27 + null x 4 + null x 4 + null x 4
## AnimationSpec
Define the animation specifications for a component
7 Fields, total size 29 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>AnimationSpec</td><td>Value: 14</td></tr>
<tr><td>INT</td><td>animationId</td><td>The ID of the animation</td></tr><tr><td>FLOAT</td><td>motionDuration</td><td>Duration of the motion animation in ms</td></tr><tr><td>INT</td><td>motionEasingType</td><td>The type of easing for motion</td></tr><tr><td>FLOAT</td><td>visibilityDuration</td><td>Duration of visibility animation in ms</td></tr><tr><td>INT</td><td>visibilityEasingType</td><td>The type of easing for visibility</td></tr><tr><td>INT</td><td>enterAnimation</td><td>The entry animation type</td></tr><tr><td>INT</td><td>exitAnimation</td><td>The exit animation type</td></tr></table>
### AnimationSpec Illustration
The `AnimationSpec` defines easing curves and durations for motion and visibility transitions.
#### Easing Curves (Cubic Bezier)
<div id="animationSpecContainer">
<canvas id="animationSpecCanvas_v1" width="500" height="250" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('animationSpecCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444';
ctx.clearRect(0, 0, 500, 250);
function drawCurve(x, y, w, h, cp1x, cp1y, cp2x, cp2y, label) {
ctx.strokeStyle = '#eee';
ctx.lineWidth = 2;
ctx.strokeRect(x, y, w, h);
ctx.beginPath();
ctx.moveTo(x, y + h);
ctx.bezierCurveTo(x + cp1x*w, y + h - cp1y*h, x + cp2x*w, y + h - cp2y*h, x + w, y);
ctx.lineWidth = 5;
ctx.strokeStyle = BLUE;
ctx.stroke();
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 18px Arial';
ctx.textAlign = 'center';
ctx.fillText(label, x + w/2, y + h + 30);
}
drawCurve(35, 35, 100, 100, 0.4, 0.0, 0.2, 1.0, "Standard");
drawCurve(185, 35, 100, 100, 0.4, 0.0, 1.0, 1.0, "Accelerate");
drawCurve(335, 35, 100, 100, 0.0, 0.0, 0.2, 1.0, "Decelerate");
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## ParticlesCreate
Create a particle system
6 Fields, total size 21 + null x 4 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ParticlesCreate</td><td>Value: 161</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the particle system</td></tr><tr><td>INT</td><td>particleCount</td><td>Number of particles to create</td></tr><tr><td>INT</td><td>varCount</td><td>Number of variables associated with each particle</td></tr><tr><td>INT</td><td>varId[0..n]</td><td>The ID of each associated variable</td></tr><tr><td>INT</td><td>equLen[0..n]</td><td>The length of the initialization equation for each variable</td></tr><tr><td>FLOAT[]</td><td>equations[0..n]</td><td>The initialization equations (RPN)</td></tr></table>
### Particles Illustration
The `ParticlesCreate` operation defines a particle emitter that generates short-lived visual elements with dynamic velocity, spread, and life.
<div id="particlesContainer">
<canvas id="particlesCanvas_v1" width="500" height="300" style="border:1px solid #ccc; background: #000; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
var particles = [];
function draw() {
var canvas = document.getElementById('particlesCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
ctx.clearRect(0, 0, 500, 300);
var ex = 250, ey = 150;
if (particles.length < 100) {
particles.push({
x: ex, y: ey,
vx: (Math.random() - 0.5) * 4,
vy: (Math.random() - 0.5) * 4,
life: 1.0,
size: 2 + Math.random() * 4
});
}
for (var i = particles.length - 1; i >= 0; i--) {
var p = particles[i];
p.x += p.vx;
p.y += p.vy;
p.life -= 0.01;
if (p.life <= 0) {
particles.splice(i, 1);
continue;
}
ctx.fillStyle = 'rgba(0, 71, 171, ' + p.life + ')';
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, 0, Math.PI*2);
ctx.fill();
}
ctx.fillStyle = '#888';
ctx.font = 'bold 20px Arial';
ctx.fillText('Dynamic Particle Emitter', 140, 40);
requestAnimationFrame(draw);
}
draw();
})();
</script>
## ParticlesLoop
Update and recycle particles in a system
6 Fields, total size 17 + null x 4 + null x 4 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ParticlesLoop</td><td>Value: 163</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the particle system</td></tr><tr><td>INT</td><td>restartLen</td><td>The length of the restart equation (recycles particle if > 0)</td></tr><tr><td>FLOAT[]</td><td>restartEquation</td><td>The restart equation (RPN)</td></tr><tr><td>INT</td><td>varCount</td><td>The number of update equations</td></tr><tr><td>INT</td><td>equLen[0..n]</td><td>The length of each update equation</td></tr><tr><td>FLOAT[]</td><td>equations[0..n]</td><td>The update equations (RPN)</td></tr></table>
## ImpulseOperation
Execute a list of actions once, and a process block for a fixed duration
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ImpulseOperation</td><td>Value: 164</td></tr>
<tr><td>FLOAT</td><td>duration</td><td>Duration of the impulse</td></tr><tr><td>FLOAT</td><td>startAt</td><td>The start time of the impulse</td></tr></table>
## ImpulseProcess
A block of operations executed during an active impulse
0 Fields, total size 1 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ImpulseProcess</td><td>Value: 165</td></tr>
</table>
## ParticlesCompare (added in v7)
Compare particles and execute conditional logic
10 Fields, total size 27 + null x 4 + null x 4 + null x 4 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ParticlesCompare</td><td>Value: 194</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the particle system</td></tr><tr><td>SHORT</td><td>flags</td><td>Configuration flags</td></tr><tr><td>FLOAT</td><td>min</td><td>The minimum index to process</td></tr><tr><td>FLOAT</td><td>max</td><td>The maximum index to process</td></tr><tr><td>INT</td><td>expLen</td><td>The length of the comparison expression</td></tr><tr><td>FLOAT[]</td><td>expression</td><td>The comparison expression (RPN)</td></tr><tr><td>INT</td><td>res1Count</td><td>The number of equations in the first result block</td></tr><tr><td>FLOAT[]</td><td>res1Equations</td><td>The equations for the first result block</td></tr><tr><td>INT</td><td>res2Count</td><td>The number of equations in the second result block</td></tr><tr><td>FLOAT[]</td><td>res2Equations</td><td>The equations for the second result block</td></tr></table>
# Logic & Expressions Operations
Dynamic calculations, conditional execution, loops, and measurement utilities.
Operations in this category:
| ID | Name | Version | Size (bytes)
| ---- | ---- | ---- | ---- |
| 81 | FloatExpression | v6 | 9
| 144 | IntegerExpression | v6 | 13 + null x 4
| 150 | ComponentValue | v6 | 13
| 157 | TouchExpression | v6 | 37
| 171 | ImageAttribute | v6 | 13
| 172 | TimeAttribute | v6 | 13
| 178 | ConditionalOperations | v6 | 10
| 192 | IdLookup | v7 | 13
| 215 | Loop | v6 | 17
## FloatExpression
Define a float via dynamic expression and optional animation
6 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>FloatExpression</td><td>Value: 81</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the resulting float</td></tr><tr><td>SHORT</td><td>expression_length</td><td>The length of the expression</td></tr><tr><td>SHORT</td><td>animation_length</td><td>The length of the animation spec</td></tr><tr><td>REPEATED FLOAT</td><td>expression</td><td>Sequence of floats representing an expression (RPN)</td></tr><tr><td>REPEATED FLOAT</td><td>animationSpec</td><td>Sequence of floats representing an animation curve</td></tr><tr><td></td><td colspan="2"><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>FLOAT</td><td>duration</td><td>Time in sec</td></tr><tr><td>INT</td><td>bits</td><td>WRAP | INITIAL VALUE | TYPE </td></tr><tr><td>REPEATED FLOAT</td><td>spec</td><td>SPEC PARAMETERS</td></tr><tr><td>FLOAT</td><td>initialValue</td><td>Initial value</td></tr><tr><td>FLOAT</td><td>wrapValue</td><td>Wrap value</td></tr></table></td></tr></table>
## IntegerExpression
Define an integer via dynamic expression
4 Fields, total size 13 + null x 4 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>IntegerExpression</td><td>Value: 144</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the resulting integer</td></tr><tr><td>INT</td><td>mask</td><td>Bitmask representing whether each value is a constant or an ID</td></tr><tr><td>INT</td><td>length</td><td>The number of elements in the expression</td></tr><tr><td>INT[]</td><td>values</td><td>The array of constants, IDs, and operators (RPN)</td></tr></table>
## ComponentValue
Expose a component's layout property (width, height, etc.) as a variable
3 Fields, total size 13 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ComponentValue</td><td>Value: 150</td></tr>
<tr><td>INT</td><td>type</td><td>The type of value to expose</td></tr><tr><td>INT</td><td>componentId</td><td>The ID of the component to reference</td></tr><tr><td>INT</td><td>valueId</td><td>The ID of the variable to store the value in</td></tr></table>
### type
| Name | Value |
| ---- | ---- |
| WIDTH | 0
| HEIGHT | 1
| POS_X | 2
| POS_Y | 3
| POS_ROOT_X | 4
| POS_ROOT_Y | 5
| CONTENT_WIDTH | 6
| CONTENT_HEIGHT | 7
## TouchExpression
Define a float value derived from touch interactions
12 Fields, total size 37 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>TouchExpression</td><td>Value: 157</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the resulting float variable</td></tr><tr><td>FLOAT</td><td>value</td><td>The initial value</td></tr><tr><td>FLOAT</td><td>min</td><td>The minimum allowed value</td></tr><tr><td>FLOAT</td><td>max</td><td>The maximum allowed value</td></tr><tr><td>FLOAT</td><td>velocityId</td><td>Reserved for velocity ID</td></tr><tr><td>INT</td><td>touchEffects</td><td>Haptic feedback and touch behavior flags</td></tr><tr><td>INT</td><td>expression_length</td><td>The length of the touch mapping expression</td></tr><tr><td>REPEATED FLOAT</td><td>expression</td><td>Sequence of floats representing touch mapping (RPN)</td></tr><tr><td>INT</td><td>stopModeAndLen</td><td>Encoded stop mode and length of stop spec</td></tr><tr><td>REPEATED FLOAT</td><td>stopSpec</td><td>Parameters for stop behavior (e.g., notches)</td></tr><tr><td>INT</td><td>easingLen</td><td>The length of the easing spec</td></tr><tr><td>REPEATED FLOAT</td><td>easingSpec</td><td>Parameters for deceleration easing</td></tr></table>
## ImageAttribute
Extract image-related properties (width, height)
5 Fields, total size 13 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ImageAttribute</td><td>Value: 171</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the float variable to store the result</td></tr><tr><td>INT</td><td>imageId</td><td>The ID of the image variable to extract from</td></tr><tr><td>SHORT</td><td>type</td><td>The type of property to extract (0=WIDTH, 1=HEIGHT)</td></tr><tr><td>SHORT</td><td>argsLength</td><td>The number of additional arguments</td></tr><tr><td>REPEATED INT</td><td>args</td><td>The additional arguments</td></tr></table>
## TimeAttribute
Extract time-related information (seconds, hours, etc.)
5 Fields, total size 13 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>TimeAttribute</td><td>Value: 172</td></tr>
<tr><td>INT</td><td>id</td><td>The ID of the float variable to store the result</td></tr><tr><td>INT</td><td>timeId</td><td>The ID of the time variable to extract from</td></tr><tr><td>SHORT</td><td>type</td><td>The type of time information to extract</td></tr><tr><td>SHORT</td><td>argsLength</td><td>The number of additional arguments</td></tr><tr><td>REPEATED INT</td><td>args</td><td>The additional arguments</td></tr></table>
## ConditionalOperations
Execute a list of operations if a condition is met
3 Fields, total size 10 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>ConditionalOperations</td><td>Value: 178</td></tr>
<tr><td>BYTE</td><td>type</td><td>The type of comparison (EQ, NEQ, LT, etc.)</td></tr><tr><td>FLOAT</td><td>varA</td><td>The first value to compare</td></tr><tr><td>FLOAT</td><td>varB</td><td>The second value to compare</td></tr></table>
### type
| Name | Value |
| ---- | ---- |
| TYPE_EQ | 0
| TYPE_NEQ | 1
| TYPE_LT | 2
| TYPE_LTE | 3
| TYPE_GT | 4
| TYPE_GTE | 5
## IdLookup (added in v7)
Look up an ID from an ID collection via index
3 Fields, total size 13 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>IdLookup</td><td>Value: 192</td></tr>
<tr><td>INT</td><td>textId</td><td>The ID of the integer variable to store the result</td></tr><tr><td>FLOAT</td><td>dataSet</td><td>The ID of the collection</td></tr><tr><td>FLOAT</td><td>index</td><td>The index of the ID to retrieve</td></tr></table>
## Loop
Execute a list of operations in a loop
4 Fields, total size 17 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>Loop</td><td>Value: 215</td></tr>
<tr><td>INT</td><td>indexId</td><td>The ID of the variable to store the loop index</td></tr><tr><td>FLOAT</td><td>from</td><td>Starting value</td></tr><tr><td>FLOAT</td><td>step</td><td>Increment value</td></tr><tr><td>FLOAT</td><td>until</td><td>Stop value (exclusive)</td></tr></table>
# Matrix Operations
Coordinate system transformations, including translation, scaling, rotation, skewing, and matrix math.
Operations in this category:
| ID | Name | Version | Size (bytes)
| ---- | ---- | ---- | ---- |
| 126 | MatrixScale | v6 | 17
| 127 | MatrixTranslate | v6 | 9
| 128 | MatrixSkew | v6 | 9
| 129 | MatrixRotate | v6 | 13
| 130 | MatrixSave | v6 | 1
| 131 | MatrixRestore | v6 | 1
| 181 | MatrixFromPath | v7 | 17
| 186 | MatrixConstant | v7 | 9 + null x 4
| 187 | MatrixExpression | v7 | 9 + null x 4
| 188 | MatrixVectorMath | v7 | 15 + null x 4 + null x 4
## MatrixScale
Scale the following draw commands
4 Fields, total size 17 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>MatrixScale</td><td>Value: 126</td></tr>
<tr><td>FLOAT</td><td>scaleX</td><td>The amount to scale in X</td></tr><tr><td>FLOAT</td><td>scaleY</td><td>The amount to scale in Y</td></tr><tr><td>FLOAT</td><td>pivotX</td><td>The x-coordinate for the pivot point</td></tr><tr><td>FLOAT</td><td>pivotY</td><td>The y-coordinate for the pivot point</td></tr></table>
### MatrixScale Illustration
The `MatrixScale` operation scales the coordinate system by `scaleX` and `scaleY` relative to a pivot point `(pivotX, pivotY)`.
<div id="matrixScaleContainer">
<canvas id="matrixScaleCanvas_v1" width="500" height="330" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
var phase = 0;
function draw() {
var canvas = document.getElementById('matrixScaleCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
ctx.clearRect(0, 0, 500, 330);
var px = 100, py = 100;
var sx = 1.2 + Math.sin(phase) * 0.8;
var sy = 1.2 + Math.cos(phase * 0.7) * 0.4;
phase += 0.02;
function drawHouse(ctx, x, y, w, h, color, alpha) {
ctx.save();
ctx.globalAlpha = alpha || 1.0;
ctx.translate(x, y);
// Body
ctx.fillStyle = color;
ctx.fillRect(0, h * 0.4, w, h * 0.6);
// Roof
ctx.beginPath();
ctx.moveTo(0, h * 0.4);
ctx.lineTo(w / 2, 0);
ctx.lineTo(w, h * 0.4);
ctx.closePath();
ctx.fill();
// Door
ctx.fillStyle = 'white';
ctx.fillRect(w * 0.35, h * 0.75, w * 0.3, h * 0.25);
ctx.restore();
}
// Original
ctx.strokeStyle = '#eee';
ctx.lineWidth = 1;
ctx.setLineDash([4, 4]);
ctx.strokeRect(px, py, 80, 80);
ctx.setLineDash([]);
drawHouse(ctx, px, py, 80, 80, '#ddd', 0.5);
ctx.font = '16px Arial';
ctx.fillStyle = '#bbb';
ctx.fillText('Original (80x80)', px, py - 12);
// Scaled
ctx.save();
ctx.translate(px, py);
ctx.scale(sx, sy);
ctx.translate(-px, -py);
drawHouse(ctx, px, py, 80, 80, BLUE, 0.6);
ctx.lineWidth = 2;
ctx.strokeStyle = BLUE;
ctx.strokeRect(px, py, 80, 80);
ctx.restore();
// Pivot Anchor
ctx.beginPath();
ctx.arc(px, py, 7, 0, 2 * Math.PI);
ctx.fillStyle = DARK_GRAY;
ctx.fill();
// Labels
ctx.font = 'bold 20px Arial';
ctx.fillStyle = DARK_GRAY;
ctx.fillText('Pivot (px, py)', px + 15, py - 8);
ctx.font = 'bold 22px Arial';
ctx.fillText('scaleX: ' + sx.toFixed(2), 280, 160);
ctx.fillText('scaleY: ' + sy.toFixed(2), 280, 200);
requestAnimationFrame(draw);
}
draw();
})();
</script>
## MatrixTranslate
Preconcat the current matrix with the specified translation
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>MatrixTranslate</td><td>Value: 127</td></tr>
<tr><td>FLOAT</td><td>dx</td><td>The distance to translate in X</td></tr><tr><td>FLOAT</td><td>dy</td><td>The distance to translate in Y</td></tr></table>
### MatrixTranslate Illustration
The `MatrixTranslate` operation moves the coordinate system by `dx` and `dy`.
<div id="matrixTranslateContainer">
<canvas id="matrixTranslateCanvas_v1" width="500" height="250" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
var phase = 0;
function draw() {
var canvas = document.getElementById('matrixTranslateCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
ctx.clearRect(0, 0, 500, 250);
// Dynamic DX/DY
var dx = 100 + Math.sin(phase) * 100;
var dy = 40 + Math.cos(phase * 0.5) * 30;
phase += 0.02;
var x = 30, y = 30, w = 80, h = 80;
function drawHouse(ctx, x, y, w, h, color, alpha) {
ctx.save();
ctx.globalAlpha = alpha || 1.0;
ctx.translate(x, y);
// Body
ctx.fillStyle = color;
ctx.fillRect(0, 0, w, h);
// Roof
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(w / 2, -h * 0.5);
ctx.lineTo(w, 0);
ctx.closePath();
ctx.fill();
// Door
ctx.fillStyle = 'white';
ctx.fillRect(w * 0.35, h * 0.6, w * 0.3, h * 0.4);
ctx.restore();
}
// Original (Reference)
ctx.strokeStyle = '#eee';
ctx.lineWidth = 1;
ctx.setLineDash([4, 4]);
ctx.strokeRect(x, y, w, h);
ctx.setLineDash([]);
drawHouse(ctx, x, y, w, h, '#ddd', 0.5);
ctx.font = 'bold 18px Arial';
ctx.fillStyle = '#bbb';
ctx.fillText('Original', x, y - 10);
// Transformed
ctx.save();
ctx.translate(dx, dy);
drawHouse(ctx, x, y, w, h, BLUE, 0.6);
ctx.lineWidth = 2;
ctx.strokeStyle = BLUE;
ctx.strokeRect(x, y, w, h);
ctx.restore();
// Translation Arrow
ctx.beginPath();
ctx.moveTo(x + w/2, y + h/2);
ctx.lineTo(x + w/2 + dx, y + h/2 + dy);
ctx.strokeStyle = GRAY;
ctx.setLineDash([8, 5]);
ctx.stroke();
ctx.setLineDash([]);
ctx.fillStyle = 'red';
ctx.beginPath(); ctx.arc(x + w/2 + dx, y + h/2 + dy, 5, 0, Math.PI*2); ctx.fill();
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 22px Arial';
ctx.fillText('dx: ' + Math.round(dx) + ', dy: ' + Math.round(dy), 250, 40);
requestAnimationFrame(draw);
}
draw();
})();
</script>
## MatrixSkew
Current matrix with the specified skew.
2 Fields, total size 9 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>MatrixSkew</td><td>Value: 128</td></tr>
<tr><td>FLOAT</td><td>skewX</td><td>The amount to skew in X</td></tr><tr><td>FLOAT</td><td>skewY</td><td>The amount to skew in Y</td></tr></table>
### MatrixSkew Illustration
The `MatrixSkew` operation applies a skew (shear) transformation to the coordinate system.
<div id="matrixSkewContainer">
<canvas id="matrixSkewCanvas_v1" width="500" height="330" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
var phase = 0;
function draw() {
var canvas = document.getElementById('matrixSkewCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
ctx.clearRect(0, 0, 500, 330);
var skX = Math.sin(phase) * 0.8;
var skY = Math.cos(phase * 0.6) * 0.3;
phase += 0.02;
var x = 120, y = 120, w = 80, h = 80;
function drawHouse(ctx, x, y, w, h, color, alpha) {
ctx.save();
ctx.globalAlpha = alpha || 1.0;
ctx.translate(x, y);
// Body
ctx.fillStyle = color;
ctx.fillRect(0, 0, w, h);
// Roof
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(w / 2, -h * 0.5);
ctx.lineTo(w, 0);
ctx.closePath();
ctx.fill();
// Door
ctx.fillStyle = 'white';
ctx.fillRect(w * 0.35, h * 0.6, w * 0.3, h * 0.4);
ctx.restore();
}
// Original
ctx.strokeStyle = '#eee';
ctx.lineWidth = 1;
ctx.setLineDash([4, 4]);
ctx.strokeRect(x, y, w, h);
ctx.setLineDash([]);
drawHouse(ctx, x, y, w, h, '#ddd', 0.5);
ctx.font = '16px Arial';
ctx.fillStyle = '#bbb';
ctx.fillText('Original', x, y - 10);
// Skewed
ctx.save();
ctx.translate(x, y);
ctx.transform(1, skY, skX, 1, 0, 0);
drawHouse(ctx, 0, 0, w, h, BLUE, 0.6);
ctx.lineWidth = 2;
ctx.strokeStyle = BLUE;
ctx.strokeRect(0, 0, w, h);
ctx.restore();
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 22px Arial';
ctx.fillText('skewX: ' + skX.toFixed(2), 35, 50);
ctx.fillText('skewY: ' + skY.toFixed(2), 35, 80);
requestAnimationFrame(draw);
}
draw();
})();
</script>
## MatrixRotate
Apply rotation to matrix
3 Fields, total size 13 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>MatrixRotate</td><td>Value: 129</td></tr>
<tr><td>FLOAT</td><td>rotate</td><td>Angle to rotate</td></tr><tr><td>FLOAT</td><td>pivotX</td><td>X Pivot point</td></tr><tr><td>FLOAT</td><td>pivotY</td><td>Y Pivot point</td></tr></table>
### MatrixRotate Illustration
The `MatrixRotate` operation rotates the coordinate system by `rotate` degrees relative to a pivot point `(pivotX, pivotY)`.
<div id="matrixRotateContainer">
<canvas id="matrixRotateCanvas_v1" width="500" height="330" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
var angle = 0;
function draw() {
var canvas = document.getElementById('matrixRotateCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
ctx.clearRect(0, 0, 500, 330);
var px = 250, py = 165;
angle = (angle + 1) % 360;
function drawHouse(ctx, x, y, w, h, color, alpha) {
ctx.save();
ctx.globalAlpha = alpha || 1.0;
ctx.translate(x, y);
// Body
ctx.fillStyle = color;
ctx.fillRect(0, -h * 0.6, w, h * 0.6);
// Roof
ctx.beginPath();
ctx.moveTo(0, -h * 0.6);
ctx.lineTo(w / 2, -h);
ctx.lineTo(w, -h * 0.6);
ctx.closePath();
ctx.fill();
// Door
ctx.fillStyle = 'white';
ctx.fillRect(w * 0.35, -h * 0.25, w * 0.3, h * 0.25);
ctx.restore();
}
// Original
ctx.strokeStyle = '#eee';
ctx.lineWidth = 1;
ctx.setLineDash([4, 4]);
ctx.strokeRect(px, py - 80, 80, 80);
ctx.setLineDash([]);
drawHouse(ctx, px, py, 80, 80, '#ddd', 0.5);
ctx.font = '16px Arial';
ctx.fillStyle = '#bbb';
ctx.fillText('Original', px + 8, py - 90);
// Rotated
ctx.save();
ctx.translate(px, py);
ctx.rotate(angle * Math.PI / 180);
drawHouse(ctx, 0, 0, 80, 80, BLUE, 0.6);
ctx.lineWidth = 2;
ctx.strokeStyle = BLUE;
ctx.strokeRect(0, -80, 80, 80);
ctx.restore();
// Pivot Anchor
ctx.beginPath();
ctx.arc(px, py, 7, 0, 2 * Math.PI);
ctx.fillStyle = DARK_GRAY;
ctx.fill();
// Labels
ctx.font = 'bold 20px Arial';
ctx.fillStyle = DARK_GRAY;
ctx.fillText('Pivot (px, py)', px + 15, py + 25);
ctx.font = 'bold 24px Arial';
ctx.fillText('rotate: ' + angle + '°', 35, 50);
requestAnimationFrame(draw);
}
draw();
})();
</script>
## MatrixSave
Save the matrix and clip to a stack
0 Fields, total size 1 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>MatrixSave</td><td>Value: 130</td></tr>
</table>
### MatrixSave/Restore Illustration
`MatrixSave` pushes the current transformation and clip state onto a stack. `MatrixRestore` pops the state, returning the coordinate system to its previous configuration.
<div id="matrixSaveContainer">
<canvas id="matrixSaveCanvas_v1" width="500" height="300" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('matrixSaveCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#BBBBBB', DARK_GRAY = '#444444', BLUE_TRANS = 'rgba(0, 71, 171, 0.2)';
ctx.clearRect(0, 0, 500, 300);
// Initial State
ctx.strokeStyle = '#eee';
ctx.lineWidth = 2;
ctx.strokeRect(50, 50, 100, 100);
ctx.fillStyle = GRAY;
ctx.font = 'bold 16px Arial';
ctx.fillText('Initial Coordinate System', 50, 45);
// SAVE 1
ctx.save();
ctx.translate(150, 0);
ctx.rotate(Math.PI/8);
ctx.fillStyle = BLUE_TRANS;
ctx.fillRect(50, 50, 100, 100);
ctx.strokeStyle = BLUE;
ctx.lineWidth = 3;
ctx.strokeRect(50, 50, 100, 100);
ctx.fillStyle = BLUE;
ctx.fillText('Transformed (State 1)', 50, 45);
// RESTORE 1
ctx.restore();
// Demonstration we are back at initial
ctx.beginPath();
ctx.moveTo(50, 200);
ctx.lineTo(450, 200);
ctx.strokeStyle = DARK_GRAY;
ctx.lineWidth = 2;
ctx.setLineDash([8, 5]);
ctx.stroke();
ctx.setLineDash([]);
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 20px Arial';
ctx.fillText('Back to Initial after MatrixRestore', 100, 235);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## MatrixRestore
Restore the matrix and clip
0 Fields, total size 1 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>MatrixRestore</td><td>Value: 131</td></tr>
</table>
## MatrixFromPath (added in v7)
Set the matrix relative to a path position and tangent
4 Fields, total size 17 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>MatrixFromPath</td><td>Value: 181</td></tr>
<tr><td>INT</td><td>pathId</td><td>The ID of the path</td></tr><tr><td>FLOAT</td><td>percent</td><td>The position on the path [0..1]</td></tr><tr><td>FLOAT</td><td>vOffset</td><td>Vertical offset from the path</td></tr><tr><td>INT</td><td>flags</td><td>Flags for position/tangent</td></tr></table>
### flags
| Name | Value |
| ---- | ---- |
| POSITION_MATRIX_FLAG | 1
| TANGENT_MATRIX_FLAG | 2
### MatrixFromPath Illustration
The `MatrixFromPath` operation calculates a matrix that aligns an object to a specific point and tangent along a path.
<div id="matrixFromPathContainer">
<canvas id="matrixFromPathCanvas_v1" width="500" height="300" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('matrixFromPathCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', GRAY = '#888888', DARK_GRAY = '#444444';
ctx.clearRect(0, 0, 500, 300);
var p0 = {x: 50, y: 200};
var p1 = {x: 150, y: 50};
var p2 = {x: 350, y: 250};
var p3 = {x: 450, y: 100};
// Reference Path
ctx.beginPath();
ctx.moveTo(p0.x, p0.y);
ctx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
ctx.strokeStyle = '#eee';
ctx.lineWidth = 3;
ctx.stroke();
function getBezier(t) {
var cx = 3 * (p1.x - p0.x), bx = 3 * (p2.x - p1.x) - cx, ax = p3.x - p0.x - cx - bx;
var cy = 3 * (p1.y - p0.y), by = 3 * (p2.y - p1.y) - cy, ay = p3.y - p0.y - cy - by;
var x = (ax*t*t*t) + (bx*t*t) + (cx*t) + p0.x;
var y = (ay*t*t*t) + (by*t*t) + (cy*t) + p0.y;
var dx = (3*ax*t*t) + (2*bx*t) + cx;
var dy = (3*ay*t*t) + (2*by*t) + cy;
return {x: x, y: y, angle: Math.atan2(dy, dx)};
}
var t = 0.6;
var state = getBezier(t);
// Tangent Construction Line
ctx.beginPath();
ctx.moveTo(state.x - 50*Math.cos(state.angle), state.y - 50*Math.sin(state.angle));
ctx.lineTo(state.x + 50*Math.cos(state.angle), state.y + 50*Math.sin(state.angle));
ctx.strokeStyle = GRAY;
ctx.setLineDash([5, 5]);
ctx.stroke();
ctx.setLineDash([]);
// Aligned Object (Arrow)
ctx.save();
ctx.translate(state.x, state.y);
ctx.rotate(state.angle);
ctx.beginPath();
ctx.moveTo(-30, -15);
ctx.lineTo(30, 0);
ctx.lineTo(-30, 15);
ctx.fillStyle = BLUE;
ctx.fill();
ctx.restore();
// Anchor point
ctx.beginPath();
ctx.arc(state.x, state.y, 6, 0, 2 * Math.PI);
ctx.fillStyle = DARK_GRAY;
ctx.fill();
ctx.font = 'bold 20px Arial';
ctx.fillStyle = DARK_GRAY;
ctx.textAlign = 'center';
ctx.fillText('Matrix aligned to Path at t=' + t, 250, 40);
ctx.font = '16px Arial';
ctx.fillText('Object rotation = path tangent', state.x, state.y + 45);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## MatrixConstant (added in v7)
A constant matrix and its associated ID
3 Fields, total size 9 + null x 4 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>MatrixConstant</td><td>Value: 186</td></tr>
<tr><td>INT</td><td>matrixId</td><td>The ID of the matrix</td></tr><tr><td>INT</td><td>type</td><td>The type of matrix</td></tr><tr><td>FLOAT[]</td><td>values</td><td>The matrix values</td></tr></table>
### MatrixConstant Illustration
The `MatrixConstant` operation defines a static transformation matrix. In RemoteCompose, matrices are typically 3x3 (9 values) for 2D transformations, following the standard row-major indexing:
| | | |
|:---:|:---:|:---:|
| **MSCALE_X** (0) | **MSKEW_X** (1) | **MTRANS_X** (2) |
| **MSKEW_Y** (3) | **MSCALE_Y** (4) | **MTRANS_Y** (5) |
| **MPERSP_0** (6) | **MPERSP_1** (7) | **MPERSP_2** (8) |
<div id="matrixConstantContainer">
<canvas id="matrixConstantCanvas_v1" width="500" height="350" style="border:1px solid #ccc; background: #fff; display: block; margin: 10px 0;"></canvas>
</div>
<script>
(function() {
function draw() {
var canvas = document.getElementById('matrixConstantCanvas_v1');
if (!canvas) { setTimeout(draw, 100); return; }
var ctx = canvas.getContext('2d');
if (!ctx) return;
var BLUE = '#0047AB', DARK_GRAY = '#444444', GRAY = '#888888';
ctx.clearRect(0, 0, 500, 350);
// Matrix Representation
var startX = 50, startY = 60, cellW = 100, cellH = 40;
var values = ['sX', 'kX', 'tX', 'kY', 'sY', 'tY', 'p0', 'p1', 'p2'];
ctx.strokeStyle = DARK_GRAY;
ctx.lineWidth = 1;
ctx.font = '14px Arial';
ctx.textAlign = 'center';
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
var idx = i * 3 + j;
var x = startX + j * cellW;
var y = startY + i * cellH;
ctx.strokeRect(x, y, cellW, cellH);
ctx.fillStyle = DARK_GRAY;
ctx.fillText(values[idx], x + cellW/2, y + 25);
ctx.fillStyle = GRAY;
ctx.font = '10px Arial';
ctx.fillText('index ' + idx, x + cellW - 25, y + 12);
ctx.font = '14px Arial';
}
}
ctx.fillStyle = DARK_GRAY;
ctx.font = 'bold 20px Arial';
ctx.textAlign = 'left';
ctx.fillText('3x3 Transformation Matrix Layout', startX, 40);
// Example explanation
var exY = 220;
ctx.font = 'bold 16px Arial';
ctx.fillStyle = BLUE;
ctx.textAlign = 'left';
ctx.fillText('Example: Translation only', startX, exY);
ctx.font = '16px monospace';
ctx.fillText('[ 1, 0, tx,', startX + 20, exY + 30);
ctx.fillText(' 0, 1, ty,', startX + 20, exY + 55);
ctx.fillText(' 0, 0, 1 ]', startX + 20, exY + 80);
}
if (document.readyState === 'complete') { draw(); }
else { window.addEventListener('load', draw); setTimeout(draw, 500); }
})();
</script>
## MatrixExpression (added in v7)
A matrix defined by an expression
3 Fields, total size 9 + null x 4 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>MatrixExpression</td><td>Value: 187</td></tr>
<tr><td>INT</td><td>matrixId</td><td>The ID of the matrix</td></tr><tr><td>INT</td><td>type</td><td>The type of matrix</td></tr><tr><td>FLOAT[]</td><td>expression</td><td>The matrix expression</td></tr></table>
## MatrixVectorMath (added in v7)
Evaluates a matrix * vector and outputs a vector
6 Fields, total size 15 + null x 4 + null x 4 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>MatrixVectorMath</td><td>Value: 188</td></tr>
<tr><td>INT</td><td>matrixId</td><td>The ID of the matrix</td></tr><tr><td>SHORT</td><td>opType</td><td>The type of operation (0=multiply)</td></tr><tr><td>INT</td><td>outLength</td><td>The length of the output vector</td></tr><tr><td>INT[]</td><td>outputs</td><td>The IDs to write the output vector</td></tr><tr><td>INT</td><td>inLength</td><td>The length of the input vector</td></tr><tr><td>FLOAT[]</td><td>inputs</td><td>The input vector values</td></tr></table>
# Accessibility Operations
Semantics and properties used to expose UI information to assistive technologies.
Operations in this category:
| ID | Name | Version | Size (bytes)
| ---- | ---- | ---- | ---- |
| 250 | CoreSemantics | v6 | 15
## CoreSemantics
Define accessibility semantics for a component
7 Fields, total size 15 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>CoreSemantics</td><td>Value: 250</td></tr>
<tr><td>INT</td><td>contentDescriptionId</td><td>ID of the content description string</td></tr><tr><td>BYTE</td><td>role</td><td>The accessibility role (BUTTON, CHECKBOX, etc.)</td></tr><tr><td>INT</td><td>textId</td><td>ID of the text string</td></tr><tr><td>INT</td><td>stateDescriptionId</td><td>ID of the state description string</td></tr><tr><td>BYTE</td><td>mode</td><td>Semantics merge mode (SET, MERGE)</td></tr><tr><td>BOOLEAN</td><td>enabled</td><td>Whether the component is enabled</td></tr><tr><td>BOOLEAN</td><td>clickable</td><td>Whether the component is clickable</td></tr></table>
# Miscellaneous Operations
System-level feedback operations like Haptic Feedback.
Operations in this category:
| ID | Name | Version | Size (bytes)
| ---- | ---- | ---- | ---- |
| 177 | HapticFeedback | v6 | 5
## HapticFeedback
Generate an haptic feedback
1 Fields, total size 5 bytes
<br><table><tr><th>Type</th><th>Name</th><th>Description</th></tr>
<tr><td>BYTE</td><td>HapticFeedback</td><td>Value: 177</td></tr>
<tr><td>INT</td><td>hapticFeedbackType</td><td>Type of haptic feedback</td></tr></table>
# List of Version 6 Operations
The following 119 operations were added in version 6 of the format.
| ID | Name | Category | Description |
| ---- | ---- | ---- | ---- |
| 0 | Header | Document Protocol Operations | Document metadata, containing the version, original size & density, capabilities mask |
| 2 | ComponentStart | Layout Operations | Basic component encapsulating draw commands. This is not resizable. |
| 14 | AnimationSpec | Animation & Particles Operations | Define the animation specifications for a component |
| 16 | WidthModifierOperation | Modifier Operations | Set the width dimension on a component |
| 40 | PaintData | Paint & Styles Operations | Encode a Paint object with various properties |
| 42 | DrawRect | Canvas Operations | Draw the specified rectangle |
| 43 | DrawText | Text Operations | Draw a run of text, all in a single direction |
| 44 | DrawBitmap | Canvas Operations | Draw a bitmap |
| 45 | ShaderData | Paint & Styles Operations | Define a shader with associated uniforms |
| 46 | DrawCircle | Canvas Operations | Draw a Circle |
| 47 | DrawLine | Canvas Operations | Draw a line segment |
| 48 | DrawBitmapFontText | Text Operations | Draw text using a bitmap font |
| 51 | DrawRoundRect | Canvas Operations | Draw the specified round-rect |
| 52 | DrawSector | Canvas Operations | Draw the specified sector (pie shape)which will be scaled to fit inside the specified oval |
| 53 | DrawTextOnPath | Text Operations | Draw text along a path |
| 54 | RoundedClipRectModifierOperation | Modifier Operations | Clip the component's content to its rounded rectangular bounds |
| 55 | BackgroundModifierOperation | Modifier Operations | Define a background color or shape for a component |
| 56 | DrawOval | Canvas Operations | Draw the specified oval |
| 57 | DrawTextOnCircle | Text Operations | Draw text along a circle |
| 58 | PaddingModifierOperation | Modifier Operations | Define padding around a component |
| 59 | ClickModifier | Modifier Operations | Click modifier. This operation contains a list of action operations executed on click |
| 63 | Theme | Document Protocol Operations | Set a theme |
| 64 | ClickArea | Protocol Operations | Define a region you can click on |
| 65 | RootContentBehavior | Protocol Operations | If sizing is SIZING_SCALE, mode is one of SCALE_* values. If sizing is SIZING_LAYOUT, mode is one of LAYOUT_* values. |
| 66 | DrawBitmapInt | Canvas Operations | Draw a bitmap using integer coordinates |
| 67 | HeightModifierOperation | Modifier Operations | Set the height dimension on a component |
| 80 | FloatConstant | Data Operations | A float and its associated id |
| 81 | FloatExpression | Logic & Expressions Operations | Define a float via dynamic expression and optional animation |
| 101 | BitmapData | Data Operations | Embed or reference bitmap image data |
| 102 | TextData | Text Operations | Define a static string and associate it with an ID |
| 103 | RootContentDescription | Protocol Operations | Content description of root |
| 107 | BorderModifierOperation | Modifier Operations | Define a border for a component |
| 108 | ClipRectModifierOperation | Modifier Operations | Clip the component's content to its rectangular bounds |
| 124 | DrawPath | Canvas Operations | Draw a path |
| 125 | DrawTweenPath | Canvas Operations | Draw an interpolated path between two paths |
| 126 | MatrixScale | Matrix Operations | Scale the following draw commands |
| 127 | MatrixTranslate | Matrix Operations | Preconcat the current matrix with the specified translation |
| 128 | MatrixSkew | Matrix Operations | Current matrix with the specified skew. |
| 129 | MatrixRotate | Matrix Operations | Apply rotation to matrix |
| 130 | MatrixSave | Matrix Operations | Save the matrix and clip to a stack |
| 131 | MatrixRestore | Matrix Operations | Restore the matrix and clip |
| 133 | DrawTextAnchored | Text Operations | Draw text centered about an anchor point |
| 134 | ColorExpression | Paint & Styles Operations | Define a color via dynamic expression (HSV, ARGB, or Interpolation) |
| 135 | TextFromFloat | Text Operations | Convert a float value into a formatted string |
| 136 | TextMerge | Text Operations | Merge two strings into one |
| 137 | NamedVariable | Data Operations | Add a string name for an ID |
| 138 | ColorConstant | Paint & Styles Operations | Define a static color and associate it with an ID |
| 139 | DrawContent | Canvas Operations | Draw the component content |
| 140 | IntegerConstant | Data Operations | A integer and its associated id |
| 143 | BooleanConstant | Data Operations | A boolean and its associated id |
| 144 | IntegerExpression | Logic & Expressions Operations | Define an integer via dynamic expression |
| 145 | DataMapIds | Data Operations | Encode a collection of named variable IDs |
| 146 | IdListData | Data Operations | A list of IDs |
| 147 | IdListData | Data Operations | A list of floats |
| 148 | LongConstant | Data Operations | A long and its associated id |
| 149 | DrawBitmapScaled | Canvas Operations | Draw a bitmap with scaling and alignment options |
| 150 | ComponentValue | Logic & Expressions Operations | Expose a component's layout property (width, height, etc.) as a variable |
| 151 | TextFromFloat | Text Operations | Look up a string from a collection via index |
| 152 | DrawArc | Canvas Operations | Draw the specified arcwhich will be scaled to fit inside the specified oval |
| 153 | TextLookupInt | Text Operations | Look up a string from a collection via an integer index variable |
| 154 | DataMapLookup | Data Operations | Look up a value in a data map |
| 155 | TextMeasure | Text Operations | Measure text dimensions and store the result in a float variable |
| 156 | TextLength | Text Operations | Get the length of a string and store it in a float variable |
| 157 | TouchExpression | Logic & Expressions Operations | Define a float value derived from touch interactions |
| 158 | PathTween | Canvas Operations | Interpolate between two paths and store the result in a new path ID |
| 159 | PathCreate | Canvas Operations | Start the creation of a dynamic path |
| 160 | PathAppend | Canvas Operations | Append segments to an existing dynamic path |
| 161 | ParticlesCreate | Animation & Particles Operations | Create a particle system |
| 163 | ParticlesLoop | Animation & Particles Operations | Update and recycle particles in a system |
| 164 | ImpulseOperation | Animation & Particles Operations | Execute a list of actions once, and a process block for a fixed duration |
| 165 | ImpulseProcess | Animation & Particles Operations | A block of operations executed during an active impulse |
| 167 | BitmapFontData | Text Operations | Define a bitmap font with glyph metadata and optional kerning |
| 170 | TextMeasure | Text Operations | Extract text-related properties (width, length, etc.) |
| 171 | ImageAttribute | Logic & Expressions Operations | Extract image-related properties (width, height) |
| 172 | TimeAttribute | Logic & Expressions Operations | Extract time-related information (seconds, hours, etc.) |
| 173 | CanvasOperations | Canvas Operations | A collection of canvas operations |
| 174 | DrawContentOperation | Modifier Operations | A modifier that triggers drawing of the component's content |
| 175 | PathCombine | Canvas Operations | Combine two paths using a boolean operation (Union, Intersect, etc.) |
| 176 | FitBoxLayout | Layout Managers | FitBox layout implementation. Only displays the first child component that fits in the available space. |
| 177 | HapticFeedback | Miscellaneous Operations | Generate an haptic feedback |
| 178 | ConditionalOperations | Logic & Expressions Operations | Execute a list of operations if a condition is met |
| 179 | DebugMessage | Protocol Operations | Print debugging messages |
| 180 | ColorAttribute | Paint & Styles Operations | Extract components (Hue, RGB, Alpha) from a color |
| 200 | RootLayout | Layout Operations | Root element for a document. Other components / layout managers are children in the component tree starting from this Root component. |
| 201 | LayoutContent | Layout Operations | Container for child components. BoxLayout, RowLayout and ColumnLayout expect a LayoutComponentContent as a child, encapsulating the components that need to be laid out. |
| 202 | BoxLayout | Layout Managers | Box layout implementation. Child components are laid out independently from one another, and painted in their hierarchy order (first children drawnbefore the latter). Horizontal and Vertical positioningare supported. |
| 203 | RowLayout | Layout Managers | Row layout implementation, positioning components one after the other horizontally. It supports weight and horizontal/vertical positioning. |
| 204 | ColumnLayout | Layout Managers | Column layout implementation, positioning components one after the other vertically. It supports weight and horizontal/vertical positioning. |
| 205 | CanvasLayout | Layout Managers | Canvas implementation. Encapsulates drawing operations. |
| 208 | TextLayout | Text Operations | Text layout implementation |
| 209 | HostAction | Layout Operations | Host action. This operation represents a host action |
| 210 | HostNamedAction | Layout Operations | Host Named action. This operation represents a host action |
| 211 | ComponentVisibilityOperation | Modifier Operations | Set component visibility from a provided integer variable |
| 212 | ValueIntegerChangeActionOperation | Actions & Events Operations | Action that sets a new value for an integer variable |
| 213 | ValueStringChangeActionOperation | Actions & Events Operations | Action that sets a new value for a string variable |
| 214 | ContainerEnd | Document Protocol Operations | End tag for a container component (Row, Column, etc.) |
| 215 | Loop | Logic & Expressions Operations | Execute a list of operations in a loop |
| 216 | HostActionMetadata | Layout Operations | Host action + metadata. This operation represents a host action that can also provides some metadata |
| 217 | StateLayout | Layout Operations | A layout that switches between child layouts based on an index |
| 218 | ValueIntegerExpressionChangeActionOperation | Actions & Events Operations | Action that updates an integer variable via a dynamic expression |
| 219 | TouchModifier | Modifier Operations | Touch down modifier. This operation contains a list of action operations executed on touch down |
| 220 | TouchUpModifier | Modifier Operations | Touch up modifier. This operation contains a list of action operations executed on touch up |
| 221 | OffsetModifierOperation | Modifier Operations | Shift the component's position |
| 222 | ValueFloatChangeActionOperation | Actions & Events Operations | Action that sets a new value for a float variable |
| 223 | ZIndexModifierOperation | Modifier Operations | Define the Z-Index of a component |
| 224 | GraphicsLayerModifierOperation | Modifier Operations | Define transformations (scale, rotation, alpha) for a component |
| 225 | TouchCancelModifier | Modifier Operations | Touch cancel modifier. This operation contains a list of action operations executed on touch cancel |
| 226 | ScrollModifierOperation | Modifier Operations | Define a scrolling behavior for a component |
| 227 | ValueFloatExpressionChangeActionOperation | Actions & Events Operations | Action that updates a float variable via a dynamic expression |
| 228 | MarqueeModifierOperation | Modifier Operations | Define a scrolling marquee effect for a component |
| 229 | RippleModifier | Modifier Operations | Ripple modifier. This modifier will do a ripple animation on touch down |
| 230 | CollapsibleRow | Layout Managers | A row layout that can hide children if space is insufficient |
| 231 | WidthInModifierOperation | Modifier Operations | Add additional constraints to the width |
| 232 | HeightInModifierOperation | Modifier Operations | Add additional constraints to the height |
| 233 | CollapsibleColumn | Layout Managers | A column layout that can hide children if space is insufficient |
| 234 | ImageLayout | Layout Managers | Image layout implementation |
| 235 | CollapsiblePriorityModifierOperation | Modifier Operations | Add additional priority to children of collapsible layouts |
| 236 | RunAction | Operations | This operation runs child actions |
| 250 | CoreSemantics | Accessibility Operations | Define accessibility semantics for a component |
# List of Version 7 Operations
The following 22 operations were added in version 7 of the format.
| ID | Name | Category | Description |
| ---- | ---- | ---- | ---- |
| 49 | DrawBitmapFontTextOnPath | Text Operations | Draw text using a bitmap font along a path |
| 181 | MatrixFromPath | Matrix Operations | Set the matrix relative to a path position and tangent |
| 182 | TextSubtext | Text Operations | Extract a substring from a source string |
| 183 | BitmapTextMeasure | Text Operations | Measure text dimensions specifically for bitmap fonts |
| 184 | DrawBitmapTextAnchored | Text Operations | Draw bitmap font text anchored to a point with alignment (pan) |
| 185 | Rem | Document Protocol Operations | Embed a remark or comment string in the document |
| 186 | MatrixConstant | Matrix Operations | A constant matrix and its associated ID |
| 187 | MatrixExpression | Matrix Operations | A matrix defined by an expression |
| 188 | MatrixVectorMath | Matrix Operations | Evaluates a matrix * vector and outputs a vector |
| 189 | FontData | Data Operations | Embed raw font data in the document |
| 190 | DrawToBitmap | Canvas Operations | Draw to a bitmap |
| 191 | WakeIn | Protocol Operations | Wake up the render loop after a certain amount of time |
| 192 | IdLookup | Logic & Expressions Operations | Look up an ID from an ID collection via index |
| 194 | ParticlesCompare | Animation & Particles Operations | Compare particles and execute conditional logic |
| 196 | ColorTheme | Paint & Styles Operations | Define a color that adapts to the current theme (light/dark) |
| 197 | DataDynamicListFloat | Data Operations | A dynamic list of floats |
| 198 | UpdateDynamicFloatList | Data Operations | Update a value in a dynamic float list |
| 199 | TextTransform | Text Operations | Transform a string (case conversion, trimming, etc.) |
| 237 | AlignByModifierOperation | Modifier Operations | Align a component based on a specific baseline or anchor |
| 238 | LayoutCompute | Modifier Operations | Compute component position and measure via dynamic expressions |
| 239 | CoreText | Layout Managers | Core text layout implementation with advanced styling |
| 240 | FlowLayout | Layout Managers | Flow layout implementation. Positions components one after the other horizontally and wraps to the next line if space is exhausted. |
# Experimental Operations
The following 6 operations are considered experimental and may change in future versions.
| ID | Name | Version | Category | Description |
| ---- | ---- | ---- | ---- | ---- |
| 196 | ColorTheme | v7 | Paint & Styles Operations | Define a color that adapts to the current theme (light/dark) |
| 199 | TextTransform | v7 | Text Operations | Transform a string (case conversion, trimming, etc.) |
| 237 | AlignByModifierOperation | v7 | Modifier Operations | Align a component based on a specific baseline or anchor |
| 238 | LayoutCompute | v7 | Modifier Operations | Compute component position and measure via dynamic expressions |
| 239 | CoreText | v7 | Layout Managers | Core text layout implementation with advanced styling |
| 240 | FlowLayout | v7 | Layout Managers | Flow layout implementation. Positions components one after the other horizontally and wraps to the next line if space is exhausted. |
# Appendix 1: FloatExpressions
| Name | Value | Value | EXAMPLE|
| ---- | ---- | ---- | ---- |
| ADD | Nan(1) | addition | 3,2,ADD -> 5|
| SUB | Nan(2) | subtraction | 3,2,SUB -> 1|
| MUL | Nan(3) | multiplication | 3,2,MUL -> 1|
| DIV | Nan(4) | division | 3,2,DIV -> 1.5 |
| MOD | Nan(5) | Modulus | 3,2,DIV -> 1|
| MIN | Nan(6) | Minimum | 3,2,MIN -> 2|
| MAX | Nan(7) | Maximum | 3,2,MAX -> 3 |
| POW | Nan(8) | power | 3,2,POW -> 9 |
| SQRT | Nan(9) | square root | 2,SQRT-> 1.414 |
| ABS | Nan(10) | absolute value | -2,ABS -> 2 |
| SIGN | Nan(11) | sign | -3, SIGN -> -1 |
| COPY_SIGN | Nan(12) | transfer sign |7, -3, COPY_SIGN -> -7 |
| EXP | Nan(13) | exponent | 1, EXP -> 2.7182 |
| FLOOR | Nan(14) | floor | 32.3,FLOOR -> 32 |
| LOG | Nan(15) | log() | 100, LOG -> 2 |
| LN | Nan(16) | ln() | 100, LN -> 4.605|
| ROUND | Nan(17) | round | 3.5, ROUND -> 4 |
| SIN | Nan(18) | sin | 3.141/2, SIN -> 1.00 |
| COS | Nan(19) | cosine | 3.141/2, COS -> 0.0 |
| TAN | Nan(20) | tan | 3.141/4, TAN -> 1.0 |
| ASIN | Nan(21) | asin | 1, ASIN -> 1.57 |
| ACOS | Nan(22) | acos | 1, ACOS -> 0 |
| ATAN | Nan(23) | atan | 1, ATAN -> 0.785 |
| ATAN2 | Nan(24) | atan2 | x,y,ATAN2 - > atan2(x,y) |
| MAD | Nan(25) | Multiple and add |7, -3, 2, MAD -> -7 |
| IFELSE | Nan(26) | ?: | 1,2,0,IFELES -> 1 |
| CLAMP | Nan(27) | clamp |v,min,max,CLAMP -> v |
| CBRT | Nan(28) | Cube root | |
| DEG | Nan(29) | radians to degree | |
| RAD | Nan(30 | degrees to radians | |
| CEIL | Nan(31) | Ceiling |3.14, CEIL -> 4 |
**Array operation**
Array operation examples are for [1,2,3,4,5]
| Name | Value | Value | EXAMPLE|
| ---- | ---- | ---- | ---- |
| A_DEREF | Nan(32) | Get a value from array | 2, a, A_DEREF -> 3|
| A_MAX | Nan(33) | Maximum of array | a, A_MAX -> 5|
| A_MIN | Nan(34) | Minimum of array | a, A_MIN -> 1|
| A_SUM | Nan(35) | Sum of array | a, A_SUM -> 15|
| A_AVG | Nan(36) | Average of array | a, A_AVG -> 3|
| A_LEN | Nan(37) | Length of array | a, A_LEN -> 5|
WIP
# Appendix 2: IntegerExpressions
| Name | Value | Value | EXAMPLE|
| ---- | ---- | ---- | ---- |
| ADD | Nan(1) | addition | 3,2,ADD -> 5|
| SUB | Nan(2) | subtraction | 3,2,SUB -> 1|
| MUL | Nan(3) | multiplication | 3,2,MUL -> 1|
| DIV | Nan(4) | division | 3,2,DIV -> 1 |
| MOD | Nan(5) | Modulus | 3,2,DIV -> 1|
| SHL | Nan(5) | Modulus | 3,2,SHL -> 1|
| SHR | Nan(5) | Modulus | 3,2,SHR -> 1|
| USHR | Nan(5) | Modulus | 3,2,USHR -> 1|
| OR | Nan(5) | Modulus | 3,2,OR -> 1|
| AND | Nan(5) | Modulus | 3,2,AND -> 1|
| XOR | Nan(5) | Modulus | 3,2,XOR -> 1|
| COPY_SIGN | Nan(5) | Modulus | 3,2,COPY_SIGN -> 1|
| MIN | Nan(5) | Modulus | 3,2,MIN -> 2|
| MAX | Nan(5) | Modulus | 3,2,MAX -> 3|
| NEG | Nan(5) | Modulus | 2,NEG -> -2|
| ABS | Nan(5) | Modulus | -2,ABS -> 2|
| INCR | Nan(5) | Modulus | 2,INCR -> 3|
| DECR | Nan(5) | Modulus | 2,DECR -> 1|
| NOT | Nan(5) | Modulus | 2,NOT -> FFFFFFFD|
| SIGN | Nan(5) | Modulus | 2,SIGN -> 1|
| CLAMP | Nan(5) | Modulus | 4,2,3CLAMP -> 3|
| IFELSE | Nan(5) | Modulus | 3,2,IFELSE -> 1|
| MAD | Nan(5) | Modulus | 4,3,2,MAD -> 1|
<script>
window.markdeepOptions = {
tocDepth: 2,
detectMath: true
};
</script>
<!-- Markdeep: --><style class="fallback">body{visibility:hidden;white-space:pre;font-family:monospace}</style><script src="markdeep.min.js" charset="utf-8"></script><script src="https://morgan3d.github.io/markdeep/latest/markdeep.min.js" charset="utf-8"></script><script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>