AMAP Conventions Reference

This page documents the AMAP core profile used by default:

set_geometry_from_attributes!(
    mtg,
    ref_meshes;
    convention=default_amap_geometry_convention(),
)

If you are new to AMAP-style reconstruction, read this page in this order:

  1. Alias tables (what attribute names are accepted).
  2. Practical parameter guide (what each parameter changes in geometry).
  3. Option impact plots (what the changes look like in 3D).

The same MTG can be reconstructed very differently depending on orientation and insertion options. Most differences come from a small set of parameters (InsertionMode, insertion/euler angles, order overrides, and verticil handling), so those are explained with short examples below.

If you want a scenario-based chooser ("I have start coordinates but no end coordinates"), use the AMAP Reconstruction Decision Guide.

1. Naming Conventions (Column Aliases)

1.1 Geometry Convention (default_amap_geometry_convention())

Scale columns

Semantic meaningAlias lookup orderDefault
LengthLength, length, L, l1.0
WidthWidth, width, W, w1.0
ThicknessThickness, thickness, Depth, depthWidth value

Angle columns

Semantic meaningAlias lookup orderFrameUnit defaultDefault
X insertionXInsertionAngle, x_insertion_angle, xinsertionangleLocaldeg0
Y insertionYInsertionAngle, y_insertion_angle, yinsertionangleLocaldeg0
Z insertionZInsertionAngle, z_insertion_angle, zinsertionangleLocaldeg0
X EulerXEuler, x_euler, xeulerLocaldeg0
Y EulerYEuler, y_euler, yeulerLocaldeg0
Z EulerZEuler, z_euler, zeulerLocaldeg0

Translation columns

Semantic meaningAlias lookup orderDefault
X translationXX, xx0.0
Y translationYY, yy0.0
Z translationZZ, zz0.0

1.2 AMAP Options (default_amap_reconstruction_options())

Option semanticDefault aliases / values
Insertion mode aliasesInsertionMode, insertion_mode, Insertion, insertion
Phyllotaxy aliasesPhyllotaxy, phyllotaxy, PHYLLOTAXY
Verticil mode:rotation360
Geometrical constraint aliasesGeometricalConstraint, geometrical_constraint, GeometryConstraint, geometry_constraint
Explicit-coordinate handling mode (explicit_coordinate_mode, alias: coordinate_delegate_mode):topology_default (:explicit_rewire_previous, :explicit_start_end_required)
Azimuth aliasesAzimuth, azimuth
Elevation aliasesElevation, elevation
Deviation aliasesDeviationAngle, deviation_angle
Orthotropy aliasesOrthotropy, orthotropy
Stiffness angle aliasesStiffnessAngle, stiffness_angle
Stiffness source aliasesStifness, stifness, Stiffness, stiffness
Stiffness tapering aliasesStifnessTapering, stifness_tapering, StiffnessTapering, stiffness_tapering
Stiffness apply aliasesStiffnessApply, stiffness_apply
Stiffness straightening aliasesStiffnessStraightening, stiffness_straightening
Broken-segment aliasesBroken, broken
Plagiotropy aliasesPlagiotropy, plagiotropy
NormalUp aliasesNormalUp, normal_up
Orientation reset aliasesOrientationReset, orientation_reset, Global, global
Endpoint X aliasesEndX, end_x, endx
Endpoint Y aliasesEndY, end_y, endy
Endpoint Z aliasesEndZ, end_z, endz
Allometry enabledtrue
Allometry width/height interpolationtrue
Allometry terminal default length1.0
Allometry terminal default width1.0
Allometry terminal default height1.0
Order attribute:branching_order
Auto order computetrue
Order override mode:override
Insertion-by-order mapempty Dict{Int,Float64}
Phyllotaxy-by-order mapempty Dict{Int,Float64}

1.3 Topology columns (used when XX/YY/ZZ are missing)

ColumnAliasesMeaningDefault
OffsetOffset, offsetPosition along bearer where + organ startsBearer Length
Border insertion offsetBorderInsertionOffset, border_insertion_offset, BorderOffset, border_offsetLateral shift for insertion modeDepends on mode
Insertion modeInsertionMode, insertion_mode, Insertion, insertionCENTER, BORDER (SURFACE alias), WIDTH, HEIGHTBORDER
PhyllotaxyPhyllotaxy, phyllotaxy, PHYLLOTAXYFallback insertion azimuth when XInsertionAngle is missing0

