Client-side 3D charting for Libertas protocol data
This is the V1 chart contract for schema authors and compatible clients. Schema support and renderer availability are distinct: a client that cannot display a chart follows the chart failure policy. This reference does not certify feature parity for every distributed build.
Presentation uses the schema, exact dependency versions, and locale document associated with the returned value; unrelated installed versions do not redefine that value. See the schema reference.
The existing two-dimensional contract remains independently normative in
Client-side charting for Libertas protocol data.
This document adds a parallel, opt-in 3D namespace. Nothing here changes the
meaning, validation, canonicalization, layout, or rendering of Chart,
ChartChannel, ChartScale, or ChartGuide.
1. Purpose and compatibility boundary
A Libertas protocol response or subscription-data payload may carry a server-prepared three-dimensional chart. The App remains responsible for every domain calculation, including sampling functions, calculating contours, projecting data, grouping series, and selecting topology. The client resolves scales, constructs the declared primitives, lays out guides, presents an accessible camera, and preserves the ordinary typed-data view.
The language-neutral schema uses five attributes:
| Attribute | Placement | Purpose |
|---|---|---|
Chart3D |
A chart-data List, unit-carrier Table, or composition Table |
Select one 3D primitive mark or composition. |
Chart3DChannel |
A direct scalar field of a 3D chart row | Map the field to geometry, appearance, topology, or structural roles. |
Chart3DScale |
A field with one scale-bearing Chart3DChannel role |
Override automatic scale behavior or share a scale. |
Chart3DGuide |
A direct scalar field of a 3D chart row | Configure, suppress, or source an axis, legend, or facet heading. |
Chart3DView |
A unit or composition root, subject to physical-scene ownership | Set projection, initial camera, and independently enabled camera interactions. |
The boundary is strict:
- a node cannot carry both a `Chart3D*` attribute and a `Chart*` attribute;
- a visible result, carrier, composition, or Union wrapper cannot mix two-dimensional and three-dimensional chart children (see composition children and Union wrappers);
- a `Chart3DScale.id` never resolves to a `ChartScale.id`, even when their spelling is identical;
-
clients never interpret `Chart3DChannel`, `Chart3DScale`, `Chart3DGuide`, or
Chart3DViewas aliases of their two-dimensional counterparts; and - a schema without `Chart3D*` attributes has exactly its pre-3D behavior and final schema representation.
An older client does not recognize Chart3D as Chart, so it never enters its
two-dimensional renderer. An unsupported 3D presentation follows the shared
chart failure policy; the protocol response itself remains accepted.
The Hub has no chart renderer and needs no 3D-specific message, persistence, cache, or runtime-value handling. It transports the validated attributes and existing typed values through the ordinary schema and protocol paths.
2. Source and generated syntax
TypeScript source uses:
/**
* Samples in three dimensions.
* @LibertasChart3D(point)
* @LibertasChart3DView(projection=perspective, azimuth=45,
* elevation=30, zoom=1, fov=45, orbit=true)
*/
export type Samples = Sample[];
export interface Sample {
/** @LibertasChart3DChannel(x, tooltip) */
x: number;
/** @LibertasChart3DChannel(y, tooltip) */
y: number;
/** @LibertasChart3DChannel(z, tooltip) */
z: number;
}
Rust source uses:
#[derive(LibertasExport, LibertasAvroDecode)]
pub struct Sample {
#[libertas_chart3d_channel(x, tooltip)]
pub x: f64,
#[libertas_chart3d_channel(y, tooltip)]
pub y: f64,
#[libertas_chart3d_channel(z, tooltip)]
pub z: f64,
}
#[libertas_chart3d(point)]
#[libertas_chart3d_view(
projection = perspective,
azimuth = 45,
elevation = 30,
zoom = 1,
fov = 45,
orbit = true
)]
pub type Samples = Vec<Sample>;
The generated attributes are ordinary schema attributes:
{
"Attributes": [
{ "Name": "Chart3D", "Value": "point" },
{
"Name": "Chart3DView",
"Value": "projection=perspective,azimuth=45,elevation=30,zoom=1,fov=45,orbit=true"
}
]
}
Every attribute value is closed and canonical. Source-language whitespace and quoting are removed, duplicate roles are deduplicated where specified, and properties are written in the fixed orders below. Each schema node may contain at most one attribute of each of the five names. Attribute inheritance uses the ordinary nearest-declaration rule: the nearest value replaces the complete same-named value rather than merging individual roles or properties.
Chart3DScale uses source property kind; the parser result uses type,
which Studio compiles into the package's Display Template as a
scale hint and omits from the final schema. A source declaration using
type is invalid. Numeric properties use the same
finite shortest-round-tripping binary64 canonical spelling as ChartScale.
3. Placement and authoritative data
3.1 Unit charts
A unit chart is a complete snapshot with this effective shape:
List<Table Row> carrying Chart3D=<mark>
The List carries Chart3D. Direct scalar fields of Row carry
Chart3DChannel and optional Chart3DScale and Chart3DGuide. An App duplicates
a logical value into separate fields when it needs independently scaled roles.
A row field may carry Hidden=true. It is chart data, not presentation: the
client keeps it in the retained row value, so a custom chart request can
CopyFrom it.
Chart presentation data is never Hidden: a row field carrying Hidden=true cannot carry Chart3DChannel,
Chart3DScale, or Chart3DGuide; Studio rejects that chart metadata.
A mark may instead be applied to a carrier table:
Table UnitCarrier carrying Chart3D=<mark> {
data: List<Table Row>
transactionContext: any value carrying Hidden=true
...
}
data is the exactly one non-hidden direct field and is the real chart data.
It does not repeat Chart3D; the enclosing table owns the mark. Every other
direct field carries Hidden=true. Hidden values may use any otherwise valid
Libertas type. They do not become marks, channels, guide content, topology,
tooltips, interaction identity, accessibility content, or chart resource
usage. The client retains the complete carrier so a later typed request or
CopyFrom operation can reuse transaction context.
3.2 Compositions
A composition has this effective shape:
Table Composition carrying Chart3D=<composition> {
childA: List<Table RowA> carrying Chart3D=<mark>
childB: List<Table RowB> carrying Chart3D=<mark>
transactionContext: any value carrying Hidden=true
...
}
Every non-hidden direct child must independently carry Chart3D. At least one
such child is required. Other direct fields must carry Hidden=true. Children
are traversed in schema order.
3.3 Protocol Union wrappers
A Response or SubscriptionData Union variant may carry Chart3D itself, or
it may be an unannotated carrier table containing exactly one non-hidden direct
field whose effective type carries Chart3D. Every sibling must carry
Hidden=true.
Clients never recursively search for an arbitrary annotated descendant and
never choose among several chart fields. The effective selected variant or its
one permitted direct chart field is the only presentation root. More than one
visible chart field, one visible non-chart field beside a chart, or a mixture
of Chart and Chart3D invalidates chart presentation.
The complete selected variant remains the authoritative source tree even when the renderer projects its one chart field. Hidden context is never cloned away.
The selected Response or SubscriptionData variant's localized name and
description supply the overall chart title and description, even when the
chart payload is a child field. Child charts within a composition retain their
own localized names and descriptions.
3.4 Complete snapshots
Every response, and every complete subscription snapshot, replaces the prior chart snapshot. A subscription report may instead extend or update the working value through the shared mechanisms described in the 2D snapshot rule. Clients do not otherwise join, patch, or merge rows, grids, series, or topology. An empty list is a valid empty chart when its schema is valid. A data-changing interaction sends an ordinary typed protocol request and receives another complete snapshot. Snapshot replacement does not imply presentation-camera reset; Section 8.2 defines camera continuity.
4. Chart3D
Chart3D contains exactly one lowercase token:
mark = "point" | "line" | "bar" |
"surface" | "wireframe" | "stem"
composition = "layer" | "hconcat" | "vconcat"
chart3d = mark | composition
4.1 Primitive marks
Every primitive requires one x, one y, and one z binding.
| Mark | Additional geometry | Meaning |
|---|---|---|
point |
Forbids x2,y2,z2,u,v |
A three-dimensionally positioned symbol. size produces Bubble 3D. |
line |
Forbids x2,y2,z2,u,v |
One or more ordered 3D polylines. |
bar |
Banded/value, sparse-column, or complete explicit-cuboid form | An axis-aligned 3D bar or column with independently controlled base extents. |
surface |
Continuous x,y,z; optional paired u,v; no secondary endpoints |
Filled rectangular-grid or parameter-grid surface. |
wireframe |
Same topology as surface |
Surface grid edges without filled faces. |
stem |
Optional z2; forbids x2,y2,u,v |
A segment from (x,y,z2) to (x,y,z), using the resolved z-scale base plane when z2 is absent. |
tooltip, detail, key, row, and column are available to every mark.
order is meaningful for line and for deterministic independent-mark paint
and hit-test ties. Surface and wireframe topology is owned by x,y or u,v,
not by list order; an order binding does not change mesh connectivity.
Point and line
point and line use the mapped location (x,y,z). A null or non-finite
required coordinate suppresses a point and breaks a line path. Line rows are
partitioned first by facet and then by the composite raw detail identity.
List order is the default vertex order; an order binding sorts ascending
within each path with source order retained for ties. An Unordered source
list carrying line must declare order.
Each of x, y, and z may independently be continuous, categorical, or an explicitly discrete numeric/temporal position. A categorical point axis uses a point scale unless explicitly banded; the renderer never coerces its localized label back into geometry.
Point and stem-tip symbols may be camera-facing or volumetric. The complete portable identity catalog and automatic categorical assignment order is:
circle,square,triangle,diamond,sphere,cube,pyramid,cylinder
The first four are camera-facing symbols; the final four are plot-space 3D
primitives. circle is the default. An automatic shape scale assigns categories
in canonical raw-domain order, cycles through this list, and repeats from
circle only after exhausting it. An identity shape must be one of these
tokens; it is never a platform class, path, model, image, or URL. One client
cannot substitute a native-only shape or change this assignment order.
size is a finite non-negative relative magnitude. Zero suppresses a point or
stem-tip marker without removing its row from tooltips or accessibility. An
automatic size scale preserves authored zero and chooses a responsive positive
output range for positive values from the plot dimensions, available display
area, and accessibility settings. The schema imposes no fixed projected-radius
range. Numeric identity size is likewise finite and non-negative. For a
camera-facing symbol it controls projected size; for a volumetric symbol it
controls plot-space diameter and therefore participates in perspective. The
resolved shape determines that interpretation consistently on every client.
This permits both screen-stable Bubble 3D symbols and volume-calibrated solid
symbols without making either model mandatory.
Bar
A bar has one of three deterministic forms. Classification is unambiguous: all six positions select the explicit cuboid; otherwise a continuous z selects the normal x/y-base sparse column; every remaining valid shorthand must satisfy the legacy two-band/one-value rule. A z-oriented legacy-shaped row therefore has the same geometry as a normal sparse column and follows the normal form.
The legacy banded/value form has:
- two primary axes with effective
bandscales; and - one continuous value axis, optionally with its matching secondary endpoint.
The value axis may be x, y, or z. When its secondary is present, both endpoints are explicit and either order is valid. Otherwise the client uses the resolved scale base plane: zero when zero is representable and included in the resolved domain, or the lower resolved domain boundary otherwise. The sole continuous axis identifies the value axis, so this form may point along x, y, or z without another orientation property.
The normal sparse-column form uses x and y as base positions and z as the value axis:
x[,x2],y[,y2],z[,z2]
Each base axis may be continuous or discrete. A band base starts with its full
domain-slot bandwidth. An explicit point-scale base, or a continuous base
without a secondary, derives a centered interval from adjacent distinct mapped
positions in the same facet/detail group. Its available width is the smallest
positive mapped gap to either neighbor, or the sole gap at an endpoint. An
isolated position uses one tenth of the mapped axis length. Scale padding is
then applied to that band, point, or inferred continuous interval. A continuous
base with a matching secondary instead uses the authored interval, and padding
has no effect on it. z2 is the authored baseline. Without z2, the baseline
is the same resolved scale base plane described above. Positive log domains,
all-positive or all-negative linear domains, and temporal domains remain valid
without z2; they use their lower resolved boundary.
The explicit-cuboid form requires all six positions:
x,x2,y,y2,z,z2
Every axis is continuous; each secondary has the same effective scalar class, unit, and scale as its primary. Endpoint order is not semantic. No baseline or width is invented.
The sparse-column form may contain any subset of the three secondaries. They
are independent: x2 and y2 author continuous base extents, while z2
authors the baseline. In the banded/value form, only the sole continuous value
axis may have a secondary; band bases cannot. The explicit-cuboid form requires
all three. See secondary-channel compatibility and the
bar forms.
size on a bar is a finite non-negative per-row multiplier applied to both base
extents after explicit/inferred extent and padding resolution; zero produces a
degenerate bar without removing its source row, and the multiplier never
changes the value endpoint or baseline. Automatic shape scales use the portable
bar-shape order box,cylinder,pyramid,sphere, cycling only after all four.
Identity bar shapes use the same four-token catalog; the primitive is aligned
between the baseline and value endpoint and scaled to the two resolved base
extents. The default is box.
Stem
stem uses (x,y,z) as its tip and (x,y,z2) as its base. x2 and y2 are
never accepted. The z binding is continuous. With no z2, the base is the
resolved z-scale base plane: zero when representable and included, otherwise
the lower resolved domain boundary. With z2, both z fields are compatible
continuous values. The x and y positions retain the general continuous or
discrete position compatibility from Section 5.2. size, shape, and fill
roles add a marker at the tip; omitting them produces the path alone.
4.2 Appearance applicability
Appearance is closed rather than renderer-dependent:
| Mark | Accepted appearance roles |
|---|---|
point |
color,fill,stroke,opacity,fillOpacity,strokeOpacity,size,shape,strokeWidth,strokeDash |
line |
color,stroke,opacity,strokeOpacity,strokeWidth,strokeDash |
bar |
color,fill,stroke,opacity,fillOpacity,strokeOpacity,size,shape,strokeWidth,strokeDash |
surface |
color,fill,stroke,opacity,fillOpacity,strokeOpacity,strokeWidth,strokeDash |
wireframe |
color,stroke,opacity,strokeOpacity,strokeWidth,strokeDash |
stem |
color,fill,stroke,opacity,fillOpacity,strokeOpacity,size,shape,strokeWidth,strokeDash |
An appearance role outside its row is invalid rather than ignored. Existing
V1 paint palettes, identity-token validation, opacity ranges, stroke-width
ranges, dash tokens, theme contrast rules, the
Display Template palette, and the
requirement that paint not be the sole carrier of meaning apply unchanged, but through the independent
Chart3DChannel and Chart3DScale attributes.
color supplies the mark-default paint: point fill and boundary, line,
wireframe, and stem stroke, or bar and surface fill. Explicit fill or stroke
overrides that component. Stroke metadata alone never creates a boundary when
neither stroke nor an applicable color resolves one.
For stem, size and shape describe a marker at the tip, while fill and
fillOpacity describe that marker's interior. The stem segment remains the
stroke component. A renderer with no native impulse-marker primitive constructs
the same result from a segment and point marker; that lowering is not visible
in schema or interaction semantics.
4.3 Compositions
| Token | Meaning |
|---|---|
layer |
Draw every child in one depth-tested 3D scene. |
hconcat |
Place independent 3D scenes left to right. |
vconcat |
Place independent 3D scenes top to bottom. |
A layer child is a unit chart or another layer. A direct concat child is
invalid because it does not share one physical scene. Nested layers flatten
depth-first in schema order. Concat children may be units, layers, or further
concats, allowing explicit grids.
Depth normally determines visibility. When two fragments have equal mapped depth, the later flattened child wins, then the later canonical row. A renderer may use a bounded render-only depth bias to implement that tie; it must not change data coordinates, scales, domains, tooltip values, hit identities, or requests. This rule makes server-emitted contour lines layered after a surface stable rather than subject to platform-specific z-fighting.
row and column create facets. Every facet is an independent camera scene
using the unit or enclosing layer's view. A layer may lower compatible children
into one facet grid only when their ordered raw facet domains are identical;
otherwise it uses independent grids or follows the chart failure policy in §12.6.
5. Chart3DChannel
The value is a comma-separated set of roles. Studio deduplicates and emits them in this order:
x,x2,y,y2,z,z2,u,v,
color,fill,stroke,opacity,fillOpacity,strokeOpacity,
size,shape,strokeWidth,strokeDash,
tooltip,detail,order,key,row,column
The vocabulary is:
| Group | Roles | Meaning |
|---|---|---|
| Position | x,x2,y,y2,z,z2 |
Three primary coordinates and paired endpoints. |
| Surface topology | u,v |
Parameter-grid adjacency; not visual scales. |
| Paint | color,fill,stroke |
Default paint or explicit component paint. |
| Paint strength | opacity,fillOpacity,strokeOpacity |
Overall or component alpha. |
| Symbol/stroke | size,shape,strokeWidth,strokeDash |
Point/bar geometry or path appearance. |
| Inspection | tooltip |
Values included in the typed inspector. |
| Structure | detail,order,key |
Series grouping, path/paint order, and stable row identity. |
| Facet | row,column |
Small-multiple partitioning. |
5.1 Multiplicity
All roles except tooltip and detail are singletons within a unit.
tooltip and detail may occur on multiple fields; multiple details form a
composite raw series key in schema field order.
A field may drive multiple scale-bearing roles when every one uses its implicit
scale; for example, z,color,tooltip can position and color by the same raw
value. A field-level Chart3DScale still requires exactly one scale-bearing
role because that attribute has no target property with which to disambiguate
the scale. A source=scale or source=none guide does have a target, so it
may select one matching role on a multi-role field. A field may also combine
scaled roles with non-scale roles such as tooltip, detail, order, key,
or one topology role. Thus x,u,tooltip is valid, and a field-level
Chart3DScale unambiguously configures x because u is not scale-bearing. A
field may not carry both u and v.
x2, y2, and z2 use the corresponding primary scale. A secondary cannot
carry Chart3DScale or exist without its primary. u and v never own a
scale or guide; a field carrying either topology role cannot declare
Chart3DScale for that topology role. A scale on the same field may configure
one other scale-bearing role such as x, y, or z.
5.2 Scalar compatibility
V1 imports the typed scalar identities, formatting, categorical ordering, and provenance rules from the 2D contract. Compatibility is closed:
| Role | Permitted effective scalar values |
|---|---|
x,y,z |
Continuous or explicitly discrete Number, DateOnly Number, DateTime, TimeOnly, or a categorical V1 chart scalar |
x2,y2,z2 |
A continuous ordinary Number, DateOnly Number, DateTime, or TimeOnly compatible with its primary |
u,v |
Finite Number without DateOnly or a topology-owned visual scale |
row,column |
Categorical, or an allowed explicitly banded discrete numeric/temporal scalar |
color,fill,stroke |
Continuous Number, categorical value, or validated identity String |
opacity,fillOpacity,strokeOpacity,size,strokeWidth |
Ordinary Number |
shape,strokeDash |
Categorical or validated identity String; an allowed discrete integral Number may opt into ordinal behavior |
tooltip,detail,order,key |
Any V1 chart scalar |
surface and wireframe additionally require continuous x,y,z; their
geometry cannot use categorical, ordinal, band, or point position scales.
An omitted bar or stem endpoint uses the resolved value-scale base plane and
therefore remains valid for numeric, logarithmic, or temporal continuous axes.
Physical units and Format remain presentation metadata on the same typed raw
value. V1 performs no unit conversion. Paired endpoints and shared scales have
identical effective PhysicalUnit and compatible Format requirements.
5.3 Identity, order, and missing values
Grouping, uniqueness, category domains, series identity, and key identity use canonical raw typed values, never localized labels. Floating negative zero is canonicalized to positive zero for identity. Non-finite values cannot become keys or topology indices.
A non-null key is unique within its unit, facet, and detail tuple. Duplicate
keys invalidate 3D presentation. Without a key, a source row index is identity
only for the current complete snapshot.
Null or non-finite required point/bar/stem coordinates suppress that row. They break line paths. For surface and wireframe they remove the vertex and every incident face or edge. Null optional appearance uses the theme-native default. Null tooltips are omitted.
6. Chart3DScale
Chart3DScale starts with the ChartScale property and type vocabulary, adds
portable logarithm-base control, automatic-domain growth, and 3D bar-base
padding, and participates only in a Chart3D tree:
property-order = id,type,logBase,min,max,mid,zero,reverse,
growMin,growMax,padding
type = linear | log | symlog | sqrt | time | utc |
ordinal | band | point | identity
The generated grammar is a nonempty comma-delimited list of unique
key=value properties in property order. id is a canonical schema
identifier. zero and reverse are Boolean. Numeric or temporal bounds use
the existing type-specific canonical representation. logBase is a finite
number greater than 1, is valid only with type=log, and defaults to 10.
padding is a finite number in 0 <= padding <= 1. It is valid only on a band,
point, or continuous position scale used as a bar base. It removes that
fraction from the resolved domain-slot or neighbor-derived interval, centered
equally on both sides; padding=1 intentionally produces a degenerate
zero-width base rather than invalidating the schema. The default bar-base
padding is 0.3. An explicit matching secondary endpoint is authoritative, so
padding has no effect on that authored extent.
growMin and growMax are independent finite non-negative fractions for an
automatic continuous primary x, y, or z domain. They default to zero and have
no artificial upper bound. After resolving the ordinary non-degenerate domain,
the client maps it into the scale's continuous coordinate, subtracts
growMin * span from an automatic lower side, adds growMax * span to an
automatic upper side, and maps back. This gives logarithmic axes multiplicative
growth while preserving positivity. An explicit min or max remains
authoritative on that side; the corresponding growth property is accepted but
has no effect. Growth is not bar width and is independent of padding.
Absent scale types infer as follows:
- ordinary Number and TimeOnly positions use `linear`;
- DateOnly and DateTime positions use `time`;
- categorical point/line/point-cloud positions use `point`;
- discrete bar bases use `band`, continuous sparse-column bases keep their continuous position scale;
- categorical paint and shape use `ordinal`; and
- numeric paint, opacity, size, and stroke width use `linear`.
identity is not valid for x,y,z, facets, or topology. It is valid only for
the same bounded literal appearance values admitted by the 2D V1 contract and
has no implicit guide.
Automatic domains use every finite, type-valid contributing value before
faceting and viewport culling. Secondary values contribute through their
primary scale. Explicit min/max/mid, zero inclusion, transform restrictions,
DateOnly and DateTime bounds, TimeOnly range, log positivity, sqrt
non-negativity, and categorical ordering are identical to ChartScale.
6.1 Shared scales
Outside a physical layer, an unnamed scale is local to one unit. Within one
flattened layer, compatible unnamed x, y, or z position scales automatically
share by role and union their domains. This is the ordinary multiple-series
case and requires no artificial scale identifier. A categorical point scale
and band scale are compatible in such a group: the common scale is banded when
any contributing bar requires a band, and point/line coordinates use band
centers. The same point-to-band promotion applies to an explicitly named
positional shared-scale group.
A nonempty id explicitly shares a scale inside the containing Chart3D
composition. An explicit different id opts out of automatic layer sharing.
Contributors must have the same position/appearance family, effective scalar
and presentation class, compatible inferred or explicit type, PhysicalUnit,
Format, and output-range class. Explicit domain and logarithm-base properties
cannot conflict. Domain contributions union independently of composition
traversal order. Shared automatic growMin and growMax resolve independently
to the greatest requested fraction, so adding a series never removes requested
margin. padding remains local to each bar binding and may differ between
series sharing the same position scale.
A same-spelled ChartScale.id is unrelated. A scale never crosses a concat
pane. Compatible scales in one flattened layer may share one guide.
7. Chart3DGuide
The grammar is named, closed, and has no position property:
canonical-order = target,source,title,domain,ticks,labels,grid,tickCount,tickStep
target = x | y | z |
color | fill | stroke |
opacity | fillOpacity | strokeOpacity |
size | shape | strokeWidth | strokeDash |
detail | row | column
source = scale | value | span | none
component = auto | none
target and source are required. Optional title, domain, ticks,
labels, and grid values are auto or none. tickCount is a positive safe
integer maximum for generated major ticks. tickStep is a positive finite
major-tick interval in the target scale's native interval convention: raw
mapped numeric units for continuous axes, exponent stride for log, seconds for
DateTime and TimeOnly, and days for DateOnly. On a band or point scale it is a
positive safe-integer stride through the ordered category domain. tickCount
and tickStep are mutually exclusive and are valid only for x/y/z with
source=scale. Keys cannot repeat.
7.1 Placement
source=scale and ordinary source=none are declared on the field owning the
matching primary scale-bearing role. The target must equal one scale-bearing
role on that field and disambiguates a field with multiple implicit roles. The
detail exception is described below. A secondary endpoint and u/v cannot own
a guide.
source=value and source=span are declared on a direct scalar guide-label
field. That field has no scale-bearing Chart3DChannel and no Chart3DScale,
though it may additionally carry tooltip, detail, or key.
- `value` for `x,y,z` creates candidates at the target coordinate of eligible rows.
-
`span` is valid only for `x,y,z` and requires the corresponding
x2,y2,z2binding in the same unit. It describes the explicit continuous interval; a band extent or invented zero is not an authored span. - `value` with `target=detail` is declared on one field carrying `detail`. Its candidates are the complete composite detail tuples, not just that field. The client joins the localized component labels in schema order using its localized list grammar. At most one detail guide exists in a unit.
- `none` with `target=detail` suppresses an otherwise requested detail guide.
- appearance and facet targets accept only `scale` or `none`.
7.2 Components and inference
| Guide | Targets | Components |
|---|---|---|
| 3D axis | x,y,z |
title,domain,ticks,labels,grid |
| Value/span axis labels | x,y,z |
title,domain,ticks,labels,grid |
| Appearance legend | Appearance targets | title,ticks,labels |
| Series legend | detail |
title,ticks,labels |
| Facet heading | row,column |
title,labels |
| Suppressed | Every target | None |
Unsupported properties are errors rather than inert. source=none permits no
optional component property. A value/span guide defaults title=none and its
other supported components to auto. Other guide components default to
auto; unsupported components default to none.
Without an explicit declaration, every non-identity primary binding has
source=scale with defaults. Identity appearance has source=none. One or more
detail bindings infer one composite source=value series legend with default
components. An explicit detail guide configures that legend or suppresses it
with source=none; it never creates separate legends for individual fields of
the composite detail tuple.
3D position axes are placed on camera-visible plot-box edges by a deterministic
client layout; the schema does not expose unstable edge or screen-side choices.
domain is the axis line, ticks and labels are typed scale candidates, and
grid extends them over the applicable plot-box faces. Appearance legends use
the responsive full-screen orientation policy in Section 12.5; previews retain
the inherited bottom-shell form. Facet headings remain attached to their pane
grid in every orientation.
7.3 Position-guide cadence and thinning
An automatic continuous x, y, or z source=scale guide—that is, one without
tickStep—adopts the complete deterministic cadence contract in
Section 6.4.5 of the general chart specification.
Quantitative guides use its closed {1,2,5} * 10^k ladder, temporal guides use
its closed typed ladder, and every probe retains its 4096-candidate bound. The
resolved domain endpoints bound an ordinary aligned tier but do not enter it
unless that lattice generates their exact identity. If no ordinary tier wins,
the priorities-plus-resolved-endpoints terminal tier follows. A nondegenerate
automatic tier with only one candidate cannot win over that terminal endpoint
pair; only a degenerate domain has one automatic raw candidate.
Cadence selection occurs only after the camera, plot box, and viewport have
settled. A quantitative guide projects every candidate onto the final
camera-visible axis edge and selects the first ladder tier whose adjacent
projected anchors meet the general fixed pitch: 64 logical points for a
screen-horizontal edge, the greater of 40 logical points and 1.4 times the
effective caption-font line height for a screen-vertical edge, or 40 logical
points when labels are suppressed. For a quantitative transform, an
edge-length-derived candidate count alone is not sufficient.
A temporal guide instead derives its fixed-pitch capacity from that settled projected edge and uses the general ICU-free nominal-width probe. It constructs the exact UTC or local calendar run only once, after selecting the cadence. Calendar or daylight-saving variation may make that run denser than its nominal probe geometry; the selected-run thinning rule below handles the difference without selecting another cadence or replacing it with terminal endpoints. Neither probe performs localization, text formatting, shaping, wrapping, or measurement, and the temporal probe does not construct a calendar, resolve a time zone, or invoke ICU.
tickStep is an authored semantic cadence and therefore bypasses the automatic
ladder. It generates only values aligned to that interval in the native
convention defined above; it does not synthesize a non-aligned domain endpoint.
tickCount cannot accompany tickStep. Without tickStep, tickCount only
lowers the automatic tier's viewport-derived candidate capacity; it never asks
for exactly that many values, invents evenly spaced samples, or replaces the
required nondegenerate terminal endpoint pair. Thus tickCount=1 does not turn
a nondegenerate automatic guide into a one-label summary: if its essential
terminal candidates cannot fit, ordinary guide fallback applies.
One settled camera-and-viewport state owns one immutable cadence plan. Camera gesture frames transform that plan with the marks and do not iterate the ladder; a changed projected-geometry key selects a replacement only when the camera or viewport settles. Locale and final label width never select a cadence. After selection, the client constructs exactly one bounded visible candidate run and formats the complete run deterministically through the typed rules in the general contract, including its cadence-derived whole-run numeric precision guard. It never formats candidates one at a time or formats a rejected tier.
Final localized text can still require visual thinning. The client tests the
complete anchored power-of-two tiers with strides 1,2,4,8,..., anchored at
candidate index zero and always retaining the last semantic candidate. It
selects the first tier whose final projected label boxes fit with at least four
logical points of separation. A removed candidate's enabled label, tick, and
grid paint are removed atomically; retained accessibility text remains
unabridged and exposes that exact selected semantic run rather than rebuilding
a default-capacity run independently of the settled camera. This paint-only
thinning never changes or reprobes the selected automatic cadence,
never changes an authored tickStep, and never substitutes a different
candidate ladder merely because fewer labels remain visible.
7.4 Other numeric guide runs
A continuous numeric appearance source=scale guide adopts the general
continuous-legend cadence on its exact 160-logical-point rail. Candidate
selection uses the applicable closed automatic ladder and its bounds before
any localization or formatting. The client then formats the complete selected
candidate run, including every retained essential endpoint, as one set. An
appearance legend cannot derive its semantic cadence from an incidental plot
axis or format each rail label independently.
Numeric source=value and source=span guides retain their authored row
candidate identities and therefore do not invoke an automatic scale ladder.
They still format each complete bounded candidate run as one set. For every 3D
numeric guide run, a nonempty authored Format is authoritative. Otherwise the
client uses the general cadence-derived whole-run precision rule: authored
tickStep, selected automatic quantitative cadence, associated scale cadence,
or the normalized smallest positive adjacent gap, in that order as applicable.
It never starts from a generic 15-significant-digit default and never chooses
precision independently per label. The declared value remains in its authored
PhysicalUnit; after the one numeric formatting pass, the client attaches that
provided unit with the general CLDR rule and performs neither automatic unit
conversion nor a second rounding pass. A client must not use a per-value
platform-default number conversion for visible or accessibility guide text.
7.5 Layer ownership
Explicitly shared compatible scale IDs and automatically shared unnamed layer position scales merge their implicit guides. Identical explicit guide declarations in a shared group merge and replace the group default; conflicting declarations are invalid. Different-ID scales remain independent.
After shared-guide resolution, one physical 3D layer permits at most one non-suppressed x guide, one y guide, and one z guide. Because the grammar has no axis-position property, multiple visible axes in one family are ambiguous. Independent layered position scales are permitted only when every extra guide is explicitly suppressed. Appearance legends remain independently resolvable under the existing guide resource and layout policy.
Guide text comes from existing typed/localized sources. No Chart3DGuide
property contains user-visible text.
8. Chart3DView
Chart3DView is optional and contains unique named properties in this
canonical order:
projection,azimuth,elevation,zoom,fov,orbit,
aspectX,aspectY,aspectZ,targetX,targetY,targetZ,pan,zoomEnabled
The closed value contract is:
| Property | Values | Default |
|---|---|---|
projection |
perspective or orthographic |
perspective |
aspectX,aspectY,aspectZ |
Positive finite plot-box ratios | 1,1,1 |
targetX,targetY,targetZ |
Finite normalized plot-space coordinates | 0.5,0.5,0.5 |
azimuth |
Any finite degrees; canonicalized modulo 360 | 45 |
elevation |
Any finite degrees; effective client value clamped to [-90,90] |
30 |
zoom |
Any positive finite scalar | 1 |
fov |
Finite vertical degrees with 0 < fov < 180 |
45 for perspective |
orbit |
true or false |
true |
pan |
true or false |
true |
zoomEnabled |
true or false |
true |
Azimuth canonicalization produces the equivalent value in
-180 <= azimuth < 180; for example, 180 becomes -180. Elevation remains
an arbitrary finite schema value for wire compatibility, but the client clamps
the effective authored/default elevation and every transient update to the
closed [-90,90] degree interval before camera evaluation. Values below -90
therefore behave as -90, and values above 90 behave as 90; the authored
schema value itself is not rewritten. An orthographic view accepts fov for
portable shared declarations but ignores it. Omitted properties use the
defaults without being inserted into final schema JSON.
8.1 Placement
A standalone unit may carry one Chart3DView. In a layer, the outer physical
layer root is the sole view owner; child units and nested layers cannot declare
competing views. An hconcat or vconcat root may carry a default view inherited
by each descendant physical scene that has no nearer view. A child unit or outer
child layer may replace that complete default, and a nested concat may establish
a nearer default for its descendants. This is view inheritance, not camera-state
sharing: every concat child remains an independently interactive scene.
A Chart3DView anywhere else, including a row field, hidden field, function,
or a child of a physical layer, is invalid.
8.2 Camera semantics
The data coordinate system is right-handed with positive z as up. After scale
mapping, x, y, and z span a fitted plot box whose world dimensions have the
normalized aspectX:aspectY:aspectZ ratio. The default remains a cube. Target
coordinates use this normalized post-scale space: each axis cube runs from 0
to 1, the default 0.5,0.5,0.5 is its center, and finite targets outside the
box are permitted.
azimuth rotates around the positive-z axis and elevation moves above or
below the x/y plane. Before evaluating the camera, implementations clamp the
effective elevation to the closed [-90,90] degree interval. The exact poles
are valid camera states; clients do not substitute an epsilon such as 89 or
89.9. At +90 or -90, the client retains authored or transient azimuth and
selects the same deterministic projected-positive-y, then positive-x,
camera-up fallback on every platform. Continuing an outward vertical orbit at
a pole leaves elevation at that pole. Reversing direction moves away
immediately without hysteresis, pole reflection, a 180-degree azimuth change,
or camera roll.
At zoom=1, the complete resolved plot box fits in the available scene after title,
guide, and safe-area layout. Perspective uses fov as its vertical field of
view. Orthographic projection ignores perspective foreshortening and derives
its fitted extent from the same box. Near and far clipping planes are bounded
renderer-derived values and are not schema inputs.
When orbit=true, a view.orbit action updates only transient camera azimuth
and elevation, clamping the resulting elevation to [-90,90]. When
zoomEnabled=true, a view.zoom action updates transient positive zoom without
a fixed schema-level cap. When pan=true, a view.pan action updates the
transient target in normalized plot space. orbit=false fixes rotation;
pan=false fixes the target; zoomEnabled=false disables every user zoom
action while retaining the authored initial zoom. These three properties are
independent capability gates, not a selected interaction mode. They do not
change projection or initial camera meaning. view.reset returns the complete
camera to the effective authored/default view, including its clamped effective
elevation. Section 12 defines the one universal mapping from touch, visible
controls, keyboard input, and accessibility actions to this vocabulary.
A complete data snapshot does not by itself reset presentation state. The
client preserves transient camera orientation, zoom, and target while the
presentation-root identity and effective Chart3DView remain compatible. It
resets only on an explicit user reset, an authored view change, an incompatible
presentation root, or a non-finite retained target. Presentation-root identity
is the selected response field path plus its effective chart/composition schema,
not the current row contents.
Camera motion never alters scale domains, topology, source identity, tooltip values, accessibility order, or request data.
8.3 Universal client action vocabulary
Every Android and iOS renderer exposes the following semantic actions. The names are normative for cross-platform fixtures and localization mapping, but they are client-internal: they are not schema attributes, protocol messages, or serialized camera data. Every datum or camera action also receives its explicit physical-scene identity; it never resolves an implicit last-touched scene.
| Action | Meaning |
|---|---|
inspect.at |
Inspect the nearest eligible visible datum at a screen point. |
inspect.clear |
Clear transient datum inspection. |
datum.next, datum.previous |
Traverse eligible source rows independently of occlusion. |
view.orbit |
Apply a continuous screen-space drag to azimuth and clamped elevation without camera roll. |
view.orbit.step(left/right/up/down) |
Rotate the visible scene by one discrete screen-relative step. |
view.pan |
Apply a continuous screen-plane translation to the camera target. |
view.pan.step(left/right/up/down) |
Move the visible scene by one discrete screen-relative step. |
view.zoom |
Apply a continuous positive zoom factor about the current target. |
view.zoom.step(in/out) |
Apply one discrete zoom factor. |
view.reset |
Restore projection, orientation, target, and zoom to the effective authored/default view. |
fullscreen.open, fullscreen.close |
Push or pop the existing full-screen Chart Explorer destination. |
A discrete orbit step is 15 degrees and clamps its resulting elevation to
[-90,90]. A discrete pan step inverse-projects a screen displacement equal to
10 percent of the physical scene viewport width or height at the current
camera-target plane, using the deterministic camera-right/up basis from
Section 8.2, then moves the visible scene in the requested screen direction. A
discrete zoom-in step multiplies zoom by 1.25; zoom-out divides by 1.25. A
client stops before producing a non-positive or non-finite zoom or any other
non-finite camera value. Continuous gesture sensitivity and damping may remain
platform-native, but direction and action meaning cannot differ.
Client UI uses localized, nontechnical wording: Rotate view, Move view,
Zoom in, Zoom out, Reset view, Explore 3D chart, Back,
Next data point, and Previous data point. orbit and pan remain schema
and internal terms and are not exposed as untranslated user-facing labels.
There is no camera-operation mode, interaction mode, selected
rotate-versus-move state, or corresponding schema/wire variable. Pointer count
selects the continuous action, and each tap-only or accessibility control names
its action explicitly. The existing preview-versus-full-screen navigation state
alone determines whether direct chart manipulation is active.
9. Surface and wireframe topology
Surface topology is explicit and resource-safe. The client never triangulates an arbitrary point cloud, runs Delaunay construction, infers nearest neighbors, or executes a formula.
Rows are partitioned first by facet and then by the composite raw detail
identity. Each group independently defines one grid.
9.1 Rectangular height grid
When u and v are absent:
- x and y are the two topology coordinates as well as visual positions;
- their distinct finite raw values are sorted by canonical typed ordering;
- every present ordered x/y pair has at most one source row;
- a missing pair is an explicit hole, while a duplicate pair invalidates the complete 3D chart; and
- x and y spacing may be non-uniform.
This form represents an ordinary height map z=f(x,y) without copying x/y into
separate index fields. A row with a null or non-finite z retains its unique grid
slot but contributes no finite vertex, so its incident cells form a hole under
Section 9.3.
9.2 Parameter grid
When u and v are present:
- both are required finite ordinary Numbers (see [scalar compatibility](#52-scalar-compatibility) and surface requirements);
- each distinct raw domain is sorted by canonical numeric ordering and may be negative, fractional, non-uniform, or sparse;
- `(u,v)` is unique within the facet/detail group;
- x, y, and z remain arbitrary continuous positions and need not form a height function; and
- a missing `(u,v)` pair creates a hole rather than causing neighbor inference.
This form represents non-separable, curvilinear, folded, or parameterized
surfaces. The App computes the sample coordinates; u,v describe only
adjacency.
9.3 Cells
Let u[i],u[i+1] and v[j],v[j+1] be consecutive entries in the two
canonically sorted distinct topology domains. The corresponding cell is:
a = (u[i], v[j])
b = (u[i+1], v[j])
c = (u[i+1], v[j+1])
d = (u[i], v[j+1])
A cell exists only when all four vertices are present and have finite mapped
x/y/z. surface triangulates it as (a,b,c) and (a,c,d), fixing the diagonal
across platforms. Faces are visible from either side; winding does not remove
data. wireframe draws only the four grid edges and never exposes the internal
triangulation diagonal.
Implementations find cells through checked row/index lookup. They do not
allocate an unchecked u-count * v-count matrix. Repeated edges are painted
once per grid group. Missing or invalid vertices remove incident cells and
break wire paths.
List order does not affect cells. It remains the stable source order for tooltips, accessibility, and equal hit-test ties.
10. Supported families
The supported family names are recipes over the closed primitive set, not additional tokens:
| Family | V1 representation |
|---|---|
| 3D scatter | point with x,y,z |
| 3D line | line with x,y,z; detail partitions paths and order orders vertices |
| 3D bars/columns | bar using x/y sparse or discrete bases plus z value, the any-orientation two-band shorthand, or a complete explicit cuboid |
| Bubble 3D | point plus numeric size |
| Surface/height map | surface using the rectangular x/y grid |
| Wireframe | wireframe using either grid form |
| Contours on surface | A layer with surface followed by server-emitted line children; contour identity/level uses typed detail and tooltip fields |
| Non-uniform surface grid | Non-uniform x/y coordinates, or arbitrary finite ordered u,v domains for a curvilinear grid |
| Waterfall/spectrum 3D | line with one detail identity per trace and an order field along each spectrum |
| Impulse/stem 3D | stem, optionally layered with point tips |
| Arbitrary mathematical surface | App code evaluates and samples the function, then emits surface; parametric functions use u,v |
| 3D point cloud | point, optionally with size,color,detail,key,tooltip |
| Multiple 3D series | detail groups for homogeneous marks or layer for independently typed units and mark kinds |
There are intentionally no bubble, contour, pointCloud, waterfall, or
formula-expression tokens. Contours are not calculated by the client. An App
that changes a formula or sampling density returns another complete typed
snapshot.
11. Localization and visible text
3D semantics have no schema-document dependency:
schema + ScriptValue snapshot -> semantic Chart3DSpec
semantic Chart3DSpec + resolved document? + client resources -> presentation
Geometry, topology, grouping, scales, keys, and interaction identities use only schema and raw values. The existing localized package document supplies optional presentation:
| Existing source | 3D use |
|---|---|
Type DisplayName and Description |
Scene title, help, accessibility context |
Field/member localized Name and Description |
Axis/legend/tooltip captions and help |
| Enumeration member document | Localized category label and help |
| Libertas object resolver | Dynamic names for Device, VirtualDevice, User, Endpoint, or LanDevice identities |
Package StringResources |
Render FormattedText Bytes through LMF1 |
| Client localization catalog | Section 8.3 control terms, errors, fallback, camera actions, and accessibility announcement grammar |
No Chart3D* property contains user-visible wording.
Every visibly rendered row value preserves its semantic source type:
- a closed App vocabulary is an Enumeration;
- App-authored dynamic or parameterized text is
FormattedTextBytes; - a Libertas system identity remains its typed object reference;
- only genuine literal domain/user text, such as a proper name or free-form annotation, is a plain String; and
- a key-only or guide-less identity appearance String is not rendered text.
Localized labels never replace raw identity, change topology, merge categories, or affect ordering. A missing optional label is omitted. A missing essential category/object/formatted-text label invokes the chart failure policy in §12.6 for the complete 3D presentation rather than exposing an internal token.
12. Interaction, accessibility, and fallback
12.1 Inspection and protocol boundary
Hit testing uses final visible, depth-tested geometry. The nearest visible fragment wins; equal-depth ties use flattened layer and row order. A line or wire edge resolves to one of its original endpoint rows. A surface cell resolves to its nearest contributing source vertex. The renderer never synthesizes a request row.
In Chart Explorer, a tap on eligible visible geometry invokes inspect.at; a
tap on empty scene space invokes inspect.clear. Movement beyond the native
touch-slop threshold cancels the pending tap. Inspection and tooltips retain the
original ScriptValue. The projected chart also retains its complete response,
carrier, or selected Union variant so hidden transaction fields remain
available to typed NextRequest and CopyFrom handling. Inspection is
transient presentation state for the current snapshot; the tap itself never
sends a protocol request.
Camera movement is presentation-only. It does not send a protocol request, change the selected row, modify source values, or create a persistent App state. A data-changing control uses the normal typed protocol interaction and replaces the snapshot.
12.2 Preview and full-screen ownership
The application-shell rules in
Runtime endpoint presentation apply to both
2D and 3D charts. A 3D preview is a live but gesture-neutral summary. Its plot
registers no inspection, orbit, pan, pinch, zoom, double-tap, or long-press
recognizer. A tap on the preview or its localized Explore 3D chart action
invokes fullscreen.open and always pushes full-screen Chart Explorer. Ordinary
touches that begin over the preview therefore remain available to the containing
workspace scroll.
Only full-screen Chart Explorer owns direct 3D manipulation, inspection,
tooltips, and camera controls. The application shell's normal Back affordance
invokes fullscreen.close, pops exactly that chart destination, and reveals the
retained previous chart or workspace. It is not a second chart control or a
separate Close button. There is no engaged inline presentation, interaction
mode, or mode variable.
Within Chart Explorer, a pointer sequence belongs to the physical scene in whose final plot bounds it begins. Concat children and facets remain independent scenes; the gesture changes only that scene's transient camera. A layer has one physical scene and therefore one camera. Touches outside a scene remain available to the Chart Explorer shell and its outer scroller.
12.3 Required direct-touch bindings
With screen-reader touch exploration inactive, clients use exactly this V1 mapping inside a full-screen physical scene:
| Touch input | Semantic action |
|---|---|
| Tap visible eligible geometry | inspect.at for the nearest depth-tested datum. |
| Tap empty scene space | inspect.clear. |
| One-finger drag | view.orbit; the visible scene follows the drag until elevation reaches its closed pole. |
| Two-finger centroid translation | view.pan; the visible scene follows the centroid. |
| Pinch or spread | view.zoom; pinch reduces and spread increases zoom about the current target. |
| Simultaneous two-finger translation and span change | Apply enabled view.pan and view.zoom components together. |
One-finger orbit never changes the target or zoom. Two-finger translation never
changes azimuth/elevation. Pinch/spread never changes the target, so an
off-center pinch cannot bypass pan=false. The two-finger twist component is
ignored because V1 has no camera roll.
Double tap, touch-and-hold, edge-origin gestures, and gestures with three or more fingers have no Chart3D V1 action. In particular, double tap never resets the camera, and a client dispatches a recognized single tap without delaying it to await a possible second tap. Touch-and-hold may acquire a future ordinary context-menu meaning, but it cannot be required for inspection or camera control. Operating-system navigation and assistive-technology gestures take priority and clients do not install system-gesture exclusion regions for a chart. A renderer-library default that assigns any of these inputs differently must be overridden.
12.4 Gesture arbitration and continuity
Clients use native touch slop and gesture recognizers and do not capture a sequence merely because its first pointer went down. Control hit targets, assistive technology, and system-edge navigation resolve before chart gestures. For a sequence that reaches the chart:
- exceeding touch slop cancels a pending tap before camera movement begins;
- the arrival of a second pointer atomically ends one-finger orbit, cancels a pending tap, and establishes a new centroid and span without a camera jump;
- any pointer-count change rebases the remaining two-finger transform; after a two-finger transform falls back to one pointer, orbit does not resume until every pointer has lifted and a new sequence begins;
- the arrival of a third pointer cancels chart recognition without changing the last finite camera state, and recognition cannot restart until every pointer has lifted;
-
`orbit=false` ignores and does not consume one-finger drag rather than
remapping it to pan;
pan=falseignores the translation component without consuming or remapping it; andzoomEnabled=falseignores the span-change component without consuming or remapping it; - a sequence with both pan and zoom disabled remains available to an eligible ancestor recognizer; and
- cancellation or interruption commits the last finite camera state, produces no inertia after cancellation, and never resets the view or emits a request.
Native damping or post-release inertia is allowed for an otherwise completed orbit or pan, remains presentation-only, stops on the next input, and is disabled when the platform's reduced-motion preference is active. Reset uses an immediate or short non-spatial transition under reduced motion. Auto-rotation remains excluded.
12.5 Visible and accessible controls
Preview exposes only its Configure action when otherwise applicable and the action that opens Chart Explorer. Each independently interactive physical scene owns one scene-local control set in full-screen Chart Explorer; a root, concat, or facet control never silently targets the last-touched scene. Under the rules in Section 4.3, an unfaceted unit or layer has one scene, while every resolved facet is a separate scene. For a root with one physical scene, its scene-local chrome is also the root chrome.
Full-screen scene chrome responds to the current usable viewport after safe
areas, system bars, and persistent application chrome. Let its finite positive
width and height be W,H. H > W is portrait; H <= W is landscape or
square. Clients use this measured viewport classification rather than device
orientation notifications, the assigned plot-box aspect ratio, or a stale
pre-rotation size. Rotation or window resizing re-evaluates only placement; it
does not recreate the physical scene, reset its camera or inspection, replace
its settled guide semantics, or reset either outer scroll position.
In portrait, one full-width scene-local control strip immediately precedes the physical scene. The client first attempts one complete row. If it does not fit, the same fixed-order controls remain in one conventional horizontal control strip; it scrolls only on its control axis and does not wrap into multiple tall rows. A directional group remains intact and no control is hidden behind a mode, selector, disclosure, or modal surface. The physical scene follows that strip at its assigned size. Its appearance and detail guides follow the scene in the inherited full-width bottom form.
In landscape or square, the client first attempts one trailing scene-local utility rail beside the physical scene. The rail contains the complete control set first. Below the controls, complete appearance and detail guide blocks use their ordinary horizontal form and greedily pack in semantic order from the physical leading edge to the trailing edge, then wrap downward. The controls retain their fixed reading order and wrap only between complete groups. Position guides remain on their deterministic camera-visible plot-box edges, and facet row/column headings remain attached to their pane grid; neither enters the utility rail.
The client measures the localized controls-width rail with the active
accessibility font and platform touch targets. The controls may use that rail
when the remaining physical scene retains a finite positive viewport and
satisfies the existing M = min(200,C) final post-guide plot-width rule, where
C is the plot frame remaining after the utility rail and its gap have been
removed. A positive C < 200 is valid when the final plot retains all of C.
Guides use the same right rail whenever it can contain at least one complete
localized guide block without clipping, shrinking or eliding it. Only when it
cannot fit one complete guide do the guides alone fall back to their full-width
horizontal flow below the physical-scene/control row; admitted controls remain
in the trailing rail. In either placement, guide height never reduces the
physical scene's normally assigned full-screen height. Overflow increases the
one outer vertical Chart Explorer scroll extent; it never creates a private
guide scroller or overlays the scene. Concat children and facets apply this
decision independently to their own final pane width and scene-local ownership.
Each scene's safe control area contains, in fixed reading order when applicable:
the Rotate view directional group, the Move view directional group,
Zoom out, Reset view, then Zoom in. Each directional group contains
explicit left, right, up, and down buttons mapped to its corresponding .step
action. A disabled capability removes its complete directional or zoom group
and leaves no empty slot. Reset view is always present and remains operable
as an idempotent action while the transient camera already equals the effective
authored/default view. The application shell's Back affordance remains outside
every scene-local control set.
Every visible control has a minimum touch target of 44 x 44 points on iOS and
48 x 48 density-independent pixels on Android and remains inside platform
safe and system-gesture insets. Controls and guides never overlay the physical
scene. They do not reduce its assigned height; only an admitted landscape
utility rail may consume its cross-axis width after the required post-guide
plot width has been proven.
The directional groups are directly visible tap-only controls. A client never replaces them with a Rotate/Move selector, disclosure, modal surface, or hidden last-used action, so they add no control mode or presentation state. Direction labels describe movement of the visible scene on screen, not internal camera motion.
The same localized actions are exposed through the platform accessibility API:
- Rotate view left, right, up, and down when `orbit=true`;
- Move view left, right, up, and down when `pan=true`;
- Zoom in and Zoom out when `zoomEnabled=true`;
- Reset view for the explicitly identified physical scene, including while it is already at its effective authored/default view;
- Next data point and Previous data point for virtualized source traversal; and
- Explore 3D chart in preview and the normal Back action in Chart Explorer.
Screen readers own their normal touch gestures; direct-touch passthrough is not
the only way to operate a chart. Visible buttons and accessibility actions call
the same semantic action implementation, and switch control, voice control,
keyboard, and pointer activation remain equivalent. Keyboard operation uses
the focused named controls and introduces no hidden shortcut or mode. Wheel or
trackpad magnification over a physical scene invokes view.zoom when enabled.
After a discrete camera action, or after a continuous gesture and any permitted inertia have reached their final settled state, the client announces exactly one bounded localized summary rather than announcing every animation frame. The summary identifies the physical scene and contains projection, azimuth, elevation, and zoom. When pan changed the target, the same completion announcement includes its normalized x/y/z target as locale-formatted percentages. Angle values are rounded to the nearest degree and zoom is shown with at most two locale-formatted fractional digits.
Accessibility additionally provides:
- localized scene, series, facet, axis, and tooltip descriptions;
- a virtualized traversal of eligible source rows independent of occlusion;
- raw typed values formatted through the same locale, Format, PhysicalUnit, object resolver, and LMF1 pipeline as visible output;
- a nonvisual indication for every meaning otherwise encoded through paint, size, shape, or depth.
Depth, perspective, lighting, or color can never be the only carrier of domain meaning.
12.6 Fallback
The shared chart failure policy applies to all 3D presentation failures, including unknown future marks, channels, scale/view properties, guide tokens, and unsupported valid features.
13. Security and resources
All metadata and data are untrusted, but Chart3D adds no fixed schema-validity
cap for composition depth, unit count, facets, guides, tooltip fields, source
rows, point-cloud points, surface vertices, cells, or line vertices. Neither an
ECharts-GL nor a SciChart-compatible shape becomes invalid merely because it
crosses a Libertas-only count. The enclosing ScriptValue transport owns payload
admission, and each client applies its ordinary configurable memory, work, text,
and native-renderer budgets. Exceeding a runtime budget causes cancellation and
invokes the chart failure policy in §12.6; it never retroactively makes the schema or
protocol response invalid.
A superseded source, schema, locale, size, or camera query cancels pending work. Visible level of detail does not change source domains, tooltips, requests, accessibility, or the ordinary data view. Non-finite geometry is suppressed; recoverable allocation or rendering failures follow the chart failure policy in §12.6. Diagnostics remain bounded and do not dump complete payloads.
The contract admits no executable expressions, arbitrary field paths, HTML, URLs, remote images, shader text, material programs, platform objects, model assets, client-side joins, client topology inference, or arbitrary JSON option blobs.
14. Validation
Studio checks the full schema contract after language extraction and dependency resolution. Clients check the effective schema and returned values when presenting a chart. Invalid or unsupported presentation follows the chart failure policy; it does not alter the response payload.
15. Cross-platform rendering and Hub behavior
Android and iOS may use different native graphics libraries, but they share:
- schema parsing and validation;
- mark geometry and bar/stem lowering;
- scale inference, domains, grouping, ordering, and topology;
- fixed grid triangulation and wire edges;
- flattened layer order and equal-depth ties;
- guide inference, suppression, and single-visible-position-axis rules;
- view defaults, numeric normalization, aspect, target, projection, reset, orbit, pan, zoom, discrete steps, and snapshot-continuity semantics;
- gesture-neutral preview, full-screen-only manipulation, gesture-to-action assignment, pointer-count arbitration, control vocabulary, and the absence of any rotate/pan mode;
- raw row ownership for hit testing and typed requests;
- localization and accessibility meaning;
- resource accounting and cancellation; and
- the shared chart failure policy in §12.6, with its implementation choice.
Theme-native fonts, antialiasing, lighting that carries no data meaning, continuous gesture sensitivity, damping, and permitted inertia may differ. Gesture-to-action assignment, visible-scene direction, discrete step sizes, capability gating, control meaning, geometry, scale meaning, topology, identity, literal colors, camera defaults, and supported/unsupported classification may not differ.
16. Deliberate exclusions
V1 excludes:
- client-evaluated mathematical expressions or arbitrary functions;
- client-calculated contours, regression, aggregation, bins, stacks, or interpolation;
- Delaunay/nearest-neighbor or arbitrary triangle-mesh inference;
- volume, voxel, isosurface, geographic-globe, and 3D model rendering;
- camera roll, authored motion paths, or auto-rotation;
- inline chart manipulation, App-authored gesture mappings or sensitivities, double-tap camera reset, and any interaction/rotate/pan mode variable;
- executable shader/material programs and unverified external texture or image URLs;
- screen-space annotations or 3D text marks;
- persistent selection and linked cross-filter expressions;
- chart-specific incremental mesh/data patch formats; and
- mixing
ChartandChart3Dinside one presented result.
If an App can compute a feature and return explicit typed rows and existing 3D primitives, it does so. A future schema extension is warranted only for a portable presentation or interaction capability that cannot be expressed safely that way.
17. Design references
The vocabulary, supported geometry, and constraints were compared against platform interaction/accessibility guidance and the official ECharts-GL and SciChart contracts. In particular:
-
Apple's gesture guidance reserves system gestures and favors familiar direct manipulation, while its accessibility guidance requires simple alternatives and supports the
44-point iOS target used here; -
Android's gesture guidance defines native tap/drag/transform arbitration, while its accessibility guidance requires alternate actions and supports the
48-dp Android target used here; -
the W3C input-modality guidance documents the need for single-pointer alternatives to complex gestures and dragging; the visible directional and zoom controls provide those alternatives;
-
ECharts-GL's Cartesian bar layout accepts continuous sparse base positions and configurable widths;
-
both ECharts-GL's 3D logarithmic-axis contract and SciChart's LogarithmicAxis3D contract expose a configurable logarithm base;
-
ECharts-GL's 3D axis defaults expose
boundaryGap, while SciChart's Axis3D options expose asymmetricgrowBydomain expansion; -
its 3D grid exposes independent box dimensions, while its view-control model exposes a movable camera center and pan;
-
its surface series accepts sampled equation and arbitrary finite parameter domains, and its surface renderer treats invalid vertices as holes rather than rejecting the scene;
-
ECharts' legend contract provides series selection and labeling, while ECharts-GL's 3D axis defaults expose axis ticks, labels, and grid lines;
-
SciChart's 3D column contract accepts sparse XYZ columns, independent base widths, and multiple shapes;
-
SciChart's 3D camera contract exposes world dimensions, target, projection-specific fov, and zoom, while its 3D modifier contract provides orbit, pan/free-look, and zoom;
-
ECharts-GL's view control can independently disable zoom through zero
zoomSensitivity, while SciChart independently installs its 3D zoom modifier; -
ECharts-GL's point builder retains zero marker size and symbol tokens, while SciChart documents its 3D point-marker primitives;
-
SciChart's 3D legend contract and 3D axis contract cover per-series legends and configurable axis tick generation; and
-
SciChart's surface-mesh contract covers non-uniform grids, holes, wireframes, and contours, while its free-surface contract covers arbitrary finite parameter domains.
Those systems establish the portability floor: Libertas does not impose a semantic restriction that both renderers avoid. Libertas still retains its own typed payload, server-computed safe data, closed metadata, localization, fallback, and cross-platform semantics.