A Perspective component only exposes width and height in its position properties. Scaling those stretches a trapezoid as one block and gives no control over the height of each end. To get that control, draw the trapezoid as an embedded SVG drawing (ia.shapes.svg) and bind the path's d attribute to an expression that rebuilds the vertex string whenever an input changes. Work through the checks below in order. Each check names what to read, what the result means, and where to go next.
Check 1: Drawing Component Type
Before anything else, confirm the shape is an embedded drawing and not a linked image. Select the shape in the Designer and read its component type in the Property Editor.
-
Type is
ia.shapes.svgwith aprops.elementsarray: the vertices are live properties. Go to Check 2. - Type is an image component pointing at a file path: the SVG was imported with Save and link. The browser renders the file as a flat picture, and none of its geometry is exposed to bindings. Delete the component and import the file again (see below).
When you drag and drop an .svg file onto a view in the Designer, a dialog offers Save and link or Embed image. Choose Embed image. The SVG markup is parsed into a drawing object, and every element (path, rect, group) becomes an entry in props.elements with its attributes as editable properties.
If you have no source drawing, you can start from a blank SVG. Save this text as a .svg file and drag it onto the view with Embed image selected:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="50mm" height="50mm" viewBox="0 0 50 50" version="1.1">
</svg>
This produces an empty ia.shapes.svg. You then add the path element in the Property Editor and set the viewBox to suit the geometry. Do not move on until the component shows a props.elements array.
Check 2: Path Element Location in props.elements
Expand props.elements and find the element whose type is path and which holds a d string. Its index sets the binding target.
-
Path sits at the top level, for example
elements[0]: the binding target isprops.elements[0].d. Go to Check 3. -
Path sits inside a group (
type: group) with its ownelementsarray: this is typical of Inkscape files. Inkscape wraps drawing content in layer groups and often puts atransform="translate(...)"on the group. Either bind the nested path, for exampleprops.elements[0].elements[0].d, or flatten the drawing before import. If you keep the group, its transform offsets every coordinate you write intod. Account for that offset, or delete the transform and re-anchor the path. -
Element type is
rectorpolygon: a rect cannot become a trapezoid. A polygon usespointsinstead ofd. Replace the element with apath, or bindpointsusing the same technique.
For a simple trapezoid, hand-building the path in the blank drawing is usually faster than cleaning up an Inkscape export. The working reference element is a stroked outline with a transparent fill:
"props": {
"elements": [
{
"type": "path",
"fill": { "paint": "#00000000" },
"stroke": { "paint": "#000000" }
}
],
"preserveAspectRatio": "xMinYMin",
"viewBox": "0 0 200 50"
}
Check 3: viewBox Coordinates and Vertex Map
Read props.viewBox. Every number in d is expressed in viewBox units, not in pixels. The component's position width/height only scales the whole viewBox onto the screen. In the reference build the component is 600 x 150 px and the viewBox is 0 0 200 50, so the drawing space is 200 units wide and 50 units tall.
SVG y grows downward. A larger y value moves a vertex toward the bottom. This is the most common reason a bound shape moves the opposite way from what you expect.
The reference path string is M10 40 L190 40 L180 {value} L20 {value} Z. Its vertices map as follows:
| Command | Vertex | Role |
|---|---|---|
M10 40 |
(10, 40) | Bottom-left corner, start point |
L190 40 |
(190, 40) | Bottom-right corner; baseline fixed at y = 40 |
L180 {value} |
(180, value) | Top-right corner, inset 10 units |
L20 {value} |
(20, value) | Top-left corner, inset 10 units |
Z |
back to (10, 40) | Closes the outline |
- All computed coordinates stay inside 0–200 horizontally and 0–50 vertically: go to Check 4.
- Any vertex can leave that range: the shape clips at the viewBox edge. Either rescale the expression or enlarge the viewBox.
preserveAspectRatio: xMinYMin keeps the drawing proportional and anchored to the top-left of the component. Resizing the component therefore scales the trapezoid without distorting it. With none, the drawing stretches to fill the box and the slopes change with the component size.
Check 4: Binding Chain on props.elements[n].d
Open the binding on the d property. The reference uses an expression binding followed by an expression transform, which keeps the numeric scaling separate from the string assembly.
Expression binding (numeric result):
max({../Slider.props.value},5)/100 * 40
Expression transform (builds the path string from {value}):
concat("M10 40 L190 40 L180 ", {value}, " L20 ", {value}, "Z")
Here ../Slider is a sibling ia.input.slider component. A label bound to ../Slider.props.value displays the raw input for commissioning. The max(...,5) clamp sets a floor on the input. The multiplier maps a 0–100 slider onto the 0–40 band above the baseline. Confirm the slider's min and max properties are 0 and 100 before reusing this scaling.
| Slider value | Computed y (top edge) | Trapezoid height (40 − y) |
|---|---|---|
| 0 | 2 (clamped from 5) | 38 |
| 50 | 20 | 20 |
| 100 | 40 | 0 (top edge lies on the baseline) |
The input drives the top-edge y coordinate, not the height. Raising the slider shrinks the shape, and at 100 it collapses to a line. If the input should mean height, invert it inside the binding, for example 40 - max({../Slider.props.value},5)/100 * 40.
Watch the spacing inside the concat literals. Each number must be separated from the next token by a space or command letter. "L180 " followed by the value is valid. A missing space, as in L1802, produces the wrong vertex without raising an error.
Symptoms Versus Causes
| Symptom | Cause | Correction |
|---|---|---|
No elements property after import |
File imported with Save and link | Re-import with Embed image |
| Binding runs but shape does not change | Binding sits on the wrong element index or on a group, not the path | Re-read the index in props.elements; target the nested path's d
|
| Shape shrinks as the input rises | Input mapped to the y coordinate; SVG y increases downward | Use baseline - scaled_input
|
| Shape flattens to a line at full scale | Scaled y reaches the baseline (40) | Cap the scale below the baseline, or invert the mapping |
| Part of the shape cut off | Computed coordinates exceed the viewBox | Rescale the multiplier or widen the viewBox
|
| Slopes change when the component is resized |
preserveAspectRatio set to none
|
Use xMinYMin
|
| Whole shape offset from its expected position | Inkscape group transform applied on top of the bound coordinates |
Remove the transform or compensate in the expression |
| Vertices land in the wrong place | Missing spaces in concat literals merge numbers |
Check the rendered d string in the Property Editor |
Independent End Heights: Build Procedure
The reference drives both top corners with one value, which gives a symmetric trapezoid. To set the height of each end separately, give each end its own input and its own y term. The procedure below builds a shape with vertical ends on a fixed baseline. Its left and right heights are set independently, which suits hoppers, tapered conveyors, and wedge indicators.
- Create the drawing from the blank SVG with Embed image. Set
props.viewBoxto0 0 200 50andprops.preserveAspectRatiotoxMinYMin. Confirm: the component shows an emptyprops.elementsarray. - Add one element to
props.elementswithtype=path,fill.paint=#00000000, andstroke.paint=#000000. Confirm: the element appears aselements[0]. - Add two
ia.input.slidercomponents and name themSliderLeftandSliderRight. Leave the range at 0–100. Confirm: a label bound to each slider'sprops.valuetracks the handle. - Add an expression binding on
props.elements[0].d. Each end height maps 0–100 onto 0–38 units, so the top vertex never goes above y = 2:
Confirm: the binding preview shows a string that starts withconcat( "M10 40 L190 40 L190 ", 40 - min(max({../SliderRight.props.value},0),100) * 0.38, " L10 ", 40 - min(max({../SliderLeft.props.value},0),100) * 0.38, " Z" )M10 40 L190 40 L190and has no error overlay. - For sloped sides instead of vertical ends, change the top x values to insets, for example
L180andL20, as in the reference. The heights stay independent. - For a live process value, replace each slider reference with a tag or view property reference. Keep the
min/maxclamp so an out-of-range value cannot push a vertex outside the viewBox. - Optionally wrap each numeric term in
numberFormat(..., "0.0"). This keeps thedstring short and readable when you inspect it during commissioning.
Verification
- Put the view in preview mode. Move
SliderLeftalone. Confirm: only the left end rises and falls, while the right end and the baseline stay fixed. - Move
SliderRightalone. Confirm: only the right end moves. - Drive both sliders to 0. Confirm: the shape collapses to a line on y = 40. This is the zero-height state; if it collapses at 100 instead, the mapping is inverted.
- Drive both sliders to 100. Confirm: both ends reach y = 2 with no clipping at the top of the drawing.
- Resize the
ia.shapes.svgcomponent in the Designer. Confirm: the trapezoid scales proportionally and stays anchored top-left, with slopes unchanged. - Select the component and read the evaluated
props.elements[0].dstring at an intermediate setting. Confirm: it contains exactly four coordinate pairs separated by spaces and ends withZ.
FAQ
Can I change the trapezoid shape by binding the component width and height?
No. Position width/height only scales the whole viewBox, so the slopes and end heights keep their ratios. Bind props.elements[n].d on an embedded ia.shapes.svg to move individual vertices.
Does an Inkscape SVG work as a bindable Perspective drawing?
Yes, if you drop it on the view and choose Embed image rather than Save and link. Inkscape usually nests the path inside a layer group with a translate transform, so bind the nested element's d and account for the offset, or hand-build the path in a blank embedded SVG.
Can I drive each end of the trapezoid from a different tag?
Yes. Build the d string with concat() and use a separate y term for each end, such as 40 - clampedValue * 0.38, each referencing its own tag or property. Clamp each input with min/max so every vertex stays inside the viewBox.
Why does my SVG trapezoid get shorter when the value goes up?
SVG y coordinates increase downward. An expression like value/100 * 40 moves the top edge down toward the y = 40 baseline. Invert it to 40 - value/100 * 40 so a higher value produces a taller shape.