First matching alias wins.

2. Parameter Guide for First-Time Users

2.1 Size and scale parameters

Length, Width, and Thickness scale the reference mesh in local coordinates (+X is organ length in AMAP). If Thickness is absent, width is reused, which can make flat organs look unnaturally thick.

Example: changing a leaf from Length=0.20 to Length=0.32 stretches only the local +X axis and increases overlap with neighbors without changing insertion position.

When to use what:

  • Use measured organ dimensions when available.
  • Keep Thickness explicit for leaves if your reference mesh is not already very thin.
  • Use a single fallback width/thickness policy only for synthetic plants or quick debugging.

2.2 Insertion angles and Euler angles

Insertion angles define attachment orientation relative to the bearer; Euler angles are post-attachment local refinements.

Example: increasing YInsertionAngle from 35 to 65 lifts a leaf away from the stem axis. Adding ZEuler=20 then twists that leaf around its own axis without changing its insertion anchor.

When to use what:

  • Use insertion angles for phyllotaxy/branch architecture.
  • Use Euler angles for mesh-pose corrections (twist/roll) after topology is correct.
  • Avoid compensating missing insertion data with large Euler rotations, because that breaks architectural meaning.

2.3 Explicit translation vs topology placement

When XX/YY/ZZ are present, placement is explicit. When missing, PlantGeom reconstructs position from topology (Offset, insertion mode, bearer frame).

With endpoint columns (EndX/EndY/EndZ), PlantGeom uses:

  • start = explicit translation if present, else topology-derived base
  • end = provided endpoint coordinates
  • orientation and effective Length = inferred from (start -> end)

Example: setting XX/YY/ZZ for one leaf detaches it from topology and can produce a correct but non-botanical placement if coordinates are inconsistent with bearer geometry.

When to use what:

  • Use topology-based placement for botanical reconstructions.
  • Use explicit XX/YY/ZZ when ingesting already solved 3D coordinates from another pipeline.
  • Use EndX/EndY/EndZ when start-end coordinates are known and must dominate angle-derived orientation.
  • Use AmapReconstructionOptions(explicit_coordinate_mode=:explicit_start_end_required) to require complete start/end coordinates on explicit-coordinate nodes (AMAP CoordinateDelegate3 behavior).
  • Use AmapReconstructionOptions(explicit_coordinate_mode=:explicit_rewire_previous) for topology-editor style imports where each node position rewires the previous segment and the current node becomes a point-anchor (AMAP CoordinateDelegate2 behavior).

2.6 Endpoint coordinates (EndX/EndY/EndZ)

EndX/EndY/EndZ activate endpoint-driven reconstruction for that node.

Practical precedence:

  1. Base position is resolved first (explicit XX/YY/ZZ if present, otherwise topology).
  2. If EndX/EndY/EndZ are all numeric, orientation and length are computed from base-to-end.
  3. Angle stages (Insertion, azimuth/elevation, orthotropy/stiffness angle, deviation, Euler, projection, geometrical constraint) are skipped for that node.
  4. Width/thickness scaling still uses Width/Thickness.

Notes:

  • If only some endpoint columns are present, endpoint override is ignored (lenient fallback).
  • If endpoint equals base (zero-length vector), endpoint override is ignored.
  • Successor :< nodes continue from this computed endpoint through normal topology rules.

2.7 Allometry delegate semantics (AMAP core)

PlantGeom now applies AMAP-style allometry preprocessing before geometric stages:

  • Missing Width/Height can be interpolated along :< axis neighborhoods.
  • If only one of width/height is provided, the other is mirrored.
  • If a node has components (:/), measured allometry is propagated to missing component values.
  • Component length propagation uses AMAP split-vs-copy behavior: successor-chain components split parent Length; direct (no-succession) components copy parent Length.
  • For non-terminal nodes with no measured allometry, size collapses to zero (controller behavior).
  • Terminal nodes with missing allometry receive configurable defaults.
  • Missing predecessor TopWidth/TopHeight are smoothed from next same-type successor bottom sizes.
  • Missing complex-node allometry is accumulated from terminal components (length sum, width max).

