Split Arrow ​
The Split Arrow (split-arrow, 990004) is a Classic Arrow whose shaft splits into separate flows that may merge again. Each flow is a plain stroked line. A flow ends in a Classic Arrow head unless it merges into another flow or continues into flows that split from its tip. It is a generic graphic, not a doctrinal symbol.
It stores its flows the same way as the Flow Arrow, so the same rules for control points, splitFrom and mergeInto apply:
import { renderControlMeasure } from "@orbat-mapper/control-measures";
// The trunk runs east over points 0-3. Flow 1 (points 4 and 5) splits off
// the trunk at its vertex 1 and ends in a barbed head.
const render = renderControlMeasure({
id: "routes",
kind: "split-arrow",
controlPoints: [
[10.0, 59.0], // trunk 0: rear
[10.2, 59.0], // trunk 1: flow 1 splits here
[10.4, 59.0], // trunk 2
[10.6, 59.0], // trunk 3: tip
[10.35, 59.08], // flow 1
[10.5, 59.12], // flow 1: tip
],
options: {
flows: [
{ count: 4 },
{ count: 2, splitFrom: { flow: 0, vertex: 1 }, arrowheadStyle: "barbed" },
],
},
});Without flows, every control point is one trunk and the Split Arrow draws like a Classic Arrow.
Options ​
On the graphic:
| Option | Description |
|---|---|
arrowheadStyle | The head of every flow that doesn't set its own: any Classic Arrow style (triangle, barbed, open, dot, …). |
tailArrowheadStyle | A head on every free rear, pointing away from the shaft, or none (default). |
arrowheadLengthPixels / arrowheadLength | The length of every head, in screen pixels or meters, as on the Classic Arrow (pixel and meter sizes). |
arrowheadWidthRatio | Head width as a ratio of its length, for every flow that doesn't set its own. |
tailArrowhead* | The tail heads' own size; they follow the tip heads until set. |
smooth / smoothResolution | Curve each flow through its points. A split flow leaves, and a merging flow arrives, along the other flow. |
labelVerticalAlignment | Where every flow's label sits across its line, unless the flow sets its own: above (default), on or below. |
On each flow:
| Option | Description |
|---|---|
arrowheadStyle | This flow's head style, overriding the graphic's. |
arrowheadScale | This flow's head length as a multiple of the graphic's (0.1 to 10; default 1). |
arrowheadWidthRatio | This flow's head width as a ratio of its length, overriding the graphic's. |
id | A stable name for the flow, unique within the arrow; its moved label is stored under it. |
labelPosition | Where the flow's label sits, from its start (0) to its tip (1). Unset: placed automatically. Also places the trunk's T. |
designation | Text drawn along this flow, like Field T along the trunk. Ignored on the trunk. |
labelVerticalAlignment | This flow's label alignment (above, on or below), overriding the graphic's. Also aligns the trunk's T. |
Arrowhead sizes ​
The graphic's arrowheadLengthPixels / arrowheadLength sizes every head at once. A flow's arrowheadScale makes its own head a multiple of that size, so setting the graphic's length later still resizes every head and keeps their proportions. Because the scale has no unit, it stays the same when a pixel size is baked to meters on commit.
options: {
arrowheadLength: 4000, // every head: 4 km
flows: [
{ count: 4 },
{ count: 2, splitFrom: { flow: 0, vertex: 1 }, arrowheadScale: 0.5 }, // this head: 2 km
],
}Dragging a tip head's grip sets that flow's arrowheadScale and arrowheadWidthRatio, and those of any other selected flow with a head. Alt+click on the grip clears them on the same flows, so the heads follow the graphic's size again. Hold Ctrl/⌘ (TacticalDraw's modifierDrag key) to size every head at once: Ctrl/⌘ + drag sets the graphic's size from that grip, and Ctrl/⌘ + Alt+click resets it to the default. Either way, every flow's own size is cleared. Tail heads share one size, set on the graphic. A flow pulled out with Ctrl/⌘ + drag takes the head style and size of the flow it splits from.
Labels ​
The trunk shows the graphic's unique designation, Field T. Every other flow shows its own designation, above its line by default. A label sits in the middle of the longest stretch of its line between the line's ends and the points where other flows split off or merge in, so it stays clear of them. Set a flow's labelPosition to place it yourself, from the flow's start (0) to its tip (1); the trunk's labelPosition places Field T. Each label moves on its own: the trunk's placement is stored under T in amplifierPlacements, a flow's under splitArrowFlowLabelKey(flow, index). A label dragged onto its flow's line knocks a gap out of the stroke.
labelVerticalAlignment moves a label across its line: above (the default), on, centred on the line with the stroke broken around it, or below. Above and below are as the text reads, so they hold whichever way a flow points. Set it on the graphic for every label, or on a flow for just that flow's. A label you have dragged stays where you put it. labelPadding adds clearance: it pushes an above or below label further from its line, and widens the break around a label on it.
renderControlMeasure({
id: "routes",
kind: "split-arrow",
controlPoints,
textAmplifiers: { T: "ALPHA" },
options: {
flows: [{ count: 4 }, { count: 2, splitFrom: { flow: 0, vertex: 1 }, designation: "BRAVO" }],
},
});A flow's placement key is label:flow:<id> when the flow has an id, so a moved label stays with its flow when earlier flows are removed. Ctrl/⌘ + drag gives every flow it pulls out a fresh id (f1, f2, …). A flow you write without one falls back to label:flow-<index>, which follows its position in flows instead.
Editing ​
The Split Arrow is drawn tip first, like the Classic Arrow: the first click places the trunk's tip. The control points are stored rear to tip.
TacticalDraw edits it with the Flow Arrow's gestures: Ctrl/⌘ + drag from a point pulls out a new flow, and Ctrl/⌘ + drag from a free tip merges it onto a ringed point of another flow. The edit session's selected flow works the same way.
Three plain functions edit flows from a host's own UI. They work like the Flow Arrow's and return new controlPoints and options, or null:
reverseSplitArrowFlowruns a flow the other way.detachSplitArrowFlowEndgives a merging flow its own tip and arrowhead back.removeSplitArrowFlowremoves a flow and every flow that splits from it.