🚧 These docs are a work in progress — content is incomplete and may change without notice.
Skip to content

Variable-width arrows ​

Attack-style arrows (Main Attack, Supporting Attack, Airborne and Attack Helicopter attacks, Frontal Attack, Turning Movement, Counterattack, Counterattack by Fire, Movement to Contact) and the generic Block Arrow draw a body that tapers from rearWidthRatio at the rear to shaftWidthRatio where the shaft meets the head. Per-vertex widths let the body swell, pinch, or lean to one side along the way.

Set a width at a vertex ​

Each side has its own array, indexed like the control points:

  • vertexLeftWidthRatios
  • vertexRightWidthRatios

Left and right are as seen from the rear, looking towards the tip. Values use the same unit as shaftWidthRatio: a value equal to shaftWidthRatio puts that edge where the plain shaft edge would be.

ts
import { renderControlMeasure } from "@orbat-mapper/control-measures";

// Axis1 order: tip, neck, …shaft vertices, rear, width point.
const render = renderControlMeasure({
  id: "attack-1",
  kind: "main-attack",
  controlPoints: [
    [10.5, 59.0], // 0 tip
    [10.4, 59.0], // 1 neck
    [10.2, 59.02], // 2 shaft vertex
    [10.0, 59.0], // 3 rear
    [10.45, 59.01], // 4 width point
  ],
  options: {
    // Swell the left side at control point 2; the right side keeps its taper.
    vertexLeftWidthRatios: [null, null, 1.2],
  },
});
  • A null or missing entry takes a width interpolated along that side between the nearest set vertices, so set only the vertices you want to change.
  • The rear entry overrides rearWidthRatio for that side only.
  • The tip and width-point entries are ignored, and so is any vertex the arrowhead has swallowed.
  • Arrows without these options render exactly as before.

Smooth curves ​

Set smooth: true to smooth the shaft. smoothMode picks how: "rounded-corners" keeps the straight segments and rounds the outer corners, while "curve" runs the shaft along a smooth curve through the control points, and per-vertex widths ease in and out along it without overshooting. The attack arrows default to "rounded-corners", the Block Arrow to "curve":

ts
options: {
  smooth: true,
  smoothMode: "curve",
  vertexLeftWidthRatios: [null, null, 1.2],
}

smoothResolution sets how finely the curve is sampled, or how many segments approximate each rounded corner. On the Block Arrow the curve picks its own sample count: each segment gets as many points as keep it within a small tolerance of the true curve, set by the arrow's length and smoothResolution. Points crowd into bends, so short or straight segments stay light and long sweeping bends stay round. Raise smoothResolution for a finer curve; doubling it roughly doubles the points.

Editing ​

TacticalDraw can show a left and a right width grip beside each shaft vertex, plus a left grip at the rear. These grips are detail handles and stay hidden until you turn them on. See Width grips.

When a user inserts or deletes a vertex, the widths must move with their vertices. TacticalDraw handles this for you. Other editors can call remapVertexAlignedOptions(kind, options, { type: "insert" | "delete", index }) and merge the returned patch. getControlMeasureVertexAlignedOptions(kind) lists the option keys that behave this way.