2.4 AMAP option parameters (practical decisions)

InsertionMode: controls lateral insertion offset on the bearer cross-section.

  • CENTER: attach at section center.
  • BORDER / SURFACE: project insertion direction to the bearer cross-section and stick to the section border.
  • WIDTH: shift along width axis.
  • HEIGHT: shift along height axis.

Important: these axes are in the bearer local frame (the bearer reference mesh axes), not in global/world axes.

For default_amap_geometry_convention() (length axis = local +X):

  • width axis is local +Y
  • height axis is local +Z (same direction as thickness/top-height semantics)

So WIDTH means a lateral shift in local Y, and HEIGHT means a lateral shift in local Z.

Use BORDER/SURFACE for "stick to bearer surface" behavior; use CENTER for neutral attachment; use WIDTH/HEIGHT when organs are known to emerge preferentially on one side of the local cross-section.

verticil_mode: controls sibling spread when XInsertionAngle is missing.

  • :rotation360: siblings are distributed around 360 degrees.
  • :none: no additional spread term.

Use :rotation360 for whorled/regular sibling insertion with missing azimuth data; use :none if azimuth is already fully defined elsewhere.

order_override_mode with order maps (insertion_y_by_order, phyllotaxy_by_order):

  • :override: map values replace present attributes.
  • :missing_only: map values fill only missing attributes.

Use :override for strict calibration by branch order. Use :missing_only when measured node-level values must stay authoritative.

StiffnessAngle vs Orthotropy: both bend orientation, but StiffnessAngle takes precedence if both are present.

Use StiffnessAngle when a mechanical bending angle is available; keep Orthotropy as a fallback heuristic.

OrientationReset (Global): resets the orientation basis before insertion/euler stages.

Use it when switching from inherited frame behavior to absolute local interpretation at selected nodes.

NormalUp and Plagiotropy: projection stages applied in this order.

NormalUp is mainly a robustness option for dorsiventral organs (typical leaves): it keeps the organ normal oriented toward world +Z after insertion/euler/bending stages, so leaves are less likely to end up upside-down due to noisy angles.

Practical effect:

  • It does not move the insertion point.
  • It does not redefine topology.
  • It adjusts orientation to keep the organ "up-facing" in world space.

Use NormalUp=true for leaf datasets where adaxial/abaxial flips appear. Add Plagiotropy only if you also need directional projection control in the insertion plane.

2.5 Why node_convention.length_axis is critical

node_convention.length_axis defines which local reference-mesh axis is considered the organ main axis.

This affects multiple stages, not just scaling:

  • allometry: which axis receives Length
  • insertion frame: which direction is considered the organ direction
  • bending (Orthotropy/StiffnessAngle): bending axis is derived from this main direction
  • projection (NormalUp/Plagiotropy): which columns are treated as direction/secondary/normal
  • topology placement defaults: how "along the bearer axis" is interpreted

So if the reference mesh was authored with length on local +Z but the convention says length_axis=:x, orientation and projection can look "wrong" even with correct angle values.

Rule of thumb:

  • Use default_amap_geometry_convention() (length_axis=:x) for AMAP-style meshes.
  • Use a custom GeometryConvention(..., length_axis=:z) when meshes are authored with length on local +Z.

Stifness / StifnessTapering propagation:

  • If a node has a stiffness value and /-linked component children, PlantGeom propagates computed StiffnessAngle values to those components.
  • StiffnessApply=false disables this propagation for that node.
  • Positive Stifness propagates downward-sign bending angles; negative values propagate upward-sign bending angles.
  • StifnessTapering controls the curvature profile (0.5 default when missing).
  • StiffnessStraightening (0..1 or 0..100) progressively damps propagated bending after that relative position.
  • Broken (0..100) forces downstream component angles to -180 after the break threshold.

2.8 What GeometricalConstraint is for

GeometricalConstraint is an envelope rule applied after angle/projection stages to keep an organ direction inside a geometric domain (cone/cylinder/plane families).

Typical use cases:

  • constrain synthetic roots to stay inside a soil exploration envelope
  • keep generated axes inside a training shape when angle data are sparse/noisy
  • enforce directional limits without manually tuning every node angle

Important:

  • it is an orientation clamp, not a full collision solver
  • it does not move the already computed base insertion point
  • for chained :< axes, changing orientation still changes downstream positions through topology

3. Stage Order and Semantics

Reconstruction applies stages in this order:

  1. Allometry preprocessing (interpolation, propagation, smoothing, complex accumulation).
  2. Translation / topology base frame.
  3. Endpoint override check (EndX/EndY/EndZ).
  4. Insertion angles + insertion mode offset.
  5. Azimuth/Elevation world orientation override.
  6. Orthotropy/StiffnessAngle bending.
  7. DeviationAngle world rotation.
  8. Euler stage.
  9. Projection stage (NormalUp, then Plagiotropy).
  10. Geometrical constraint stage (GeometricalConstraint) for cone/cylinder/plane families.
  11. Stiffness propagation stage (Stifness / StifnessTapering) writing StiffnessAngle on / components.

Key rules:

  • StiffnessAngle takes precedence over Orthotropy.
  • OrientationReset=true resets orientation basis to the AMAP base orientation for that node before insertion/euler stages.
  • If both projection flags are enabled, NormalUp is applied before Plagiotropy.
  • Geometrical constraint stage only re-orients the local basis; node base position remains topology/translation driven.
  • Propagated stiffness angles are written to component children before those children are reconstructed.
  • Explicit translation attributes keep the node position explicit; topology-based placement is used only when XX/YY/ZZ are absent.
  • When endpoint override is active, angle stages are skipped and node Length is inferred from endpoint distance.
  • Allometry preprocessing can write inferred values back to nodes (Length, Width, Thickness, TopWidth, TopHeight) when these are missing.

GeometricalConstraint accepted forms:

  • String/Symbol kind: "cone", "elliptic_cone", "cylinder", "elliptic_cylinder", "cone_cylinder", "elliptic_cone_cylinder", "plane".
  • Dict/NamedTuple with type (or kind) and optional parameters (primary_angle, secondary_angle, radius, secondary_radius, cone_length, normal, d, origin, axis).
  • Parameters can also be provided as node columns using aliases such as ConstraintAngle, ConstraintRadius, ConstraintLength, ConstraintNormalX/Y/Z, ConstraintPlaneD, ConstraintOriginX/Y/Z, ConstraintAxisX/Y/Z.

4. Local vs Global Angles in GeometryConvention

Angle scope is controlled per entry in GeometryConvention.angle_map:

  • Local angle (frame=:local): composed as T = T ∘ R.
  • Global angle (frame=:global): composed as T = recenter(R, pivot) ∘ T.

Global pivots:

  • :origin
  • Attribute tuple, e.g. (:pivot_x, :pivot_y, :pivot_z)
  • Numeric tuple, e.g. (0.0, 0.0, 0.0)

Minimal example:

base = default_amap_geometry_convention()
leaf_conv = GeometryConvention(
    scale_map=base.scale_map,
    angle_map=[
        (names=[:XInsertionAngle], axis=:x, frame=:local, unit=:deg, pivot=:origin),
        (names=[:YInsertionAngle], axis=:y, frame=:local, unit=:deg, pivot=:origin),
        (names=[:Heading], axis=:z, frame=:global, unit=:deg, pivot=(:pivot_x, :pivot_y, :pivot_z)),
        (names=[:XEuler], axis=:x, frame=:local, unit=:deg, pivot=:origin),
    ],
    translation_map=base.translation_map,
    length_axis=:x,
)

5. Order-Based Defaults and Overrides

When auto_compute_branching_order=true and no numeric order exists in order_attribute, PlantGeom computes branching_order! automatically.

Then for each node:

  • insertion_y_by_order[order] can override YInsertionAngle.
  • phyllotaxy_by_order[order] can override the phyllotaxy fallback used when XInsertionAngle is missing.

Override modes:

  • :override: map values replace attributes when available.
  • :missing_only: map values apply only if the attribute is missing.

Effective fallback for missing XInsertionAngle:

effective_x_insertion = phyllotaxy_effective + verticil_term

  • verticil_mode=:rotation360 (default): verticil_term = 360 * rank / total
  • verticil_mode=:none: verticil_term = 0

6. Option Impact Examples

6.1 InsertionMode: BORDER (default) vs CENTER vs WIDTH vs HEIGHT

InsertionMode mainly changes where the organ base sits on the bearer cross-section. In practice, this changes self-shading and apparent clumping even if all angles stay identical.

In this example, with AMAP defaults (length=+X) and local frame = reference mesh axes, the distinction is:

  • BORDER: offset along the projected insertion direction, to place the organ on bearer surface
  • WIDTH: offset along local Y
  • HEIGHT: offset along local Z

Use CENTER for symmetric prototypes, WIDTH when organs are known to emerge on lateral flanks, and HEIGHT when emergence is biased toward upper/lower surfaces. If you want "stick to bearer surface" behavior, use InsertionMode="BORDER" (or InsertionMode="SURFACE" alias): this follows insertion direction and projects to the bearer cross-section border.

Why the four panels differ:

  • All four cases use the same insertion angles and Offset along the internode.
  • Only the lateral insertion direction changes (CENTER = none, BORDER = projected insertion direction, WIDTH = local Y, HEIGHT = local Z).
  • So the leaf keeps similar orientation, but its attachment point moves on different cross-section directions.
  • In this demo, BorderInsertionOffset is intentionally left missing so defaults are mode-dependent: BORDER -> TopWidth/2, WIDTH -> TopWidth/2, HEIGHT -> TopHeight/2.
  • The side can appear left or right depending on orientation because WIDTH/HEIGHT pick +axis or -axis based on alignment with the leaf insertion direction (it is not “always +Y” or “always +Z” in screen space).

Offset and insertion mode are independent:

  • Offset: moves the attachment point along the internode axis (+X here).
  • BORDER/WIDTH/HEIGHT: move the attachment point across the internode cross-section.
_plot_modes(
    ("BORDER", "CENTER", "WIDTH", "HEIGHT"),
    insertion_mode_example;
    titles=("BORDER (default)", "CENTER", "WIDTH", "HEIGHT"),
    size=(960, 760),
    ncols=2,
    azimuth=1.22pi,
    elevation=0.30,
    zoom_padding=0.035,
)

6.2 verticil_mode: sibling spread when XInsertionAngle is missing

When insertion azimuth is missing, verticil_mode controls how sibling organs are separated. :rotation360 reduces overlap by design; :none keeps siblings close to the same fallback azimuth.

Use :rotation360 for regular whorls with incomplete azimuth measurements. Use :none if azimuth is already controlled by measured attributes or external rules.

What this figure is doing:

  • One internode has 6 sibling leaves.
  • Leaves intentionally have no XInsertionAngle (to trigger verticil_mode behavior).
  • All siblings share the same Phyllotaxy and YInsertionAngle; only verticil_mode differs.

So the visible difference should be:

  • :none: sibling leaves keep the same fallback azimuth and overlap.
  • :rotation360: siblings receive an additional spread term (0, 60, 120, ... degrees here), so they form a whorl around the bearer.

To make this easier to see, the demo uses one short bearer with 6 sibling leaves, missing XInsertionAngle, and an eccentric leaf mesh so azimuth spread is visually obvious.

_plot_modes(
    (:none, :rotation360),
    verticil_mode_example;
    titles=("none: siblings overlap", "rotation360: siblings spread"),
    size=(860, 360),
    azimuth=1.18pi,
    elevation=0.38,
    zoom_padding=0.04,
)

6.3 Order-map overrides (:override vs :missing_only)

Order maps are useful for calibrated architecture classes. :override enforces class-level values everywhere; :missing_only preserves measured node attributes and only fills gaps.

Use :override for strict synthetic generators. Use :missing_only for mixed datasets where some organs are measured and others are inferred.

What this specific example does:

  • Two leaves share branching_order = 2.
  • The order map sets insertion_y_by_order = Dict(2 => 60.0).
  • Leaf A has no YInsertionAngle in attributes.
  • Leaf B has a measured YInsertionAngle = 15.0.

So the visible difference is:

  • :override: both leaves use 60.0, giving a symmetric high insertion posture.
  • :missing_only: Leaf A uses 60.0, Leaf B keeps 15.0, giving a clearly asymmetric pair.
_plot_modes(
    (:override, :missing_only),
    order_override_example;
    size=(860, 360),
    azimuth=1.02pi,
    elevation=0.44,
    zoom_padding=0.045,
)

6.4 Stiffness propagation (StiffnessApply=false vs true)

This option controls whether node-level Stifness/StifnessTapering are converted into propagated StiffnessAngle values for /-linked component children.

Important: this is a component-based bending mechanism. A single undecomposed reference mesh will not be smoothly bent by this stage; you need a segmented organ representation (explicit per-segment nodes).

What this figure is doing:

  • Each controller node has stiffness (Stifness, StifnessTapering) and two / components: an invisible anchor + a visible cylindrical segment.
  • StiffnessApply=true propagates a non-zero StiffnessAngle to the visible component.
  • Successor :< nodes continue from the last component top (AMAPStudio-like behavior), so propagated component bending changes downstream position.
  • Only StiffnessApply changes between panels.

Topology sketch:

AxisNode(i)
├─ / AxisDummy   (anchor, hidden)
└─ / AxisSegment (visible)
< AxisNode(i+1) starts at top(AxisSegment)

So the visible difference should be:

  • StiffnessApply=false: segments stay aligned.
  • StiffnessApply=true: segments bend and the chain goes downward.

Important: like AMAPStudio, bending changes orientation, and topology controls where the next element starts. If you set explicit XX/YY/ZZ, you override this topological positioning.

_plot_modes(
    (:disabled, :propagate),
    stiffness_propagation_example;
    titles=("StiffnessApply=false", "StiffnessApply=true"),
    size=(980, 320),
    azimuth=1.5pi,
    elevation=0.20,
    zoom_padding=0.05,
)

6.5 GeometricalConstraint: unconstrained vs constrained axis

This shows the practical role of GeometricalConstraint.

  • Left: same insertion/deviation angles, no constraint; the axis drifts away.
  • Right: same MTG and angles, with a shared cone_cylinder constraint; directions are clamped and the axis stays inside the envelope.

Use this when you want global shape control (for example root exploration domain) without hand-tuning every segment angle.

_plot_modes(
    (:free, :constrained),
    geometrical_constraint_example;
    titles=("No constraint", "Cone-cylinder constraint"),
    size=(920, 340),
    azimuth=1.35pi,
    elevation=0.26,
    zoom_padding=0.06,
)

6.6 Explicit-coordinate handling mode (explicit_coordinate_mode)

This option controls how explicit start coordinates (XX/YY/ZZ) are used during reconstruction. explicit_coordinate_mode is the recommended API name, and coordinate_delegate_mode is kept as a compatible alias.

In this section, a point-anchor means a node that stays in the MTG but has zero geometric length (a point in space, no cylinder).

What changes between the three panels is mainly the reconstruction rule, not the base MTG. The same 4-internode chain and the same base attributes are used in all cases. The only mode-specific data tweak is for :explicit_start_end_required, where internode 2 is forced to Length=0 with EndX/EndY/EndZ = XX/YY/ZZ so the node stays visible as a point-anchor for side-by-side comparison.

topology_default keeps internode 2 as a normal segment that starts at XX/YY/ZZ. explicit_rewire_previous treats internode 2 as a control point: the previous segment is redirected to this point, and internode 2 itself becomes a point-anchor. explicit_start_end_required applies strict start/end logic: only nodes with a full explicit segment definition can generate a segment.

Use explicit_rewire_previous when you know coordinates for only a few nodes along a long axis. In plain terms, those measured nodes act as waypoints that pull the reconstructed axis toward known positions, while the rest of the axis is still reconstructed from topology and local geometry attributes.

_plot_coordinate_delegate_modes_with_skeleton(
    (:topology_default, :explicit_rewire_previous, :explicit_start_end_required),
    titles=(
        "topology_default: i2 stays segment",
        "explicit_rewire_previous: i2 is anchor",
        "explicit_start_end_required: i2 forced anchor",
    ),
    size=(1260, 640),
    azimuth=1.36pi,
    elevation=0.26,
    zoom_padding=0.065,
)

Numeric interpretation of the same scene (same data, same options):

for mode in (:topology_default, :explicit_rewire_previous, :explicit_start_end_required)
    _print_coordinate_delegate_mode_summary(mode)
end
mode = topology_default
internode | status       | start (x,y,z)        | end (x,y,z)          | length
---------|--------------|----------------------|----------------------|--------
i1       | segment     | (0.0, 0.0, 0.0)      | (0.295, 0.0, -0.052) | 0.3
i2       | segment     | (0.3, 0.06, 0.0)     | (0.518, 0.06, -0.101) | 0.24
i3       | segment     | (0.518, 0.06, -0.101) | (0.644, 0.06, -0.282) | 0.22
i4       | segment     | (0.644, 0.06, -0.282) | (0.695, 0.06, -0.475) | 0.2

mode = explicit_rewire_previous
internode | status       | start (x,y,z)        | end (x,y,z)          | length
---------|--------------|----------------------|----------------------|--------
i1       | segment     | (0.0, 0.0, 0.0)      | (0.3, 0.06, 0.0)     | 0.306
i2       | point-anchor| (0.3, 0.06, 0.0)     | (0.3, 0.06, 0.0)     | 0.0
i3       | segment     | (0.3, 0.06, 0.0)     | (0.491, 0.06, -0.11) | 0.22
i4       | segment     | (0.491, 0.06, -0.11) | (0.619, 0.06, -0.263) | 0.2

mode = explicit_start_end_required
internode | status       | start (x,y,z)        | end (x,y,z)          | length
---------|--------------|----------------------|----------------------|--------
i1       | segment     | (0.0, 0.0, 0.0)      | (0.295, 0.0, -0.052) | 0.3
i2       | point-anchor| (0.3, 0.06, 0.0)     | (0.3, 0.06, 0.0)     | 0.0
i3       | segment     | (0.3, 0.06, 0.0)     | (0.426, 0.06, -0.18) | 0.22
i4       | segment     | (0.426, 0.06, -0.18) | (0.478, 0.06, -0.373) | 0.2

MTG content used in each panel (topology + key attributes):

for mode in (:topology_default, :explicit_rewire_previous, :explicit_start_end_required)
    _print_mode_mtg(mode)
end
mode = topology_default
/Plant1
^/Internode1  Length=0.3  XX=missing  YY=missing  ZZ=missing  EndX=-  EndY=-  EndZ=-  status=segment
^<Internode2  Length=0.24  XX=0.3  YY=0.06  ZZ=0.0  EndX=-  EndY=-  EndZ=-  status=segment
^<Internode3  Length=0.22  XX=missing  YY=missing  ZZ=missing  EndX=-  EndY=-  EndZ=-  status=segment
^<Internode4  Length=0.2  XX=missing  YY=missing  ZZ=missing  EndX=-  EndY=-  EndZ=-  status=segment

mode = explicit_rewire_previous
/Plant1
^/Internode1  Length=0.3  XX=missing  YY=missing  ZZ=missing  EndX=-  EndY=-  EndZ=-  status=segment
^<Internode2  Length=0.24  XX=0.3  YY=0.06  ZZ=0.0  EndX=-  EndY=-  EndZ=-  status=point-anchor
^<Internode3  Length=0.22  XX=missing  YY=missing  ZZ=missing  EndX=-  EndY=-  EndZ=-  status=segment
^<Internode4  Length=0.2  XX=missing  YY=missing  ZZ=missing  EndX=-  EndY=-  EndZ=-  status=segment

mode = explicit_start_end_required
/Plant1
^/Internode1  Length=0.3  XX=missing  YY=missing  ZZ=missing  EndX=missing  EndY=missing  EndZ=missing  status=segment
^<Internode2  Length=0.0  XX=0.3  YY=0.06  ZZ=0.0  EndX=0.3  EndY=0.06  EndZ=0.0  status=point-anchor
^<Internode3  Length=0.22  XX=missing  YY=missing  ZZ=missing  EndX=missing  EndY=missing  EndZ=missing  status=segment
^<Internode4  Length=0.2  XX=missing  YY=missing  ZZ=missing  EndX=missing  EndY=missing  EndZ=missing  status=segment