@yamat47/playmaker 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -18
- package/dist/playmaker.d.ts +139 -107
- package/dist/playmaker.js +872 -770
- package/dist/playmaker.js.map +1 -1
- package/package.json +2 -2
package/dist/playmaker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playmaker.js","names":["fontUrl","toDisposable","fn","DisposableStore","item","Disposable","GRID_STEP_YARDS","snapYard","yard","snapToGrid","position","isRecord","value","isFiniteNumber","isNonEmptyString","isOneOf","values","candidate","parseBoundedArray","raw","parsed","end","max","index","parse","PLAYER_SHAPE_VALUES","DEFAULT_PLAYER_SHAPE","PLAYER_FILL_VALUES","PLAYER_RADIUS_YARDS","isPlayerShape","isPlayerFill","parseFieldPosition","lateralYard","downfieldYard","normalizePlayer","normalizePlayers","clonePlayer","player","MIN_LINE_PX","YARD_LINE_WIDTH_PER_W","HASH_TICK_LENGTH_PER_U","NUMBER_HEIGHT_PER_U","NUMBER_MIN_PX","MIDFIELD_LINE_WIDTH_PER_YARD_LINE","PYLON_SIZE_PER_U","PYLON_MIN_PX","GOALPOST_CROSSBAR_HALF_PER_U","GOALPOST_UPRIGHT_LENGTH_PER_U","GOALPOST_STROKE_PER_D","GOALPOST_STROKE_MIN_PX","MARKER_LABEL_PER_D","MARKER_LABEL_MIN_PX","ROUTE_WIDTH_PER_D","ROUTE_WIDTH_MIN_PX","BLOCK_CAP_LENGTH_PER_D","ARROW_LENGTH_PER_D","ARROW_HALF_WIDTH_PER_D","MOTION_DASH_ON_PER_U","MOTION_DASH_OFF_PER_U","GRID_DOT_RADIUS_PER_U","GRID_DOT_RADIUS_MIN_PX","computeFieldMetrics","w","fieldPixelWidth","u","unitPx","yardLineWidth","d","routeWidth","EDITOR_TOOL_VALUES","NO_SELECTION","isSameSelection","a","b","isToolAvailable","tool","state","canLoadFormation","formation","Emitter","onListenerError","listener","err","CommandService","model","history","changed","command","UndoRedoService","revert","reapply","from","run","to","TEAM_SIDE_VALUES","isTeamSide","deepFreeze","child","toFormationPlayer","_id","template","normalizeFormation","players","instantiateFormation","taken","takenIds","id","ids","IdFactory","prefix","LINE_KIND_VALUES","LINE_INTERPOLATION_VALUES","DEFAULT_LINE_KIND","DEFAULT_LINE_INTERPOLATION","isLineKind","isLineInterpolation","isLineThickness","normalizeLine","validPlayerIds","normalizeLines","entry","cloneLine","line","p","lineAnchorPoints","start","playersById","indexPlayersById","map","FIELD_ZONE_VALUES","DEFAULT_FIELD_ZONE","FIELD_ZONE_LABELS","LOS_YARD_BY_ZONE","CURRENT_PLAY_DATA_VERSION","isFieldZone","fieldStateForZone","zone","clonePlayData","data","resolvePlayData","source","makeIdsUnique","items","seen","n","toV2Position","rest","absoluteYard","losYard","toV2Player","toV2Line","migrateV1ToV2","field","l","PLAY_DATA_MIGRATIONS","readDeclaredVersion","applyPlayDataMigrations","acc","step","steps","declaredVersion","migratePlayData","insertAt","what","assertNewId","message","assertWithinLimit","count","limit","assertWithinLimits","PlayModel","initialData","target","removedLines","lines","i","removal","remaining","removals","prev","next","requireApplied","SetFieldZoneCommand","LoadFormationCommand","asRecord","applyPatch","current","key","patch","patchChangesAnything","applyLinePatch","AddLineCommand","RemoveLineCommand","lineId","UpdateLineCommand","applyPlayerPatch","AddPlayerCommand","RemovePlayerCommand","playerId","UpdatePlayerCommand","FIELD_WIDTH_YARDS","HASH_CENTER_OFFSET_YARDS_BY_LEAGUE","DEFAULT_FIELD_LEAGUE","fieldZoneWindow","zoneWindowLength","endYard","startYard","fieldWindowAspect","clampToZoneWindow","displayYardNumber","yardLinesInWindow","stepYards","first","y","FieldGeometry","viewportWidth","viewportHeight","windowLength","h","x","point","segments","previous","points","polylineLength","total","path","trimPolylineEnd","distance","maxFraction","segLen","t","cubicBezierPoint","p0","c1","c2","p1","catmullRomBezierControls","p2","p3","dedupeConsecutive","out","last","sampleLinePath","pts","interpolation","samplesPerSegment","result","s","hitTestPlayer","radiusYards","r2","dx","dy","LINE_HIT_TOLERANCE_YARDS","distanceToSegment","abx","aby","apx","apy","lenSq","distanceToPolyline","only","pointDistance","min","hitTestLine","toleranceYards","byId","anchors","WAYPOINT_HANDLE_RADIUS_YARDS","hitLineHandle","within","VIEW_STATE_EQUALS","VIEW_STATE_KEYS","isSameEntry","isSameView","EditorNotifier","readView","action","view","isSamePosition","MAX_DRAFT_POINTS","startDrag","origin","pointer","dragPosition","drag","dragPatch","scene","splitDraftPoints","startDrawing","addDraftPoint","accepts","draw","committableDraft","split","anchor","draftToLine","shape","DRAFT_LINE_ID","applyDragPatch","drop","draftLine","composePreview","interaction","computeOverlay","selection","EditorController","commands","pos","moved","added","loaded","handle","hit","openDocument","store","PlaySession","normalized","opened","controller","DEFAULT_EXPORT_WIDTH","MAX_EXPORT_WIDTH","resolveImageExportWidth","width","resolveImageExportSize","options","pt","shortOf","lateral","beside","OL","FRONT_4","END_OVER_TE","FRONT_3","LB_43","LB_NICKEL","cornerSpots","depth","offensiveLine","front4","strongEnd","front3","corners","left","right","iFormation","singlebackAce","shotgunSpread","trips","empty","pistol","bunch","defense43","defense34","nickelFront","NICKEL_BASE","nickel","shell","dime","defense425","defense335","FORMATION_PRESETS","getFormationPreset","f","ARROW_TRIM_MAX_FRACTION","lineEnd","tip","length","trimForArrowHead","arrowLength","arrowHeadVertices","baseX","ux","baseY","uy","halfWidth","blockCapEndpoints","half","capLength","CIRCLE_DRAW_SCALE","playerMarkerOutline","radius","resolveKeyAction","mod","stroke","ln","play","COVER_2_SHELL","COVER_3_SHELL","PLAY_PRESETS","getPlayPreset","isFormControl","PointerInput","canvas","surface","toYard","activePointerId","onPointerDown","onPointerMove","endPress","onPointerUp","onPointerAbort","onDoubleClick","onKeyDown","listeners","root","playmaker_saira_subset_default","FIELD_FONT_NAME","FIELD_FONT_FAMILY","fieldFont","sizePx","loadFieldFont","registerFieldFont","face","context","resolveContext","ctx","normalizeCssColor","resolved","fromBlack","WHITE","BLACK","RED","BLUE","GREEN","ORANGE","GREY_100","GREY_200","GREY_400","GREY_500","THEME_TOKENS","themeReaderFrom","token","readProperty","property","fallback","createThemeReader","styles","element","ARROW_LENGTH_YARDS","ARROW_HALF_WIDTH_YARDS","ARROW_OFFSET_FROM_NUMBER_YARDS","NUMBER_FROM_SIDELINE_YARDS","NUMBER_DIGIT_SPACING_PER_HEIGHT","fieldTheme","read","FieldRenderer","frame","theme","geometry","bounds","yardWindow","metrics","yTop","yBottom","offset","center","hashLeft","hashRight","tick","goalPath","midPath","minorPath","top","bottom","leftX","rightX","arrowBaseHalfPx","halfArrowLength","gap","drawRotatedLabel","head","tail","label","rotation","drawArrow","xc","tipY","numberY","towardGoal","arrowCenterYard","pylon","drawPylon","goalY","endY","cx","crossHalf","uprightLen","dir","uprights","lineY","GridRenderer","gridDots","dots","size","LINE_COLOR_TOKENS","LineRenderer","color","isBlock","strokePath","kind","vertices","endpoints","OverlayRenderer","overlay","drawSelectedPlayer","r","drawLineHandles","fill","outline","handleHalf","paintHandle","waypoint","handles","PlayerRenderer","defaultColor","grass","fontPx","isSolid","halfSide","tm","labelY","createDefaultLayers","renderLayers","layer","layers","CanvasSurface","parent","observer","rect","clientX","clientY","height","blob","resolve","reject","dpr","clientWidth","clientHeight","editorFrame","replaceKeepingFocus","doc","container","hadFocus","update","COLOR_PALETTE","SHAPE_LABELS","FILL_LABELS","KIND_LABELS","INTERPOLATION_LABELS","createSwatch","btn","name","PropertyPanel","focusFallback","selected","shown","hint","setLabel","v","setShape","choices","opt","setPressed","choice","setKind","setInterpolation","setThickness","text","labelText","control","onReset","group","span","row","children","reset","input","onChange","swatches","isPressed","select","o","optionLabels","TOOL_LABELS","FORMATION_LIMIT_REASON","TOOL_UNAVAILABLE_REASONS","SIDE_LABELS","setEnabled","button","enabled","setTitle","reason","title","Toolbar","onClick","sep","placeholder","side","option","preset","isAnyUnavailable","isDevelopment","Playmaker","stage","mode","method","ui"],"sources":["../src/common/base/lifecycle.ts","../src/common/geometry/grid.ts","../src/common/model/guards.ts","../src/common/model/player.ts","../src/common/design/metrics.ts","../src/common/editing/editor.ts","../src/common/base/event.ts","../src/common/commands/command-service.ts","../src/common/commands/undo-redo-service.ts","../src/common/presets/shared.ts","../src/common/formations/formation.ts","../src/common/model/id-factory.ts","../src/common/model/line.ts","../src/common/model/play-data.ts","../src/common/model/migration-v2.ts","../src/common/model/migration.ts","../src/common/model/play-model.ts","../src/common/commands/command.ts","../src/common/commands/field-commands.ts","../src/common/commands/formation-commands.ts","../src/common/commands/patch.ts","../src/common/commands/line-commands.ts","../src/common/commands/player-commands.ts","../src/common/geometry/field.ts","../src/common/geometry/polyline.ts","../src/common/geometry/bezier.ts","../src/common/geometry/hit-test.ts","../src/common/editing/editor-notifier.ts","../src/common/editing/interaction.ts","../src/common/editing/preview.ts","../src/common/editing/editor-controller.ts","../src/common/editing/play-session.ts","../src/common/export/image-export.ts","../src/common/presets/alignments.ts","../src/common/formations/presets.ts","../src/common/geometry/line-decoration.ts","../src/common/geometry/player-marker.ts","../src/common/input/keymap.ts","../src/common/plays/presets.ts","../src/browser/input/pointer-input.ts","../src/assets/playmaker-saira-subset.woff2","../src/browser/theme/field-font.ts","../src/browser/theme/field-font-face.ts","../src/browser/theme/css-color.ts","../src/browser/theme/tokens.ts","../src/browser/theme/theme-reader.ts","../src/browser/rendering/field-renderer.ts","../src/browser/rendering/grid-renderer.ts","../src/browser/rendering/line-renderer.ts","../src/browser/rendering/overlay-renderer.ts","../src/browser/rendering/player-renderer.ts","../src/browser/rendering/layer.ts","../src/browser/rendering/canvas-surface.ts","../src/browser/ui/focus.ts","../src/browser/theme/palette.ts","../src/browser/ui/property-panel.ts","../src/browser/ui/toolbar.ts","../src/playmaker.ts"],"sourcesContent":["export interface IDisposable {\n dispose(): void;\n}\n\nexport function toDisposable(fn: () => void): IDisposable {\n return { dispose: fn };\n}\n\n/** まとめて解放する。解放したあとに add したものは、その場で解放する。 */\nexport class DisposableStore implements IDisposable {\n private readonly items = new Set<IDisposable>();\n private disposed = false;\n\n /** 非同期の処理が終わったとき、破棄したあとなら何もしないための判定に使える。 */\n get isDisposed(): boolean {\n return this.disposed;\n }\n\n add<T extends IDisposable>(item: T): T {\n if (this.disposed) {\n item.dispose();\n return item;\n }\n this.items.add(item);\n return item;\n }\n\n dispose(): void {\n if (this.disposed) {\n return;\n }\n this.disposed = true;\n for (const item of this.items) {\n item.dispose();\n }\n this.items.clear();\n }\n}\n\n/** `_register` に渡したものを、dispose のときに一緒に解放する。 */\nexport abstract class Disposable implements IDisposable {\n protected readonly _store = new DisposableStore();\n\n protected get isDisposed(): boolean {\n return this._store.isDisposed;\n }\n\n protected _register<T extends IDisposable>(item: T): T {\n return this._store.add(item);\n }\n\n dispose(): void {\n this._store.dispose();\n }\n}\n","import type { FieldPosition } from \"../model/player.js\";\n\n/**\n * 位置を置ける格子の刻み(ヤード)。横はサイドライン、縦は LOS を原点にする。\n * 図の中の位置はすべてこの格子点にあるので、要素同士を揃える操作が無くても縦横が揃う。\n */\nexport const GRID_STEP_YARDS = 0.5;\n\nfunction snapYard(yard: number): number {\n // -0.2 を丸めると -0 になり、Object.is で比べる toEqual が 0 と区別するので 0 に寄せる。\n return Math.round(yard / GRID_STEP_YARDS) * GRID_STEP_YARDS + 0;\n}\n\n/** 位置を最も近い格子点へ寄せる。読み込んだ値と、ドラッグやクリックで決めた値の両方に使う。 */\nexport function snapToGrid(position: FieldPosition): FieldPosition {\n return {\n lateralYard: snapYard(position.lateralYard),\n downfieldYard: snapYard(position.downfieldYard),\n };\n}\n","export function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nexport function isFiniteNumber(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value);\n}\n\n/** 空白だけの文字列も空とみなす。 */\nexport function isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.trim() !== \"\";\n}\n\nexport function isOneOf<const T extends string>(value: unknown, values: readonly T[]): value is T {\n return values.some((candidate) => candidate === value);\n}\n\n/**\n * 配列の先頭 `max` 個だけを読み、`parse` が null を返した要素は捨てる。\n * 復元できない要素も読んだ数に入れるので、どんな入力でも読むのは `max` 個までで済む。\n */\nexport function parseBoundedArray<T>(\n raw: unknown,\n max: number,\n parse: (entry: unknown, index: number) => T | null,\n): T[] {\n if (!Array.isArray(raw)) {\n return [];\n }\n const parsed: T[] = [];\n const end = Math.min(raw.length, max);\n for (let index = 0; index < end; index += 1) {\n const value = parse(raw[index], index);\n if (value !== null) {\n parsed.push(value);\n }\n }\n return parsed;\n}\n","import { snapToGrid } from \"../geometry/grid.js\";\nimport {\n isFiniteNumber,\n isNonEmptyString,\n isOneOf,\n isRecord,\n parseBoundedArray,\n} from \"./guards.js\";\n\n/**\n * 選手マーカーの形状。種類が多いと図が散らかるので、丸と四角の 2 種だけにする。\n * どちらも外接円を当たり判定にできるので、hit-test は形状で分けない。\n */\nexport const PLAYER_SHAPE_VALUES = [\"circle\", \"square\"] as const;\n\nexport type PlayerShape = (typeof PLAYER_SHAPE_VALUES)[number];\n\n/** 形状を指定しないときの既定。 */\nexport const DEFAULT_PLAYER_SHAPE: PlayerShape = \"circle\";\n\n/**\n * 選手マーカーの塗り。枠線だけ(`outline`)か、色で塗りつぶす(`solid`)か。\n * 形状とは独立に持ち、攻守や役割の見分けに使う。パネルはこの順にスウォッチを並べる。\n */\nexport const PLAYER_FILL_VALUES = [\"outline\", \"solid\"] as const;\n\nexport type PlayerFill = (typeof PLAYER_FILL_VALUES)[number];\n\n/** 塗りを指定しないときの既定。黒板に描く図と同じく、枠線だけにする。 */\nexport const DEFAULT_PLAYER_FILL: PlayerFill = \"outline\";\n\n/**\n * 外から受け取る選手の上限。実際のプレー図は 22 人ほどなので、この上限に届くことは無い。\n * 壊れたデータや悪意のあるデータで描画が止まらないように、超えた分は正規化で捨てる。\n */\nexport const MAX_PLAYERS = 64;\n\n/**\n * 選手マーカーの半径(ヤード)。描く大きさと当たり判定の半径に同じ値を使う。\n * フィールドは縦横を同じ縮尺で描くので、ヤードでの円がそのまま画面上の円になる。\n */\nexport const PLAYER_RADIUS_YARDS = 0.93;\n\n/**\n * フィールド上の位置(ヤード)。縦は LOS(`field.losYard`)からの距離で持つので、\n * ゾーンを切り替えて LOS が動いても、図は形を保ったまま一緒に動く。\n * - lateralYard: 0 = 左サイドライン … FIELD_WIDTH_YARDS = 右サイドライン\n * - downfieldYard: 0 = LOS。正が攻撃方向(画面の上)、負がバックフィールド\n *\n * どちらも GRID_STEP_YARDS の倍数で、モデルの中に格子の外の位置は無い。\n */\nexport interface FieldPosition {\n readonly lateralYard: number;\n readonly downfieldYard: number;\n}\n\n/**\n * 1 人の選手。`color` は CSS の色の文字列で、省くとテーマの色で描く。\n * `fill` を省くと DEFAULT_PLAYER_FILL で描く。\n */\nexport interface Player {\n readonly id: string;\n readonly position: FieldPosition;\n readonly shape: PlayerShape;\n readonly label: string;\n readonly color?: string;\n readonly fill?: PlayerFill;\n}\n\nfunction isPlayerShape(value: unknown): value is PlayerShape {\n return isOneOf(value, PLAYER_SHAPE_VALUES);\n}\n\nfunction isPlayerFill(value: unknown): value is PlayerFill {\n return isOneOf(value, PLAYER_FILL_VALUES);\n}\n\n/**\n * 座標が有限な数でなければ復元できないので null を返す。返す位置は入力と共有しない。\n * 値は格子点へ寄せる。外から来た位置もドラッグで決めた位置と同じ格子に乗せ、図の中に格子の外の位置を作らない。\n */\nexport function parseFieldPosition(raw: unknown): FieldPosition | null {\n if (!isRecord(raw)) {\n return null;\n }\n const { lateralYard, downfieldYard } = raw;\n if (!isFiniteNumber(lateralYard) || !isFiniteNumber(downfieldYard)) {\n return null;\n }\n return snapToGrid({ lateralYard, downfieldYard });\n}\n\n/**\n * 外から受け取った 1 要素を Player に正規化する。位置が座標でないものは null を返して捨てる。\n * 形状、ラベル、色、塗りが欠けていても捨てずに既定で補う。古い版で保存したデータを読めなくしないため。\n */\nfunction normalizePlayer(raw: unknown, index: number): Player | null {\n if (!isRecord(raw)) {\n return null;\n }\n const position = parseFieldPosition(raw.position);\n if (position === null) {\n return null;\n }\n\n // id が無ければ配列の中の位置から決まる id を振り、同じデータを何度読んでも同じ id になるようにする。\n const id = isNonEmptyString(raw.id) ? raw.id : `p${index}`;\n return {\n id,\n position,\n shape: isPlayerShape(raw.shape) ? raw.shape : DEFAULT_PLAYER_SHAPE,\n label: typeof raw.label === \"string\" ? raw.label : \"\",\n // exactOptionalPropertyTypes では undefined を入れられないので、色と塗りがあるときだけキーを置く。\n ...(isNonEmptyString(raw.color) ? { color: raw.color } : {}),\n ...(isPlayerFill(raw.fill) ? { fill: raw.fill } : {}),\n };\n}\n\n/**\n * 外から受け取った players を Player[] に正規化する。配列でなければ空配列を返す。\n * 先頭の MAX_PLAYERS 個より後ろの要素は読まずに捨てる。\n */\nexport function normalizePlayers(raw: unknown): Player[] {\n return parseBoundedArray(raw, MAX_PLAYERS, normalizePlayer);\n}\n\n/** 位置まで複製し、元の選手と参照を共有しない。 */\nexport function clonePlayer(player: Player): Player {\n return {\n id: player.id,\n position: { ...player.position },\n shape: player.shape,\n label: player.label,\n ...(player.color === undefined ? {} : { color: player.color }),\n ...(player.fill === undefined ? {} : { fill: player.fill }),\n };\n}\n","// 寸法は px で決めず、フィールドの幅 W と 1 ヤードの px U からの比で出す。\n// フィールドの白線、ハッシュ、数字は本物の寸法の比に合わせる。\n// 選手とプレーの線は見やすさを優先して本物より大きく描き、選手マーカーの直径 D からの比で揃える。\n\nimport { PLAYER_RADIUS_YARDS } from \"../model/player.js\";\n\n// 小さく描いたときにラインが消えないよう、1 px は残す。\nconst MIN_LINE_PX = 1;\n// 本物のラインの幅 4 インチを、フィールドの幅 160 フィートで割った値。\nconst YARD_LINE_WIDTH_PER_W = 0.0021;\n// 本物のハッシュの長さ 24 インチ。\nconst HASH_TICK_LENGTH_PER_U = 0.375;\n// 本物の数字の高さは 6 フィート(2 ヤード)だが、小さく描いたときにも読めるよう少し大きくする。\nconst NUMBER_HEIGHT_PER_U = 2.4;\nconst NUMBER_MIN_PX = 10;\n// センターのヤードラインは、ほかのヤードラインより少し太くして目印にする。\nconst MIDFIELD_LINE_WIDTH_PER_YARD_LINE = 1.5;\n// エンドゾーンの隅のパイロン。本物は 4 インチ角だが、小さく描いても見えるよう少し大きくする。\nconst PYLON_SIZE_PER_U = 0.45;\nconst PYLON_MIN_PX = 3;\n// ゴールポストは真上から見た形で、横木とフィールドへ伸びる 2 本の柱だけを描く。横木の半分は本物の 18.5 フィートの半分。\nconst GOALPOST_CROSSBAR_HALF_PER_U = 1.85;\nconst GOALPOST_UPRIGHT_LENGTH_PER_U = 1.2;\nconst GOALPOST_STROKE_PER_D = 0.2;\nconst GOALPOST_STROKE_MIN_PX = 2;\n\n// 2 文字のラベルがマーカーの内側に収まる大きさ。\nconst MARKER_LABEL_PER_D = 0.5;\nconst MARKER_LABEL_MIN_PX = 8;\nconst ROUTE_WIDTH_PER_D = 0.11;\nconst ROUTE_WIDTH_MIN_PX = 1.5;\nconst BLOCK_CAP_LENGTH_PER_D = 0.4;\n// 矢じりの底辺を線の幅の 4 倍ほどにし、線の先の棘ではなく矢印に見せる。先端の角はおよそ 37 度。\nconst ARROW_LENGTH_PER_D = 0.72;\nconst ARROW_HALF_WIDTH_PER_D = 0.24;\nconst MOTION_DASH_ON_PER_U = 1.1;\nconst MOTION_DASH_OFF_PER_U = 0.7;\n// 編集中に敷く格子の点。目印になればよいので、ハッシュより細く、消えない最小の大きさは残す。\nconst GRID_DOT_RADIUS_PER_U = 0.08;\nconst GRID_DOT_RADIUS_MIN_PX = 0.75;\n\n/** 描くときの寸法(px)。 */\nexport interface FieldMetrics {\n readonly yardLineWidth: number;\n /** ゴールラインと、サイドラインを含む窓の外枠の太さ。 */\n readonly goalLineWidth: number;\n /** センター(50 ヤード)のヤードラインの太さ。 */\n readonly midfieldLineWidth: number;\n readonly hashTickLength: number;\n readonly numberHeight: number;\n /** エンドゾーンの隅に置くパイロンの一辺。 */\n readonly pylonSize: number;\n readonly goalpostCrossbarHalfLength: number;\n readonly goalpostUprightLength: number;\n readonly goalpostStroke: number;\n /** 選手マーカーの直径 D。選手とプレーの線の寸法はこの値からの比で決める。 */\n readonly tokenDiameter: number;\n readonly markerStroke: number;\n readonly markerLabelFont: number;\n readonly routeWidth: number;\n readonly blockWidth: number;\n readonly blockCapLength: number;\n readonly arrowLength: number;\n readonly arrowHalfWidth: number;\n /** motion の破線の [描く長さ, 空ける長さ]。 */\n readonly motionDash: readonly [number, number];\n /** 編集中に敷く格子の点の半径。 */\n readonly gridDotRadius: number;\n}\n\n/** 大きさ 0 のビューポートでも NaN を返さない。 */\nexport function computeFieldMetrics(fieldPixelWidth: number, unitPx: number): FieldMetrics {\n const w = Math.max(0, fieldPixelWidth);\n const u = Math.max(0, unitPx);\n const yardLineWidth = Math.max(MIN_LINE_PX, YARD_LINE_WIDTH_PER_W * w);\n const d = 2 * PLAYER_RADIUS_YARDS * u;\n const routeWidth = Math.max(ROUTE_WIDTH_MIN_PX, ROUTE_WIDTH_PER_D * d);\n return {\n yardLineWidth,\n goalLineWidth: 2 * yardLineWidth,\n midfieldLineWidth: MIDFIELD_LINE_WIDTH_PER_YARD_LINE * yardLineWidth,\n hashTickLength: HASH_TICK_LENGTH_PER_U * u,\n numberHeight: Math.max(NUMBER_MIN_PX, NUMBER_HEIGHT_PER_U * u),\n pylonSize: Math.max(PYLON_MIN_PX, PYLON_SIZE_PER_U * u),\n goalpostCrossbarHalfLength: GOALPOST_CROSSBAR_HALF_PER_U * u,\n goalpostUprightLength: GOALPOST_UPRIGHT_LENGTH_PER_U * u,\n goalpostStroke: Math.max(GOALPOST_STROKE_MIN_PX, GOALPOST_STROKE_PER_D * d),\n tokenDiameter: d,\n // 枠線だけの選手は縁取りが見た目のすべてなので、プレーの線と同じ太さで描く。\n markerStroke: routeWidth,\n markerLabelFont: Math.max(MARKER_LABEL_MIN_PX, MARKER_LABEL_PER_D * d),\n routeWidth,\n blockWidth: routeWidth,\n blockCapLength: BLOCK_CAP_LENGTH_PER_D * d,\n arrowLength: ARROW_LENGTH_PER_D * d,\n arrowHalfWidth: ARROW_HALF_WIDTH_PER_D * d,\n motionDash: [MOTION_DASH_ON_PER_U * u, MOTION_DASH_OFF_PER_U * u],\n gridDotRadius: Math.max(GRID_DOT_RADIUS_MIN_PX, GRID_DOT_RADIUS_PER_U * u),\n };\n}\n","import type { Event } from \"../base/event.js\";\nimport type { LinePatch } from \"../commands/line-commands.js\";\nimport type { PlayerPatch } from \"../commands/player-commands.js\";\nimport type { Formation } from \"../formations/formation.js\";\nimport type { Line } from \"../model/line.js\";\nimport type { FieldZone, PlayData } from \"../model/play-data.js\";\nimport type { FieldPosition, Player } from \"../model/player.js\";\n\nexport const EDITOR_TOOL_VALUES = [\"select\", \"add-player\", \"draw-line\"] as const;\n\nexport type EditorTool = (typeof EDITOR_TOOL_VALUES)[number];\n\n/**\n * 選択は id の希望であり、対象が消えても解除しない。読み取るときに実在を確かめる。\n * 何も選んでいないことは、EditorOverlay と同じく `kind: \"none\"` で表す。\n */\nexport type EditorSelection =\n | { readonly kind: \"player\"; readonly id: string }\n | { readonly kind: \"line\"; readonly id: string }\n | { readonly kind: \"none\" };\n\nexport const NO_SELECTION: EditorSelection = { kind: \"none\" };\n\nexport function isSameSelection(a: EditorSelection, b: EditorSelection): boolean {\n if (a.kind === \"none\" || b.kind === \"none\") {\n return a.kind === b.kind;\n }\n return a.kind === b.kind && a.id === b.id;\n}\n\n/** 件数の上限に達していて、そのツールで何も置けないときは false。 */\nexport function isToolAvailable(tool: EditorTool, state: EditorViewState): boolean {\n switch (tool) {\n case \"add-player\":\n return state.remainingPlayerSlots > 0;\n case \"draw-line\":\n return state.canStartLine;\n case \"select\":\n return true;\n }\n}\n\n/** 読み込むと選手の上限を超えるフォーメーションは、1 人も置かれないので false。 */\nexport function canLoadFormation(formation: Formation, state: EditorViewState): boolean {\n return formation.players.length <= state.remainingPlayerSlots;\n}\n\n/** 描画するプレー図。版は保存するときだけ要るので持たない。 */\nexport type SceneData = Omit<PlayData, \"version\">;\n\n/** 選択の強調とハンドルを描く位置(ヤード空間)。 */\nexport type EditorOverlay =\n | { readonly kind: \"none\" }\n | { readonly kind: \"player\"; readonly playerId: string }\n | {\n readonly kind: \"line\";\n readonly waypointHandles: readonly FieldPosition[];\n readonly endpointHandle: FieldPosition;\n };\n\nexport interface EditorViewState {\n readonly tool: EditorTool;\n readonly selection: EditorSelection;\n readonly canUndo: boolean;\n readonly canRedo: boolean;\n readonly fieldZone: FieldZone;\n /** 線を作図中(確定か取り消し待ち)か。 */\n readonly isDrawing: boolean;\n /** あと何人の選手を置けるか。これより多い人数を置く操作は何もしない。 */\n readonly remainingPlayerSlots: number;\n /** 線の本数が上限に達しておらず、新しい線を描き始められるか。 */\n readonly canStartLine: boolean;\n}\n\n/** オーバーレイは、scene と同じ途中の状態から求める。 */\nexport interface EditorFrame {\n /** 確定済みのプレー図に、ドラッグや作図の途中の状態を重ねたもの。 */\n readonly scene: SceneData;\n readonly overlay: EditorOverlay;\n}\n\n/** 描画と UI の同期に使う読み取り面。 */\nexport interface IEditorScene {\n /** getFrame の結果が変わったかもしれないときに、1 つの操作につき 1 回発火する。 */\n readonly onDidChangeScene: Event<void>;\n /**\n * getViewState か、選択中の選手や線の値が変わったときだけ発火する。\n * ドラッグ中の移動のように、描く図だけが変わる操作では発火しない。\n */\n readonly onDidChangeViewState: Event<void>;\n getViewState(): EditorViewState;\n /** 選手を選択していないか、対象が消えていれば undefined。 */\n getSelectedPlayer(): Player | undefined;\n /** 線を選択していないか、対象が消えていれば undefined。 */\n getSelectedLine(): Line | undefined;\n getFrame(): EditorFrame;\n}\n\n/** ポインタの操作(ヤード空間)。 */\nexport interface IEditorGestures {\n pointerDown(pos: FieldPosition): void;\n pointerMove(pos: FieldPosition): void;\n pointerUp(pos: FieldPosition): void;\n}\n\n/** ツールバー、パネル、キー操作から呼ぶ編集。 */\nexport interface IEditorActions {\n setTool(tool: EditorTool): void;\n commitLine(): void;\n cancelInteraction(): void;\n deleteSelection(): void;\n updateSelectedPlayer(patch: PlayerPatch): void;\n updateSelectedLine(patch: LinePatch): void;\n setFieldZone(zone: FieldZone): void;\n /**\n * 選手を既存の図に追記し、置いたら true を返す。\n * 置ける選手が無いときと、置くと上限を超えるときは何もせず false を返す。\n */\n loadFormation(formation: Formation): boolean;\n /** 作図中は最後の打点を取り消し、履歴には触れない。 */\n undo(): void;\n redo(): void;\n}\n\n/** ツールバーやパネルのように、表示を読んで編集を呼ぶ UI が使う面。 */\nexport type IEditorUi = IEditorActions & IEditorScene;\n\nexport interface IEditorController extends IEditorScene, IEditorGestures, IEditorActions {}\n","import { type IDisposable, toDisposable } from \"./lifecycle.js\";\n\n// common は ECMAScript の lib だけで型検査するので、ホストが持つ console には型がない。\n// どのホスト(ブラウザ、Node)にもある console.error だけを、このモジュールの中で宣言して使う。\ndeclare const console: { error(...data: unknown[]): void };\n\n/** リスナを登録し、購読をやめるための IDisposable を返す。 */\nexport type Event<T> = (listener: (e: T) => void) => IDisposable;\n\n/** リスナが throw しても、残りのリスナには通知する。 */\nexport class Emitter<T> implements IDisposable {\n private listeners = new Set<(e: T) => void>();\n private disposed = false;\n private readonly onListenerError: (err: unknown) => void;\n\n /**\n * リスナが投げた例外を受け取る。既定は console.error に出す。\n * 投げ直さないのは、1 つのリスナの失敗で、残りのリスナと fire を呼んだ側まで止めないため。\n */\n constructor(onListenerError: (err: unknown) => void = console.error) {\n this.onListenerError = onListenerError;\n }\n\n readonly event: Event<T> = (listener) => {\n if (this.disposed) {\n return toDisposable(() => {});\n }\n this.listeners.add(listener);\n return toDisposable(() => {\n this.listeners.delete(listener);\n });\n };\n\n fire(e: T): void {\n if (this.disposed) {\n return;\n }\n // 通知の途中でリスナが登録や解除をしても、この回に通知する相手は変えない。\n for (const listener of [...this.listeners]) {\n try {\n listener(e);\n } catch (err) {\n this.onListenerError(err);\n }\n }\n }\n\n dispose(): void {\n this.disposed = true;\n this.listeners.clear();\n }\n}\n","import type { IPlayModel } from \"../model/play-model.js\";\nimport type { ICommand } from \"./command.js\";\nimport type { IUndoRedoService } from \"./undo-redo-service.js\";\n\n/**\n * 編集はすべてここを通す。コマンドの適用、取り消し、やり直しを 1 か所で行い、\n * 履歴と Model を食い違わせない。\n */\nexport interface ICommandService {\n readonly canUndo: boolean;\n readonly canRedo: boolean;\n /**\n * コマンドを適用し、Model を変えたら履歴に積んで true を返す。何も変えなかったときは\n * 積まずに false を返す。適用が throw したときも積まない。\n */\n execute(command: ICommand): boolean;\n /** 取り消しが throw したら、履歴は動かさない。戻す対象が無ければ何もしない。 */\n undo(): void;\n /** やり直しが throw したら、履歴は動かさない。やり直す対象が無ければ何もしない。 */\n redo(): void;\n}\n\nexport class CommandService implements ICommandService {\n private readonly model: IPlayModel;\n private readonly history: IUndoRedoService;\n\n constructor(model: IPlayModel, history: IUndoRedoService) {\n this.model = model;\n this.history = history;\n }\n\n get canUndo(): boolean {\n return this.history.canUndo;\n }\n\n get canRedo(): boolean {\n return this.history.canRedo;\n }\n\n execute(command: ICommand): boolean {\n const changed = command.apply(this.model);\n if (changed) {\n this.history.push(command);\n }\n return changed;\n }\n\n undo(): void {\n this.history.undo((command) => command.undo(this.model));\n }\n\n redo(): void {\n this.history.redo((command) => command.apply(this.model));\n }\n}\n","import type { ICommand } from \"./command.js\";\n\n/**\n * 適用済みコマンドの履歴。Model を持たず、コマンドを実行するのは呼び出し側の役目。\n * 履歴は分岐させない。新しいコマンドを積むと redo の履歴は捨てる。\n */\nexport interface IUndoRedoService {\n readonly canUndo: boolean;\n readonly canRedo: boolean;\n /** 実行を済ませたコマンドを積む。 */\n push(command: ICommand): void;\n /**\n * 最後に積んだコマンドを revert に渡し、revert が戻ったら redo の側へ移す。\n * revert が throw したら履歴は動かさない。戻す対象が無ければ何もしない。\n */\n undo(revert: (command: ICommand) => void): void;\n /** undo の逆向き。reapply が throw したら履歴は動かさない。 */\n redo(reapply: (command: ICommand) => void): void;\n}\n\nexport class UndoRedoService implements IUndoRedoService {\n private readonly undoStack: ICommand[] = [];\n private readonly redoStack: ICommand[] = [];\n\n get canUndo(): boolean {\n return this.undoStack.length > 0;\n }\n\n get canRedo(): boolean {\n return this.redoStack.length > 0;\n }\n\n push(command: ICommand): void {\n this.undoStack.push(command);\n this.redoStack.length = 0;\n }\n\n undo(revert: (command: ICommand) => void): void {\n this.move(this.undoStack, this.redoStack, revert);\n }\n\n redo(reapply: (command: ICommand) => void): void {\n this.move(this.redoStack, this.undoStack, reapply);\n }\n\n // 実行が成功してから積み替える。先に取り出すと、実行が throw したときにコマンドが履歴から消える。\n private move(from: ICommand[], to: ICommand[], run: (command: ICommand) => void): void {\n const command = from.at(-1);\n if (command === undefined) {\n return;\n }\n run(command);\n from.pop();\n to.push(command);\n }\n}\n","import { isOneOf } from \"../model/guards.js\";\n\nexport const TEAM_SIDE_VALUES = [\"offense\", \"defense\"] as const;\n\nexport type TeamSide = (typeof TEAM_SIDE_VALUES)[number];\n\nexport function isTeamSide(value: unknown): value is TeamSide {\n return isOneOf(value, TEAM_SIDE_VALUES);\n}\n\n/**\n * 値とその中のオブジェクトをすべて凍結して返す。プリセットは全利用者が共有するので、\n * 1 人が書き換えると他の読み込み結果まで変わってしまう。\n */\nexport function deepFreeze<T>(value: T): T {\n if (typeof value === \"object\" && value !== null && !Object.isFrozen(value)) {\n Object.freeze(value);\n for (const child of Object.values(value)) {\n deepFreeze(child);\n }\n }\n return value;\n}\n","import { isNonEmptyString, isRecord } from \"../model/guards.js\";\nimport type { IIdFactory } from \"../model/id-factory.js\";\nimport { clonePlayer, normalizePlayers, type Player } from \"../model/player.js\";\nimport { isTeamSide, type TeamSide } from \"../presets/shared.js\";\n\n/**\n * 隊形の 1 人。`id` は持たず、読み込むたびに振る。同じ隊形を 2 回読んでも、\n * 図にいる選手とも Undo で戻る選手とも id がぶつからない。\n */\nexport type FormationPlayer = Omit<Player, \"id\">;\n\n/** 読み込むと、players を今の図に足す。プリセットのほかに、利用者が作った隊形も渡せる。 */\nexport interface Formation {\n readonly id: string;\n readonly name: string;\n /** 読み込んだときの選手の配置には影響しない。 */\n readonly side: TeamSide;\n /** 1 人以上。 */\n readonly players: readonly FormationPlayer[];\n}\n\n/** 省略できるキーの扱いを clonePlayer と揃え、選手に値が増えてもここを直さずに済ませる。 */\nexport function toFormationPlayer(player: Player): FormationPlayer {\n const { id: _id, ...template } = clonePlayer(player);\n return template;\n}\n\n/**\n * 外から受け取った隊形を Formation に正規化する。選手は図の選手と同じ規則で正規化し、\n * 置ける選手が 1 人もいなければ null を返す。id、name、side が欠けていても既定で補う。\n * 返り値は新しいオブジェクトで、入力と参照を共有しない。\n */\nexport function normalizeFormation(raw: unknown): Formation | null {\n if (!isRecord(raw)) {\n return null;\n }\n const players = normalizePlayers(raw.players).map(toFormationPlayer);\n if (players.length === 0) {\n return null;\n }\n return {\n id: isNonEmptyString(raw.id) ? raw.id : \"formation\",\n name: isNonEmptyString(raw.name) ? raw.name : \"フォーメーション\",\n side: isTeamSide(raw.side) ? raw.side : \"offense\",\n players,\n };\n}\n\n/**\n * 隊形の選手に id を振って Player にする。id は takenIds とも、同じ隊形のほかの選手とも重ならない。\n */\nexport function instantiateFormation(\n formation: Formation,\n ids: IIdFactory,\n takenIds: Iterable<string>,\n): Player[] {\n const taken = new Set(takenIds);\n return formation.players.map((template) => {\n const id = ids.next(\"player\", taken);\n taken.add(id);\n return { ...template, id };\n });\n}\n","export interface IIdFactory {\n /** `${prefix}-${n}` 形式で、taken に無い未使用 id を返す。 */\n next(prefix: string, taken: ReadonlySet<string>): string;\n}\n\n/**\n * prefix ごとのカウンタで id を払い出す。一度使った番号は、その id の要素が消えても使い直さない。\n * 消した要素を Undo で戻したとき、あとから足した要素と id がぶつからないようにするため。\n */\nexport class IdFactory implements IIdFactory {\n private readonly counters = new Map<string, number>();\n\n next(prefix: string, taken: ReadonlySet<string>): string {\n let n = this.counters.get(prefix) ?? 0;\n let id: string;\n do {\n n += 1;\n id = `${prefix}-${n}`;\n } while (taken.has(id));\n this.counters.set(prefix, n);\n return id;\n }\n}\n","import {\n isFiniteNumber,\n isNonEmptyString,\n isOneOf,\n isRecord,\n parseBoundedArray,\n} from \"./guards.js\";\nimport { type FieldPosition, type Player, parseFieldPosition } from \"./player.js\";\n\n/**\n * 線の種別。色ではなく終点の記法と線種で見分ける。\n * - `route`: レシーバーやランナーの走路。終点に矢印を描く\n * - `block`: ブロックの割り当て。route と同じ太さで、終点に T 字のバーを描く\n * - `motion`: スナップ前の選手の移動。破線で、終点に矢印を描く\n */\nexport const LINE_KIND_VALUES = [\"route\", \"block\", \"motion\"] as const;\n\nexport type LineKind = (typeof LINE_KIND_VALUES)[number];\n\n/**\n * 線の補間方法。どの種別の線にも効く。\n * - `straight`: 制御点を直線で結ぶ\n * - `bezier`: 制御点をすべて通る滑らかな曲線で結ぶ\n */\nexport const LINE_INTERPOLATION_VALUES = [\"straight\", \"bezier\"] as const;\n\nexport type LineInterpolation = (typeof LINE_INTERPOLATION_VALUES)[number];\n\n/** 種別を指定しないときの既定。 */\nexport const DEFAULT_LINE_KIND: LineKind = \"route\";\n\n/** 補間を指定しないときの既定。 */\nexport const DEFAULT_LINE_INTERPOLATION: LineInterpolation = \"straight\";\n\n/** 太さを指定しないときの倍率。種別ごとの既定の太さでそのまま描く。 */\nexport const DEFAULT_LINE_THICKNESS = 1;\n\n/**\n * 外から受け取る線と、1 本あたりの waypoint の上限。実際のプレー図は線 20 本ほどで、\n * waypoint も数個なので、この上限に届くことは無い。壊れたデータや悪意のあるデータで\n * 描画が止まらないように、超えた分は正規化で捨てる。\n */\nexport const MAX_LINES = 128;\nexport const MAX_WAYPOINTS_PER_LINE = 32;\n\n/**\n * 1 本の線。起点は座標ではなく選手の id で持つので、選手を動かすと線の起点も付いてくる。\n * `color` と `thickness` を省くと、テーマの色と種別ごとの既定の太さで描く。\n */\nexport interface Line {\n readonly id: string;\n readonly kind: LineKind;\n /** 起点となる選手の id。実在しない参照は復元時に除外される。 */\n readonly startPlayerId: string;\n /** 起点と終点の間の中継点(0 個可)。 */\n readonly waypoints: readonly FieldPosition[];\n /** 終点(ヤード空間)。 */\n readonly end: FieldPosition;\n readonly interpolation: LineInterpolation;\n /** CSS の色の文字列。 */\n readonly color?: string;\n /** 種別ごとの既定の太さに対する倍率。省くと DEFAULT_LINE_THICKNESS。 */\n readonly thickness?: number;\n}\n\nfunction isLineKind(value: unknown): value is LineKind {\n return isOneOf(value, LINE_KIND_VALUES);\n}\n\nfunction isLineInterpolation(value: unknown): value is LineInterpolation {\n return isOneOf(value, LINE_INTERPOLATION_VALUES);\n}\n\n/** 太さの倍率として受け付ける値か。0 以下では線が見えなくなるので受け付けない。 */\nexport function isLineThickness(value: unknown): value is number {\n return isFiniteNumber(value) && value > 0;\n}\n\n/**\n * 外から受け取った 1 要素を Line に正規化する。オブジェクトでないもの、起点の選手が実在しないもの、\n * 終点が座標でないものは null を返して捨てる。\n * 種別、補間、id が欠けていても捨てずに既定で補う。古い版で保存したデータを読めなくしないため。\n */\nfunction normalizeLine(\n raw: unknown,\n index: number,\n validPlayerIds: ReadonlySet<string>,\n): Line | null {\n if (!isRecord(raw)) {\n return null;\n }\n\n // 起点の選手が無い線は、描くことも掴むこともできないので捨てる。\n if (!isNonEmptyString(raw.startPlayerId) || !validPlayerIds.has(raw.startPlayerId)) {\n return null;\n }\n const end = parseFieldPosition(raw.end);\n if (end === null) {\n return null;\n }\n\n return {\n id: isNonEmptyString(raw.id) ? raw.id : `l${index}`,\n kind: isLineKind(raw.kind) ? raw.kind : DEFAULT_LINE_KIND,\n startPlayerId: raw.startPlayerId,\n // 数でない waypoint は個別に捨て、線自体は保持する。\n waypoints: parseBoundedArray(raw.waypoints, MAX_WAYPOINTS_PER_LINE, parseFieldPosition),\n end,\n interpolation: isLineInterpolation(raw.interpolation)\n ? raw.interpolation\n : DEFAULT_LINE_INTERPOLATION,\n // exactOptionalPropertyTypes では undefined を入れられないので、値があるときだけキーを置く。\n ...(isNonEmptyString(raw.color) ? { color: raw.color } : {}),\n ...(isLineThickness(raw.thickness) ? { thickness: raw.thickness } : {}),\n };\n}\n\n/**\n * 外から受け取った lines を Line[] に正規化する。配列でなければ空配列を返す。\n * `validPlayerIds` には正規化を済ませた選手の id を渡す。そこに無い選手を起点にする線は捨てる。\n * 先頭の MAX_LINES 個より後ろの要素は読まずに捨てる。\n */\nexport function normalizeLines(raw: unknown, validPlayerIds: ReadonlySet<string>): Line[] {\n return parseBoundedArray(raw, MAX_LINES, (entry, index) =>\n normalizeLine(entry, index, validPlayerIds),\n );\n}\n\n/** waypoints と各座標まで複製し、元の線と参照を共有しない。 */\nexport function cloneLine(line: Line): Line {\n return {\n id: line.id,\n kind: line.kind,\n startPlayerId: line.startPlayerId,\n waypoints: line.waypoints.map((p) => ({ ...p })),\n end: { ...line.end },\n interpolation: line.interpolation,\n ...(line.color === undefined ? {} : { color: line.color }),\n ...(line.thickness === undefined ? {} : { thickness: line.thickness }),\n };\n}\n\n/**\n * 線の制御点の列 [起点の選手の位置, ...waypoints, 終点] を組み立てる。\n * 起点の選手が見つからなければ undefined を返す。\n */\nexport function lineAnchorPoints(\n line: Line,\n playersById: ReadonlyMap<string, Player>,\n): FieldPosition[] | undefined {\n const start = playersById.get(line.startPlayerId);\n if (start === undefined) {\n return undefined;\n }\n return [{ ...start.position }, ...line.waypoints.map((p) => ({ ...p })), { ...line.end }];\n}\n\nexport function indexPlayersById(players: readonly Player[]): Map<string, Player> {\n const map = new Map<string, Player>();\n for (const player of players) {\n map.set(player.id, player);\n }\n return map;\n}\n","import { isOneOf, isRecord } from \"./guards.js\";\nimport { cloneLine, type Line, normalizeLines } from \"./line.js\";\nimport { clonePlayer, normalizePlayers, type Player } from \"./player.js\";\n\n/**\n * フィールドのどの範囲を映すか。\n * - `middle`: センター付近\n * - `redzone`: 相手のゴール側\n * - `own-redzone`: 自陣のゴール側\n */\nexport const FIELD_ZONE_VALUES = [\"own-redzone\", \"middle\", \"redzone\"] as const;\n\nexport type FieldZone = (typeof FIELD_ZONE_VALUES)[number];\n\n/** ゾーンを指定しないときの既定。LOS の位置を選ばない図が多いので中央にする。 */\nexport const DEFAULT_FIELD_ZONE: FieldZone = \"middle\";\n\nexport const FIELD_ZONE_LABELS = {\n \"own-redzone\": \"自陣RZ\",\n middle: \"中央\",\n redzone: \"相手RZ\",\n} as const satisfies Record<FieldZone, string>;\n\n/**\n * ゾーンごとの LOS の絶対ヤード(0 = 自ゴール、100 = 相手ゴール)。\n * 窓の中にバックフィールドとダウンフィールドの両方が収まる位置に置く。\n * - `middle`: センター\n * - `redzone`: 相手 15 ヤード\n * - `own-redzone`: 自陣 10 ヤード\n */\nexport const LOS_YARD_BY_ZONE = {\n \"own-redzone\": 10,\n middle: 50,\n redzone: 85,\n} as const satisfies Record<FieldZone, number>;\n\nexport interface FieldState {\n readonly zone: FieldZone;\n /**\n * LOS の絶対ヤード。今はゾーンから決まり、読み込んだデータの値は使わない。\n * 保存するデータから選手の絶対位置を復元できるよう、値としては持たせる。\n */\n readonly losYard: number;\n}\n\n/**\n * 今の PlayData のスキーマの版。スキーマを変えるときは 1 上げ、`PLAY_DATA_MIGRATIONS` に\n * 1 つ前の版から変換する段を足す。\n */\nexport const CURRENT_PLAY_DATA_VERSION = 2 as const;\n\n/**\n * プレー図のデータ。保存したものを `restorePlayData` に渡すと、古い版や版の無いもの、\n * 新しい版のものも今の版に寄せて読む。\n */\nexport interface PlayData {\n readonly version: typeof CURRENT_PLAY_DATA_VERSION;\n readonly field: FieldState;\n /** 後の要素ほど上に重ねて描く。 */\n readonly players: readonly Player[];\n /** 後の要素ほど上に重ねて描く。 */\n readonly lines: readonly Line[];\n}\n\n/**\n * 型付きで組み立てて読み込む図。LOS の位置は読み込むときにゾーンから決め直すので、\n * `field.losYard` は省略できる。\n */\nexport interface PlayDataInput extends Omit<PlayData, \"field\"> {\n readonly field: Omit<FieldState, \"losYard\"> & { readonly losYard?: number | undefined };\n}\n\nexport function isFieldZone(value: unknown): value is FieldZone {\n return isOneOf(value, FIELD_ZONE_VALUES);\n}\n\nexport function fieldStateForZone(zone: FieldZone): FieldState {\n return { zone, losYard: LOS_YARD_BY_ZONE[zone] };\n}\n\n/** field、選手、線まで複製し、元のデータと参照を共有しない。 */\nexport function clonePlayData(data: PlayData): PlayData {\n return {\n version: CURRENT_PLAY_DATA_VERSION,\n field: { ...data.field },\n players: data.players.map(clonePlayer),\n lines: data.lines.map(cloneLine),\n };\n}\n\n/**\n * 今の版の形をしている値を PlayData に正規化する。版を見て変換する段は通さないので、\n * 古い版のデータは `migratePlayData` に渡す。\n * 欠けた項目は既定で補い、読めない要素は捨てる。返り値は新しいオブジェクトで、入力と参照を共有しない。\n */\nexport function resolvePlayData(data: unknown): PlayData {\n const source: Record<string, unknown> = isRecord(data) ? data : {};\n const zone = isRecord(source.field) ? source.field.zone : undefined;\n // 線の起点参照を弾けるよう、先に選手を確定してから id 集合を渡す。\n // 重複した選手 id は後ろの方を振り直すので、その id を指す線は先頭の選手に付く。\n const players = makeIdsUnique(normalizePlayers(source.players));\n const validPlayerIds = new Set(players.map((p) => p.id));\n return {\n version: CURRENT_PLAY_DATA_VERSION,\n field: fieldStateForZone(isFieldZone(zone) ? zone : DEFAULT_FIELD_ZONE),\n players,\n lines: makeIdsUnique(normalizeLines(source.lines, validPlayerIds)),\n };\n}\n\n/**\n * 2 回目以降に現れた id を `${id}-2` のような未使用の id へ振り直す(先頭はそのまま)。\n * 同じ id が 2 つあると、hit-test は末尾を返すのに更新は先頭に当たり、別の要素が編集される。\n * 振り直した id は、入力にあるほかの id とも重ねない。\n */\nfunction makeIdsUnique<T extends { readonly id: string }>(items: readonly T[]): T[] {\n const taken = new Set(items.map((item) => item.id));\n const seen = new Set<string>();\n return items.map((item) => {\n if (!seen.has(item.id)) {\n seen.add(item.id);\n return item;\n }\n let n = 2;\n while (taken.has(`${item.id}-${n}`)) {\n n += 1;\n }\n const id = `${item.id}-${n}`;\n taken.add(id);\n seen.add(id);\n return { ...item, id };\n });\n}\n","import { isFiniteNumber, isRecord } from \"./guards.js\";\nimport { MAX_LINES, MAX_WAYPOINTS_PER_LINE } from \"./line.js\";\nimport { DEFAULT_FIELD_ZONE, fieldStateForZone, isFieldZone } from \"./play-data.js\";\nimport { MAX_PLAYERS } from \"./player.js\";\n\n/**\n * v1 の線の太さは CSS px だった。プロパティパネルは未指定の線を 2 px と表示していたので、\n * v2 ではその値を倍率 1 とみなして割り戻す。\n */\nexport const V1_DEFAULT_THICKNESS_PX = 2;\n\n/** 形が崩れた値はそのまま返し、捨てるかどうかは後の正規化に任せる。 */\nfunction toV2Position(raw: unknown, losYard: number): unknown {\n if (!isRecord(raw)) {\n return raw;\n }\n const { absoluteYard, ...rest } = raw;\n if (!isFiniteNumber(absoluteYard)) {\n return raw;\n }\n return { ...rest, downfieldYard: absoluteYard - losYard };\n}\n\nfunction toV2Player(raw: unknown, losYard: number): unknown {\n if (!isRecord(raw)) {\n return raw;\n }\n return { ...raw, position: toV2Position(raw.position, losYard) };\n}\n\nfunction toV2Line(raw: unknown, losYard: number): unknown {\n if (!isRecord(raw)) {\n return raw;\n }\n return {\n ...raw,\n waypoints: Array.isArray(raw.waypoints)\n ? raw.waypoints.slice(0, MAX_WAYPOINTS_PER_LINE).map((p) => toV2Position(p, losYard))\n : raw.waypoints,\n end: toV2Position(raw.end, losYard),\n thickness: isFiniteNumber(raw.thickness)\n ? raw.thickness / V1_DEFAULT_THICKNESS_PX\n : raw.thickness,\n };\n}\n\n/**\n * v1 は縦の位置を絶対ヤード(`absoluteYard`)で持っていた。v2 はゾーンの LOS からの距離\n * (`downfieldYard`)で持つので、v1 のゾーンの LOS を引いて、画面上の位置を変えずに移す。\n * 配列は正規化と同じく上限の個数までしか読まない。\n */\nexport function migrateV1ToV2(data: Readonly<Record<string, unknown>>): Record<string, unknown> {\n const zone =\n isRecord(data.field) && isFieldZone(data.field.zone) ? data.field.zone : DEFAULT_FIELD_ZONE;\n const field = fieldStateForZone(zone);\n const { losYard } = field;\n return {\n ...data,\n field,\n players: Array.isArray(data.players)\n ? data.players.slice(0, MAX_PLAYERS).map((p) => toV2Player(p, losYard))\n : data.players,\n lines: Array.isArray(data.lines)\n ? data.lines.slice(0, MAX_LINES).map((l) => toV2Line(l, losYard))\n : data.lines,\n };\n}\n","import { isFiniteNumber, isRecord } from \"./guards.js\";\nimport { migrateV1ToV2 } from \"./migration-v2.js\";\nimport { type PlayData, resolvePlayData } from \"./play-data.js\";\n\n/**\n * 古い版のデータを 1 つ上の版の形へ寄せる段。段を通したあとで resolvePlayData が形を整えるので、\n * 段は次の版で意味が変わる項目だけを変換すればよい。\n */\nexport interface PlayDataMigration {\n /** この段を通したあとの版。 */\n readonly to: number;\n migrate(data: Readonly<Record<string, unknown>>): Record<string, unknown>;\n}\n\n/** `to` の小さい順に並べる。 */\nexport const PLAY_DATA_MIGRATIONS: readonly PlayDataMigration[] = [\n { to: 2, migrate: migrateV1ToV2 },\n];\n\n/**\n * データに書かれた版を読む。有限の数でなければ 0 を返し、版を持たなかった最初期のデータとして\n * すべての段を通す。\n */\nexport function readDeclaredVersion(raw: unknown): number {\n if (isRecord(raw) && isFiniteNumber(raw.version)) {\n return raw.version;\n }\n return 0;\n}\n\n/**\n * 書かれた版より新しい段だけを、渡した順に通す。オブジェクトでない値は段を通さずにそのまま返す。\n * 最後の段より新しい版のデータも、段を通さずにそのまま返す。\n */\nexport function applyPlayDataMigrations(\n raw: unknown,\n declaredVersion: number,\n steps: readonly PlayDataMigration[],\n): unknown {\n if (!isRecord(raw)) {\n return raw;\n }\n let acc = raw;\n for (const step of steps) {\n if (step.to > declaredVersion) {\n acc = step.migrate(acc);\n }\n }\n return acc;\n}\n\n/**\n * どの版で保存したデータでも、今の版の PlayData にする。壊れたデータを渡しても投げない。\n * 返り値は新しいオブジェクトで、入力と参照を共有しない。\n * 選手、線、waypoint は MAX_PLAYERS、MAX_LINES、MAX_WAYPOINTS_PER_LINE の個数までしか読まない。\n */\nexport function migratePlayData(raw: unknown): PlayData {\n const declared = readDeclaredVersion(raw);\n const migrated = applyPlayDataMigrations(raw, declared, PLAY_DATA_MIGRATIONS);\n // 新しい版のデータも、resolvePlayData が version を今の版に書き換える。\n return resolvePlayData(migrated);\n}\n","import { Emitter, type Event } from \"../base/event.js\";\nimport { Disposable } from \"../base/lifecycle.js\";\nimport { type Line, MAX_LINES, MAX_WAYPOINTS_PER_LINE } from \"./line.js\";\nimport { migratePlayData } from \"./migration.js\";\nimport { clonePlayData, type FieldZone, fieldStateForZone, type PlayData } from \"./play-data.js\";\nimport { MAX_PLAYERS, type Player } from \"./player.js\";\n\n/**\n * 選手 1 人の削除を戻すのに要る値。選手と一緒に消えた線を元の添字付きで持ち、\n * 戻すときに同じ並びへ差し込む。\n */\nexport interface PlayerRemoval {\n readonly player: Player;\n readonly index: number;\n readonly removedLines: readonly LineRemoval[];\n}\n\n/** 線 1 本の削除を戻すのに要る値。 */\nexport interface LineRemoval {\n readonly line: Line;\n readonly index: number;\n}\n\n/**\n * プレー図の状態。実装は、起点の選手が消えた線を残さない。\n * 削除と差し替えは、戻すのに要る変更前の値を返す。\n */\nexport interface IPlayModel {\n /** いずれかの変更後に、getSnapshot と同じ値で 1 回発火する。 */\n readonly onDidChange: Event<PlayData>;\n /** 現在状態の深いコピー。呼ぶたびに図全体を複製する。 */\n getData(): PlayData;\n /**\n * 現在状態をコピーせずに返す。状態は変更のたびに新しいオブジェクトへ差し替えるので、\n * 受け取った値は後の変更で書き換わらない。\n */\n getSnapshot(): PlayData;\n getFieldZone(): FieldZone;\n /** 無ければ undefined。getSnapshot と同じく内部の値をそのまま返す。 */\n findPlayer(id: string): Player | undefined;\n /** 無ければ undefined。getSnapshot と同じく内部の値をそのまま返す。 */\n findLine(id: string): Line | undefined;\n /** findPlayer と同じだが、無い id なら throw する。 */\n getPlayer(id: string): Player;\n /** findLine と同じだが、無い id なら throw する。 */\n getLine(id: string): Line;\n /** LOS もゾーンの既定の位置へ移す。選手と線は LOS からの位置なので、図ごと一緒に動く。 */\n setFieldZone(zone: FieldZone): void;\n /**\n * 既にある id の選手を渡すと throw する。\n * 選手が MAX_PLAYERS 人を超える追加も throw する。\n */\n addPlayer(player: Player): void;\n /**\n * 何人足しても onDidChange は最後に 1 回だけ発火する。\n * 既存と重複する id があるか、MAX_PLAYERS 人を超えるときは、1 人も足さずに throw する。\n */\n addPlayers(players: readonly Player[]): void;\n /** 起点がその選手の線も一緒に消す。 */\n removePlayer(id: string): PlayerRemoval;\n /**\n * 起点が消える選手の線も一緒に消し、onDidChange は最後に 1 回だけ発火する。\n * 無い id か重複した id があれば、1 人も消さずに throw する。\n */\n removePlayers(ids: readonly string[]): PlayerRemoval[];\n /**\n * removePlayer で消した選手と線を、元の並びへ戻す。\n * 同じ id の選手が既にあるか、戻すと上限を超えるか、添字が今の並びの外を指すときは throw する。\n */\n restorePlayer(removal: PlayerRemoval): void;\n /** 同じ id の選手を差し替え、差し替える前の選手を返す。 */\n updatePlayer(player: Player): Player;\n /**\n * 既にある id の線を渡すと throw する。線が MAX_LINES 本を超えるときと、\n * waypoint が MAX_WAYPOINTS_PER_LINE 個を超える線も throw する。\n */\n addLine(line: Line): void;\n /** index は 0 から線の本数までで渡し、外れると throw する。ほかは addLine と同じ条件で throw する。 */\n insertLine(line: Line, index: number): void;\n removeLine(id: string): LineRemoval;\n /** 同じ id の線を差し替え、差し替える前の線を返す。waypoint が MAX_WAYPOINTS_PER_LINE 個を超えると throw する。 */\n updateLine(line: Line): Line;\n}\n\n// 削除の記録に無い添字で差し込むと、slice が黙って端へ寄せ、Undo で並びが崩れたことに気づけない。\nfunction insertAt<T>(items: readonly T[], index: number, item: T, what: string): T[] {\n if (!Number.isInteger(index) || index < 0 || index > items.length) {\n throw new Error(`PlayModel: ${what} index out of range: ${index} (0..${items.length})`);\n }\n return [...items.slice(0, index), item, ...items.slice(index)];\n}\n\n// 選択と編集は id で対象を決めるので、同じ id の要素を 2 つ持たせない。\nfunction assertNewId(items: readonly { id: string }[], id: string, message: string): void {\n if (items.some((item) => item.id === id)) {\n throw new Error(`${message} \"${id}\"`);\n }\n}\n\n// 件数の上限は外部データの正規化と同じ値にする。編集で超えられると、\n// getData で書き出した図を読み戻したときに黙って切り詰められる。\nfunction assertWithinLimit(count: number, limit: number, what: string): void {\n if (count > limit) {\n throw new Error(`PlayModel: too many ${what}: ${count} > ${limit}`);\n }\n}\n\nfunction assertWithinLimits(data: PlayData): void {\n assertWithinLimit(data.players.length, MAX_PLAYERS, \"players\");\n assertWithinLimit(data.lines.length, MAX_LINES, \"lines\");\n for (const line of data.lines) {\n assertWithinLimit(line.waypoints.length, MAX_WAYPOINTS_PER_LINE, \"waypoints\");\n }\n}\n\n/**\n * 変更系メソッドは、状態を新しいオブジェクトへ差し替えてから onDidChange を 1 回だけ発火する。\n * 入力は複製せずに取り込む。型が読み取り専用なので、入力も状態もあとから書き換えられない。\n * 無い id を指す操作は、編集の組み立て違いなのでその場で throw する。\n */\nexport class PlayModel extends Disposable implements IPlayModel {\n private readonly _onDidChange = this._register(new Emitter<PlayData>());\n readonly onDidChange = this._onDidChange.event;\n private state: PlayData;\n\n constructor(initialData?: unknown) {\n super();\n this.state = migratePlayData(initialData);\n }\n\n getData(): PlayData {\n return clonePlayData(this.state);\n }\n\n getSnapshot(): PlayData {\n return this.state;\n }\n\n getFieldZone(): FieldZone {\n return this.state.field.zone;\n }\n\n findPlayer(id: string): Player | undefined {\n return this.state.players.find((p) => p.id === id);\n }\n\n findLine(id: string): Line | undefined {\n return this.state.lines.find((l) => l.id === id);\n }\n\n getPlayer(id: string): Player {\n const player = this.findPlayer(id);\n if (player === undefined) {\n throw new Error(`PlayModel: unknown player id \"${id}\"`);\n }\n return player;\n }\n\n getLine(id: string): Line {\n const line = this.findLine(id);\n if (line === undefined) {\n throw new Error(`PlayModel: unknown line id \"${id}\"`);\n }\n return line;\n }\n\n setFieldZone(zone: FieldZone): void {\n // 同じゾーンでも発火する。変わらない操作を履歴に積まないのは編集の側で決める。\n this.commit({ ...this.state, field: fieldStateForZone(zone) });\n }\n\n addPlayer(player: Player): void {\n this.addPlayers([player]);\n }\n\n addPlayers(players: readonly Player[]): void {\n // 1 人でも重複があれば何も足さずに throw する。途中まで足してから投げると、\n // 通知も履歴も伴わない変更が残る。\n const taken = new Set(this.state.players.map((p) => p.id));\n for (const player of players) {\n if (taken.has(player.id)) {\n throw new Error(`PlayModel.addPlayers: duplicate player id \"${player.id}\"`);\n }\n taken.add(player.id);\n }\n this.commit({ ...this.state, players: [...this.state.players, ...players] });\n }\n\n private removePlayerCore(id: string): PlayerRemoval {\n const target = this.state.players.find((p) => p.id === id);\n if (target === undefined) {\n throw new Error(`PlayModel.removePlayer: unknown player id \"${id}\"`);\n }\n const index = this.state.players.indexOf(target);\n const removedLines: LineRemoval[] = [];\n const lines: Line[] = [];\n this.state.lines.forEach((line, i) => {\n if (line.startPlayerId === id) {\n // 起点を失う線は描けないので一緒に消し、戻すときのために添字を控える。\n removedLines.push({ line, index: i });\n } else {\n lines.push(line);\n }\n });\n // 削除では上限を超えないので commit を通さない。通知は呼び出し側がまとめて 1 回出す。\n this.state = {\n ...this.state,\n players: this.state.players.filter((p) => p !== target),\n lines,\n };\n return { player: target, index, removedLines };\n }\n\n removePlayer(id: string): PlayerRemoval {\n const removal = this.removePlayerCore(id);\n this.emitChange();\n return removal;\n }\n\n removePlayers(ids: readonly string[]): PlayerRemoval[] {\n // 1 人でも消せない id があれば何も消さずに throw する。途中まで消してから投げると、\n // 通知も履歴も伴わない変更が残る。\n const remaining = new Set(this.state.players.map((p) => p.id));\n for (const id of ids) {\n if (!remaining.delete(id)) {\n throw new Error(`PlayModel.removePlayers: unknown or repeated player id \"${id}\"`);\n }\n }\n const removals = ids.map((id) => this.removePlayerCore(id));\n this.emitChange();\n return removals;\n }\n\n restorePlayer(removal: PlayerRemoval): void {\n assertNewId(\n this.state.players,\n removal.player.id,\n \"PlayModel.restorePlayer: duplicate player id\",\n );\n const players = insertAt(this.state.players, removal.index, removal.player, \"player\");\n // 元の添字の小さい順に差し込むと、消す前の並びに戻る。\n let lines = this.state.lines;\n for (const { line, index } of [...removal.removedLines].sort((a, b) => a.index - b.index)) {\n lines = insertAt(lines, index, line, \"line\");\n }\n this.commit({ ...this.state, players, lines });\n }\n\n updatePlayer(player: Player): Player {\n const prev = this.getPlayer(player.id);\n this.commit({\n ...this.state,\n players: this.state.players.map((p) => (p === prev ? player : p)),\n });\n return prev;\n }\n\n addLine(line: Line): void {\n this.insertLine(line, this.state.lines.length);\n }\n\n insertLine(line: Line, index: number): void {\n assertNewId(this.state.lines, line.id, \"PlayModel: duplicate line id\");\n this.commit({ ...this.state, lines: insertAt(this.state.lines, index, line, \"line\") });\n }\n\n removeLine(id: string): LineRemoval {\n const target = this.state.lines.find((l) => l.id === id);\n if (target === undefined) {\n throw new Error(`PlayModel.removeLine: unknown line id \"${id}\"`);\n }\n const index = this.state.lines.indexOf(target);\n this.commit({ ...this.state, lines: this.state.lines.filter((l) => l !== target) });\n return { line: target, index };\n }\n\n updateLine(line: Line): Line {\n const prev = this.getLine(line.id);\n this.commit({\n ...this.state,\n lines: this.state.lines.map((l) => (l === prev ? line : l)),\n });\n return prev;\n }\n\n // 件数が増えうる変更はすべてここを通し、上限を超える状態を持たない。\n private commit(next: PlayData): void {\n assertWithinLimits(next);\n this.state = next;\n this.emitChange();\n }\n\n private emitChange(): void {\n this._onDidChange.fire(this.state);\n }\n}\n","import type { IPlayModel } from \"../model/play-model.js\";\n\n/**\n * 編集操作 1 つ。redo は apply をもう一度呼んで表す。\n * 取り消しに要る直前の状態は、apply のたびに各コマンドが自分で控える。\n */\nexport interface ICommand {\n /** 履歴の表示に使う日本語のラベル。 */\n readonly label: string;\n /**\n * Model を変えたら true を返す。何も変えないときは Model に触れずに false を返し、\n * 履歴に空の段を積ませない。\n */\n apply(model: IPlayModel): boolean;\n undo(model: IPlayModel): void;\n}\n\n/** apply で控えた値を undo で取り出す。apply より前に undo されたら throw する。 */\nexport function requireApplied<T>(value: T | undefined, command: ICommand): T {\n /* v8 ignore start -- 履歴は apply が済んだコマンドしか積まないので、編集の操作からは届かない。 */\n if (value === undefined) {\n throw new Error(`${command.label}: apply より前に undo された`);\n }\n /* v8 ignore stop */\n return value;\n}\n","import type { FieldZone } from \"../model/play-data.js\";\nimport type { IPlayModel } from \"../model/play-model.js\";\nimport { type ICommand, requireApplied } from \"./command.js\";\n\nexport class SetFieldZoneCommand implements ICommand {\n readonly label = \"フィールドゾーンの切替\";\n private readonly zone: FieldZone;\n private previous: FieldZone | undefined;\n\n constructor(zone: FieldZone) {\n this.zone = zone;\n }\n\n apply(model: IPlayModel): boolean {\n this.previous = model.getFieldZone();\n if (this.previous === this.zone) {\n return false;\n }\n model.setFieldZone(this.zone);\n return true;\n }\n\n undo(model: IPlayModel): void {\n model.setFieldZone(requireApplied(this.previous, this));\n }\n}\n","import type { IPlayModel } from \"../model/play-model.js\";\nimport type { Player } from \"../model/player.js\";\nimport type { ICommand } from \"./command.js\";\n\n/**\n * 採番済みの選手をまとめて追加する。既にある選手と線は残すので、攻撃と守備の隊形を順に重ねられる。\n * Model への変更は 1 回にまとめ、通知も 1 回だけにする。\n */\nexport class LoadFormationCommand implements ICommand {\n readonly label = \"フォーメーションの読み込み\";\n private readonly players: readonly Player[];\n\n constructor(players: readonly Player[]) {\n this.players = players;\n }\n\n apply(model: IPlayModel): boolean {\n if (this.players.length === 0) {\n return false;\n }\n model.addPlayers(this.players);\n return true;\n }\n\n undo(model: IPlayModel): void {\n // 追加した選手を起点にした線は、それより後のコマンドなので先に取り消されている。\n model.removePlayers(this.players.map((p) => p.id));\n }\n}\n","type OptionalKeys<T> = {\n [K in keyof T]-?: Record<never, never> extends Pick<T, K> ? K : never;\n}[keyof T];\n\n/**\n * 指定したキーだけを差し替えるパッチ。指定しないキーは今の値を保つ。\n * 省略できるキーだけは null を指定でき、値を消して既定に戻す。\n */\nexport type Patch<T> = {\n readonly [K in keyof T]?: K extends OptionalKeys<T> ? T[K] | null : T[K];\n};\n\n// Line や Player はインターフェースなので添字シグネチャを持たず、そのままではキーを文字列で引けない。\n// パッチの規則をキーの一覧に依らず 1 か所に書くため、この 2 関数の中だけ見方を変える。\nfunction asRecord(value: object): Readonly<Record<string, unknown>> {\n return value as Record<string, unknown>;\n}\n\n/** current にパッチを当てた新しいオブジェクトを返す。current は書き換えない。 */\nexport function applyPatch<T extends object>(current: T, patch: Patch<T>): T {\n const next = { ...asRecord(current) };\n for (const [key, value] of Object.entries(patch)) {\n if (value === null) {\n delete next[key];\n } else {\n next[key] = value;\n }\n }\n // パッチの値は Patch<T> で T の各キーの型に揃っているので、当てた結果も T になる。\n return next as T;\n}\n\n/** パッチを当てると、current から何か 1 つでも値が変わるか。オブジェクトの値は参照で比べる。 */\nexport function patchChangesAnything<T extends object>(current: T, patch: Patch<T>): boolean {\n const values = asRecord(current);\n return Object.entries(patch).some(([key, value]) => (value ?? undefined) !== values[key]);\n}\n","import type { Line } from \"../model/line.js\";\nimport type { IPlayModel, LineRemoval } from \"../model/play-model.js\";\nimport { type ICommand, requireApplied } from \"./command.js\";\nimport { applyPatch, type Patch, patchChangesAnything } from \"./patch.js\";\n\n/** id と起点の選手は線を見分ける鍵なので、パッチでは変えない。 */\nexport type LinePatch = Patch<Omit<Line, \"id\" | \"startPlayerId\">>;\n\nexport function applyLinePatch(current: Line, patch: LinePatch): Line {\n return applyPatch<Line>(current, patch);\n}\n\nexport class AddLineCommand implements ICommand {\n readonly label = \"線の追加\";\n private readonly line: Line;\n\n constructor(line: Line) {\n this.line = line;\n }\n\n apply(model: IPlayModel): boolean {\n model.addLine(this.line);\n return true;\n }\n\n undo(model: IPlayModel): void {\n model.removeLine(this.line.id);\n }\n}\n\n/** undo は線を元の並びの位置へ戻す。 */\nexport class RemoveLineCommand implements ICommand {\n readonly label = \"線の削除\";\n private readonly lineId: string;\n private removal: LineRemoval | undefined;\n\n constructor(lineId: string) {\n this.lineId = lineId;\n }\n\n apply(model: IPlayModel): boolean {\n this.removal = model.removeLine(this.lineId);\n return true;\n }\n\n undo(model: IPlayModel): void {\n const { line, index } = requireApplied(this.removal, this);\n model.insertLine(line, index);\n }\n}\n\n/** 種別、補間、waypoint、終点、色、太さをまとめて編集する。 */\nexport class UpdateLineCommand implements ICommand {\n readonly label = \"線の編集\";\n private readonly lineId: string;\n private readonly patch: LinePatch;\n private previous: Line | undefined;\n\n constructor(lineId: string, patch: LinePatch) {\n this.lineId = lineId;\n this.patch = { ...patch };\n }\n\n apply(model: IPlayModel): boolean {\n const current = model.getLine(this.lineId);\n if (!patchChangesAnything(current, this.patch)) {\n return false;\n }\n this.previous = model.updateLine(applyLinePatch(current, this.patch));\n return true;\n }\n\n undo(model: IPlayModel): void {\n model.updateLine(requireApplied(this.previous, this));\n }\n}\n","import type { IPlayModel, PlayerRemoval } from \"../model/play-model.js\";\nimport type { Player } from \"../model/player.js\";\nimport { type ICommand, requireApplied } from \"./command.js\";\nimport { applyPatch, type Patch, patchChangesAnything } from \"./patch.js\";\n\n/** id は選手を見分ける鍵なので、パッチでは変えない。 */\nexport type PlayerPatch = Patch<Omit<Player, \"id\">>;\n\nexport function applyPlayerPatch(current: Player, patch: PlayerPatch): Player {\n return applyPatch<Player>(current, patch);\n}\n\nexport class AddPlayerCommand implements ICommand {\n readonly label = \"選手の追加\";\n private readonly player: Player;\n\n constructor(player: Player) {\n this.player = player;\n }\n\n apply(model: IPlayModel): boolean {\n model.addPlayer(this.player);\n return true;\n }\n\n undo(model: IPlayModel): void {\n // 追加した直後の選手を起点にした線は、それより後のコマンドなので先に取り消されている。\n // 削除で控える従属線は空なので捨てる。\n model.removePlayer(this.player.id);\n }\n}\n\n/** 起点がその選手の線も一緒に消す。undo は選手と線を元の並びの位置へ戻す。 */\nexport class RemovePlayerCommand implements ICommand {\n readonly label = \"選手の削除\";\n private readonly playerId: string;\n private removal: PlayerRemoval | undefined;\n\n constructor(playerId: string) {\n this.playerId = playerId;\n }\n\n apply(model: IPlayModel): boolean {\n this.removal = model.removePlayer(this.playerId);\n return true;\n }\n\n undo(model: IPlayModel): void {\n model.restorePlayer(requireApplied(this.removal, this));\n }\n}\n\n/** 位置、ラベル、形状、色、塗りをまとめて編集する。 */\nexport class UpdatePlayerCommand implements ICommand {\n readonly label = \"選手の編集\";\n private readonly playerId: string;\n private readonly patch: PlayerPatch;\n private previous: Player | undefined;\n\n constructor(playerId: string, patch: PlayerPatch) {\n this.playerId = playerId;\n this.patch = { ...patch };\n }\n\n apply(model: IPlayModel): boolean {\n const current = model.getPlayer(this.playerId);\n if (!patchChangesAnything(current, this.patch)) {\n return false;\n }\n this.previous = model.updatePlayer(applyPlayerPatch(current, this.patch));\n return true;\n }\n\n undo(model: IPlayModel): void {\n model.updatePlayer(requireApplied(this.previous, this));\n }\n}\n","import type { FieldState, FieldZone } from \"../model/play-data.js\";\nimport type { FieldPosition } from \"../model/player.js\";\n\n/**\n * サイドラインの間の幅。規定は 160 フィート(53⅓ ヤード)だが、中央(26.5)が\n * 0.5 ヤードの格子点に乗るように 53 ヤードへ丸める。本物の寸法との差は 1 フィートで、\n * 図の見た目には出ない。\n */\nexport const FIELD_WIDTH_YARDS = 53;\n\n/**\n * middle ゾーンで映す縦の長さ。レッドゾーンの窓は、ヤード数字が端で見切れないよう\n * これより深い。窓ごとの長さは zoneWindowLength で得る。\n */\nexport const ZONE_WINDOW_LENGTH_YARDS = 30;\n\n/**\n * リーグごとの、フィールドの中央からハッシュマークまでの距離(ヤード)。\n * 左右のハッシュの間隔は NCAA が 40 フィート、NFL が 18.5 フィート、NFHS が 53⅓ フィート。\n * 選手をハッシュの上に置けるように、格子点(0.5 ヤード刻み)に丸めてある。\n * 利用者がリーグを選ぶ API は無く、DEFAULT_FIELD_LEAGUE で描く。\n */\nexport const HASH_CENTER_OFFSET_YARDS_BY_LEAGUE = {\n ncaa: 6.5,\n nfl: 3,\n nfhs: 9,\n} as const;\n\nexport type FieldLeague = keyof typeof HASH_CENTER_OFFSET_YARDS_BY_LEAGUE;\n\n/** 日本のフィールドは NCAA のハッシュの間隔で引かれていることが多い。 */\nexport const DEFAULT_FIELD_LEAGUE: FieldLeague = \"ncaa\";\n\n/**\n * レッドゾーンの窓に映す、ゴールラインから手前への奥行き。本来のレッドゾーン(20 ヤード)より\n * 広いのは、20 ヤードラインが窓の端に乗って数字が見切れるのを避けるため。\n */\nexport const RED_ZONE_DEPTH_YARDS = 25;\n\n/** ヤードラインの間隔。描くのは browser だが、格子の点をラインの上に置かないために共有する。 */\nexport const YARD_LINE_INTERVAL_YARDS = 5;\n\n/** エンドゾーンの奥行き。絶対ヤードは自陣のエンドラインの -10 から相手のエンドラインの 110 まで取る。 */\nexport const END_ZONE_DEPTH_YARDS = 10;\n\nexport interface YardWindow {\n /** 窓の手前側(画面下、攻撃方向の後方)の絶対ヤード。 */\n readonly startYard: number;\n /** 窓の奥側(画面上、攻撃方向の前方)の絶対ヤード。 */\n readonly endYard: number;\n}\n\n/**\n * ゾーンで映す絶対ヤードの範囲。絶対ヤードは自陣のゴールラインを 0、相手のゴールラインを 100 とし、\n * 大きいほど攻撃方向(画面の上)になる。レッドゾーンの窓はエンドゾーンまで映す。\n */\nexport function fieldZoneWindow(zone: FieldZone): YardWindow {\n switch (zone) {\n case \"own-redzone\":\n return { startYard: -END_ZONE_DEPTH_YARDS, endYard: RED_ZONE_DEPTH_YARDS };\n case \"redzone\":\n return { startYard: 100 - RED_ZONE_DEPTH_YARDS, endYard: 100 + END_ZONE_DEPTH_YARDS };\n case \"middle\": {\n const half = ZONE_WINDOW_LENGTH_YARDS / 2;\n return { startYard: 50 - half, endYard: 50 + half };\n }\n }\n}\n\nexport function zoneWindowLength(zone: FieldZone): number {\n const { startYard, endYard } = fieldZoneWindow(zone);\n return endYard - startYard;\n}\n\n/** ゾーンの窓の幅と縦の長さの比。レッドゾーンの窓は middle より縦に長い。 */\nexport function fieldWindowAspect(zone: FieldZone): number {\n return FIELD_WIDTH_YARDS / zoneWindowLength(zone);\n}\n\n/**\n * 位置をゾーン窓の中(左右はサイドライン間、縦は窓の端から端)へ寄せる。\n * 画面の外で離したドラッグや余白へのクリックで、見えない位置に選手や点を置かないためのもの。\n */\nexport function clampToZoneWindow(position: FieldPosition, field: FieldState): FieldPosition {\n const { startYard, endYard } = fieldZoneWindow(field.zone);\n return {\n lateralYard: Math.min(Math.max(position.lateralYard, 0), FIELD_WIDTH_YARDS),\n downfieldYard: Math.min(\n Math.max(position.downfieldYard, startYard - field.losYard),\n endYard - field.losYard,\n ),\n };\n}\n\n/**\n * 絶対ヤードに対応する、フィールドに書く番号(1..50)。両ゴールから数える(…40、50、40…)。\n * ゴールラインとエンドゾーンには番号を書かないので null を返す。\n */\nexport function displayYardNumber(absoluteYard: number): number | null {\n if (absoluteYard <= 0 || absoluteYard >= 100) {\n return null;\n }\n return 50 - Math.abs(50 - absoluteYard);\n}\n\n/** ゾーンの窓の中にある、stepYards の倍数の絶対ヤードを小さい順に返す。 */\nexport function yardLinesInWindow(zone: FieldZone, stepYards: number): number[] {\n if (stepYards <= 0) {\n throw new Error(\"Playmaker: stepYards は正の数である必要があります。\");\n }\n const { startYard, endYard } = fieldZoneWindow(zone);\n const lines: number[] = [];\n // stepYards が整数でないと足し算を重ねた誤差が出るので、\n // 窓の奥の端にあるラインを落とさないよう少し余裕を持たせる。\n const first = Math.ceil(startYard / stepYards) * stepYards;\n for (let y = first; y <= endYard + 1e-9; y += stepYards) {\n lines.push(y);\n }\n return lines;\n}\n\nexport interface CanvasPoint {\n readonly x: number;\n readonly y: number;\n}\n\n/**\n * ゾーンの窓を縦横比を保ったままビューポートの中央に収め、ヤードと Canvas の px を変換する。\n * フィールドの線や番号は絶対ヤードで、選手と線は LOS からの位置(FieldPosition)で扱う。\n */\nexport class FieldGeometry {\n readonly viewportWidth: number;\n readonly viewportHeight: number;\n readonly zone: FieldZone;\n /** LOS の絶対ヤード。FieldPosition の downfieldYard はここからの距離。 */\n readonly losYard: number;\n readonly yardWindow: YardWindow;\n /** 1 ヤードあたりの px。縦横で同じ値。 */\n readonly scale: number;\n readonly fieldPixelWidth: number;\n readonly fieldPixelHeight: number;\n /** フィールドの左上の Canvas 座標。縦横比が合わない分の余白になる。 */\n readonly offsetX: number;\n readonly offsetY: number;\n\n constructor(viewportWidth: number, viewportHeight: number, field: FieldState) {\n this.viewportWidth = viewportWidth;\n this.viewportHeight = viewportHeight;\n this.zone = field.zone;\n this.losYard = field.losYard;\n this.yardWindow = fieldZoneWindow(field.zone);\n const windowLength = this.yardWindow.endYard - this.yardWindow.startYard;\n const w = Math.max(0, viewportWidth);\n const h = Math.max(0, viewportHeight);\n this.scale = Math.min(w / FIELD_WIDTH_YARDS, h / windowLength);\n this.fieldPixelWidth = FIELD_WIDTH_YARDS * this.scale;\n this.fieldPixelHeight = windowLength * this.scale;\n this.offsetX = (w - this.fieldPixelWidth) / 2;\n this.offsetY = (h - this.fieldPixelHeight) / 2;\n }\n\n xForLateralYard(lateralYard: number): number {\n return this.offsetX + lateralYard * this.scale;\n }\n\n yForAbsoluteYard(absoluteYard: number): number {\n return this.offsetY + (this.yardWindow.endYard - absoluteYard) * this.scale;\n }\n\n toCanvas(position: FieldPosition): CanvasPoint {\n return {\n x: this.xForLateralYard(position.lateralYard),\n y: this.yForAbsoluteYard(this.losYard + position.downfieldYard),\n };\n }\n\n // 大きさ 0 のビューポートでは scale が 0 で割れないので、フィールドの左上の位置を返して NaN を出さない。\n lateralYardForX(x: number): number {\n return this.scale > 0 ? (x - this.offsetX) / this.scale : 0;\n }\n\n absoluteYardForY(y: number): number {\n return this.scale > 0\n ? this.yardWindow.endYard - (y - this.offsetY) / this.scale\n : this.yardWindow.endYard;\n }\n\n fromCanvas(point: CanvasPoint): FieldPosition {\n return {\n lateralYard: this.lateralYardForX(point.x),\n downfieldYard: this.absoluteYardForY(point.y) - this.losYard,\n };\n }\n\n /** 端も含む。 */\n containsYard(absoluteYard: number): boolean {\n return absoluteYard >= this.yardWindow.startYard && absoluteYard <= this.yardWindow.endYard;\n }\n}\n","import type { CanvasPoint } from \"./field.js\";\n\n/** 隣り合う 2 点の組を先頭から順に返す。2 点未満なら何も返さない。 */\nexport function* segments<T>(points: readonly T[]): Generator<readonly [T, T]> {\n // 前の点を箱に入れて持つ。T が undefined を含んでも「前の点が無い」と区別できる。\n let previous: { readonly point: T } | undefined;\n for (const point of points) {\n if (previous !== undefined) {\n yield [previous.point, point];\n }\n previous = { point };\n }\n}\n\n/** 隣接点間の距離を足し上げた全長。2 点未満なら 0。 */\nexport function polylineLength(path: readonly CanvasPoint[]): number {\n let total = 0;\n for (const [a, b] of segments(path)) {\n total += Math.hypot(b.x - a.x, b.y - a.y);\n }\n return total;\n}\n\n/**\n * 終点から折れ線に沿って distance だけ戻った点で終わる、新しい折れ線を返す。\n * 最後の区間だけで戻らないのは、細かく刻んだ曲線では 1 区間が distance より短いため。\n * maxFraction は削る長さの上限で、全長に対する割合で渡す。既定の 1 では上限が無く、\n * distance が全長以上なら始点 1 点だけを返す。\n */\nexport function trimPolylineEnd(\n path: readonly CanvasPoint[],\n distance: number,\n maxFraction = 1,\n): CanvasPoint[] {\n if (path.length < 2 || distance <= 0) {\n return [...path];\n }\n let remaining =\n maxFraction >= 1 ? distance : Math.min(distance, polylineLength(path) * maxFraction);\n if (remaining <= 0) {\n return [...path];\n }\n\n // 矢じりの長さは全長よりずっと短く、たいてい終点側の数区間で止まるので、配列を複製せずに遡る。\n for (let i = path.length - 1; i > 0; i--) {\n const a = path[i - 1];\n const b = path[i];\n /* v8 ignore start -- i は 1 から path.length - 1 までなので、a も b も範囲の内側にある。 */\n if (a === undefined || b === undefined) {\n break;\n }\n /* v8 ignore stop */\n const segLen = Math.hypot(b.x - a.x, b.y - a.y);\n // 等号で切ると a と同じ座標を足し、長さ 0 の区間が末尾に残る(丸キャップが点を描く)。\n if (segLen > remaining) {\n const t = (segLen - remaining) / segLen;\n return [...path.slice(0, i), { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t }];\n }\n remaining -= segLen;\n }\n return path.slice(0, 1);\n}\n","import type { LineInterpolation } from \"../model/line.js\";\nimport type { FieldPosition } from \"../model/player.js\";\nimport { segments } from \"./polyline.js\";\n\n/** ベジェの 1 区間を何分割するか。プレー図の大きさでは、これで角が見えない。 */\nexport const DEFAULT_BEZIER_SAMPLES_PER_SEGMENT = 16;\n\n/** p0 から p1 へ、制御点 c1 と c2 で曲げた 3 次ベジェの、t(0 以上 1 以下)での位置。 */\nexport function cubicBezierPoint(\n p0: FieldPosition,\n c1: FieldPosition,\n c2: FieldPosition,\n p1: FieldPosition,\n t: number,\n): FieldPosition {\n const u = 1 - t;\n const a = u * u * u;\n const b = 3 * u * u * t;\n const c = 3 * u * t * t;\n const d = t * t * t;\n return {\n lateralYard: a * p0.lateralYard + b * c1.lateralYard + c * c2.lateralYard + d * p1.lateralYard,\n downfieldYard:\n a * p0.downfieldYard + b * c1.downfieldYard + c * c2.downfieldYard + d * p1.downfieldYard,\n };\n}\n\n/**\n * p1 から p2 への区間を、前後の点 p0 と p3 から決まる向きで通す 3 次ベジェの制御点 [c1, c2]。\n * 一様な Catmull-Rom 曲線と同じ形になる。端の区間では、無い側の点に端の点そのものを渡す。\n * waypoint をそのままベジェの制御点にしないのは、曲線が waypoint を通らなくなるため。\n */\nexport function catmullRomBezierControls(\n p0: FieldPosition,\n p1: FieldPosition,\n p2: FieldPosition,\n p3: FieldPosition,\n): [FieldPosition, FieldPosition] {\n return [\n {\n lateralYard: p1.lateralYard + (p2.lateralYard - p0.lateralYard) / 6,\n downfieldYard: p1.downfieldYard + (p2.downfieldYard - p0.downfieldYard) / 6,\n },\n {\n lateralYard: p2.lateralYard - (p3.lateralYard - p1.lateralYard) / 6,\n downfieldYard: p2.downfieldYard - (p3.downfieldYard - p1.downfieldYard) / 6,\n },\n ];\n}\n\n/** 同じ座標が続くと向きが 0 になり曲線が崩れるので、1 つに畳む。 */\nfunction dedupeConsecutive(points: readonly FieldPosition[]): FieldPosition[] {\n const out: FieldPosition[] = [];\n for (const p of points) {\n const last = out[out.length - 1];\n if (\n last === undefined ||\n last.lateralYard !== p.lateralYard ||\n last.downfieldYard !== p.downfieldYard\n ) {\n out.push({ lateralYard: p.lateralYard, downfieldYard: p.downfieldYard });\n }\n }\n return out;\n}\n\n/**\n * 制御点の列を、描くときと当たり判定に使う折れ線にする。`straight` と、同じ座標を畳んで\n * 2 点以下になる列は、制御点をそのまま結ぶ。返り値は新しい配列。samplesPerSegment は 1 以上に丸める。\n */\nexport function sampleLinePath(\n points: readonly FieldPosition[],\n interpolation: LineInterpolation,\n samplesPerSegment: number = DEFAULT_BEZIER_SAMPLES_PER_SEGMENT,\n): FieldPosition[] {\n const pts = dedupeConsecutive(points);\n if (interpolation === \"straight\" || pts.length < 3) {\n return pts;\n }\n const steps = Math.max(1, Math.floor(samplesPerSegment));\n\n const result: FieldPosition[] = pts.slice(0, 1);\n for (const [i, [p1, p2]] of [...segments(pts)].entries()) {\n const p0 = pts[i - 1] ?? p1;\n const p3 = pts[i + 2] ?? p2;\n const [c1, c2] = catmullRomBezierControls(p0, p1, p2, p3);\n // t=0 の点は前の区間の終点と同じなので、t は 0 を除いて 1 まで刻む。\n for (let s = 1; s <= steps; s++) {\n result.push(cubicBezierPoint(p1, c1, c2, p2, s / steps));\n }\n }\n return result;\n}\n","import { indexPlayersById, type Line, lineAnchorPoints } from \"../model/line.js\";\nimport type { FieldPosition, Player } from \"../model/player.js\";\nimport { PLAYER_RADIUS_YARDS } from \"../model/player.js\";\nimport { sampleLinePath } from \"./bezier.js\";\nimport { segments } from \"./polyline.js\";\n\n/**\n * target に重なる選手のうち、いちばん上に描いたものを返す。後の要素ほど上に描くので末尾から探す。\n * 四角の選手も外接円で当てる。形ごとに当たりを変えるほどの差が無いため。\n */\nexport function hitTestPlayer(\n players: readonly Player[],\n target: FieldPosition,\n radiusYards: number = PLAYER_RADIUS_YARDS,\n): Player | undefined {\n if (radiusYards <= 0) {\n throw new Error(\"Playmaker: radiusYards は正の数である必要があります。\");\n }\n const r2 = radiusYards * radiusYards;\n for (const player of [...players].reverse()) {\n const dx = player.position.lateralYard - target.lateralYard;\n const dy = player.position.downfieldYard - target.downfieldYard;\n if (dx * dx + dy * dy <= r2) {\n return player;\n }\n }\n return undefined;\n}\n\n/**\n * 線に当たったとみなす距離(ヤード)。線の太さでは細すぎて掴めないので、太さとは別に持つ。\n * 選手の半径より小さくし、選手の近くを押したときに線を掴みにくくしている。\n */\nexport const LINE_HIT_TOLERANCE_YARDS = 0.6;\n\nexport function distanceToSegment(p: FieldPosition, a: FieldPosition, b: FieldPosition): number {\n const abx = b.lateralYard - a.lateralYard;\n const aby = b.downfieldYard - a.downfieldYard;\n const apx = p.lateralYard - a.lateralYard;\n const apy = p.downfieldYard - a.downfieldYard;\n const lenSq = abx * abx + aby * aby;\n // a と b が同じ点なら、a までの距離にする。\n const t = lenSq > 0 ? Math.max(0, Math.min(1, (apx * abx + apy * aby) / lenSq)) : 0;\n const dx = apx - t * abx;\n const dy = apy - t * aby;\n return Math.sqrt(dx * dx + dy * dy);\n}\n\n/** 点が 0 個なら +Infinity。 */\nfunction distanceToPolyline(p: FieldPosition, points: readonly FieldPosition[]): number {\n const only = points.length === 1 ? points[0] : undefined;\n if (only !== undefined) {\n return pointDistance(p, only);\n }\n let min = Number.POSITIVE_INFINITY;\n for (const [a, b] of segments(points)) {\n min = Math.min(min, distanceToSegment(p, a, b));\n }\n return min;\n}\n\n/**\n * target に重なる線のうち、いちばん上に描いたものを返す。描くときと同じ折れ線への距離で測るので、\n * 曲線も見えている形のとおりに当たる。起点の選手が無い線は描かれないので、当てない。\n */\nexport function hitTestLine(\n lines: readonly Line[],\n players: readonly Player[],\n target: FieldPosition,\n toleranceYards: number = LINE_HIT_TOLERANCE_YARDS,\n): Line | undefined {\n if (toleranceYards <= 0) {\n throw new Error(\"Playmaker: toleranceYards は正の数である必要があります。\");\n }\n const byId = indexPlayersById(players);\n for (const line of [...lines].reverse()) {\n const anchors = lineAnchorPoints(line, byId);\n if (anchors === undefined) {\n continue;\n }\n const polyline = sampleLinePath(anchors, line.interpolation);\n if (distanceToPolyline(target, polyline) <= toleranceYards) {\n return line;\n }\n }\n return undefined;\n}\n\n/** 選択中の線の waypoint と終点のハンドルを掴める半径(ヤード)。 */\nexport const WAYPOINT_HANDLE_RADIUS_YARDS = 0.9;\n\nexport function pointDistance(a: FieldPosition, b: FieldPosition): number {\n return Math.hypot(a.lateralYard - b.lateralYard, a.downfieldYard - b.downfieldYard);\n}\n\n/** 線のハンドルに当たったときの、掴んだハンドルとその位置。 */\nexport type LineHandleHit =\n | { readonly kind: \"endpoint\"; readonly point: FieldPosition }\n | { readonly kind: \"waypoint\"; readonly index: number; readonly point: FieldPosition };\n\n/**\n * target に当たる線のハンドルを返す。終点を waypoint より先に当て、先端を動かしたい操作を\n * 最後の waypoint に奪われないようにする。waypoint は後ろほど手前に描くので、末尾から探す。\n */\nexport function hitLineHandle(\n line: Pick<Line, \"waypoints\" | \"end\">,\n target: FieldPosition,\n): LineHandleHit | undefined {\n const within = (point: FieldPosition) =>\n pointDistance(target, point) <= WAYPOINT_HANDLE_RADIUS_YARDS;\n if (within(line.end)) {\n return { kind: \"endpoint\", point: line.end };\n }\n for (const [index, point] of [...line.waypoints.entries()].reverse()) {\n if (within(point)) {\n return { kind: \"waypoint\", index, point };\n }\n }\n return undefined;\n}\n","import { Emitter } from \"../base/event.js\";\nimport { Disposable } from \"../base/lifecycle.js\";\nimport type { Line } from \"../model/line.js\";\nimport type { Player } from \"../model/player.js\";\nimport { type EditorViewState, isSameSelection } from \"./editor.js\";\n\n/** 表示状態の通知は、この値の組が前回の通知(まだ通知していなければ構築時)と違うときだけ出す。 */\ninterface ViewSnapshot {\n readonly state: EditorViewState;\n readonly player: Player | undefined;\n readonly line: Line | undefined;\n}\n\n// 表示状態の項目ごとの比べ方。項目を足すと、比べ方を書くまでここが型エラーになる。\nconst VIEW_STATE_EQUALS: {\n readonly [K in keyof EditorViewState]: (a: EditorViewState[K], b: EditorViewState[K]) => boolean;\n} = {\n tool: Object.is,\n selection: isSameSelection,\n canUndo: Object.is,\n canRedo: Object.is,\n fieldZone: Object.is,\n isDrawing: Object.is,\n remainingPlayerSlots: Object.is,\n canStartLine: Object.is,\n};\n\nconst VIEW_STATE_KEYS = Object.keys(VIEW_STATE_EQUALS) as (keyof EditorViewState)[];\n\nfunction isSameEntry<K extends keyof EditorViewState>(\n key: K,\n a: EditorViewState,\n b: EditorViewState,\n): boolean {\n return VIEW_STATE_EQUALS[key](a[key], b[key]);\n}\n\n// 選手と線は変更のたびに差し替わるので、参照で比べれば値の変化が分かる。\nfunction isSameView(a: ViewSnapshot, b: ViewSnapshot): boolean {\n return (\n VIEW_STATE_KEYS.every((key) => isSameEntry(key, a.state, b.state)) &&\n a.player === b.player &&\n a.line === b.line\n );\n}\n\n/**\n * 描く図と表示状態の通知を出し分ける。batch の中で何度変わっても、通知は\n * batch の終わりに 1 回ずつにまとめる。Model はコマンドの apply の中で通知するので、\n * そのまま転送すると購読側が更新前の canUndo / canRedo を読んでしまう。\n */\nexport class EditorNotifier extends Disposable {\n private readonly _onDidChangeScene = this._register(new Emitter<void>());\n readonly onDidChangeScene = this._onDidChangeScene.event;\n private readonly _onDidChangeViewState = this._register(new Emitter<void>());\n readonly onDidChangeViewState = this._onDidChangeViewState.event;\n\n private readonly readView: () => ViewSnapshot;\n private depth = 0;\n private sceneChanged = false;\n private lastView: ViewSnapshot;\n\n constructor(readView: () => ViewSnapshot) {\n super();\n this.readView = readView;\n this.lastView = readView();\n }\n\n batch<T>(action: () => T): T {\n this.depth++;\n let result: T;\n try {\n result = action();\n } finally {\n this.depth--;\n }\n this.flushIfIdle();\n return result;\n }\n\n markSceneChanged(): void {\n this.sceneChanged = true;\n this.flushIfIdle();\n }\n\n private flushIfIdle(): void {\n if (this.depth === 0) {\n this.flush();\n }\n }\n\n private flush(): void {\n if (this.sceneChanged) {\n this.sceneChanged = false;\n this._onDidChangeScene.fire();\n }\n const view = this.readView();\n if (!isSameView(view, this.lastView)) {\n this.lastView = view;\n this._onDidChangeViewState.fire();\n }\n }\n}\n","import type { LinePatch } from \"../commands/line-commands.js\";\nimport type { PlayerPatch } from \"../commands/player-commands.js\";\nimport {\n DEFAULT_LINE_INTERPOLATION,\n DEFAULT_LINE_KIND,\n type Line,\n MAX_WAYPOINTS_PER_LINE,\n} from \"../model/line.js\";\nimport type { FieldPosition, Player } from \"../model/player.js\";\nimport type { SceneData } from \"./editor.js\";\n\n/** ドラッグで動かす対象。 */\nexport type DragTarget =\n | { readonly kind: \"player\"; readonly playerId: string }\n | { readonly kind: \"waypoint\"; readonly lineId: string; readonly index: number }\n | { readonly kind: \"endpoint\"; readonly lineId: string };\n\nexport interface DragInteraction {\n readonly type: \"drag\";\n readonly target: DragTarget;\n /** 掴んだときの対象の位置。 */\n readonly origin: FieldPosition;\n /** 対象の位置からポインタを引いた差。離すまで保ち、掴んだ点がずれないようにする。 */\n readonly grabOffset: FieldPosition;\n /** 窓の中へ寄せたあとの、いまの対象の位置。 */\n readonly current: FieldPosition;\n}\n\nexport interface DrawInteraction {\n readonly type: \"draw-line\";\n readonly startPlayerId: string;\n /** 作図を始めた時点の起点選手の位置。最初の打点が同じ位置かの判定に使う。 */\n readonly start: FieldPosition;\n /** 打った点。最後の点が終点になり、残りが waypoint になる。 */\n readonly points: readonly FieldPosition[];\n readonly cursor: FieldPosition;\n}\n\n/** ドラッグや作図の途中の状態。Model には載せず、描画のときに重ねる。 */\nexport type Interaction = DragInteraction | DrawInteraction;\n\n/** 座標が完全に一致するか。格子点どうしの比較では、これが「同じ点」の判定になる。 */\nexport function isSamePosition(a: FieldPosition, b: FieldPosition): boolean {\n return a.lateralYard === b.lateralYard && a.downfieldYard === b.downfieldYard;\n}\n\n/** 作図で打てる点の数。最後の点は終点になるので、waypoint の上限より 1 つ多い。 */\nexport const MAX_DRAFT_POINTS = MAX_WAYPOINTS_PER_LINE + 1;\n\nexport function startDrag(\n target: DragTarget,\n origin: FieldPosition,\n pointer: FieldPosition,\n): DragInteraction {\n return {\n type: \"drag\",\n target,\n origin,\n grabOffset: {\n lateralYard: origin.lateralYard - pointer.lateralYard,\n downfieldYard: origin.downfieldYard - pointer.downfieldYard,\n },\n current: origin,\n };\n}\n\n/** 掴んだ点とポインタのずれを保ったまま、対象を置く位置。 */\nexport function dragPosition(drag: DragInteraction, pointer: FieldPosition): FieldPosition {\n return {\n lateralYard: pointer.lateralYard + drag.grabOffset.lateralYard,\n downfieldYard: pointer.downfieldYard + drag.grabOffset.downfieldYard,\n };\n}\n\n/** ドラッグ先に対象を置く変更。プレビューと確定の両方がこれを当てるので、見た目と結果がずれない。 */\nexport type DragPatch =\n | { readonly kind: \"player\"; readonly playerId: string; readonly patch: PlayerPatch }\n | { readonly kind: \"line\"; readonly lineId: string; readonly patch: LinePatch };\n\n/** 対象を to に置く変更。対象が scene に無ければ undefined。 */\nexport function dragPatch(\n scene: SceneData,\n target: DragTarget,\n to: FieldPosition,\n): DragPatch | undefined {\n switch (target.kind) {\n case \"player\":\n return scene.players.some((p) => p.id === target.playerId)\n ? { kind: \"player\", playerId: target.playerId, patch: { position: to } }\n : undefined;\n case \"endpoint\":\n return scene.lines.some((l) => l.id === target.lineId)\n ? { kind: \"line\", lineId: target.lineId, patch: { end: to } }\n : undefined;\n case \"waypoint\": {\n const line = scene.lines.find((l) => l.id === target.lineId);\n return line === undefined\n ? undefined\n : {\n kind: \"line\",\n lineId: line.id,\n patch: { waypoints: line.waypoints.map((w, i) => (i === target.index ? to : w)) },\n };\n }\n }\n}\n\n/** 打った点を、確定する線の waypoint と終点に分ける。点が無ければ undefined。 */\nexport function splitDraftPoints(\n points: readonly FieldPosition[],\n): { readonly waypoints: readonly FieldPosition[]; readonly end: FieldPosition } | undefined {\n const end = points.at(-1);\n return end === undefined ? undefined : { waypoints: points.slice(0, -1), end };\n}\n\n/** 線の起点は必ず選手なので、選手から作図を始める。 */\nexport function startDrawing(player: Player): DrawInteraction {\n return {\n type: \"draw-line\",\n startPlayerId: player.id,\n start: player.position,\n points: [],\n cursor: player.position,\n };\n}\n\n/**\n * point に点を打つ。直前の点(まだ点が無ければ起点の選手)と同じ位置のときと、\n * 打てる点の上限に達したときは、打たずにカーソルだけ進める。\n * 同じ位置を弾くのは、ダブルクリックで確定すると pointerdown が 2 回届き、\n * 直前の点の真上にもう 1 点打ってしまうため。\n */\nexport function addDraftPoint(draw: DrawInteraction, point: FieldPosition): DrawInteraction {\n const last = draw.points.at(-1) ?? draw.start;\n const accepts = !isSamePosition(last, point) && draw.points.length < MAX_DRAFT_POINTS;\n return { ...draw, points: accepts ? [...draw.points, point] : draw.points, cursor: point };\n}\n\n/**\n * 確定する線の waypoint と終点。anchor はいまの起点選手の位置。点が無いときと、\n * 打った点がすべて anchor と同じで長さが 0 のときは undefined。長さ 0 の線は見えず、選択もできない。\n */\nexport function committableDraft(\n draw: DrawInteraction,\n anchor: FieldPosition,\n): { readonly waypoints: readonly FieldPosition[]; readonly end: FieldPosition } | undefined {\n const split = splitDraftPoints(draw.points);\n if (split === undefined) {\n return undefined;\n }\n return draw.points.every((p) => isSamePosition(p, anchor)) ? undefined : split;\n}\n\n/** 作図から線を組み立てる。プレビューと確定で同じ既定値を使う。 */\nexport function draftToLine(\n draw: DrawInteraction,\n id: string,\n shape: { readonly waypoints: readonly FieldPosition[]; readonly end: FieldPosition },\n): Line {\n return {\n id,\n kind: DEFAULT_LINE_KIND,\n startPlayerId: draw.startPlayerId,\n waypoints: shape.waypoints,\n end: shape.end,\n interpolation: DEFAULT_LINE_INTERPOLATION,\n };\n}\n","import { applyLinePatch } from \"../commands/line-commands.js\";\nimport { applyPlayerPatch } from \"../commands/player-commands.js\";\nimport type { Line } from \"../model/line.js\";\nimport type { EditorOverlay, EditorSelection, SceneData } from \"./editor.js\";\nimport {\n type DragPatch,\n type DrawInteraction,\n draftToLine,\n dragPatch,\n type Interaction,\n MAX_DRAFT_POINTS,\n splitDraftPoints,\n} from \"./interaction.js\";\n\n/** 作図中の線の id。Model には入らず、プレビューの中だけで使う。 */\nconst DRAFT_LINE_ID = \"__playmaker_draft_line__\";\n\n// 起点が選手の線は、選手を差し替えるだけで追従する。\nfunction applyDragPatch(data: SceneData, drop: DragPatch): SceneData {\n switch (drop.kind) {\n case \"player\":\n return {\n ...data,\n players: data.players.map((p) =>\n p.id === drop.playerId ? applyPlayerPatch(p, drop.patch) : p,\n ),\n };\n case \"line\":\n return {\n ...data,\n lines: data.lines.map((l) => (l.id === drop.lineId ? applyLinePatch(l, drop.patch) : l)),\n };\n }\n}\n\nfunction draftLine(draw: DrawInteraction): Line {\n // 打点が上限に達したら、確定される線と同じく最後の打点を終点として描く。\n const full = draw.points.length >= MAX_DRAFT_POINTS ? splitDraftPoints(draw.points) : undefined;\n return draftToLine(draw, DRAFT_LINE_ID, full ?? { waypoints: draw.points, end: draw.cursor });\n}\n\n/** 確定済みの図に、ドラッグや作図の途中の状態を重ねる。data は書き換えない。 */\nexport function composePreview(data: SceneData, interaction: Interaction | undefined): SceneData {\n if (interaction === undefined) {\n return data;\n }\n switch (interaction.type) {\n case \"drag\": {\n const drop = dragPatch(data, interaction.target, interaction.current);\n return drop === undefined ? data : applyDragPatch(data, drop);\n }\n case \"draw-line\":\n return { ...data, lines: [...data.lines, draftLine(interaction)] };\n }\n}\n\n/**\n * 選択の強調とハンドルの位置。scene には composePreview で途中の状態を重ねたものを渡す。\n * そうするとドラッグ中のハンドルもドラッグ先に描ける。\n */\nexport function computeOverlay(scene: SceneData, selection: EditorSelection): EditorOverlay {\n if (selection.kind === \"player\") {\n return { kind: \"player\", playerId: selection.id };\n }\n const line =\n selection.kind === \"line\" ? scene.lines.find((l) => l.id === selection.id) : undefined;\n if (line === undefined) {\n return { kind: \"none\" };\n }\n return { kind: \"line\", waypointHandles: line.waypoints, endpointHandle: line.end };\n}\n","import type { Event } from \"../base/event.js\";\nimport { Disposable } from \"../base/lifecycle.js\";\nimport type { ICommand } from \"../commands/command.js\";\nimport type { ICommandService } from \"../commands/command-service.js\";\nimport { SetFieldZoneCommand } from \"../commands/field-commands.js\";\nimport { LoadFormationCommand } from \"../commands/formation-commands.js\";\nimport {\n AddLineCommand,\n type LinePatch,\n RemoveLineCommand,\n UpdateLineCommand,\n} from \"../commands/line-commands.js\";\nimport {\n AddPlayerCommand,\n type PlayerPatch,\n RemovePlayerCommand,\n UpdatePlayerCommand,\n} from \"../commands/player-commands.js\";\nimport { type Formation, instantiateFormation } from \"../formations/formation.js\";\nimport { clampToZoneWindow } from \"../geometry/field.js\";\nimport { snapToGrid } from \"../geometry/grid.js\";\nimport { hitLineHandle, hitTestLine, hitTestPlayer } from \"../geometry/hit-test.js\";\nimport type { IIdFactory } from \"../model/id-factory.js\";\nimport { type Line, MAX_LINES } from \"../model/line.js\";\nimport type { FieldZone } from \"../model/play-data.js\";\nimport type { IPlayModel } from \"../model/play-model.js\";\nimport {\n DEFAULT_PLAYER_SHAPE,\n type FieldPosition,\n MAX_PLAYERS,\n type Player,\n} from \"../model/player.js\";\nimport {\n canLoadFormation,\n type EditorFrame,\n type EditorSelection,\n type EditorTool,\n type EditorViewState,\n type IEditorController,\n isSameSelection,\n isToolAvailable,\n NO_SELECTION,\n} from \"./editor.js\";\nimport { EditorNotifier } from \"./editor-notifier.js\";\nimport {\n addDraftPoint,\n committableDraft,\n type DragInteraction,\n type DragTarget,\n type DrawInteraction,\n draftToLine,\n dragPatch,\n dragPosition,\n type Interaction,\n isSamePosition,\n startDrag,\n startDrawing,\n} from \"./interaction.js\";\nimport { composePreview, computeOverlay } from \"./preview.js\";\n\n/**\n * ツール、選択、ジェスチャを受けてコマンドを発行する。Model を直接書き換えない。\n * ドラッグや作図の途中は Model に載せず、getFrame で重ねて見せるので、\n * 確定までは Model の onDidChange も履歴も動かない。\n */\nexport class EditorController extends Disposable implements IEditorController {\n private readonly model: IPlayModel;\n private readonly commands: ICommandService;\n private readonly ids: IIdFactory;\n\n private readonly notifier: EditorNotifier;\n readonly onDidChangeScene: Event<void>;\n readonly onDidChangeViewState: Event<void>;\n\n private tool: EditorTool = \"select\";\n private selection: EditorSelection = NO_SELECTION;\n private interaction: Interaction | undefined;\n\n constructor(model: IPlayModel, commands: ICommandService, ids: IIdFactory) {\n super();\n this.model = model;\n this.commands = commands;\n this.ids = ids;\n this.notifier = this._register(\n new EditorNotifier(() => ({\n state: this.getViewState(),\n player: this.getSelectedPlayer(),\n line: this.getSelectedLine(),\n })),\n );\n this.onDidChangeScene = this.notifier.onDidChangeScene;\n this.onDidChangeViewState = this.notifier.onDidChangeViewState;\n this._register(this.model.onDidChange(() => this.notifier.markSceneChanged()));\n }\n\n getViewState(): EditorViewState {\n const { players, lines } = this.model.getSnapshot();\n return {\n tool: this.tool,\n selection:\n this.getSelectedPlayer() === undefined && this.getSelectedLine() === undefined\n ? NO_SELECTION\n : this.selection,\n canUndo: this.commands.canUndo,\n canRedo: this.commands.canRedo,\n fieldZone: this.model.getFieldZone(),\n isDrawing: this.interaction?.type === \"draw-line\",\n remainingPlayerSlots: MAX_PLAYERS - players.length,\n canStartLine: lines.length < MAX_LINES,\n };\n }\n\n getSelectedPlayer(): Player | undefined {\n const s = this.selection;\n return s.kind === \"player\" ? this.model.findPlayer(s.id) : undefined;\n }\n\n getSelectedLine(): Line | undefined {\n const s = this.selection;\n return s.kind === \"line\" ? this.model.findLine(s.id) : undefined;\n }\n\n getFrame(): EditorFrame {\n const scene = composePreview(this.model.getSnapshot(), this.interaction);\n return { scene, overlay: computeOverlay(scene, this.selection) };\n }\n\n setTool(tool: EditorTool): void {\n if (tool === this.tool) {\n return;\n }\n this.notifier.batch(() => {\n this.tool = tool;\n this.setInteraction(undefined);\n });\n }\n\n pointerDown(pos: FieldPosition): void {\n this.notifier.batch(() => {\n switch (this.tool) {\n case \"add-player\":\n this.addPlayerAt(pos);\n return;\n case \"draw-line\":\n this.drawLinePointerDown(pos);\n return;\n case \"select\":\n this.selectPointerDown(pos);\n return;\n }\n });\n }\n\n pointerMove(pos: FieldPosition): void {\n const interaction = this.interaction;\n if (interaction === undefined) {\n return;\n }\n this.notifier.batch(() =>\n this.setInteraction(\n interaction.type === \"draw-line\"\n ? { ...interaction, cursor: this.clampToField(pos) }\n : { ...interaction, current: this.clampToField(dragPosition(interaction, pos)) },\n ),\n );\n }\n\n pointerUp(pos: FieldPosition): void {\n const interaction = this.interaction;\n // 作図はクリック(pointerDown)で点を打つので、up では何もしない。\n if (interaction?.type !== \"drag\") {\n return;\n }\n this.notifier.batch(() => {\n this.setInteraction(undefined);\n const moved = dragPosition(interaction, pos);\n // 寄せる前の位置で比べる。窓の外にある選手をクリックしただけで、窓の端へ動かさない。\n if (isSamePosition(moved, interaction.origin)) {\n return;\n }\n const to = this.clampToField(moved);\n // 同じ格子点に収まる動きは図を変えないので、履歴にも通知にも載せない。\n if (isSamePosition(to, interaction.origin)) {\n return;\n }\n const command = this.dropCommand(interaction, to);\n if (command !== undefined) {\n this.commands.execute(command);\n }\n });\n }\n\n cancelInteraction(): void {\n this.notifier.batch(() => this.setInteraction(undefined));\n }\n\n commitLine(): void {\n const interaction = this.interaction;\n if (interaction?.type !== \"draw-line\") {\n return;\n }\n this.notifier.batch(() => {\n this.setInteraction(undefined);\n const line = this.lineFromDraft(interaction);\n if (line === undefined) {\n return;\n }\n this.commands.execute(new AddLineCommand(line));\n // 作図の直後は選択ツールへ戻し、引いた線を選ぶ。続けて引くより、すぐ編集できるほうを取る。\n this.setTool(\"select\");\n this.setSelection({ kind: \"line\", id: line.id });\n });\n }\n\n deleteSelection(): void {\n const player = this.getSelectedPlayer();\n const line = this.getSelectedLine();\n this.notifier.batch(() => {\n if (player !== undefined) {\n this.commands.execute(new RemovePlayerCommand(player.id));\n } else if (line !== undefined) {\n this.commands.execute(new RemoveLineCommand(line.id));\n } else {\n return;\n }\n this.setSelection(NO_SELECTION);\n });\n }\n\n updateSelectedPlayer(patch: PlayerPatch): void {\n const player = this.getSelectedPlayer();\n if (player === undefined) {\n return;\n }\n this.notifier.batch(() => this.commands.execute(new UpdatePlayerCommand(player.id, patch)));\n }\n\n updateSelectedLine(patch: LinePatch): void {\n const line = this.getSelectedLine();\n if (line === undefined) {\n return;\n }\n this.notifier.batch(() => this.commands.execute(new UpdateLineCommand(line.id, patch)));\n }\n\n setFieldZone(zone: FieldZone): void {\n this.notifier.batch(() => {\n // 同じゾーンを選び直しただけなら、途中のドラッグや作図は残す。\n if (this.commands.execute(new SetFieldZoneCommand(zone))) {\n this.setInteraction(undefined);\n }\n });\n }\n\n loadFormation(formation: Formation): boolean {\n if (!canLoadFormation(formation, this.getViewState())) {\n return false;\n }\n const { players } = this.model.getSnapshot();\n const added = instantiateFormation(\n formation,\n this.ids,\n players.map((p) => p.id),\n );\n return this.notifier.batch(() => {\n const loaded = this.commands.execute(new LoadFormationCommand(added));\n if (loaded) {\n // 読み込んだあとは、途中の操作と元の選択に意味が無いので外す。\n this.setInteraction(undefined);\n this.setSelection(NO_SELECTION);\n }\n return loaded;\n });\n }\n\n undo(): void {\n this.notifier.batch(() => {\n const interaction = this.interaction;\n if (interaction?.type === \"draw-line\") {\n this.setInteraction(\n interaction.points.length === 0\n ? undefined\n : { ...interaction, points: interaction.points.slice(0, -1) },\n );\n return;\n }\n this.setInteraction(undefined);\n this.commands.undo();\n });\n }\n\n redo(): void {\n this.notifier.batch(() => {\n this.setInteraction(undefined);\n this.commands.redo();\n });\n }\n\n private setSelection(next: EditorSelection): void {\n if (isSameSelection(this.selection, next)) {\n return;\n }\n this.selection = next;\n this.notifier.markSceneChanged();\n }\n\n private setInteraction(next: Interaction | undefined): void {\n if (next === this.interaction) {\n return;\n }\n this.interaction = next;\n this.notifier.markSceneChanged();\n }\n\n // 窓の端(横はサイドライン、縦は LOS から整数ヤード)はどれも格子点なので、寄せてから丸めても窓の外へは出ない。\n private clampToField(pos: FieldPosition): FieldPosition {\n return snapToGrid(clampToZoneWindow(pos, this.model.getSnapshot().field));\n }\n\n // ドラッグ中に対象が消えていたら undefined を返し、コマンドを出さない。\n private dropCommand(drag: DragInteraction, to: FieldPosition): ICommand | undefined {\n const drop = dragPatch(this.model.getSnapshot(), drag.target, to);\n if (drop === undefined) {\n return undefined;\n }\n return drop.kind === \"player\"\n ? new UpdatePlayerCommand(drop.playerId, drop.patch)\n : new UpdateLineCommand(drop.lineId, drop.patch);\n }\n\n // 起点の選手が消えていれば確定しない。\n private lineFromDraft(draw: DrawInteraction): Line | undefined {\n const start = this.model.findPlayer(draw.startPlayerId);\n const split = start === undefined ? undefined : committableDraft(draw, start.position);\n if (split === undefined) {\n return undefined;\n }\n const id = this.ids.next(\"line\", new Set(this.model.getSnapshot().lines.map((l) => l.id)));\n return draftToLine(draw, id, split);\n }\n\n private selectPointerDown(pos: FieldPosition): void {\n const data = this.model.getSnapshot();\n // 選択中の線のハンドルを最優先で掴む。選手や線と重なっていても編集できるようにする。\n const handle = this.hitSelectedLineHandle(pos);\n if (handle !== undefined) {\n this.setInteraction(handle);\n return;\n }\n // 選手は選択しつつドラッグを始める。動かさずに離せばただの選択になる。\n const player = hitTestPlayer(data.players, pos);\n if (player !== undefined) {\n this.setSelection({ kind: \"player\", id: player.id });\n this.setInteraction(startDrag({ kind: \"player\", playerId: player.id }, player.position, pos));\n return;\n }\n const line = hitTestLine(data.lines, data.players, pos);\n this.setSelection(line === undefined ? NO_SELECTION : { kind: \"line\", id: line.id });\n }\n\n private hitSelectedLineHandle(pos: FieldPosition): DragInteraction | undefined {\n const line = this.getSelectedLine();\n const hit = line === undefined ? undefined : hitLineHandle(line, pos);\n if (line === undefined || hit === undefined) {\n return undefined;\n }\n const target: DragTarget =\n hit.kind === \"endpoint\"\n ? { kind: \"endpoint\", lineId: line.id }\n : { kind: \"waypoint\", lineId: line.id, index: hit.index };\n return startDrag(target, hit.point, pos);\n }\n\n private drawLinePointerDown(pos: FieldPosition): void {\n const interaction = this.interaction;\n if (interaction?.type === \"draw-line\") {\n this.setInteraction(addDraftPoint(interaction, this.clampToField(pos)));\n return;\n }\n if (!isToolAvailable(\"draw-line\", this.getViewState())) {\n return;\n }\n const data = this.model.getSnapshot();\n // 選手以外から始めようとしたら無視する。\n const player = hitTestPlayer(data.players, pos);\n if (player === undefined) {\n return;\n }\n this.setInteraction(startDrawing(player));\n }\n\n private addPlayerAt(pos: FieldPosition): void {\n if (!isToolAvailable(\"add-player\", this.getViewState())) {\n return;\n }\n const { players } = this.model.getSnapshot();\n const id = this.ids.next(\"player\", new Set(players.map((p) => p.id)));\n this.commands.execute(\n new AddPlayerCommand({\n id,\n position: this.clampToField(pos),\n shape: DEFAULT_PLAYER_SHAPE,\n label: \"\",\n }),\n );\n this.setSelection({ kind: \"player\", id });\n }\n}\n","import { Emitter, type Event } from \"../base/event.js\";\nimport { Disposable, DisposableStore } from \"../base/lifecycle.js\";\nimport { CommandService } from \"../commands/command-service.js\";\nimport { UndoRedoService } from \"../commands/undo-redo-service.js\";\nimport { type Formation, normalizeFormation } from \"../formations/formation.js\";\nimport { IdFactory } from \"../model/id-factory.js\";\nimport type { FieldZone, PlayData } from \"../model/play-data.js\";\nimport { PlayModel } from \"../model/play-model.js\";\nimport type { IEditorController } from \"./editor.js\";\nimport { EditorController } from \"./editor-controller.js\";\n\ninterface PlayDocument {\n readonly store: DisposableStore;\n readonly model: PlayModel;\n readonly controller: EditorController;\n}\n\nfunction openDocument(data: unknown): PlayDocument {\n const store = new DisposableStore();\n const model = store.add(new PlayModel(data));\n const commands = new CommandService(model, new UndoRedoService());\n const controller = store.add(new EditorController(model, commands, new IdFactory()));\n return { store, model, controller };\n}\n\n/**\n * 1 つのプレー図の編集を受け持つ。\n * setPlayData は履歴ごと作り直すので、controller も別のオブジェクトになる。\n */\nexport class PlaySession extends Disposable {\n private readonly _onDidReset = this._register(new Emitter<void>());\n /** setPlayData で controller が作り直されたあとに発火する。 */\n readonly onDidReset = this._onDidReset.event;\n\n private readonly _onDidEdit = this._register(new Emitter<void>());\n /**\n * 編集の確定ごとに 1 回、最新の図の深いコピーを渡す。構築時と setPlayData では発火しない\n * (読み込みは編集ではないため)。\n * コピーはリスナごとに作るので、受け手が書き換えても、ほかのリスナとこの図には波及しない。\n */\n readonly onDidChange: Event<PlayData> = (listener) =>\n this._onDidEdit.event(() => listener(this.getPlayData()));\n\n private readonly _onDidChangeScene = this._register(new Emitter<void>());\n /**\n * 今の controller の onDidChangeScene を転送する。setPlayData で読み直したときも、\n * onDidReset のあとに 1 回出す。controller が作り直されても購読し直さなくてよい。\n */\n readonly onDidChangeScene = this._onDidChangeScene.event;\n\n private readonly _onDidChangeViewState = this._register(new Emitter<void>());\n /**\n * 今の controller の onDidChangeViewState を転送する。setPlayData で読み直したときも、\n * 表示状態が変わったかどうかにかかわらず onDidReset のあとに 1 回出す。\n */\n readonly onDidChangeViewState = this._onDidChangeViewState.event;\n\n private document: PlayDocument;\n\n constructor(data: unknown) {\n super();\n this.document = this.open(data);\n }\n\n get controller(): IEditorController {\n return this.document.controller;\n }\n\n get fieldZone(): FieldZone {\n return this.document.model.getFieldZone();\n }\n\n /** 現在の図の深いコピー。受け取った値を書き換えても、この図には波及しない。 */\n getPlayData(): PlayData {\n return this.document.model.getData();\n }\n\n /** 現在の図をコピーせずに返す。 */\n getSnapshot(): PlayData {\n return this.document.model.getSnapshot();\n }\n\n setFieldZone(zone: FieldZone): void {\n this.document.controller.setFieldZone(zone);\n }\n\n /** 外から来た隊形を正規化してから読む。置ける選手が 1 人もいなければ何もせず false を返す。 */\n loadFormation(formation: Formation): boolean {\n const normalized = normalizeFormation(formation);\n return normalized !== null && this.document.controller.loadFormation(normalized);\n }\n\n setPlayData(data: unknown): void {\n this.document.store.dispose();\n this.document = this.open(data);\n this._onDidReset.fire();\n this._onDidChangeScene.fire();\n this._onDidChangeViewState.fire();\n }\n\n private open(data: unknown): PlayDocument {\n const opened = openDocument(data);\n const { store, model, controller } = opened;\n store.add(model.onDidChange(() => this._onDidEdit.fire()));\n store.add(controller.onDidChangeScene(() => this._onDidChangeScene.fire()));\n store.add(controller.onDidChangeViewState(() => this._onDidChangeViewState.fire()));\n return opened;\n }\n\n override dispose(): void {\n this.document.store.dispose();\n super.dispose();\n }\n}\n","import { fieldWindowAspect } from \"../geometry/field.js\";\nimport { isFiniteNumber } from \"../model/guards.js\";\nimport type { FieldZone } from \"../model/play-data.js\";\n\n/** 幅を指定しないときの PNG の幅(px)。資料に貼っても粗く見えない大きさ。 */\nexport const DEFAULT_EXPORT_WIDTH = 1600;\n\n/**\n * 書き出す幅の上限(px)。縦に長いレッドゾーンの窓でも約 1100 万画素に収まり、\n * canvas の面積を約 1678 万画素までに制限するブラウザ(iOS の Safari)でも描ける。\n */\nexport const MAX_EXPORT_WIDTH = 4096;\n\nexport interface ImageExportOptions {\n /**\n * PNG の幅(px)。高さはゾーンの窓の縦横比から決める。\n * 未指定、有限でない値、1 未満は 1600 にし、4096 を超える幅は 4096 に切り詰める。\n */\n width?: number | undefined;\n}\n\nexport interface ImageExportSize {\n readonly width: number;\n readonly height: number;\n}\n\n/**\n * 外から渡された幅を整数の px にする。有限でない値と 1 未満は既定の幅にし、上限を超える幅は上限に切り詰める。\n */\nexport function resolveImageExportWidth(width: number | undefined): number {\n if (!isFiniteNumber(width) || width < 1) {\n return DEFAULT_EXPORT_WIDTH;\n }\n return Math.min(Math.round(width), MAX_EXPORT_WIDTH);\n}\n\n/**\n * 高さをゾーンの窓の縦横比から決めるので、画像に余白は付かない。\n * 窓の縦横比は 2 未満なので、幅が 1 以上なら四捨五入した高さも 1 以上になる。\n */\nexport function resolveImageExportSize(\n zone: FieldZone,\n options: ImageExportOptions = {},\n): ImageExportSize {\n const width = resolveImageExportWidth(options.width);\n return { width, height: Math.round(width / fieldWindowAspect(zone)) };\n}\n","// 隊形とプレーのプリセットで共有する配置。縦は LOS からの位置(LOS が 0 で、攻撃方向が正)で、\n// 横はセンターを 26.5 ヤード(フィールドの中央)に置く。位置はすべて 0.5 ヤードの格子点で書く。\n// どのゾーンで読み込んでも窓に収まる。名称は英語で書く。フィールド上の表記を英語に揃えるため。\n//\n// OL は LOS の 1 ヤード後ろ、守備ラインは 1.5 ヤード前に置き、マーカーが重ならない隙間を空ける。\n// LOS 上の選手(OL、TE、外側の WR)は -1、LOS から下がる選手(スロット、フランカー)は -2、\n// センター直下の QB は -3。守備ラインは OL の肩に付ける(シェード)ので、どのギャップを誰が\n// 守るかが読める。TE が付く隊形にはオーバーで構え、強い側の E を TE の外側の肩(9 テク)に置く。\n// 守備の選手には全員ラベルを付ける。\n//\n// 選手は id を持つ。プレーの線が起点の選手を id で指すためで、隊形にするときは id を外す。\n\nimport type { FieldPosition, Player, PlayerShape } from \"../model/player.js\";\n\nexport function pt(lateralYard: number, downfieldYard: number): FieldPosition {\n return { lateralYard, downfieldYard };\n}\n\nexport function player(\n id: string,\n label: string,\n position: FieldPosition,\n shape: PlayerShape = \"circle\",\n): Player {\n return { id, position, shape, label };\n}\n\n/**\n * 相手の 1 ヤード手前。ブロックの終点に使い、T 字のバーが相手のマーカーの縁に付く。\n * lateral で相手の肩の側へずらす(リーチやダウンブロック)。\n */\nexport function shortOf(target: FieldPosition, lateral = 0): FieldPosition {\n return pt(target.lateralYard + lateral, target.downfieldYard - 1);\n}\n\n/** 相手の肩の横。コンボブロックで LB へ上がる側が経由する。lateral は 1 ヤード以上離す。 */\nexport function beside(target: FieldPosition, lateral: number): FieldPosition {\n return pt(target.lateralYard + lateral, target.downfieldYard);\n}\n\nexport const OL = {\n lt: pt(21.5, -1),\n lg: pt(24, -1),\n c: pt(26.5, -1),\n rg: pt(29, -1),\n rt: pt(31.5, -1),\n} as const;\n\n// 4 ダウンのオーバーフロント。右を強い側として、E は LT の外側の肩、N は C の弱い側の肩、\n// T は RG の外側の肩に付ける。強い側の E は、TE が無ければ RT の外側の肩に付ける。\nexport const FRONT_4 = {\n eL: pt(20.5, 1.5),\n n: pt(25.5, 1.5),\n t: pt(30, 1.5),\n eR: pt(32.5, 1.5),\n} as const;\n\n/** TE が右に付くときの強い側の E。TE の外側の肩(9 テク)。 */\nexport const END_OVER_TE = pt(35.5, 1.5);\n\n// 3 ダウンのオッドフロント。N は C の真上、E は両タックルの内側の肩。\nexport const FRONT_3 = {\n eL: pt(22.5, 1.5),\n n: pt(26.5, 1.5),\n eR: pt(30.5, 1.5),\n} as const;\n\n// 4-3 オーバーの LB。S は TE の真上に付く。\nexport const LB_43 = { w: pt(22.5, 5), m: pt(27, 5), s: pt(34.5, 5) } as const;\n\n// ニッケルの ILB 2 人と、スロットに付くニッケルバック。\nexport const LB_NICKEL = { m: pt(24, 5), w: pt(30, 5), nb: pt(14, 5) } as const;\n\nexport function cornerSpots(depth: number): { left: FieldPosition; right: FieldPosition } {\n return { left: pt(6.5, depth), right: pt(46.5, depth) };\n}\n\nexport function offensiveLine(): Player[] {\n return [\n player(\"lt\", \"LT\", OL.lt, \"square\"),\n player(\"lg\", \"LG\", OL.lg, \"square\"),\n player(\"c\", \"C\", OL.c, \"square\"),\n player(\"rg\", \"RG\", OL.rg, \"square\"),\n player(\"rt\", \"RT\", OL.rt, \"square\"),\n ];\n}\n\nexport function front4(strongEnd: FieldPosition = FRONT_4.eR): Player[] {\n return [\n player(\"de-l\", \"E\", FRONT_4.eL),\n player(\"dt-l\", \"N\", FRONT_4.n),\n player(\"dt-r\", \"T\", FRONT_4.t),\n player(\"de-r\", \"E\", strongEnd),\n ];\n}\n\nexport function front3(): Player[] {\n return [\n player(\"de-l\", \"E\", FRONT_3.eL),\n player(\"nt\", \"N\", FRONT_3.n),\n player(\"de-r\", \"E\", FRONT_3.eR),\n ];\n}\n\nexport function corners(depth: number): Player[] {\n const { left, right } = cornerSpots(depth);\n return [player(\"cb-l\", \"CB\", left), player(\"cb-r\", \"CB\", right)];\n}\n\n// ---- 攻撃の隊形 ----\n\nexport function iFormation(): Player[] {\n return [\n ...offensiveLine(),\n player(\"y\", \"Y\", pt(34.5, -1), \"square\"),\n player(\"qb\", \"QB\", pt(26.5, -3)),\n player(\"fb\", \"FB\", pt(26.5, -6)),\n player(\"rb\", \"RB\", pt(26.5, -8.5)),\n player(\"x\", \"X\", pt(6.5, -1)),\n player(\"z\", \"Z\", pt(46.5, -2)),\n ];\n}\n\nexport function singlebackAce(): Player[] {\n return [\n ...offensiveLine(),\n player(\"y\", \"Y\", pt(34.5, -1), \"square\"),\n player(\"qb\", \"QB\", pt(26.5, -3)),\n player(\"rb\", \"RB\", pt(26.5, -7.5)),\n player(\"x\", \"X\", pt(6, -1)),\n player(\"h\", \"H\", pt(40, -2)),\n player(\"z\", \"Z\", pt(47, -1)),\n ];\n}\n\nexport function shotgunSpread(): Player[] {\n return [\n ...offensiveLine(),\n player(\"qb\", \"QB\", pt(26.5, -5)),\n player(\"rb\", \"RB\", pt(29.5, -5)),\n player(\"x\", \"X\", pt(5.5, -1)),\n player(\"y\", \"Y\", pt(13, -2)),\n player(\"h\", \"H\", pt(40, -2)),\n player(\"z\", \"Z\", pt(47.5, -1)),\n ];\n}\n\nexport function trips(): Player[] {\n return [\n ...offensiveLine(),\n player(\"qb\", \"QB\", pt(26.5, -5)),\n player(\"rb\", \"RB\", pt(24, -5)),\n player(\"x\", \"X\", pt(5.5, -1)),\n player(\"y\", \"Y\", pt(34.5, -1), \"square\"),\n player(\"h\", \"H\", pt(41, -2)),\n player(\"z\", \"Z\", pt(47.5, -1)),\n ];\n}\n\nexport function empty(): Player[] {\n return [\n ...offensiveLine(),\n player(\"qb\", \"QB\", pt(26.5, -5)),\n player(\"x\", \"X\", pt(5, -1)),\n player(\"f\", \"F\", pt(12, -2)),\n player(\"y\", \"Y\", pt(34.5, -1), \"square\"),\n player(\"h\", \"H\", pt(41, -2)),\n player(\"z\", \"Z\", pt(48, -1)),\n ];\n}\n\nexport function pistol(): Player[] {\n return [\n ...offensiveLine(),\n player(\"y\", \"Y\", pt(34.5, -1), \"square\"),\n player(\"qb\", \"QB\", pt(26.5, -4.5)),\n player(\"rb\", \"RB\", pt(26.5, -7.5)),\n player(\"x\", \"X\", pt(6, -1)),\n player(\"h\", \"H\", pt(40, -2)),\n player(\"z\", \"Z\", pt(47, -1)),\n ];\n}\n\nexport function bunch(): Player[] {\n return [\n ...offensiveLine(),\n player(\"qb\", \"QB\", pt(26.5, -5)),\n player(\"rb\", \"RB\", pt(24, -5)),\n player(\"x\", \"X\", pt(5.5, -1)),\n player(\"z\", \"Z\", pt(37, -1)),\n player(\"h\", \"H\", pt(34.5, -2)),\n player(\"y\", \"Y\", pt(39.5, -2)),\n ];\n}\n\n// ---- 守備の隊形 ----\n\n/** 4-3 オーバー。TE が右に付く隊形に構える。 */\nexport function defense43(): Player[] {\n return [\n ...front4(END_OVER_TE),\n player(\"wlb\", \"W\", LB_43.w),\n player(\"mlb\", \"M\", LB_43.m),\n player(\"slb\", \"S\", LB_43.s),\n ...corners(6),\n player(\"fs\", \"FS\", pt(23, 11)),\n player(\"ss\", \"SS\", pt(32, 9)),\n ];\n}\n\nexport function defense34(): Player[] {\n return [\n ...front3(),\n player(\"wlb\", \"W\", pt(19, 2.5)),\n player(\"mlb\", \"M\", pt(24.5, 5)),\n player(\"tlb\", \"T\", pt(29, 5)),\n player(\"slb\", \"S\", pt(34.5, 2.5)),\n ...corners(6),\n player(\"fs\", \"FS\", pt(23, 11)),\n player(\"ss\", \"SS\", pt(30, 11)),\n ];\n}\n\n/** ニッケルの前の 6 人(4 ダウンと ILB 2 人)。 */\nexport function nickelFront(strongEnd: FieldPosition = FRONT_4.eR): Player[] {\n return [...front4(strongEnd), player(\"mlb\", \"M\", LB_NICKEL.m), player(\"wlb\", \"W\", LB_NICKEL.w)];\n}\n\n/** ニッケルの後ろの 5 人の置き方。カバレッジやプレッシャーごとに変わる。 */\nexport interface NickelShell {\n readonly cornerDepth: number;\n readonly fs: FieldPosition;\n readonly ss: FieldPosition;\n}\n\n/** CB 6 ヤード、セイフティ 11 ヤードの 2 ハイ。 */\nexport const NICKEL_BASE: NickelShell = { cornerDepth: 6, fs: pt(22, 11), ss: pt(31, 11) };\n\nexport function nickel(shell: NickelShell, strongEnd: FieldPosition = FRONT_4.eR): Player[] {\n return [\n ...nickelFront(strongEnd),\n player(\"nb\", \"NB\", LB_NICKEL.nb),\n ...corners(shell.cornerDepth),\n player(\"fs\", \"FS\", shell.fs),\n player(\"ss\", \"SS\", shell.ss),\n ];\n}\n\nexport function dime(): Player[] {\n return [\n ...front4(),\n player(\"mlb\", \"M\", pt(27, 5)),\n player(\"nb\", \"NB\", LB_NICKEL.nb),\n player(\"db\", \"$\", pt(39, 5)),\n ...corners(6),\n player(\"fs\", \"FS\", pt(22, 11)),\n player(\"ss\", \"SS\", pt(31, 11)),\n ];\n}\n\nexport function defense425(): Player[] {\n return [\n ...front4(),\n player(\"mlb\", \"M\", pt(24.5, 5)),\n player(\"wlb\", \"W\", LB_NICKEL.w),\n player(\"nb\", \"NB\", pt(39, 5)),\n ...corners(6),\n player(\"fs\", \"FS\", pt(23, 11)),\n player(\"ss\", \"SS\", pt(31, 9)),\n ];\n}\n\nexport function defense335(): Player[] {\n return [\n ...front3(),\n player(\"wlb\", \"W\", pt(20.5, 5)),\n player(\"mlb\", \"M\", pt(26.5, 5)),\n player(\"slb\", \"S\", pt(32.5, 5)),\n player(\"nb\", \"NB\", LB_NICKEL.nb),\n ...corners(6),\n player(\"fs\", \"FS\", pt(23, 11)),\n player(\"ss\", \"SS\", pt(31, 9)),\n ];\n}\n","// 配置は presets/alignments.ts にあり、プレーのプリセットと共有する。ここでは id を外して隊形にする。\n\nimport type { Player } from \"../model/player.js\";\nimport {\n bunch,\n defense34,\n defense43,\n defense335,\n defense425,\n dime,\n empty,\n iFormation,\n NICKEL_BASE,\n nickel,\n pistol,\n shotgunSpread,\n singlebackAce,\n trips,\n} from \"../presets/alignments.js\";\nimport { deepFreeze, type TeamSide } from \"../presets/shared.js\";\nimport { type Formation, toFormationPlayer } from \"./formation.js\";\n\nfunction formation(id: string, name: string, side: TeamSide, players: Player[]): Formation {\n return { id, name, side, players: players.map(toFormationPlayer) };\n}\n\nexport const FORMATION_PRESETS: readonly Formation[] = /* @__PURE__ */ deepFreeze([\n formation(\"i-formation\", \"I-Formation\", \"offense\", iFormation()),\n formation(\"singleback-ace\", \"Singleback Ace\", \"offense\", singlebackAce()),\n formation(\"shotgun-spread\", \"Shotgun Spread\", \"offense\", shotgunSpread()),\n formation(\"trips\", \"Trips\", \"offense\", trips()),\n formation(\"empty\", \"Empty\", \"offense\", empty()),\n formation(\"pistol\", \"Pistol\", \"offense\", pistol()),\n formation(\"bunch\", \"Bunch\", \"offense\", bunch()),\n formation(\"defense-4-3\", \"4-3 Over\", \"defense\", defense43()),\n formation(\"defense-3-4\", \"3-4\", \"defense\", defense34()),\n formation(\"defense-nickel\", \"Nickel\", \"defense\", nickel(NICKEL_BASE)),\n formation(\"defense-dime\", \"Dime\", \"defense\", dime()),\n formation(\"defense-4-2-5\", \"4-2-5\", \"defense\", defense425()),\n formation(\"defense-3-3-5\", \"3-3-5\", \"defense\", defense335()),\n]);\n\nexport function getFormationPreset(id: string): Formation | undefined {\n return FORMATION_PRESETS.find((f) => f.id === id);\n}\n","import type { CanvasPoint } from \"./field.js\";\nimport { trimPolylineEnd } from \"./polyline.js\";\n\n// 線が矢じりより短くても消えないよう、切り詰めは全長のこの割合までに留める(残りは矢じりが覆う)。\nconst ARROW_TRIM_MAX_FRACTION = 0.9;\n\ninterface LineEnd {\n readonly tip: CanvasPoint;\n /** 終点へ向かう単位ベクトル。 */\n readonly ux: number;\n readonly uy: number;\n}\n\n/**\n * 終点と、終点へ向かう向き。曲線の末尾には終点と同じ座標の点が並ぶことがあるので、\n * 終点と座標の違う直近の点から向きを求める。すべての点が同じ座標なら undefined。\n */\nfunction lineEnd(path: readonly CanvasPoint[]): LineEnd | undefined {\n const tip = path.at(-1);\n if (tip === undefined) {\n return undefined;\n }\n // 毎フレーム線ごとに呼ぶので、配列を複製せずに終点側から探す。\n for (let i = path.length - 2; i >= 0; i--) {\n const from = path[i];\n if (from !== undefined && (from.x !== tip.x || from.y !== tip.y)) {\n const dx = tip.x - from.x;\n const dy = tip.y - from.y;\n const length = Math.hypot(dx, dy);\n return { tip, ux: dx / length, uy: dy / length };\n }\n }\n return undefined;\n}\n\n/** 矢じりの根元で止めた線。矢じりより短い線も、全長の 1 割は残す。 */\nexport function trimForArrowHead(path: readonly CanvasPoint[], arrowLength: number): CanvasPoint[] {\n return trimPolylineEnd(path, arrowLength, ARROW_TRIM_MAX_FRACTION);\n}\n\n/**\n * 終点を先端にして進行方向へ向けた、矢じりの三角形の頂点(先端、根元の両端)。\n * 向きが決まらない線では undefined。\n */\nexport function arrowHeadVertices(\n path: readonly CanvasPoint[],\n arrowLength: number,\n halfWidth: number,\n): readonly [CanvasPoint, CanvasPoint, CanvasPoint] | undefined {\n const end = lineEnd(path);\n if (end === undefined) {\n return undefined;\n }\n const { tip, ux, uy } = end;\n const baseX = tip.x - arrowLength * ux;\n const baseY = tip.y - arrowLength * uy;\n return [\n tip,\n { x: baseX - halfWidth * uy, y: baseY + halfWidth * ux },\n { x: baseX + halfWidth * uy, y: baseY - halfWidth * ux },\n ];\n}\n\n/** 終点を中点にして進行方向と直交する、T 字の横棒の両端。向きが決まらない線では undefined。 */\nexport function blockCapEndpoints(\n path: readonly CanvasPoint[],\n capLength: number,\n): readonly [CanvasPoint, CanvasPoint] | undefined {\n const end = lineEnd(path);\n if (end === undefined) {\n return undefined;\n }\n const { tip, ux, uy } = end;\n const half = capLength / 2;\n return [\n { x: tip.x + half * uy, y: tip.y - half * ux },\n { x: tip.x - half * uy, y: tip.y + half * ux },\n ];\n}\n","import type { PlayerShape } from \"../model/player.js\";\n\n// 円を外接円の半径で描くと、辺が円の内側に入る正方形より一回り大きく見え、LOS 際のブロックの線を覆う。\n// 描く半径だけを正方形の辺の幅へ寄せる。円は錯視で小さく見えるので、辺の幅より少し大きくしてある。\n// 当たり判定は形状によらず外接円のままにする。\nconst CIRCLE_DRAW_SCALE = 0.82;\n\nexport type PlayerMarkerOutline =\n | { readonly kind: \"circle\"; readonly radius: number }\n | { readonly kind: \"square\"; readonly halfSide: number };\n\n/** 選手マーカーの輪郭。radius は当たり判定と同じ外接円の半径で、正方形は頂点がこの円に乗る大きさにする。 */\nexport function playerMarkerOutline(shape: PlayerShape, radius: number): PlayerMarkerOutline {\n switch (shape) {\n case \"circle\":\n return { kind: \"circle\", radius: radius * CIRCLE_DRAW_SCALE };\n case \"square\":\n return { kind: \"square\", halfSide: radius / Math.SQRT2 };\n }\n}\n","/** キー操作で呼ぶ編集。 */\nexport type KeyAction = \"cancel-interaction\" | \"commit-line\" | \"undo\" | \"redo\";\n\n/** KeyboardEvent のうち、割り当てを決めるのに読む値。 */\nexport interface KeyStroke {\n readonly key: string;\n readonly metaKey: boolean;\n readonly ctrlKey: boolean;\n readonly shiftKey: boolean;\n}\n\n/**\n * キーに割り当てた編集を返す。割り当てのないキーは null。\n * Mac の Cmd と、ほかの OS の Ctrl を同じ修飾キーとして扱う。\n * Shift を押すと key が大文字で届くので、z と y は大文字も同じキーとみなす。\n */\nexport function resolveKeyAction(stroke: KeyStroke): KeyAction | null {\n const mod = stroke.metaKey || stroke.ctrlKey;\n const key = stroke.key.toLowerCase();\n if (stroke.key === \"Escape\") {\n return \"cancel-interaction\";\n }\n if (stroke.key === \"Enter\") {\n return \"commit-line\";\n }\n if (mod && key === \"z\") {\n return stroke.shiftKey ? \"redo\" : \"undo\";\n }\n if (mod && key === \"y\") {\n return \"redo\";\n }\n return null;\n}\n","// 配置は presets/alignments.ts にあり、隊形のプリセットと共有する。middle ゾーンの窓(LOS の前後\n// 15 ヤード)に収まる。ルート、ブロック、モーションを持つのは主役の側だけで、相手の側は選手の\n// 配置だけを持つ。\n//\n// 線の決まり。ブロックの終点は相手の 1 ヤード手前(shortOf)に置き、T 字のバーが相手のマーカーの\n// 縁に付く。コンボブロックは一方が相手の手前で止まり、もう一方が相手の肩(beside)を経由して LB の\n// 手前へ上がる。穴をリードするプルは相手ではなく穴を狙うので座標で書く。走路とプルは、他の選手の\n// マーカーの中心から 1 ヤード以上離す(OL の間隔が 2.5 ヤードなので、ギャップを通る線は縁に\n// 触れる)。守備のラッシュは LOS(0)で止め、ゾーンのドロップは LOS から離れる向き(+)へ描く。\n\nimport type { Line, LineInterpolation, LineKind } from \"../model/line.js\";\nimport { CURRENT_PLAY_DATA_VERSION, fieldStateForZone } from \"../model/play-data.js\";\nimport type { FieldPosition, Player } from \"../model/player.js\";\nimport {\n beside,\n cornerSpots,\n defense43,\n END_OVER_TE,\n FRONT_4,\n iFormation,\n LB_43,\n LB_NICKEL,\n NICKEL_BASE,\n type NickelShell,\n nickel,\n nickelFront,\n offensiveLine,\n player,\n pt,\n shortOf,\n shotgunSpread,\n singlebackAce,\n trips,\n} from \"../presets/alignments.js\";\nimport { deepFreeze, type TeamSide } from \"../presets/shared.js\";\nimport type { PlayCategory, PlayPreset } from \"./play-preset.js\";\n\n/** 色は持たせず、テーマの既定の色で描く。攻守の見分けは選手の形とラベルで付ける。 */\nfunction ln(\n id: string,\n kind: LineKind,\n startPlayerId: string,\n waypoints: FieldPosition[],\n end: FieldPosition,\n interpolation: LineInterpolation,\n): Line {\n return { id, kind, startPlayerId, waypoints, end, interpolation };\n}\n\nfunction play(\n id: string,\n name: string,\n side: TeamSide,\n category: PlayCategory,\n personnel: string,\n summary: string,\n players: Player[],\n lines: Line[],\n): PlayPreset {\n return {\n id,\n name,\n side,\n category,\n personnel,\n summary,\n data: {\n version: CURRENT_PLAY_DATA_VERSION,\n field: fieldStateForZone(\"middle\"),\n players,\n lines,\n },\n };\n}\n\n// オフェンスのプレーに添える守備のうち、CB の深さとセイフティの置き方がカバレッジで変わるもの。\nconst COVER_2_SHELL = { cornerDepth: 5, fs: pt(18, 12), ss: pt(35, 12) } satisfies NickelShell;\nconst COVER_3_SHELL = { cornerDepth: 7, fs: pt(26.5, 12), ss: pt(34, 6) } satisfies NickelShell;\n\nconst INSIDE_ZONE = play(\n \"play-inside-zone\",\n \"Inside Zone\",\n \"offense\",\n \"run-zone\",\n \"11 pers\",\n \"ゾーンブロックで内側を一気に。RB は RG の内側を狙い、3 テクの動きで B ギャップか裏の A ギャップへ切る。\",\n [\n ...offensiveLine(),\n player(\"y\", \"Y\", pt(34.5, -1), \"square\"),\n player(\"qb\", \"QB\", pt(26.5, -5)),\n player(\"rb\", \"RB\", pt(24, -5)),\n player(\"x\", \"X\", pt(6, -1)),\n player(\"h\", \"H\", pt(40, -2)),\n player(\"z\", \"Z\", pt(47, -1)),\n ...defense43(),\n ],\n [\n ln(\"bl-lt\", \"block\", \"lt\", [], shortOf(FRONT_4.eL), \"straight\"),\n ln(\"bl-lg\", \"block\", \"lg\", [], shortOf(FRONT_4.n), \"straight\"),\n ln(\"bl-c\", \"block\", \"c\", [beside(FRONT_4.n, 1)], shortOf(LB_43.m), \"bezier\"),\n ln(\"bl-rg\", \"block\", \"rg\", [], shortOf(FRONT_4.t), \"straight\"),\n ln(\"bl-rt\", \"block\", \"rt\", [beside(FRONT_4.t, 1.5)], shortOf(LB_43.s), \"bezier\"),\n ln(\"bl-y\", \"block\", \"y\", [], shortOf(END_OVER_TE), \"straight\"),\n ln(\"run-rb\", \"route\", \"rb\", [pt(27.5, -2)], pt(28, 3.5), \"bezier\"),\n ],\n);\n\nconst OUTSIDE_ZONE = play(\n \"play-outside-zone\",\n \"Outside Zone\",\n \"offense\",\n \"run-zone\",\n \"11 pers\",\n \"ワイドゾーンで横へ伸ばす。RB は TE の外側を狙い、E と S の動きで外へ出るか縦に切り返す。\",\n [...singlebackAce(), ...defense43()],\n [\n ln(\"bl-lt\", \"block\", \"lt\", [], shortOf(FRONT_4.eL), \"straight\"),\n ln(\"bl-lg\", \"block\", \"lg\", [], shortOf(FRONT_4.n), \"straight\"),\n ln(\"bl-c\", \"block\", \"c\", [beside(FRONT_4.n, 1)], shortOf(LB_43.m, 0.5), \"bezier\"),\n ln(\"bl-rg\", \"block\", \"rg\", [], shortOf(FRONT_4.t, 0.5), \"straight\"),\n ln(\"bl-rt\", \"block\", \"rt\", [beside(FRONT_4.t, 1.5)], shortOf(LB_43.s, 0.5), \"bezier\"),\n ln(\"bl-y\", \"block\", \"y\", [], shortOf(END_OVER_TE, 0.5), \"straight\"),\n ln(\"run-rb\", \"route\", \"rb\", [pt(31, -5), pt(35.5, -2)], pt(37.5, 3), \"bezier\"),\n ],\n);\n\nconst POWER = play(\n \"play-power\",\n \"Power\",\n \"offense\",\n \"run-gap\",\n \"21 pers\",\n \"プレイサイドはダウンブロック、FB が 9 テクの E をキックアウト、バックサイドガードがプルして C ギャップをリードする。\",\n [...iFormation(), ...defense43()],\n [\n ln(\"bl-y\", \"block\", \"y\", [], shortOf(LB_43.s), \"straight\"),\n ln(\"bl-rt\", \"block\", \"rt\", [], shortOf(FRONT_4.t, 0.5), \"straight\"),\n ln(\"bl-rg\", \"block\", \"rg\", [beside(FRONT_4.t, -1.5)], shortOf(LB_43.m), \"bezier\"),\n ln(\"bl-c\", \"block\", \"c\", [], shortOf(FRONT_4.n), \"straight\"),\n ln(\"bl-lt\", \"block\", \"lt\", [], shortOf(FRONT_4.eL), \"straight\"),\n ln(\n \"lead-fb\",\n \"block\",\n \"fb\",\n [pt(31, -3.5), pt(33.5, -0.5)],\n shortOf(END_OVER_TE, -0.5),\n \"bezier\",\n ),\n ln(\n \"pull-lg\",\n \"block\",\n \"lg\",\n [pt(25, -4), pt(31, -4), pt(32.5, -2), pt(32.5, 1)],\n pt(31.5, 3.5),\n \"bezier\",\n ),\n ln(\"run-rb\", \"route\", \"rb\", [pt(30, -6), pt(33, -3)], pt(33.5, 4), \"bezier\"),\n ],\n);\n\nconst COUNTER = play(\n \"play-counter\",\n \"Counter (GT)\",\n \"offense\",\n \"run-gap\",\n \"21 pers\",\n \"バックサイドのガードが 9 テクの E をキックアウト、タックルが C ギャップへ巻き込み、FB がタックルの跡を埋める(GT カウンター)。\",\n [...iFormation(), ...defense43()],\n [\n ln(\"bl-y\", \"block\", \"y\", [], shortOf(LB_43.s), \"straight\"),\n ln(\"bl-rt\", \"block\", \"rt\", [], shortOf(FRONT_4.t, 0.5), \"straight\"),\n ln(\"bl-rg\", \"block\", \"rg\", [beside(FRONT_4.t, -1.5)], shortOf(LB_43.m), \"bezier\"),\n ln(\"bl-c\", \"block\", \"c\", [], shortOf(FRONT_4.n), \"straight\"),\n ln(\n \"pull-lg\",\n \"block\",\n \"lg\",\n [pt(25, -4), pt(32, -4), pt(33.5, -0.5)],\n shortOf(END_OVER_TE, -0.5),\n \"straight\",\n ),\n ln(\n \"pull-lt\",\n \"block\",\n \"lt\",\n [pt(23, -5), pt(31, -5), pt(32.5, -2), pt(32.5, 1)],\n pt(31.5, 3.5),\n \"straight\",\n ),\n ln(\"fill-fb\", \"block\", \"fb\", [pt(24, -3), pt(22.5, -0.5)], shortOf(FRONT_4.eL, 1), \"bezier\"),\n ln(\"run-rb\", \"route\", \"rb\", [pt(24, -7.5), pt(30, -6.5), pt(33, -3)], pt(33.5, 4), \"bezier\"),\n ],\n);\n\nconst FOUR_VERTICALS = play(\n \"play-four-verticals\",\n \"Four Verticals\",\n \"offense\",\n \"pass-deep\",\n \"10 pers\",\n \"トリップスから 4 本の縦。対シングルハイのシームで勝負する。\",\n [...trips(), ...nickel(COVER_3_SHELL, END_OVER_TE)],\n [\n ln(\"go-x\", \"route\", \"x\", [pt(4, 4)], pt(4, 13), \"straight\"),\n ln(\"seam-y\", \"route\", \"y\", [pt(33, 5)], pt(31, 13), \"bezier\"),\n ln(\"seam-h\", \"route\", \"h\", [pt(40, 5)], pt(38, 13), \"bezier\"),\n ln(\"go-z\", \"route\", \"z\", [pt(49, 4)], pt(49, 13), \"straight\"),\n ln(\"chk-rb\", \"route\", \"rb\", [], pt(20, -3), \"straight\"),\n ],\n);\n\nconst MESH = play(\n \"play-mesh\",\n \"Mesh\",\n \"offense\",\n \"pass-dropback\",\n \"10 pers\",\n \"浅いクロスの交差。対マン/ゾーン両対応で空いた所へ運ぶ。\",\n [...shotgunSpread(), ...nickel(NICKEL_BASE)],\n [\n ln(\"cross-y\", \"route\", \"y\", [pt(20, 3.5)], pt(38, 4), \"straight\"),\n ln(\"cross-h\", \"route\", \"h\", [pt(33, 3)], pt(15, 3.5), \"straight\"),\n ln(\"corner-x\", \"route\", \"x\", [pt(5, 5)], pt(2, 9), \"bezier\"),\n ln(\"sit-z\", \"route\", \"z\", [], pt(47, 5), \"straight\"),\n ln(\"swing-rb\", \"route\", \"rb\", [pt(33, -4)], pt(41, -3), \"straight\"),\n ],\n);\n\nconst SMASH = play(\n \"play-smash\",\n \"Smash\",\n \"offense\",\n \"pass-dropback\",\n \"11 pers\",\n \"コーナー+ヒッチのハイロー。対カバー 2 のコーナーを攻める。\",\n [...singlebackAce(), ...nickel(COVER_2_SHELL, END_OVER_TE)],\n [\n ln(\"hitch-z\", \"route\", \"z\", [], pt(47, 3), \"straight\"),\n ln(\"corner-h\", \"route\", \"h\", [pt(41, 4)], pt(46, 10), \"bezier\"),\n ln(\"dig-y\", \"route\", \"y\", [pt(34.5, 8)], pt(27, 9), \"straight\"),\n ln(\"hitch-x\", \"route\", \"x\", [], pt(6, 3), \"straight\"),\n ln(\"flat-rb\", \"route\", \"rb\", [], pt(22, -4), \"straight\"),\n ],\n);\n\nconst STICK = play(\n \"play-stick\",\n \"Stick\",\n \"offense\",\n \"pass-quick\",\n \"11 pers\",\n \"3 ステップの速攻。スティック(座り)+フラットで素早く配球する。\",\n [...singlebackAce(), ...nickel(NICKEL_BASE, END_OVER_TE)],\n [\n ln(\"stick-y\", \"route\", \"y\", [], pt(34.5, 4), \"straight\"),\n ln(\"flat-h\", \"route\", \"h\", [], pt(45, 0), \"straight\"),\n ln(\"hitch-z\", \"route\", \"z\", [], pt(47, 3), \"straight\"),\n ln(\"slant-x\", \"route\", \"x\", [pt(6, 2)], pt(10, 3), \"bezier\"),\n ln(\"chk-rb\", \"route\", \"rb\", [], pt(21, -5), \"straight\"),\n ],\n);\n\nconst RPO_BUBBLE = play(\n \"play-rpo-bubble\",\n \"RPO (IZ + Bubble)\",\n \"offense\",\n \"rpo\",\n \"11 pers\",\n \"バブル側の W を読み、W がボックスに入ればバブル、残ればインサイドゾーンへ分岐する。\",\n [...shotgunSpread(), ...nickel(NICKEL_BASE)],\n [\n ln(\"bl-lt\", \"block\", \"lt\", [], shortOf(FRONT_4.eL), \"straight\"),\n ln(\"bl-lg\", \"block\", \"lg\", [beside(FRONT_4.n, -1)], shortOf(LB_NICKEL.m), \"bezier\"),\n ln(\"bl-c\", \"block\", \"c\", [], shortOf(FRONT_4.n), \"straight\"),\n ln(\"bl-rg\", \"block\", \"rg\", [], shortOf(FRONT_4.t), \"straight\"),\n ln(\"bl-rt\", \"block\", \"rt\", [], shortOf(FRONT_4.eR), \"straight\"),\n ln(\"run-rb\", \"route\", \"rb\", [pt(26, -2.5)], pt(23, 3), \"straight\"),\n ln(\"bubble-h\", \"route\", \"h\", [pt(43, -3)], pt(46, -0.5), \"bezier\"),\n ln(\"bl-z\", \"block\", \"z\", [], shortOf(cornerSpots(NICKEL_BASE.cornerDepth).right), \"straight\"),\n ln(\"glance-y\", \"route\", \"y\", [pt(15, 1)], pt(19, 3), \"bezier\"),\n ],\n);\n\nconst PA_BOOT = play(\n \"play-pa-boot\",\n \"PA Boot (Sail)\",\n \"offense\",\n \"pa\",\n \"11 pers\",\n \"プレイアクションで QB がブートし、フラット/セイル/縦の 3 段で攻める。\",\n [...singlebackAce(), ...defense43()],\n [\n ln(\"fake-rb\", \"motion\", \"rb\", [pt(24, -6)], pt(20.5, -4.5), \"straight\"),\n ln(\"boot-qb\", \"route\", \"qb\", [pt(31, -4)], pt(36.5, -5), \"bezier\"),\n ln(\"bl-lt\", \"block\", \"lt\", [], shortOf(FRONT_4.eL), \"straight\"),\n ln(\"bl-c\", \"block\", \"c\", [], shortOf(FRONT_4.n), \"straight\"),\n ln(\"bl-rg\", \"block\", \"rg\", [], shortOf(FRONT_4.t), \"straight\"),\n ln(\"flat-y\", \"route\", \"y\", [pt(37, -0.5)], pt(43, 0.5), \"bezier\"),\n ln(\"sail-h\", \"route\", \"h\", [pt(41, 3)], pt(46, 8), \"bezier\"),\n ln(\"go-z\", \"route\", \"z\", [pt(49, 3)], pt(49, 13), \"straight\"),\n ln(\"comeback-x\", \"route\", \"x\", [pt(5, 9)], pt(7.5, 7.5), \"bezier\"),\n ],\n);\n\nconst COVER_1 = play(\n \"play-cover-1\",\n \"Cover 1 (Man Free)\",\n \"defense\",\n \"coverage\",\n \"Nickel\",\n \"1 ディープのフリーセイフティを残し、残りは全マン。W は穴(ラット)で待つ。\",\n [...nickel({ cornerDepth: 6, fs: pt(26.5, 11), ss: pt(38, 6) }), ...shotgunSpread()],\n [\n ln(\"man-cbl\", \"motion\", \"cb-l\", [], pt(5.5, 0.5), \"straight\"),\n ln(\"man-cbr\", \"motion\", \"cb-r\", [], pt(47.5, 0.5), \"straight\"),\n ln(\"man-nb\", \"motion\", \"nb\", [], pt(13, -0.5), \"straight\"),\n ln(\"man-ss\", \"motion\", \"ss\", [], pt(40, -0.5), \"straight\"),\n ln(\"man-mlb\", \"motion\", \"mlb\", [pt(27.5, 2.5), pt(27.5, -2.5)], pt(29, -3.5), \"straight\"),\n ln(\"rat-wlb\", \"motion\", \"wlb\", [], pt(27.5, 3.5), \"straight\"),\n ln(\"free-fs\", \"motion\", \"fs\", [], pt(26.5, 13), \"straight\"),\n ],\n);\n\nconst COVER_2 = play(\n \"play-cover-2\",\n \"Cover 2\",\n \"defense\",\n \"coverage\",\n \"Nickel\",\n \"2 ディープ 5 アンダーのゾーン。深いハーフを 2 人で分け、CB はフラットに座る。\",\n [...nickel({ cornerDepth: 5, fs: pt(15, 11), ss: pt(38, 11) }), ...shotgunSpread()],\n [\n ln(\"half-fs\", \"motion\", \"fs\", [], pt(13, 14), \"straight\"),\n ln(\"half-ss\", \"motion\", \"ss\", [], pt(40, 14), \"straight\"),\n ln(\"flat-cbl\", \"motion\", \"cb-l\", [], pt(9.5, 4), \"straight\"),\n ln(\"flat-cbr\", \"motion\", \"cb-r\", [], pt(43.5, 4), \"straight\"),\n ln(\"hook-mlb\", \"motion\", \"mlb\", [], pt(21.5, 10), \"straight\"),\n ln(\"hook-wlb\", \"motion\", \"wlb\", [], pt(32.5, 10), \"straight\"),\n ln(\"curl-nb\", \"motion\", \"nb\", [], pt(16, 9), \"straight\"),\n ],\n);\n\nconst COVER_3 = play(\n \"play-cover-3\",\n \"Cover 3\",\n \"defense\",\n \"coverage\",\n \"Nickel\",\n \"3 ディープ 4 アンダー。現代の基準となるシングルハイのゾーン。\",\n [...nickel(COVER_3_SHELL), ...shotgunSpread()],\n [\n ln(\"deep-cbl\", \"motion\", \"cb-l\", [], pt(7, 13), \"straight\"),\n ln(\"deep-cbr\", \"motion\", \"cb-r\", [], pt(46, 13), \"straight\"),\n ln(\"deep-fs\", \"motion\", \"fs\", [], pt(26.5, 14), \"straight\"),\n ln(\"curl-nb\", \"motion\", \"nb\", [], pt(15.5, 9), \"straight\"),\n ln(\"hook-mlb\", \"motion\", \"mlb\", [], pt(22, 10), \"straight\"),\n ln(\"hook-wlb\", \"motion\", \"wlb\", [], pt(31, 10), \"straight\"),\n ln(\"flat-ss\", \"motion\", \"ss\", [], pt(39, 4), \"straight\"),\n ],\n);\n\nconst COVER_4 = play(\n \"play-cover-4\",\n \"Cover 4 (Quarters)\",\n \"defense\",\n \"coverage\",\n \"Nickel\",\n \"4 ディープのクォーターズ。縦パスを上から踏み潰す。\",\n [...nickel({ cornerDepth: 7, fs: pt(20, 11), ss: pt(33, 11) }), ...shotgunSpread()],\n [\n ln(\"qtr-cbl\", \"motion\", \"cb-l\", [], pt(6, 13), \"straight\"),\n ln(\"qtr-cbr\", \"motion\", \"cb-r\", [], pt(47, 13), \"straight\"),\n ln(\"qtr-fs\", \"motion\", \"fs\", [], pt(18, 14), \"straight\"),\n ln(\"qtr-ss\", \"motion\", \"ss\", [], pt(35, 14), \"straight\"),\n ln(\"under-nb\", \"motion\", \"nb\", [], pt(16, 8), \"straight\"),\n ln(\"under-mlb\", \"motion\", \"mlb\", [], pt(23, 9), \"straight\"),\n ln(\"under-wlb\", \"motion\", \"wlb\", [], pt(31, 9), \"straight\"),\n ],\n);\n\nconst FIRE_ZONE = play(\n \"play-fire-zone\",\n \"Fire Zone\",\n \"defense\",\n \"pressure\",\n \"Nickel\",\n \"5 人ブリッツ+ライン 1 枚をドロップ、3 ディープ 3 アンダーで覆う。\",\n [...nickel(COVER_3_SHELL), ...shotgunSpread()],\n [\n ln(\"rush-dtl\", \"route\", \"dt-l\", [], pt(25, 0), \"straight\"),\n ln(\"rush-dtr\", \"route\", \"dt-r\", [], pt(28.5, 0), \"straight\"),\n ln(\"rush-der\", \"route\", \"de-r\", [], pt(33, 0), \"straight\"),\n ln(\"blz-wlb\", \"route\", \"wlb\", [pt(31.5, 2.5)], pt(31, 0), \"bezier\"),\n ln(\"blz-nb\", \"route\", \"nb\", [pt(17, 2.5)], pt(20, 0), \"bezier\"),\n ln(\"drop-del\", \"motion\", \"de-l\", [pt(19, 4)], pt(17, 7), \"straight\"),\n ln(\"deep-cbl\", \"motion\", \"cb-l\", [], pt(7, 13), \"straight\"),\n ln(\"deep-cbr\", \"motion\", \"cb-r\", [], pt(46, 13), \"straight\"),\n ln(\"deep-fs\", \"motion\", \"fs\", [], pt(26.5, 14), \"straight\"),\n ln(\"hook-mlb\", \"motion\", \"mlb\", [], pt(23, 9), \"straight\"),\n ln(\"flat-ss\", \"motion\", \"ss\", [], pt(39, 4), \"straight\"),\n ],\n);\n\nconst COVER_0 = play(\n \"play-cover-0\",\n \"Cover 0 Blitz\",\n \"defense\",\n \"pressure\",\n \"Nickel\",\n \"セイフティを残さず全マン、6 人で最大限の圧力をかける。FS が RB をマンで受け持つ。\",\n [...nickel({ cornerDepth: 5, fs: pt(22, 9), ss: pt(31, 9) }), ...shotgunSpread()],\n [\n ln(\"rush-del\", \"route\", \"de-l\", [], pt(20, 0), \"straight\"),\n ln(\"rush-dtl\", \"route\", \"dt-l\", [], pt(25, 0), \"straight\"),\n ln(\"rush-dtr\", \"route\", \"dt-r\", [], pt(30.5, 0), \"straight\"),\n ln(\"rush-der\", \"route\", \"de-r\", [], pt(33, 0), \"straight\"),\n ln(\"blz-mlb\", \"route\", \"mlb\", [pt(27, 2.5)], pt(27.5, 0), \"bezier\"),\n ln(\"blz-wlb\", \"route\", \"wlb\", [pt(31.5, 2.5)], pt(31, 0), \"bezier\"),\n ln(\"man-cbl\", \"motion\", \"cb-l\", [], pt(5.5, 0.5), \"straight\"),\n ln(\"man-cbr\", \"motion\", \"cb-r\", [], pt(47.5, 0.5), \"straight\"),\n ln(\"man-nb\", \"motion\", \"nb\", [], pt(13, -0.5), \"straight\"),\n ln(\"man-fs\", \"motion\", \"fs\", [pt(22, 2.5), pt(23, -2.5)], pt(28, -4), \"straight\"),\n ln(\"man-ss\", \"motion\", \"ss\", [pt(37, 4)], pt(39.5, -0.5), \"straight\"),\n ],\n);\n\n// 両 A ギャップに LB を立てるので、DT は 2 人とも 3 テクに広がる。\nconst DOUBLE_A = play(\n \"play-double-a\",\n \"Double-A Gap\",\n \"defense\",\n \"pressure\",\n \"Nickel\",\n \"両 A ギャップに 2 人を提示。実行・シム(見せ)どちらにも化ける。\",\n [\n player(\"de-l\", \"E\", FRONT_4.eL),\n player(\"dt-l\", \"T\", pt(23, 1.5)),\n player(\"dt-r\", \"T\", FRONT_4.t),\n player(\"de-r\", \"E\", FRONT_4.eR),\n player(\"mlb\", \"M\", pt(25.5, 3)),\n player(\"wlb\", \"W\", pt(27.5, 3)),\n player(\"nb\", \"NB\", LB_NICKEL.nb),\n player(\"cb-l\", \"CB\", cornerSpots(7).left),\n player(\"cb-r\", \"CB\", cornerSpots(7).right),\n player(\"fs\", \"FS\", COVER_3_SHELL.fs),\n player(\"ss\", \"SS\", COVER_3_SHELL.ss),\n ...shotgunSpread(),\n ],\n [\n ln(\"aim-mlb\", \"route\", \"mlb\", [], pt(25.5, 0.5), \"straight\"),\n ln(\"aim-wlb\", \"route\", \"wlb\", [], pt(27.5, 0.5), \"straight\"),\n ln(\"rush-del\", \"route\", \"de-l\", [], pt(20, 0), \"straight\"),\n ln(\"rush-dtl\", \"route\", \"dt-l\", [], pt(22.5, 0), \"straight\"),\n ln(\"rush-dtr\", \"route\", \"dt-r\", [], pt(30.5, 0), \"straight\"),\n ln(\"rush-der\", \"route\", \"de-r\", [], pt(33, 0), \"straight\"),\n ln(\"bail-cbl\", \"motion\", \"cb-l\", [], pt(7, 12), \"straight\"),\n ln(\"bail-cbr\", \"motion\", \"cb-r\", [], pt(46, 12), \"straight\"),\n ln(\"deep-fs\", \"motion\", \"fs\", [], pt(26.5, 14), \"straight\"),\n ln(\"curl-nb\", \"motion\", \"nb\", [], pt(15.5, 9), \"straight\"),\n ln(\"flat-ss\", \"motion\", \"ss\", [], pt(39, 4), \"straight\"),\n ],\n);\n\n// フィールド側(右)はクォーターズ、バウンダリ側(左)はカバー 2 なので、CB の深さが左右で違い、\n// ニッケルバックはフィールド側のスロットに付く。\nconst COVER_6 = play(\n \"play-cover-6\",\n \"Cover 6\",\n \"defense\",\n \"coverage\",\n \"Nickel\",\n \"クォーター×2+ハーフの分割カバー。フィールドはクォーターズ、バウンダリは 2。\",\n [\n ...nickelFront(),\n player(\"nb\", \"NB\", pt(39, 5)),\n player(\"cb-l\", \"CB\", cornerSpots(5).left),\n player(\"cb-r\", \"CB\", cornerSpots(7).right),\n player(\"fs\", \"FS\", pt(15, 11)),\n player(\"ss\", \"SS\", pt(35, 10)),\n ...shotgunSpread(),\n ],\n [\n ln(\"flat-cbl\", \"motion\", \"cb-l\", [], pt(10, 4), \"straight\"),\n ln(\"half-fs\", \"motion\", \"fs\", [], pt(12, 14), \"straight\"),\n ln(\"qtr-cbr\", \"motion\", \"cb-r\", [], pt(47, 13), \"straight\"),\n ln(\"qtr-ss\", \"motion\", \"ss\", [], pt(38, 14), \"straight\"),\n ln(\"curl-nb\", \"motion\", \"nb\", [], pt(41, 9), \"straight\"),\n ln(\"hook-mlb\", \"motion\", \"mlb\", [], pt(22, 10), \"straight\"),\n ln(\"hook-wlb\", \"motion\", \"wlb\", [], pt(31, 10), \"straight\"),\n ],\n);\n\nexport const PLAY_PRESETS: readonly PlayPreset[] = /* @__PURE__ */ deepFreeze([\n INSIDE_ZONE,\n OUTSIDE_ZONE,\n POWER,\n COUNTER,\n FOUR_VERTICALS,\n MESH,\n SMASH,\n STICK,\n RPO_BUBBLE,\n PA_BOOT,\n COVER_1,\n COVER_2,\n COVER_3,\n COVER_4,\n FIRE_ZONE,\n COVER_0,\n DOUBLE_A,\n COVER_6,\n]);\n\nexport function getPlayPreset(id: string): PlayPreset | undefined {\n return PLAY_PRESETS.find((p) => p.id === id);\n}\n","import {\n Disposable,\n type FieldPosition,\n type IEditorActions,\n type IEditorGestures,\n type IEditorScene,\n resolveKeyAction,\n toDisposable,\n} from \"../../common/index.js\";\n\n/** ポインタを受ける canvas と、その上の位置をフィールドのヤードの位置に変える手段。 */\nexport interface IPointerSurface {\n readonly canvas: HTMLCanvasElement;\n clientToYard(clientX: number, clientY: number): FieldPosition;\n}\n\n// フォーム部品に来たキーは、その部品の標準動作(テキストの Undo など)に任せる。\nfunction isFormControl(target: EventTarget | null): boolean {\n return (\n target instanceof HTMLInputElement ||\n target instanceof HTMLSelectElement ||\n target instanceof HTMLTextAreaElement\n );\n}\n\nexport class PointerInput extends Disposable {\n /**\n * @param root ショートカットを受ける要素。ツールバーのボタンを押した直後でも\n * Cmd+Z が効くよう、canvas ではなく UI 全体を包む要素に付ける。\n */\n constructor(\n root: HTMLElement,\n surface: IPointerSurface,\n controller: IEditorGestures & IEditorActions & Pick<IEditorScene, \"getViewState\">,\n ) {\n super();\n const canvas = surface.canvas;\n // canvas は既定でフォーカスを受けないので、Esc、Enter、Undo と Redo のキーが届かない。\n canvas.tabIndex = 0;\n\n const toYard = (e: PointerEvent) => surface.clientToYard(e.clientX, e.clientY);\n // 押下中のポインタ。これ以外(2 本目の指、右ボタン)の up や cancel は無視する。\n let activePointerId: number | null = null;\n\n const onPointerDown = (e: PointerEvent) => {\n if (e.button !== 0 || !e.isPrimary) {\n return;\n }\n activePointerId = e.pointerId;\n // フォーカスでページがスクロールすると、押した位置と canvas がずれる。\n canvas.focus({ preventScroll: true });\n canvas.setPointerCapture(e.pointerId);\n controller.pointerDown(toYard(e));\n };\n // 作図中の線は、ボタンを押していなくてもポインタに付いてくるので、移動は押下の有無によらず渡す。\n const onPointerMove = (e: PointerEvent) => {\n if (e.isPrimary) {\n controller.pointerMove(toYard(e));\n }\n };\n // 押下中のポインタの終わりなら控えを消して true。up の直後にブラウザが capture を外して\n // lostpointercapture を出すので、up で先に消しておき、それを中断と取り違えない。\n const endPress = (e: PointerEvent): boolean => {\n if (e.pointerId !== activePointerId) {\n return false;\n }\n activePointerId = null;\n return true;\n };\n const onPointerUp = (e: PointerEvent) => {\n if (endPress(e)) {\n controller.pointerUp(toYard(e));\n }\n };\n // タッチのスクロール開始やウィンドウ切替で up が来ないまま終わった押下。\n // 放っておくとドラッグが外れず、次の移動で選手がついてくる。\n // 作図は押下をまたいで続く操作なので、1 回の押下の中断で打った点まで捨てない。\n const onPointerAbort = (e: PointerEvent) => {\n if (endPress(e) && !controller.getViewState().isDrawing) {\n controller.cancelInteraction();\n }\n };\n const onDoubleClick = () => controller.commitLine();\n const onKeyDown = (e: KeyboardEvent) => {\n if (isFormControl(e.target)) {\n return;\n }\n switch (resolveKeyAction(e)) {\n case \"cancel-interaction\":\n controller.cancelInteraction();\n return;\n case \"commit-line\":\n // ボタン上の Enter はそのボタンを押す操作なので、作図の確定と二重にしない。\n if (!(e.target instanceof HTMLButtonElement)) {\n controller.commitLine();\n }\n return;\n case \"undo\":\n e.preventDefault();\n controller.undo();\n return;\n case \"redo\":\n e.preventDefault();\n controller.redo();\n return;\n case null:\n return;\n }\n };\n\n const listeners = new AbortController();\n const { signal } = listeners;\n canvas.addEventListener(\"pointerdown\", onPointerDown, { signal });\n canvas.addEventListener(\"pointermove\", onPointerMove, { signal });\n canvas.addEventListener(\"pointerup\", onPointerUp, { signal });\n canvas.addEventListener(\"pointercancel\", onPointerAbort, { signal });\n canvas.addEventListener(\"lostpointercapture\", onPointerAbort, { signal });\n canvas.addEventListener(\"dblclick\", onDoubleClick, { signal });\n root.addEventListener(\"keydown\", onKeyDown, { signal });\n this._register(\n toDisposable(() => {\n listeners.abort();\n canvas.removeAttribute(\"tabindex\");\n }),\n );\n }\n}\n","export default \"data:font/woff2;base64,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\"","// ヤード数字と選手ラベルは同梱のフォントに固定し、テーマでは差し替えさせない。\n// ctx.font は CSS の var() を解釈できないので、変数ではなくこの定数から組み立てる。\n// 名前はホストのページのフォントと重ならないよう、ライブラリ専用にしてある。\nexport const FIELD_FONT_NAME = \"Playmaker Saira\";\nexport const FIELD_FONT_WEIGHT = 700;\nexport const FIELD_FONT_FAMILY = `\"${FIELD_FONT_NAME}\", sans-serif`;\n\n/** 同梱フォントで sizePx の大きさに描く、ctx.font の値。 */\nexport function fieldFont(sizePx: number): string {\n return `${FIELD_FONT_WEIGHT} ${sizePx}px ${FIELD_FONT_FAMILY}`;\n}\n","// 元のフォントは Saira Condensed Bold(SIL OFL 1.1、library/assets/fonts/OFL.txt)。作り直すには make font。\nimport fontUrl from \"../../assets/playmaker-saira-subset.woff2\";\nimport { FIELD_FONT_NAME, FIELD_FONT_WEIGHT } from \"./field-font.js\";\n\nlet loaded: Promise<void> | undefined;\n\n/**\n * 同梱フォントを document.fonts に登録し、読み込み終えたら解決する。何度呼んでも登録は 1 回だけ。\n * CSS の @font-face で宣言すると、ホストが CSS を読み込むまでフォントが見えず、\n * その間に描いた数字とラベルは代わりのフォントになる。\n * 読み込めなくても代わりのフォントで描けるので、reject しない。\n */\nexport function loadFieldFont(): Promise<void> {\n loaded ??= registerFieldFont();\n return loaded;\n}\n\nasync function registerFieldFont(): Promise<void> {\n try {\n const face = new FontFace(FIELD_FONT_NAME, `url(\"${fontUrl}\")`, {\n weight: String(FIELD_FONT_WEIGHT),\n });\n document.fonts.add(face);\n await face.load();\n } catch {\n // 代わりのフォントのまま描く。\n }\n}\n","/** 色を読むためだけの 2D コンテキスト。確保できなかったことも覚えておき、毎回作り直さない。 */\ntype ColorContext =\n | { readonly status: \"unresolved\" }\n | { readonly status: \"unavailable\" }\n | { readonly status: \"ready\"; readonly ctx: CanvasRenderingContext2D };\n\nlet context: ColorContext = { status: \"unresolved\" };\n\nfunction resolveContext(): ColorContext {\n if (context.status === \"unresolved\") {\n const ctx = document.createElement(\"canvas\").getContext(\"2d\");\n context = ctx === null ? { status: \"unavailable\" } : { status: \"ready\", ctx };\n }\n return context;\n}\n\n/**\n * CSS の色を、canvas の fillStyle が返す書式にそろえる。\n * 書き方が違っても同じ色なら同じ文字列になり、不透明な色は `#rrggbb` の小文字になる。\n * 色として読めない値は undefined を返す。2D コンテキストを確保できないときは、値をそのまま返す。\n */\nexport function normalizeCssColor(value: string): string | undefined {\n const resolved = resolveContext();\n if (resolved.status !== \"ready\") {\n return value;\n }\n const { ctx } = resolved;\n // 読めない値を代入しても、fillStyle は前の値のまま残る。\n // 違う色から 2 回代入し、結果が揃えば読めた色とみなす。\n ctx.fillStyle = \"#000000\";\n ctx.fillStyle = value;\n const fromBlack = ctx.fillStyle;\n ctx.fillStyle = \"#ffffff\";\n ctx.fillStyle = value;\n return typeof fromBlack === \"string\" && ctx.fillStyle === fromBlack ? fromBlack : undefined;\n}\n","// Material Design のパレット。配色の考え方は docs/design.md の「テーマ」にある。\nconst WHITE = \"#ffffff\";\nconst BLACK = \"#212121\";\nconst RED = \"#d32f2f\";\nconst BLUE = \"#1976d2\";\nconst GREEN = \"#388e3c\";\nconst ORANGE = \"#ff9800\";\nconst GREY_100 = \"#f5f5f5\";\nconst GREY_200 = \"#eeeeee\";\nconst GREY_400 = \"#bdbdbd\";\nconst GREY_500 = \"#9e9e9e\";\nconst GREY_700 = \"#616161\";\n\ninterface ThemeToken {\n readonly property: `--playmaker-${string}`;\n readonly fallback: string;\n}\n\n/**\n * Canvas に描く色のテーマ変数と、ホストが宣言していないときの既定値。\n * ツールバーとパネルの色は CSS だけで決まるので、ここには置かない。\n */\nexport const THEME_TOKENS = {\n fieldGrass: { property: \"--playmaker-field-grass\", fallback: WHITE },\n fieldOob: { property: \"--playmaker-field-oob\", fallback: GREY_200 },\n fieldEndzone: { property: \"--playmaker-field-endzone\", fallback: GREY_100 },\n fieldLine: { property: \"--playmaker-field-line\", fallback: GREY_500 },\n fieldNumber: { property: \"--playmaker-field-number\", fallback: GREY_400 },\n fieldGoalLine: { property: \"--playmaker-field-goal-line\", fallback: GREY_700 },\n fieldPylon: { property: \"--playmaker-field-pylon\", fallback: GREY_500 },\n fieldGoalpost: { property: \"--playmaker-field-goalpost\", fallback: GREY_500 },\n fieldGrid: { property: \"--playmaker-field-grid\", fallback: \"rgba(0, 0, 0, 0.1)\" },\n /** 色を省いた選手の色。枠線だけの選手では枠とラベル、塗りつぶした選手では塗りになる。 */\n playerColor: { property: \"--playmaker-player-color\", fallback: BLACK },\n /** 塗りつぶした選手のラベルの色。枠線だけの選手のラベルは選手の色で書く。 */\n playerLabel: { property: \"--playmaker-player-label\", fallback: WHITE },\n lineRoute: { property: \"--playmaker-line-route\", fallback: BLACK },\n lineBlock: { property: \"--playmaker-line-block\", fallback: BLACK },\n lineMotion: { property: \"--playmaker-line-motion\", fallback: BLACK },\n /** パネルで選手と線に選べる 4 色。 */\n swatch1: { property: \"--playmaker-swatch-1\", fallback: BLACK },\n swatch2: { property: \"--playmaker-swatch-2\", fallback: RED },\n swatch3: { property: \"--playmaker-swatch-3\", fallback: BLUE },\n swatch4: { property: \"--playmaker-swatch-4\", fallback: GREEN },\n selection: { property: \"--playmaker-selection\", fallback: ORANGE },\n selectionOutline: { property: \"--playmaker-selection-outline\", fallback: WHITE },\n} as const satisfies Record<string, ThemeToken>;\n\nexport type ThemeTokenName = keyof typeof THEME_TOKENS;\n\nexport type ThemeReader = (token: ThemeTokenName) => string;\n\n/** 変数の値を読む関数から ThemeReader を作る。値が空か空白だけのトークンは、既定値で描く。 */\nexport function themeReaderFrom(readProperty: (property: string) => string): ThemeReader {\n return (token) => {\n const { property, fallback } = THEME_TOKENS[token];\n const value = readProperty(property).trim();\n return value === \"\" ? fallback : value;\n };\n}\n","import { normalizeCssColor } from \"./css-color.js\";\nimport { THEME_TOKENS, type ThemeReader, themeReaderFrom } from \"./tokens.js\";\n\n/**\n * element で解決したテーマ変数を読む。canvas は var() を解釈できないので、描く前に具体的な色にする。\n * 変数は作った時点ですべて読むので、あとでホストが変数を変えても、この reader が返す色は変わらない。\n * 色は書き方をそろえて返し、色として読めない値は、宣言されていないものとして既定の色にする。\n */\nexport function createThemeReader(element: Element): ThemeReader {\n const styles = getComputedStyle(element);\n const values = new Map<string, string>();\n for (const { property } of Object.values(THEME_TOKENS)) {\n values.set(property, normalizeCssColor(styles.getPropertyValue(property).trim()) ?? \"\");\n }\n return themeReaderFrom((property) => values.get(property) ?? \"\");\n}\n","import {\n DEFAULT_FIELD_LEAGUE,\n displayYardNumber,\n FIELD_WIDTH_YARDS,\n type FieldGeometry,\n type FieldMetrics,\n HASH_CENTER_OFFSET_YARDS_BY_LEAGUE,\n YARD_LINE_INTERVAL_YARDS,\n yardLinesInWindow,\n} from \"../../common/index.js\";\nimport { fieldFont } from \"../theme/field-font.js\";\nimport type { ThemeReader } from \"../theme/tokens.js\";\nimport type { ILayerRenderer, RenderFrame } from \"./layer.js\";\n\ninterface FieldTheme {\n readonly fieldColor: string;\n /** サイドラインの外と、縦横比が合わない分の余白。 */\n readonly oobColor: string;\n readonly endzoneColor: string;\n readonly lineColor: string;\n readonly goalLineColor: string;\n readonly numberColor: string;\n readonly pylonColor: string;\n readonly goalpostColor: string;\n}\n\n/** 窓に映るフィールドの外枠(px)。サイドラインが left と right、窓の上下の端が top と bottom。 */\ninterface FieldBounds {\n readonly left: number;\n readonly right: number;\n readonly top: number;\n readonly bottom: number;\n}\n\n/** ゴールポストの柱を伸ばす向き。自陣のエンドゾーンは画面の下にあるので上へ、相手のは下へ伸ばす。 */\ntype UprightDirection = \"toward-top\" | \"toward-bottom\";\n\n// 数字の横に置く、近いほうのゴールを指す三角の寸法(ヤード)。数字より目立たないよう小さくする。\nconst ARROW_LENGTH_YARDS = 0.8;\nconst ARROW_HALF_WIDTH_YARDS = 0.3;\nconst ARROW_OFFSET_FROM_NUMBER_YARDS = 2.5;\n// 数字を置く、サイドラインからの距離。三角も同じ列に置いて縦に揃える。\nconst NUMBER_FROM_SIDELINE_YARDS = 6;\n// 2 桁の数字はヤードラインをまたいで置くので、本物のフィールドと同じく桁の間を空けてラインを通す。\nconst NUMBER_DIGIT_SPACING_PER_HEIGHT = 0.18;\n\nfunction fieldTheme(read: ThemeReader): FieldTheme {\n return {\n fieldColor: read(\"fieldGrass\"),\n oobColor: read(\"fieldOob\"),\n endzoneColor: read(\"fieldEndzone\"),\n lineColor: read(\"fieldLine\"),\n goalLineColor: read(\"fieldGoalLine\"),\n numberColor: read(\"fieldNumber\"),\n pylonColor: read(\"fieldPylon\"),\n goalpostColor: read(\"fieldGoalpost\"),\n };\n}\n\nexport class FieldRenderer implements ILayerRenderer {\n /** geometry の窓に従って、1 フレーム分のフィールドを描く。 */\n draw(ctx: CanvasRenderingContext2D, frame: RenderFrame): void {\n const { geometry, metrics } = frame;\n const theme = fieldTheme(frame.theme);\n const { viewportWidth, viewportHeight, yardWindow } = geometry;\n\n const bounds: FieldBounds = {\n left: geometry.xForLateralYard(0),\n right: geometry.xForLateralYard(FIELD_WIDTH_YARDS),\n top: geometry.yForAbsoluteYard(yardWindow.endYard),\n bottom: geometry.yForAbsoluteYard(yardWindow.startYard),\n };\n\n ctx.fillStyle = theme.oobColor;\n ctx.fillRect(0, 0, viewportWidth, viewportHeight);\n ctx.fillStyle = theme.fieldColor;\n ctx.fillRect(bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top);\n\n this.drawEndZones(ctx, geometry, bounds, theme);\n this.drawHashes(ctx, geometry, bounds, metrics, theme);\n this.drawYardLines(ctx, geometry, bounds, metrics, theme);\n this.drawNumbers(ctx, geometry, metrics, theme);\n this.drawEndZoneMarkers(ctx, geometry, bounds, metrics, theme);\n }\n\n /** エンドゾーンのうち窓に映る範囲を塗る。中に線は引かない。 */\n private drawEndZones(\n ctx: CanvasRenderingContext2D,\n geometry: FieldGeometry,\n { left, right }: FieldBounds,\n theme: FieldTheme,\n ): void {\n const { startYard, endYard } = geometry.yardWindow;\n ctx.fillStyle = theme.endzoneColor;\n if (startYard < 0) {\n const yTop = geometry.yForAbsoluteYard(0);\n const yBottom = geometry.yForAbsoluteYard(startYard);\n ctx.fillRect(left, yTop, right - left, yBottom - yTop);\n }\n if (endYard > 100) {\n const yTop = geometry.yForAbsoluteYard(endYard);\n const yBottom = geometry.yForAbsoluteYard(100);\n ctx.fillRect(left, yTop, right - left, yBottom - yTop);\n }\n }\n\n /**\n * 1 ヤードごとのハッシュと、サイドラインの目盛を描く。5 ヤードの倍数はヤードラインが覆うので省き、\n * エンドゾーンには引かない。\n */\n private drawHashes(\n ctx: CanvasRenderingContext2D,\n geometry: FieldGeometry,\n { left, right }: FieldBounds,\n metrics: FieldMetrics,\n theme: FieldTheme,\n ): void {\n const offset = HASH_CENTER_OFFSET_YARDS_BY_LEAGUE[DEFAULT_FIELD_LEAGUE];\n const center = FIELD_WIDTH_YARDS / 2;\n const hashLeft = geometry.xForLateralYard(center - offset);\n const hashRight = geometry.xForLateralYard(center + offset);\n const tick = metrics.hashTickLength;\n const half = tick / 2;\n\n ctx.strokeStyle = theme.lineColor;\n ctx.lineWidth = metrics.yardLineWidth;\n ctx.beginPath();\n for (const yard of yardLinesInWindow(geometry.zone, 1)) {\n if (yard < 0 || yard > 100 || yard % YARD_LINE_INTERVAL_YARDS === 0) {\n continue;\n }\n const y = geometry.yForAbsoluteYard(yard);\n ctx.moveTo(left, y);\n ctx.lineTo(left + tick, y);\n ctx.moveTo(right - tick, y);\n ctx.lineTo(right, y);\n ctx.moveTo(hashLeft - half, y);\n ctx.lineTo(hashLeft + half, y);\n ctx.moveTo(hashRight - half, y);\n ctx.lineTo(hashRight + half, y);\n }\n ctx.stroke();\n }\n\n /** 5 ヤードごとのヤードラインと、窓の外枠を描く。ゴールラインは外枠と同じ太さで、色で見分ける。 */\n private drawYardLines(\n ctx: CanvasRenderingContext2D,\n geometry: FieldGeometry,\n { left, right, top, bottom }: FieldBounds,\n metrics: FieldMetrics,\n theme: FieldTheme,\n ): void {\n const goalPath = new Path2D();\n const midPath = new Path2D();\n const minorPath = new Path2D();\n for (const yard of yardLinesInWindow(geometry.zone, YARD_LINE_INTERVAL_YARDS)) {\n if (yard < 0 || yard > 100) {\n continue;\n }\n const y = geometry.yForAbsoluteYard(yard);\n const target = yard === 0 || yard === 100 ? goalPath : yard === 50 ? midPath : minorPath;\n target.moveTo(left, y);\n target.lineTo(right, y);\n }\n\n ctx.strokeStyle = theme.lineColor;\n ctx.lineWidth = metrics.yardLineWidth;\n ctx.stroke(minorPath);\n ctx.lineWidth = metrics.midfieldLineWidth;\n ctx.stroke(midPath);\n\n // サイドラインと、窓の上下の端。\n ctx.lineWidth = metrics.goalLineWidth;\n ctx.beginPath();\n ctx.moveTo(left, top);\n ctx.lineTo(left, bottom);\n ctx.moveTo(right, top);\n ctx.lineTo(right, bottom);\n ctx.moveTo(left, top);\n ctx.lineTo(right, top);\n ctx.moveTo(left, bottom);\n ctx.lineTo(right, bottom);\n ctx.stroke();\n\n ctx.strokeStyle = theme.goalLineColor;\n ctx.lineWidth = metrics.goalLineWidth;\n ctx.stroke(goalPath);\n }\n\n /**\n * 10 ヤードごとのヤードラインに数字と方向の三角を描く。数字は両サイドラインから読めるよう\n * 上下を反転させ、三角は近いほうのゴールを指す。センターの 50 には三角を描かず、\n * displayYardNumber が null を返すゴールラインとエンドゾーンには何も描かない。\n */\n private drawNumbers(\n ctx: CanvasRenderingContext2D,\n geometry: FieldGeometry,\n metrics: FieldMetrics,\n theme: FieldTheme,\n ): void {\n const leftX = geometry.xForLateralYard(NUMBER_FROM_SIDELINE_YARDS);\n const rightX = geometry.xForLateralYard(FIELD_WIDTH_YARDS - NUMBER_FROM_SIDELINE_YARDS);\n const arrowBaseHalfPx = ARROW_HALF_WIDTH_YARDS * geometry.scale;\n const halfArrowLength = ARROW_LENGTH_YARDS / 2;\n\n ctx.font = fieldFont(metrics.numberHeight);\n ctx.textBaseline = \"middle\";\n ctx.textAlign = \"center\";\n\n // 桁の間の隙間の中心をヤードラインに合わせ、各桁は測った幅で隙間の端から置く。\n // ctx.letterSpacing で空けないのは、末尾の字の後ろにも間隔が付き、中央揃えが片側へずれるため。\n const gap = metrics.numberHeight * NUMBER_DIGIT_SPACING_PER_HEIGHT;\n const drawRotatedLabel = (label: string, x: number, y: number, rotation: number): void => {\n const [head, tail] = [...label];\n ctx.save();\n ctx.translate(x, y);\n ctx.rotate(rotation);\n if (label.length === 2 && head !== undefined && tail !== undefined) {\n ctx.fillText(head, -gap / 2 - ctx.measureText(head).width / 2, 0);\n ctx.fillText(tail, gap / 2 + ctx.measureText(tail).width / 2, 0);\n } else {\n ctx.fillText(label, 0, 0);\n }\n ctx.restore();\n };\n const drawArrow = (xc: number, tipY: number, baseY: number): void => {\n ctx.beginPath();\n ctx.moveTo(xc, tipY);\n ctx.lineTo(xc - arrowBaseHalfPx, baseY);\n ctx.lineTo(xc + arrowBaseHalfPx, baseY);\n ctx.closePath();\n ctx.fill();\n };\n\n for (const yard of yardLinesInWindow(geometry.zone, 10)) {\n const n = displayYardNumber(yard);\n if (n === null) {\n continue;\n }\n const numberY = geometry.yForAbsoluteYard(yard);\n ctx.fillStyle = theme.numberColor;\n drawRotatedLabel(String(n), leftX, numberY, Math.PI / 2);\n drawRotatedLabel(String(n), rightX, numberY, -Math.PI / 2);\n\n if (yard === 50) {\n continue;\n }\n const towardGoal = yard < 50 ? -1 : 1;\n const arrowCenterYard = yard + towardGoal * ARROW_OFFSET_FROM_NUMBER_YARDS;\n const tipY = geometry.yForAbsoluteYard(arrowCenterYard + towardGoal * halfArrowLength);\n const baseY = geometry.yForAbsoluteYard(arrowCenterYard - towardGoal * halfArrowLength);\n ctx.fillStyle = theme.lineColor;\n drawArrow(leftX, tipY, baseY);\n drawArrow(rightX, tipY, baseY);\n }\n }\n\n /** エンドゾーンの 4 隅にパイロン、エンドライン中央にゴールポストを描く。 */\n private drawEndZoneMarkers(\n ctx: CanvasRenderingContext2D,\n geometry: FieldGeometry,\n { left, right }: FieldBounds,\n metrics: FieldMetrics,\n theme: FieldTheme,\n ): void {\n const { startYard, endYard } = geometry.yardWindow;\n const pylon = metrics.pylonSize;\n const drawPylon = (x: number, y: number): void => {\n ctx.fillStyle = theme.pylonColor;\n ctx.fillRect(x - pylon / 2, y - pylon / 2, pylon, pylon);\n };\n\n if (startYard < 0) {\n const goalY = geometry.yForAbsoluteYard(0);\n const endY = geometry.yForAbsoluteYard(startYard);\n drawPylon(left, goalY);\n drawPylon(right, goalY);\n drawPylon(left, endY);\n drawPylon(right, endY);\n this.drawGoalpost(ctx, geometry, endY, \"toward-top\", metrics, theme);\n }\n if (endYard > 100) {\n const goalY = geometry.yForAbsoluteYard(100);\n const endY = geometry.yForAbsoluteYard(endYard);\n drawPylon(left, goalY);\n drawPylon(right, goalY);\n drawPylon(left, endY);\n drawPylon(right, endY);\n this.drawGoalpost(ctx, geometry, endY, \"toward-bottom\", metrics, theme);\n }\n }\n\n /** エンドラインの中央に、横木と 2 本の柱を描く。柱はフィールドの側へ伸ばす。 */\n private drawGoalpost(\n ctx: CanvasRenderingContext2D,\n geometry: FieldGeometry,\n lineY: number,\n uprights: UprightDirection,\n metrics: FieldMetrics,\n theme: FieldTheme,\n ): void {\n const cx = geometry.xForLateralYard(FIELD_WIDTH_YARDS / 2);\n const crossHalf = metrics.goalpostCrossbarHalfLength;\n const uprightLen = metrics.goalpostUprightLength;\n const dir = uprights === \"toward-top\" ? -1 : 1;\n\n ctx.save();\n ctx.strokeStyle = theme.goalpostColor;\n ctx.lineWidth = metrics.goalpostStroke;\n ctx.lineCap = \"round\";\n ctx.beginPath();\n ctx.moveTo(cx - crossHalf, lineY);\n ctx.lineTo(cx + crossHalf, lineY);\n ctx.moveTo(cx - crossHalf, lineY);\n ctx.lineTo(cx - crossHalf, lineY + dir * uprightLen);\n ctx.moveTo(cx + crossHalf, lineY);\n ctx.lineTo(cx + crossHalf, lineY + dir * uprightLen);\n ctx.stroke();\n ctx.restore();\n }\n}\n","import {\n FIELD_WIDTH_YARDS,\n type FieldGeometry,\n GRID_STEP_YARDS,\n YARD_LINE_INTERVAL_YARDS,\n yardLinesInWindow,\n} from \"../../common/index.js\";\nimport type { EditorRenderFrame, ILayerRenderer } from \"./layer.js\";\n\n/**\n * 位置を置ける格子点を薄い点で敷く。編集中だけの層なので、PNG と view モードには出ない。\n * 格子は横がサイドライン、縦が LOS を原点にしているが、LOS は整数ヤードにあるので、\n * 絶対ヤードで GRID_STEP_YARDS ごとに置いても同じ点になる。\n */\nexport class GridRenderer implements ILayerRenderer<EditorRenderFrame> {\n // 点は数千個あり、ドラッグのたびに組み立て直すと描く時間の大半を占める。\n // 窓と縮尺が同じ間は同じ path なので、前のフレームのものを使い回す。\n private cached: { readonly key: string; readonly dots: Path2D } | undefined;\n\n draw(ctx: CanvasRenderingContext2D, frame: EditorRenderFrame): void {\n const { geometry, metrics } = frame;\n const key = [\n geometry.zone,\n geometry.offsetX,\n geometry.offsetY,\n geometry.scale,\n metrics.gridDotRadius,\n ].join(\":\");\n if (this.cached?.key !== key) {\n this.cached = { key, dots: gridDots(geometry, metrics.gridDotRadius) };\n }\n ctx.fillStyle = frame.theme(\"fieldGrid\");\n ctx.fill(this.cached.dots);\n }\n}\n\n// 点は 1〜2 px なので、円ではなく正方形で描いても見分けが付かず、塗る負荷が小さい。\nfunction gridDots(geometry: FieldGeometry, radius: number): Path2D {\n const dots = new Path2D();\n const size = 2 * radius;\n for (const yard of yardLinesInWindow(geometry.zone, GRID_STEP_YARDS)) {\n // ヤードラインと窓の端の上には置かない。窓の端はどれも 5 ヤードの倍数だが、\n // エンドゾーンの中(0 より手前と 100 より奥)にはヤードラインが無いので、そこには置く。\n const { startYard, endYard } = geometry.yardWindow;\n const onYardLine = yard >= 0 && yard <= 100 && yard % YARD_LINE_INTERVAL_YARDS === 0;\n if (onYardLine || yard === startYard || yard === endYard) {\n continue;\n }\n const y = geometry.yForAbsoluteYard(yard);\n for (let lateral = GRID_STEP_YARDS; lateral < FIELD_WIDTH_YARDS; lateral += GRID_STEP_YARDS) {\n dots.rect(geometry.xForLateralYard(lateral) - radius, y - radius, size, size);\n }\n }\n return dots;\n}\n","import {\n arrowHeadVertices,\n blockCapEndpoints,\n type CanvasPoint,\n DEFAULT_LINE_THICKNESS,\n type FieldMetrics,\n indexPlayersById,\n type LineKind,\n lineAnchorPoints,\n sampleLinePath,\n trimForArrowHead,\n} from \"../../common/index.js\";\nimport type { ThemeTokenName } from \"../theme/tokens.js\";\nimport type { ILayerRenderer, RenderFrame } from \"./layer.js\";\n\nconst LINE_COLOR_TOKENS = {\n route: \"lineRoute\",\n block: \"lineBlock\",\n motion: \"lineMotion\",\n} as const satisfies Record<LineKind, ThemeTokenName>;\n\nexport class LineRenderer implements ILayerRenderer {\n /** 起点の選手が見つからない線は描かない。 */\n draw(ctx: CanvasRenderingContext2D, frame: RenderFrame): void {\n const { geometry, metrics } = frame;\n const { lines, players } = frame.scene;\n if (geometry.scale <= 0 || lines.length === 0) {\n return;\n }\n const byId = indexPlayersById(players);\n\n for (const line of lines) {\n const anchors = lineAnchorPoints(line, byId);\n if (anchors === undefined) {\n continue;\n }\n const path = sampleLinePath(anchors, line.interpolation).map((p) => geometry.toCanvas(p));\n if (path.length < 2) {\n continue;\n }\n\n const color = line.color ?? frame.theme(LINE_COLOR_TOKENS[line.kind]);\n // thickness は既定の太さに対する倍率なので、画面と PNG のどちらの縮尺でも見え方が揃う。\n const width = this.widthFor(line.kind, metrics) * (line.thickness ?? DEFAULT_LINE_THICKNESS);\n const isBlock = line.kind === \"block\";\n\n ctx.save();\n ctx.strokeStyle = color;\n ctx.lineWidth = width;\n ctx.lineJoin = \"round\";\n ctx.lineCap = \"round\";\n if (line.kind === \"motion\") {\n ctx.setLineDash([...metrics.motionDash]);\n }\n\n // 矢印を付ける線は、矢じりの根元で止める。先端まで引くと、線の丸いキャップが細くなる三角の先から\n // はみ出して、先端に点が付いたように見える。block は T 字のバーを当てるので終点まで引く。\n // 矢じりの向きと位置は、止める前の path から求める。\n const strokePath = isBlock ? path : trimForArrowHead(path, metrics.arrowLength);\n ctx.beginPath();\n // 空のパスへの最初の lineTo は moveTo として働く。\n for (const pt of strokePath) {\n ctx.lineTo(pt.x, pt.y);\n }\n ctx.stroke();\n ctx.setLineDash([]);\n\n if (isBlock) {\n this.drawBlockCap(ctx, path, color, metrics, width);\n } else {\n this.drawArrowHead(ctx, path, color, metrics);\n }\n ctx.restore();\n }\n }\n\n private widthFor(kind: LineKind, metrics: FieldMetrics): number {\n return kind === \"block\" ? metrics.blockWidth : metrics.routeWidth;\n }\n\n /** 終点に、進む向きへ向けた三角の矢じりを描く。 */\n private drawArrowHead(\n ctx: CanvasRenderingContext2D,\n path: readonly CanvasPoint[],\n color: string,\n metrics: FieldMetrics,\n ): void {\n const vertices = arrowHeadVertices(path, metrics.arrowLength, metrics.arrowHalfWidth);\n if (vertices === undefined) {\n return;\n }\n const [tip, left, right] = vertices;\n ctx.beginPath();\n ctx.moveTo(tip.x, tip.y);\n ctx.lineTo(left.x, left.y);\n ctx.lineTo(right.x, right.y);\n ctx.closePath();\n ctx.fillStyle = color;\n ctx.fill();\n }\n\n /** 終点に、進む向きと直交する T 字のバーを描く。 */\n private drawBlockCap(\n ctx: CanvasRenderingContext2D,\n path: readonly CanvasPoint[],\n color: string,\n metrics: FieldMetrics,\n width: number,\n ): void {\n const endpoints = blockCapEndpoints(path, metrics.blockCapLength);\n if (endpoints === undefined) {\n return;\n }\n const [from, to] = endpoints;\n ctx.beginPath();\n ctx.strokeStyle = color;\n ctx.lineWidth = width * 1.2;\n ctx.lineCap = \"round\";\n ctx.moveTo(from.x, from.y);\n ctx.lineTo(to.x, to.y);\n ctx.stroke();\n }\n}\n","import {\n type EditorOverlay,\n PLAYER_RADIUS_YARDS,\n WAYPOINT_HANDLE_RADIUS_YARDS,\n} from \"../../common/index.js\";\nimport type { EditorRenderFrame, ILayerRenderer } from \"./layer.js\";\n\n/**\n * 選択の強調と、線の waypoint と終点のハンドルを描く。\n *\n * 選択色は対象の外側に置き、対象には重ねない(選手はマーカーの外周のリング、線はハンドル)。\n * 線の色はパレットで選ばれるので、上から塗ると線の色と混ざって何色の線か読めなくなる。\n */\nexport class OverlayRenderer implements ILayerRenderer<EditorRenderFrame> {\n draw(ctx: CanvasRenderingContext2D, frame: EditorRenderFrame): void {\n const { overlay } = frame;\n switch (overlay.kind) {\n case \"none\":\n return;\n case \"player\":\n drawSelectedPlayer(ctx, frame, overlay.playerId);\n return;\n case \"line\":\n drawLineHandles(ctx, frame, overlay);\n return;\n }\n }\n}\n\nfunction drawSelectedPlayer(\n ctx: CanvasRenderingContext2D,\n frame: EditorRenderFrame,\n playerId: string,\n): void {\n const player = frame.scene.players.find((p) => p.id === playerId);\n if (player === undefined) {\n return;\n }\n const { x, y } = frame.geometry.toCanvas(player.position);\n const r = PLAYER_RADIUS_YARDS * frame.geometry.scale + 4;\n ctx.beginPath();\n ctx.arc(x, y, r, 0, Math.PI * 2);\n ctx.strokeStyle = frame.theme(\"selection\");\n ctx.lineWidth = 2;\n ctx.stroke();\n}\n\nfunction drawLineHandles(\n ctx: CanvasRenderingContext2D,\n frame: EditorRenderFrame,\n handles: Extract<EditorOverlay, { kind: \"line\" }>,\n): void {\n const { geometry } = frame;\n const fill = frame.theme(\"selection\");\n const outline = frame.theme(\"selectionOutline\");\n // アイコンは当たり判定の範囲(WAYPOINT_HANDLE_RADIUS_YARDS)の一部だけを描く。範囲いっぱいに\n // 描くとマーカー並みに大きくなるので小さく出し、掴みやすさは当たり判定の広さに任せる。\n const handleHalf = Math.max(3, 0.45 * WAYPOINT_HANDLE_RADIUS_YARDS * geometry.scale);\n // 現在の path(beginPath 済み)を、ハンドル共通の塗りと縁取りで仕上げる。\n const paintHandle = (): void => {\n ctx.fillStyle = fill;\n ctx.fill();\n ctx.lineWidth = 1.5;\n ctx.strokeStyle = outline;\n ctx.stroke();\n };\n for (const waypoint of handles.waypointHandles) {\n const { x, y } = geometry.toCanvas(waypoint);\n ctx.beginPath();\n ctx.rect(x - handleHalf, y - handleHalf, handleHalf * 2, handleHalf * 2);\n paintHandle();\n }\n // 終点のハンドルは、waypoint の四角と見分けられるよう円で描く。\n const { x, y } = geometry.toCanvas(handles.endpointHandle);\n ctx.beginPath();\n ctx.arc(x, y, handleHalf + 1, 0, Math.PI * 2);\n paintHandle();\n}\n","import {\n DEFAULT_PLAYER_FILL,\n PLAYER_RADIUS_YARDS,\n playerMarkerOutline,\n} from \"../../common/index.js\";\nimport { fieldFont } from \"../theme/field-font.js\";\nimport type { ILayerRenderer, RenderFrame } from \"./layer.js\";\n\nexport class PlayerRenderer implements ILayerRenderer {\n draw(ctx: CanvasRenderingContext2D, frame: RenderFrame): void {\n const { geometry, metrics } = frame;\n const { players } = frame.scene;\n const defaultColor = frame.theme(\"playerColor\");\n const grass = frame.theme(\"fieldGrass\");\n const label = frame.theme(\"playerLabel\");\n // 当たり判定と同じ半径の輪郭を描き、縁取りはその上に乗せる。\n const r = PLAYER_RADIUS_YARDS * geometry.scale;\n if (r <= 0) {\n return;\n }\n const fontPx = metrics.markerLabelFont;\n\n for (const player of players) {\n const center = geometry.toCanvas(player.position);\n const color = player.color ?? defaultColor;\n const isSolid = (player.fill ?? DEFAULT_PLAYER_FILL) === \"solid\";\n\n ctx.beginPath();\n const outline = playerMarkerOutline(player.shape, r);\n if (outline.kind === \"circle\") {\n ctx.arc(center.x, center.y, outline.radius, 0, Math.PI * 2);\n } else {\n const { halfSide } = outline;\n ctx.rect(center.x - halfSide, center.y - halfSide, halfSide * 2, halfSide * 2);\n }\n // 枠線だけの選手も内側を芝の色で塗り、下を通る線を隠す。エンドゾーンに置いた選手も芝の色で塗る。\n // 既定ではエンドゾーンと芝の差がわずかで気にならず、どこにいるかで塗りを変えるほどではない。\n ctx.fillStyle = isSolid ? color : grass;\n ctx.fill();\n ctx.lineWidth = metrics.markerStroke;\n ctx.strokeStyle = color;\n ctx.stroke();\n\n if (player.label !== \"\") {\n ctx.fillStyle = isSolid ? label : color;\n ctx.font = fieldFont(fontPx);\n ctx.textAlign = \"center\";\n // textBaseline を middle にすると、字の em ボックスの中央に揃うので、フォントによって上下にずれる。\n // 測った字面の中央をマーカーの中心に合わせる。\n ctx.textBaseline = \"alphabetic\";\n const tm = ctx.measureText(player.label);\n const labelY = center.y + (tm.actualBoundingBoxAscent - tm.actualBoundingBoxDescent) / 2;\n ctx.fillText(player.label, center.x, labelY);\n }\n }\n }\n}\n","import type { EditorOverlay, FieldGeometry, FieldMetrics, SceneData } from \"../../common/index.js\";\nimport type { ThemeReader } from \"../theme/tokens.js\";\nimport { FieldRenderer } from \"./field-renderer.js\";\nimport { GridRenderer } from \"./grid-renderer.js\";\nimport { LineRenderer } from \"./line-renderer.js\";\nimport { OverlayRenderer } from \"./overlay-renderer.js\";\nimport { PlayerRenderer } from \"./player-renderer.js\";\n\n/** 1 回の描画で、図の層が共有する入力。 */\nexport interface RenderFrame {\n readonly geometry: FieldGeometry;\n readonly metrics: FieldMetrics;\n readonly scene: SceneData;\n readonly theme: ThemeReader;\n}\n\n/**\n * 編集の層だけが受け取る入力。図の層にオーバーレイを渡さないので、\n * PNG に書き出した図に選択の強調やハンドルが紛れ込むことはない。\n */\nexport interface EditorRenderFrame extends RenderFrame {\n readonly overlay: EditorOverlay;\n}\n\n/**\n * 図の 1 層を描く。ctx は DPR の変換を済ませてあるので、CSS px の座標で描いてよい。\n * 層ごとに save と restore で挟むので、層の中で ctx の状態を元に戻さなくてよい。\n */\nexport interface ILayerRenderer<F extends RenderFrame = RenderFrame> {\n // メソッド記法にすると引数が双変で比べられ、編集の層を図の層に入れても型エラーにならない。\n readonly draw: (ctx: CanvasRenderingContext2D, frame: F) => void;\n}\n\n/**\n * 図を描く層(field と play)と、編集中だけ挟む層(backdrop と editor)。描く順は\n * field、backdrop、play、editor。PNG の書き出しと view モードは図の層だけで描くので、\n * 格子、選択の強調、ハンドルは書き出した画像と読み取り専用の画面に入らない。\n */\nexport interface SurfaceLayers {\n /** フィールド。図の層のうち、編集中の backdrop より下に描くもの。 */\n readonly field: readonly ILayerRenderer[];\n /** 編集中だけ、フィールドの上で線と選手の下に敷く層。位置の格子を置く。 */\n readonly backdrop: readonly ILayerRenderer<EditorRenderFrame>[];\n /** 線と選手。 */\n readonly play: readonly ILayerRenderer[];\n /** 編集中だけ、図の上に重ねる層。 */\n readonly editor: readonly ILayerRenderer<EditorRenderFrame>[];\n}\n\n/** 線は選手の下に描く。上に描くと、線の根元が選手のマーカーに重なって見える。 */\nexport function createDefaultLayers(): SurfaceLayers {\n return {\n field: [new FieldRenderer()],\n backdrop: [new GridRenderer()],\n play: [new LineRenderer(), new PlayerRenderer()],\n editor: [new OverlayRenderer()],\n };\n}\n\nexport function renderLayers<F extends RenderFrame>(\n ctx: CanvasRenderingContext2D,\n frame: F,\n layers: readonly ILayerRenderer<F>[],\n): void {\n for (const layer of layers) {\n ctx.save();\n layer.draw(ctx, frame);\n ctx.restore();\n }\n}\n","import {\n computeFieldMetrics,\n Disposable,\n type EditorOverlay,\n FieldGeometry,\n type FieldPosition,\n type ImageExportOptions,\n resolveImageExportSize,\n type SceneData,\n toDisposable,\n} from \"../../common/index.js\";\nimport { loadFieldFont } from \"../theme/field-font-face.js\";\nimport { createThemeReader } from \"../theme/theme-reader.js\";\nimport type { ThemeReader } from \"../theme/tokens.js\";\nimport {\n createDefaultLayers,\n type EditorRenderFrame,\n type RenderFrame,\n renderLayers,\n type SurfaceLayers,\n} from \"./layer.js\";\n\n/**\n * Canvas の大きさ、DPR、再描画の時機を受け持ち、渡された図を層ごとに描く。\n * 何を描くか(プレビューの合成、選択の強調)は、図を渡す側が決める。\n */\nexport class CanvasSurface extends Disposable {\n readonly canvas: HTMLCanvasElement;\n private readonly ctx: CanvasRenderingContext2D;\n private readonly layers: SurfaceLayers;\n private scene: SceneData;\n private overlay: EditorOverlay | undefined;\n private frameRequest: number | undefined;\n private dpr = 0;\n // 破棄するときに、DPR の見張りをまとめて外す。\n private readonly lifetime = new AbortController();\n\n constructor(\n parent: HTMLElement,\n scene: SceneData,\n overlay: EditorOverlay | undefined,\n layers: SurfaceLayers = createDefaultLayers(),\n ) {\n super();\n this.scene = scene;\n this.overlay = overlay;\n this.layers = layers;\n\n this.canvas = document.createElement(\"canvas\");\n this.canvas.className = \"playmaker-canvas\";\n parent.appendChild(this.canvas);\n this._register(toDisposable(() => this.canvas.remove()));\n\n const ctx = this.canvas.getContext(\"2d\");\n if (!ctx) {\n throw new Error(\"Playmaker: 2D canvas context を取得できませんでした。\");\n }\n this.ctx = ctx;\n\n const observer = new ResizeObserver(() => this.resize());\n observer.observe(parent);\n this._register(\n toDisposable(() => {\n observer.disconnect();\n this.lifetime.abort();\n this.cancelFrame();\n }),\n );\n\n // 最初の描画は、observe した直後の ResizeObserver の通知で行う。構築した時点ではホストが\n // ツールバーやパネルをまだ並べておらず、ここで描くとすぐに大きさが変わって描き直しになる。\n this.watchDevicePixelRatio();\n this.redrawWhenFontLoads();\n }\n\n /** テーマ変数は描くたびに読み直すので、ホストが変数を変えたあとに呼べば反映される。 */\n refresh(): void {\n this.invalidate();\n }\n\n /**\n * 図とオーバーレイを差し替える。描くのは次のフレームなので、\n * 同じフレームの中で何度差し替えても描くのは 1 回になる。\n * overlay を省くと、格子と選択の強調のような編集中だけの層を描かない(view モード)。\n */\n setScene(scene: SceneData, overlay: EditorOverlay | undefined): void {\n this.scene = scene;\n this.overlay = overlay;\n this.invalidate();\n }\n\n /** マウスイベントの clientX / clientY を、フィールドのヤードの位置に変える。 */\n clientToYard(clientX: number, clientY: number): FieldPosition {\n const rect = this.canvas.getBoundingClientRect();\n return this.measure().fromCanvas({ x: clientX - rect.left, y: clientY - rect.top });\n }\n\n /**\n * 指定したプレー図を PNG(Blob)として書き出す。図の層だけで描くので、格子、選択の強調、\n * ハンドルは入らない。配色は画面と同じテーマ変数から読む。\n * 同梱フォントを読み込み終えてから描くので、構築の直後でも数字とラベルは同梱フォントになる。\n */\n async exportToPngBlob(data: SceneData, options?: ImageExportOptions): Promise<Blob> {\n const { width, height } = resolveImageExportSize(data.field.zone, options);\n // 配色は呼んだ時点の変数で決める。フォントを待つ間にホストが変数を戻したり、破棄して canvas が\n // host から外れたりしても、書き出す色は変わらない。\n const theme = createThemeReader(this.host);\n await loadFieldFont();\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n throw new Error(\"Playmaker: エクスポート用 2D canvas context を取得できませんでした。\");\n }\n const geometry = new FieldGeometry(width, height, data.field);\n this.paint(ctx, this.frameFor(geometry, data, theme), undefined);\n return new Promise<Blob>((resolve, reject) => {\n canvas.toBlob((blob) => {\n if (blob) {\n resolve(blob);\n } else {\n reject(new Error(\"Playmaker: PNG への変換に失敗しました。\"));\n }\n }, \"image/png\");\n });\n }\n\n /** 初回は代わりのフォントで描いて白い画面を避け、同梱フォントを読み込めたら描き直す。 */\n private redrawWhenFontLoads(): void {\n void loadFieldFont().then(() => {\n if (!this.isDisposed) {\n this.invalidate();\n }\n });\n }\n\n /** コンテナの大きさと DPR に合わせて、描画用のバッファを作り直す。 */\n private resize(): void {\n const dpr = window.devicePixelRatio || 1;\n const { clientWidth, clientHeight } = this.host;\n const width = Math.max(1, Math.round(clientWidth * dpr));\n const height = Math.max(1, Math.round(clientHeight * dpr));\n // 要素の大きさが変わっても、バッファの大きさと DPR が同じなら描いた図がそのまま使える。\n if (width === this.canvas.width && height === this.canvas.height && dpr === this.dpr) {\n return;\n }\n this.dpr = dpr;\n this.canvas.width = width;\n this.canvas.height = height;\n // 以降は CSS px の座標で描く。geometry も CSS px で求める。\n this.ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n // バッファを作り直すと中身が消えるので、次のフレームを待たずに描く。\n this.cancelFrame();\n this.render();\n }\n\n // resolution の media query は、今の DPR と合わなくなったときに 1 回だけ change を出す。\n // ブラウザの拡大率やモニタが変わるたびに、新しい DPR で張り直す。\n private watchDevicePixelRatio(): void {\n const query = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);\n query.addEventListener(\n \"change\",\n () => {\n this.resize();\n this.watchDevicePixelRatio();\n },\n { once: true, signal: this.lifetime.signal },\n );\n }\n\n private invalidate(): void {\n // 最初の大きさが決まる前は描かない。決まったときに resize が描く。\n if (this.frameRequest !== undefined || this.dpr === 0) {\n return;\n }\n this.frameRequest = requestAnimationFrame(() => {\n this.frameRequest = undefined;\n this.render();\n });\n }\n\n private cancelFrame(): void {\n if (this.frameRequest !== undefined) {\n cancelAnimationFrame(this.frameRequest);\n this.frameRequest = undefined;\n }\n }\n\n private render(): void {\n const frame = this.frameFor(this.measure(), this.scene, createThemeReader(this.host));\n this.paint(this.ctx, frame, this.overlay);\n }\n\n /**\n * 層を field、backdrop、play、editor の順に描く。overlay が無いときは編集中だけの層\n * (backdrop と editor)を飛ばすので、view モードと PNG の書き出しは図の層だけになる。\n */\n private paint(\n ctx: CanvasRenderingContext2D,\n frame: RenderFrame,\n overlay: EditorOverlay | undefined,\n ): void {\n const editorFrame: EditorRenderFrame | undefined =\n overlay === undefined ? undefined : { ...frame, overlay };\n renderLayers(ctx, frame, this.layers.field);\n if (editorFrame !== undefined) {\n renderLayers(ctx, editorFrame, this.layers.backdrop);\n }\n renderLayers(ctx, frame, this.layers.play);\n if (editorFrame !== undefined) {\n renderLayers(ctx, editorFrame, this.layers.editor);\n }\n }\n\n private frameFor(geometry: FieldGeometry, scene: SceneData, theme: ThemeReader): RenderFrame {\n return {\n geometry,\n metrics: computeFieldMetrics(geometry.fieldPixelWidth, geometry.scale),\n scene,\n theme,\n };\n }\n\n private measure(): FieldGeometry {\n const { clientWidth, clientHeight } = this.host;\n return new FieldGeometry(clientWidth, clientHeight, this.scene.field);\n }\n\n // 大きさとテーマ変数は、canvas を置いた要素から読む。\n private get host(): HTMLElement {\n return this.canvas.parentElement ?? this.canvas;\n }\n}\n","/**\n * update で container の中身を作り直す。作り直す前に container の中にあったフォーカスが\n * 取り除かれた要素と一緒に消えたら、fallback へ移す。消えたフォーカスは body へ落ち、\n * ショートカットを受けている要素にキーが届かなくなるため。\n */\nexport function replaceKeepingFocus(\n container: HTMLElement,\n fallback: HTMLElement,\n update: () => void,\n): void {\n const doc = container.ownerDocument;\n const hadFocus = container.contains(doc.activeElement);\n update();\n if (hadFocus && !container.contains(doc.activeElement)) {\n fallback.focus({ preventScroll: true });\n }\n}\n","import type { ThemeTokenName } from \"./tokens.js\";\n\nexport interface ColorOption {\n readonly label: string;\n readonly token: ThemeTokenName;\n}\n\n/**\n * 選手と線の色は自由に選ばせず、白い芝の上で見分けやすい 4 色に絞る。\n * 保存する color は、テーマ変数を解決した具体的な色にする。canvas は var() を解釈できないため。\n */\nexport const COLOR_PALETTE: readonly ColorOption[] = [\n { label: \"黒\", token: \"swatch1\" },\n { label: \"赤\", token: \"swatch2\" },\n { label: \"青\", token: \"swatch3\" },\n { label: \"緑\", token: \"swatch4\" },\n];\n","import {\n DEFAULT_LINE_THICKNESS,\n DEFAULT_PLAYER_FILL,\n Disposable,\n type IEditorUi,\n isLineThickness,\n isOneOf,\n LINE_INTERPOLATION_VALUES,\n LINE_KIND_VALUES,\n type Line,\n type LineInterpolation,\n type LineKind,\n PLAYER_FILL_VALUES,\n PLAYER_SHAPE_VALUES,\n type Player,\n type PlayerFill,\n type PlayerShape,\n toDisposable,\n} from \"../../common/index.js\";\nimport { normalizeCssColor } from \"../theme/css-color.js\";\nimport { COLOR_PALETTE } from \"../theme/palette.js\";\nimport { createThemeReader } from \"../theme/theme-reader.js\";\nimport { replaceKeepingFocus } from \"./focus.js\";\n\nconst SHAPE_LABELS = {\n circle: \"丸\",\n square: \"四角\",\n} satisfies Record<PlayerShape, string>;\n\nconst FILL_LABELS = {\n outline: \"枠線\",\n solid: \"塗りつぶし\",\n} satisfies Record<PlayerFill, string>;\n\nconst KIND_LABELS = {\n route: \"ルート\",\n block: \"ブロック\",\n motion: \"モーション\",\n} satisfies Record<LineKind, string>;\n\nconst INTERPOLATION_LABELS = {\n straight: \"直線\",\n bezier: \"曲線\",\n} satisfies Record<LineInterpolation, string>;\n\n/** 色を選ぶ丸いボタン 1 つ分。枠線のスウォッチは縁だけを色にし、塗りつぶしは中まで塗る。 */\ninterface SwatchChoice {\n readonly name: string;\n readonly color: string;\n readonly fill: PlayerFill;\n}\n\nfunction createSwatch({ name, color, fill }: SwatchChoice): HTMLButtonElement {\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = \"playmaker-panel__swatch\";\n btn.title = name;\n btn.setAttribute(\"aria-label\", name);\n if (fill === \"outline\") {\n btn.classList.add(\"playmaker-panel__swatch--outline\");\n btn.style.borderColor = color;\n } else {\n btn.style.backgroundColor = color;\n }\n return btn;\n}\n\n/** 選択中の要素。選択は 1 つなので、選手と線が同時に来ることは型で起こらない。 */\ntype SelectedItem =\n | { readonly kind: \"player\"; readonly player: Player }\n | { readonly kind: \"line\"; readonly line: Line }\n | { readonly kind: \"none\" };\n\n/** 入力に渡す値と、値が変わったときに呼ぶ関数。onReset を渡すと、既定に戻すボタンが付く。 */\ninterface InputOptions<T> {\n readonly label: string;\n readonly onChange: (value: T) => void;\n readonly onReset?: () => void;\n}\n\n/** 表示中の入力と、選択中の要素の値をその入力に書き込む関数。 */\ntype ShownFields =\n | { readonly kind: \"player\"; readonly id: string; readonly update: (player: Player) => void }\n | { readonly kind: \"line\"; readonly id: string; readonly update: (line: Line) => void }\n | { readonly kind: \"hint\" };\n\nexport class PropertyPanel extends Disposable {\n readonly element: HTMLElement;\n private readonly controller: IEditorUi;\n private readonly focusFallback: HTMLElement;\n // 選択中の要素が同じあいだは入力を作り直さず、値だけを書き込む。作り直すと、\n // 値を確定して Tab で次の入力へ移った直後に、移った先の入力ごと消えてフォーカスが外れる。\n private shown: ShownFields | undefined;\n\n /**\n * @param focusFallback 選択が変わって入力を作り直したときに、消えた入力にあったフォーカスを移す先。\n */\n constructor(parent: HTMLElement, controller: IEditorUi, focusFallback: HTMLElement) {\n super();\n this.controller = controller;\n this.focusFallback = focusFallback;\n this.element = document.createElement(\"div\");\n this.element.className = \"playmaker-panel\";\n\n parent.appendChild(this.element);\n this._register(toDisposable(() => this.element.remove()));\n this._register(controller.onDidChangeViewState(() => this.sync()));\n this.sync();\n }\n\n /** 色の既定値とスウォッチはテーマ変数から読むので、ホストが変数を変えたあとに呼べば反映される。 */\n refresh(): void {\n this.shown = undefined;\n this.sync();\n }\n\n private sync(): void {\n const selected = this.selectedItem();\n const shown = this.shown;\n if (selected.kind === \"player\" && shown?.kind === \"player\" && shown.id === selected.player.id) {\n shown.update(selected.player);\n return;\n }\n if (selected.kind === \"line\" && shown?.kind === \"line\" && shown.id === selected.line.id) {\n shown.update(selected.line);\n return;\n }\n if (selected.kind === \"none\" && shown?.kind === \"hint\") {\n return;\n }\n replaceKeepingFocus(this.element, this.focusFallback, () => {\n this.shown = this.rebuild(selected);\n });\n }\n\n private selectedItem(): SelectedItem {\n const player = this.controller.getSelectedPlayer();\n if (player !== undefined) {\n return { kind: \"player\", player };\n }\n const line = this.controller.getSelectedLine();\n if (line !== undefined) {\n return { kind: \"line\", line };\n }\n return { kind: \"none\" };\n }\n\n private rebuild(selected: SelectedItem): ShownFields {\n this.element.replaceChildren();\n switch (selected.kind) {\n case \"player\": {\n const update = this.addPlayerFields();\n update(selected.player);\n return { kind: \"player\", id: selected.player.id, update };\n }\n case \"line\": {\n const update = this.addLineFields();\n update(selected.line);\n return { kind: \"line\", id: selected.line.id, update };\n }\n case \"none\": {\n const hint = document.createElement(\"p\");\n hint.className = \"playmaker-panel__hint\";\n hint.textContent = \"対象を選択するとプロパティを編集できます\";\n this.element.appendChild(hint);\n return { kind: \"hint\" };\n }\n }\n }\n\n private addPlayerFields(): (player: Player) => void {\n const controller = this.controller;\n this.addTitle(\"選手\");\n const setLabel = this.addText({\n label: \"ラベル\",\n onChange: (v) => controller.updateSelectedPlayer({ label: v }),\n });\n const setShape = this.addSelect(\"形状\", PLAYER_SHAPE_VALUES, SHAPE_LABELS, (v) =>\n controller.updateSelectedPlayer({ shape: v }),\n );\n // 4 色それぞれの枠線と塗りつぶしを並べた 8 つのスウォッチ。上の段が枠線、下の段が塗りつぶし。\n // 「塗りつぶしか枠線か」を別の選択肢にせず、色と一緒に 1 回で選べるようにする。\n const read = createThemeReader(this.element);\n const choices = PLAYER_FILL_VALUES.flatMap((fill) =>\n COLOR_PALETTE.map((opt) => ({\n name: `${opt.label}の${FILL_LABELS[fill]}`,\n color: read(opt.token),\n fill,\n })),\n );\n const setPressed = this.addSwatches(\"色\", choices, {\n onChange: ({ color, fill }) => controller.updateSelectedPlayer({ color, fill }),\n onReset: () => controller.updateSelectedPlayer({ color: null, fill: null }),\n });\n // 色を省いた選手は、テーマの既定色の枠線のスウォッチが押された状態になる。\n const defaultColor = read(\"playerColor\");\n return (player) => {\n setLabel(player.label);\n setShape(player.shape);\n const color = player.color === undefined ? defaultColor : normalizeCssColor(player.color);\n const fill = player.fill ?? DEFAULT_PLAYER_FILL;\n setPressed((choice) => choice.color === color && choice.fill === fill);\n };\n }\n\n private addLineFields(): (line: Line) => void {\n const controller = this.controller;\n this.addTitle(\"線\");\n const setKind = this.addSelect(\"種別\", LINE_KIND_VALUES, KIND_LABELS, (v) =>\n controller.updateSelectedLine({ kind: v }),\n );\n const setInterpolation = this.addSelect(\n \"補間\",\n LINE_INTERPOLATION_VALUES,\n INTERPOLATION_LABELS,\n (v) => controller.updateSelectedLine({ interpolation: v }),\n );\n const read = createThemeReader(this.element);\n const choices = COLOR_PALETTE.map((opt) => ({\n name: opt.label,\n color: read(opt.token),\n fill: \"solid\" as const,\n }));\n const setPressed = this.addSwatches(\"色\", choices, {\n onChange: ({ color }) => controller.updateSelectedLine({ color }),\n onReset: () => controller.updateSelectedLine({ color: null }),\n });\n const setThickness = this.addNumber({\n label: \"太さ\",\n onChange: (v) => controller.updateSelectedLine({ thickness: v }),\n onReset: () => controller.updateSelectedLine({ thickness: null }),\n });\n return (line) => {\n setKind(line.kind);\n setInterpolation(line.interpolation);\n // 保存済みの色は外から渡されたデータのこともあるので、テーマの色と同じ書き方にそろえて比べる。\n const color = line.color === undefined ? undefined : normalizeCssColor(line.color);\n setPressed((choice) => choice.color === color);\n setThickness(line.thickness ?? DEFAULT_LINE_THICKNESS);\n };\n }\n\n private addTitle(text: string): void {\n const h = document.createElement(\"h2\");\n h.className = \"playmaker-panel__title\";\n h.textContent = text;\n this.element.appendChild(h);\n }\n\n private addRow(labelText: string, control: HTMLElement, onReset?: () => void): void {\n const label = document.createElement(\"label\");\n label.className = \"playmaker-panel__label\";\n label.append(this.createRowText(labelText), control);\n this.appendRow(labelText, [label], onReset);\n }\n\n /**\n * ボタンを並べた行。label で包むと、文字を押したときに先頭のボタンが押されるので、\n * ボタンの組に名前を付ける。\n */\n private addGroupRow(\n labelText: string,\n group: HTMLElement,\n onReset: (() => void) | undefined,\n ): void {\n group.setAttribute(\"role\", \"group\");\n group.setAttribute(\"aria-label\", labelText);\n this.appendRow(labelText, [this.createRowText(labelText), group], onReset);\n }\n\n private createRowText(text: string): HTMLElement {\n const span = document.createElement(\"span\");\n span.className = \"playmaker-panel__row-text\";\n span.textContent = text;\n return span;\n }\n\n /**\n * onReset を渡すと、値を消してテーマの既定に戻すボタンを行の末尾に置く。\n * 既定のときもボタンは押せるままにする。押した直後に無効にすると、フォーカスが body へ落ちる。\n */\n private appendRow(\n labelText: string,\n children: readonly HTMLElement[],\n onReset: (() => void) | undefined,\n ): void {\n const row = document.createElement(\"div\");\n row.className = \"playmaker-panel__row\";\n row.append(...children);\n if (onReset !== undefined) {\n const reset = document.createElement(\"button\");\n reset.type = \"button\";\n reset.className = \"playmaker-panel__reset\";\n reset.textContent = \"既定\";\n reset.setAttribute(\"aria-label\", `${labelText}を既定に戻す`);\n reset.addEventListener(\"click\", onReset);\n row.appendChild(reset);\n }\n this.element.appendChild(row);\n }\n\n private addText({ label, onChange, onReset }: InputOptions<string>): (value: string) => void {\n const input = document.createElement(\"input\");\n input.type = \"text\";\n input.addEventListener(\"change\", () => onChange(input.value));\n this.addRow(label, input, onReset);\n return (value) => {\n input.value = value;\n };\n }\n\n private addNumber({ label, onChange, onReset }: InputOptions<number>): (value: number) => void {\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.min = \"0.25\";\n input.step = \"0.25\";\n input.addEventListener(\"change\", () => {\n const n = Number(input.value);\n if (isLineThickness(n)) {\n onChange(n);\n }\n });\n this.addRow(label, input, onReset);\n return (value) => {\n input.value = String(value);\n };\n }\n\n /**\n * 色のスウォッチを並べた行。選んだ choice を onChange に渡す。\n * 返す関数に、押された状態にする choice の判定を渡して、表示を選択中の要素に合わせる。\n */\n private addSwatches<T extends SwatchChoice>(\n label: string,\n choices: readonly T[],\n { onChange, onReset }: Omit<InputOptions<T>, \"label\">,\n ): (isPressed: (choice: T) => boolean) => void {\n const group = document.createElement(\"div\");\n group.className = \"playmaker-panel__swatches\";\n if (choices.some((choice) => choice.fill === \"outline\")) {\n group.classList.add(\"playmaker-panel__swatches--rows\");\n }\n const swatches = choices.map((choice) => {\n const btn = createSwatch(choice);\n btn.addEventListener(\"click\", () => onChange(choice));\n group.appendChild(btn);\n return { btn, choice };\n });\n this.addGroupRow(label, group, onReset);\n return (isPressed) => {\n for (const { btn, choice } of swatches) {\n btn.setAttribute(\"aria-pressed\", String(isPressed(choice)));\n }\n };\n }\n\n private addSelect<T extends string>(\n labelText: string,\n options: readonly T[],\n optionLabels: Readonly<Record<T, string>>,\n onChange: (v: T) => void,\n ): (value: T) => void {\n const select = document.createElement(\"select\");\n for (const opt of options) {\n const o = document.createElement(\"option\");\n o.value = opt;\n o.textContent = optionLabels[opt];\n select.appendChild(o);\n }\n select.addEventListener(\"change\", () => {\n if (isOneOf(select.value, options)) {\n onChange(select.value);\n }\n });\n this.addRow(labelText, select);\n return (value) => {\n select.value = value;\n };\n }\n}\n","import {\n canLoadFormation,\n Disposable,\n EDITOR_TOOL_VALUES,\n type EditorTool,\n type EditorViewState,\n FIELD_ZONE_LABELS,\n FIELD_ZONE_VALUES,\n type FieldZone,\n FORMATION_PRESETS,\n getFormationPreset,\n type IEditorUi,\n isToolAvailable,\n MAX_LINES,\n MAX_PLAYERS,\n TEAM_SIDE_VALUES,\n type TeamSide,\n toDisposable,\n} from \"../../common/index.js\";\n\nconst TOOL_LABELS = {\n select: \"選択\",\n \"add-player\": \"選手を追加\",\n \"draw-line\": \"線を描く\",\n} satisfies Record<EditorTool, string>;\n\n// 件数の上限に達して押せない部品に、理由として出す文。\nconst FORMATION_LIMIT_REASON = `置くと ${MAX_PLAYERS} 人を超えるフォーメーションは読み込めません`;\nconst TOOL_UNAVAILABLE_REASONS: Readonly<Partial<Record<EditorTool, string>>> = {\n \"add-player\": `選手は ${MAX_PLAYERS} 人までです`,\n \"draw-line\": `線は ${MAX_LINES} 本までです`,\n};\n\nconst SIDE_LABELS = {\n offense: \"オフェンス\",\n defense: \"ディフェンス\",\n} satisfies Record<TeamSide, string>;\n\n/**\n * disabled ではなく aria-disabled で無効を示す。disabled にすると、押した直後に\n * 無効になったボタンからフォーカスが body へ落ち、\n * 編集 UI の中で受けているショートカットが効かなくなる。\n */\nfunction setEnabled(button: HTMLButtonElement, enabled: boolean, reason?: string): void {\n button.setAttribute(\"aria-disabled\", String(!enabled));\n setTitle(button, enabled ? undefined : reason);\n}\n\nfunction setTitle(element: HTMLElement, title: string | undefined): void {\n if (title === undefined) {\n element.removeAttribute(\"title\");\n } else {\n element.title = title;\n }\n}\n\nexport class Toolbar extends Disposable {\n readonly element: HTMLElement;\n private readonly toolButtons = new Map<EditorTool, HTMLButtonElement>();\n private readonly zoneButtons = new Map<FieldZone, HTMLButtonElement>();\n private readonly undoButton: HTMLButtonElement;\n private readonly redoButton: HTMLButtonElement;\n private readonly deleteButton: HTMLButtonElement;\n private readonly commitButton: HTMLButtonElement;\n private readonly cancelButton: HTMLButtonElement;\n private readonly formationPicker: HTMLSelectElement;\n\n constructor(parent: HTMLElement, controller: IEditorUi) {\n super();\n this.element = document.createElement(\"div\");\n this.element.className = \"playmaker-toolbar\";\n\n for (const tool of EDITOR_TOOL_VALUES) {\n const btn = this.addButton(TOOL_LABELS[tool], () => controller.setTool(tool));\n this.toolButtons.set(tool, btn);\n }\n this.addSeparator();\n this.undoButton = this.addButton(\"元に戻す\", () => controller.undo());\n this.redoButton = this.addButton(\"やり直す\", () => controller.redo());\n this.deleteButton = this.addButton(\"削除\", () => controller.deleteSelection());\n this.addSeparator();\n for (const zone of FIELD_ZONE_VALUES) {\n const btn = this.addButton(FIELD_ZONE_LABELS[zone], () => controller.setFieldZone(zone));\n this.zoneButtons.set(zone, btn);\n }\n this.addSeparator();\n this.formationPicker = this.addFormationPicker(controller);\n this.addSeparator();\n this.commitButton = this.addButton(\"線を確定\", () => controller.commitLine());\n this.cancelButton = this.addButton(\"取消\", () => controller.cancelInteraction());\n\n parent.appendChild(this.element);\n this._register(toDisposable(() => this.element.remove()));\n this._register(controller.onDidChangeViewState(() => this.sync(controller)));\n this.sync(controller);\n }\n\n private addButton(label: string, onClick: () => void): HTMLButtonElement {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.textContent = label;\n button.className = \"playmaker-toolbar__button\";\n button.addEventListener(\"click\", () => {\n if (button.getAttribute(\"aria-disabled\") !== \"true\") {\n onClick();\n }\n });\n this.element.appendChild(button);\n return button;\n }\n\n private addSeparator(): void {\n const sep = document.createElement(\"span\");\n sep.className = \"playmaker-toolbar__sep\";\n sep.setAttribute(\"aria-hidden\", \"true\");\n this.element.appendChild(sep);\n }\n\n /**\n * 選んだあとはプレースホルダへ戻す。選んだ隊形を表示したままにすると、\n * 同じ隊形をもう一度選んでも change が起きず、続けて重ねられない。\n */\n private addFormationPicker(controller: IEditorUi): HTMLSelectElement {\n const select = document.createElement(\"select\");\n select.className = \"playmaker-toolbar__select\";\n select.setAttribute(\"aria-label\", \"フォーメーション\");\n\n const placeholder = document.createElement(\"option\");\n placeholder.value = \"\";\n placeholder.textContent = \"フォーメーション読込…\";\n select.appendChild(placeholder);\n\n for (const side of TEAM_SIDE_VALUES) {\n const group = document.createElement(\"optgroup\");\n group.label = SIDE_LABELS[side];\n for (const formation of FORMATION_PRESETS.filter((f) => f.side === side)) {\n const option = document.createElement(\"option\");\n option.value = formation.id;\n option.textContent = formation.name;\n group.appendChild(option);\n }\n select.appendChild(group);\n }\n\n select.addEventListener(\"change\", () => {\n const preset = getFormationPreset(select.value);\n if (preset !== undefined) {\n controller.loadFormation(preset);\n }\n select.value = \"\";\n });\n\n this.element.appendChild(select);\n return select;\n }\n\n private sync(controller: IEditorUi): void {\n const state = controller.getViewState();\n for (const [tool, btn] of this.toolButtons) {\n btn.setAttribute(\"aria-pressed\", String(tool === state.tool));\n setEnabled(btn, isToolAvailable(tool, state), TOOL_UNAVAILABLE_REASONS[tool]);\n }\n for (const [zone, btn] of this.zoneButtons) {\n btn.setAttribute(\"aria-pressed\", String(zone === state.fieldZone));\n }\n setEnabled(this.undoButton, state.canUndo);\n setEnabled(this.redoButton, state.canRedo);\n setEnabled(this.deleteButton, state.selection.kind !== \"none\");\n setEnabled(this.commitButton, state.isDrawing);\n setEnabled(this.cancelButton, state.isDrawing);\n this.syncFormationPicker(state);\n }\n\n private syncFormationPicker(state: EditorViewState): void {\n let isAnyUnavailable = false;\n for (const option of this.formationPicker.options) {\n const formation = getFormationPreset(option.value);\n option.disabled = formation !== undefined && !canLoadFormation(formation, state);\n isAnyUnavailable ||= option.disabled;\n }\n setTitle(this.formationPicker, isAnyUnavailable ? FORMATION_LIMIT_REASON : undefined);\n }\n}\n","import {\n CanvasSurface,\n PointerInput,\n PropertyPanel,\n replaceKeepingFocus,\n Toolbar,\n} from \"./browser/index.js\";\nimport {\n DisposableStore,\n type EditorOverlay,\n type Event,\n type FieldZone,\n type Formation,\n type IDisposable,\n type ImageExportOptions,\n type PlayData,\n type PlayDataInput,\n PlaySession,\n toDisposable,\n} from \"./common/index.js\";\nimport \"./styles.css\";\n\nexport type {\n Event,\n FieldPosition,\n FieldState,\n FieldZone,\n Formation,\n FormationPlayer,\n IDisposable,\n ImageExportOptions,\n Line,\n LineInterpolation,\n LineKind,\n PlayCategory,\n PlayData,\n PlayDataInput,\n Player,\n PlayerFill,\n PlayerShape,\n PlayPreset,\n TeamSide,\n} from \"./common/index.js\";\nexport {\n CURRENT_PLAY_DATA_VERSION,\n FIELD_WIDTH_YARDS,\n FIELD_ZONE_LABELS,\n FIELD_ZONE_VALUES,\n FORMATION_PRESETS,\n GRID_STEP_YARDS,\n getFormationPreset,\n getPlayPreset,\n migratePlayData,\n PLAY_PRESETS,\n} from \"./common/index.js\";\n\nexport type PlaymakerMode = \"view\" | \"edit\";\n\n// バンドラは process.env.NODE_ENV を文字列に置き換える。置き換えずにブラウザで読み込むと\n// process が無く ReferenceError になるので、そのときは本番とみなす。\ndeclare const process: { readonly env: { readonly NODE_ENV?: string } };\n\nfunction isDevelopment(): boolean {\n try {\n return process.env.NODE_ENV !== \"production\";\n } catch {\n return false;\n }\n}\n\nexport interface PlaymakerOptions {\n /**\n * 最初のモード。既定は \"edit\"。\"view\" は編集 UI を置かず、ポインタとキーの操作も受けない。\n * あとから `setMode` で切り替えられる。\n */\n mode?: PlaymakerMode | undefined;\n /** 最初に表示する図。`restorePlayData` と同じく、形を確かめずに受け取って今のスキーマへ寄せる。 */\n initialData?: unknown;\n /** 構築時に `onDidChange` へ登録するリスナ。解除するには Playmaker を dispose する。 */\n onChange?: ((data: PlayData) => void) | undefined;\n}\n\n/**\n * container の中にプレー図を描き、edit モードでは編集 UI も置く。\n * dispose すると、置いた要素をすべて取り除く。\n * dispose したあとの変更は例外を投げずに何もせず、開発時だけ console.warn で知らせる。\n * getPlayData と fieldZone は、dispose した時点の図を返す。\n */\nexport class Playmaker implements IDisposable {\n /**\n * 編集コマンドと Undo / Redo の確定ごとに 1 回、最新の図の深いコピーを渡す。\n * `version` は常に現行なので、受け取った値をそのまま永続化できる。\n * 構築時、`setPlayData` と `restorePlayData` での読み込み、PNG の書き出しでは発火しない\n * (読み込みは編集ではないため)。\n * コピーはリスナごとに作るので、受け手が書き換えても、この図とほかのリスナには波及しない。\n * 返り値を dispose すると購読をやめる。\n */\n readonly onDidChange: Event<PlayData>;\n private readonly store = new DisposableStore();\n private readonly root: HTMLElement;\n private readonly surface: CanvasSurface;\n private readonly session: PlaySession;\n private currentMode: PlaymakerMode;\n private panel: PropertyPanel | undefined;\n // 今の controller に付けた UI と入力。controller かモードが変わると付け直す。\n private ui: DisposableStore;\n\n constructor(container: HTMLElement, options: PlaymakerOptions = {}) {\n this.currentMode = options.mode ?? \"edit\";\n this.session = this.store.add(new PlaySession(options.initialData));\n this.onDidChange = this.session.onDidChange;\n if (options.onChange !== undefined) {\n this.store.add(this.session.onDidChange(options.onChange));\n }\n\n this.root = document.createElement(\"div\");\n this.root.className = \"playmaker-root\";\n this.root.dataset.mode = this.currentMode;\n container.appendChild(this.root);\n this.store.add(toDisposable(() => this.root.remove()));\n\n const stage = document.createElement(\"div\");\n stage.className = \"playmaker-stage\";\n this.root.appendChild(stage);\n const { scene, overlay } = this.session.controller.getFrame();\n this.surface = this.store.add(new CanvasSurface(stage, scene, this.surfaceOverlay(overlay)));\n // CanvasSurface は最初の大きさが決まったときに今の図を描くので、ここでは描かない。\n this.ui = this.createUi();\n this.store.add(this.session.onDidChangeScene(() => this.draw()));\n // 読み直したあとは session が描き直しを通知するので、ここでは UI だけを付け直す。\n this.store.add(this.session.onDidReset(() => this.replaceUi()));\n }\n\n get mode(): PlaymakerMode {\n return this.currentMode;\n }\n\n /**\n * モードを切り替える。図と Undo の履歴は残し、編集 UI とポインタ、キーの入力だけを付け外しする。\n * view にすると、ドラッグや作図の途中の操作は取り消し、選択の強調も描かない。\n */\n setMode(mode: PlaymakerMode): void {\n if (this.store.isDisposed) {\n this.warnIgnoredAfterDispose(\"setMode\");\n return;\n }\n if (mode === this.currentMode) {\n return;\n }\n this.currentMode = mode;\n this.root.dataset.mode = mode;\n if (mode === \"view\") {\n this.session.controller.cancelInteraction();\n }\n this.replaceUi();\n this.draw();\n }\n\n get fieldZone(): FieldZone {\n return this.session.fieldZone;\n }\n\n /**\n * LOS をそのゾーンの既定の位置へ移し、選手と線も一緒に動かす。\n * 編集なので Undo と onDidChange の対象になる。view モードでも呼べる。\n */\n setFieldZone(zone: FieldZone): void {\n if (this.store.isDisposed) {\n this.warnIgnoredAfterDispose(\"setFieldZone\");\n return;\n }\n this.session.setFieldZone(zone);\n }\n\n /**\n * 隊形の選手を今の図に追記し、置いたら true を返す。攻守の隊形を順に重ねられる。\n * 選手の位置は LOS からの相対(`lateralYard` と `downfieldYard`)で書く。\n * 外から渡した隊形は正規化してから読むので、位置の読めない選手は捨てる。\n * 置ける選手が 1 人もいないときと、置くと選手が 64 人を超えるときは、1 人も置かずに false を返す。\n * 編集なので Undo と onDidChange の対象になる。view モードでも呼べる。\n */\n loadFormation(formation: Formation): boolean {\n if (this.store.isDisposed) {\n this.warnIgnoredAfterDispose(\"loadFormation\");\n return false;\n }\n return this.session.loadFormation(formation);\n }\n\n /**\n * 型付きで組み立てた図を読み込み、今の図と置き換える。`getPlayData` の戻り値や、\n * プリセットの `data` もそのまま渡せる。\n * 型に合っていても、重複した id と件数の上限を超えた分は `restorePlayData` と同じく正規化する。\n * 読み込みは編集ではないので、Undo の履歴は消え、onDidChange は発火しない。\n */\n setPlayData(data: PlayDataInput): void {\n if (this.store.isDisposed) {\n this.warnIgnoredAfterDispose(\"setPlayData\");\n return;\n }\n this.session.setPlayData(data);\n }\n\n /**\n * 永続化しておいた値を、形を確かめずに受け取って読み込み、今の図と置き換える。\n * 旧版、版の無いもの、未来版、壊れたデータも、例外を投げずに今のスキーマへ寄せ、読めない要素だけを捨てる。\n * 選手 64 人、線 128 本、線 1 本あたり waypoint 32 個を超える分は、先頭から上限までを残して捨てる。\n * 読み込みは編集ではないので、Undo の履歴は消え、onDidChange は発火しない。\n */\n restorePlayData(raw: unknown): void {\n if (this.store.isDisposed) {\n this.warnIgnoredAfterDispose(\"restorePlayData\");\n return;\n }\n this.session.setPlayData(raw);\n }\n\n /**\n * 今の図の深いコピー。`version` は常に現行で、そのまま JSON にして永続化できる。\n * `restorePlayData` か `initialData` に戻すと、同じ図になる。\n * 編集でも件数の上限は超えられず、上限に達すると選手の追加、フォーメーションの読み込み、作図は何もしない。\n */\n getPlayData(): PlayData {\n return this.session.getPlayData();\n }\n\n /**\n * 呼んだ時点で確定しているプレー図を PNG 画像(Blob)として書き出す。\n * 選択の強調、waypoint のハンドル、ドラッグや作図の途中の形、ツールバーとパネルは入らない。\n * view と edit のどちらのモードでも使える。\n * 同梱フォントを読み込み終えてから描くので、構築の直後に呼んでもヤードの数字と選手のラベルは同梱フォントになる。\n * フォントを読み込めなかったときは代わりのフォントで描く。\n * canvas を確保できないとき、PNG に変換できないとき、dispose したあとは、例外を投げずに reject する。\n */\n exportToPng(options?: ImageExportOptions): Promise<Blob> {\n if (this.store.isDisposed) {\n return Promise.reject(new Error(\"Playmaker: dispose したあとは PNG を書き出せません。\"));\n }\n return this.surface.exportToPngBlob(this.session.getSnapshot(), options);\n }\n\n /**\n * 図とパネルの色を、テーマの CSS 変数から読み直す。CSS 変数を変えてもブラウザは知らせてこないので、\n * ホストが祖先の要素で変数を変えたあとに呼ぶ。\n */\n refresh(): void {\n if (this.store.isDisposed) {\n this.warnIgnoredAfterDispose(\"refresh\");\n return;\n }\n this.surface.refresh();\n this.panel?.refresh();\n }\n\n dispose(): void {\n this.ui.dispose();\n this.store.dispose();\n }\n\n /** dispose したあとの呼び出しは何もしないので、開発時だけそのことを知らせる。 */\n private warnIgnoredAfterDispose(method: string): void {\n if (isDevelopment()) {\n console.warn(`Playmaker: dispose したあとに ${method} を呼んだので、何もしません。`);\n }\n }\n\n private replaceUi(): void {\n // view では canvas がフォーカスを受けないので、消えたフォーカスは body に落ちたままになる。\n replaceKeepingFocus(this.root, this.surface.canvas, () => {\n this.ui.dispose();\n this.ui = this.createUi();\n });\n }\n\n private createUi(): DisposableStore {\n const ui = new DisposableStore();\n const controller = this.session.controller;\n this.panel = undefined;\n if (this.currentMode === \"edit\") {\n ui.add(new Toolbar(this.root, controller));\n this.panel = ui.add(new PropertyPanel(this.root, controller, this.surface.canvas));\n ui.add(new PointerInput(this.root, this.surface, controller));\n }\n return ui;\n }\n\n private draw(): void {\n const { scene, overlay } = this.session.controller.getFrame();\n this.surface.setScene(scene, this.surfaceOverlay(overlay));\n }\n\n /** view モードでは undefined を返し、格子や選択の強調のような編集中だけの層を描かせない。 */\n private surfaceOverlay(overlay: EditorOverlay): EditorOverlay | undefined {\n return this.currentMode === \"edit\" ? overlay : undefined;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"playmaker.js","names":["fontUrl","ja","maxPlayers","maxLines","color","fill","field","toDisposable","fn","DisposableStore","item","Disposable","GRID_STEP_YARDS","snapYard","yard","snapToGrid","position","isRecord","value","isFiniteNumber","isNonEmptyString","isOneOf","values","candidate","parseBoundedArray","raw","parsed","end","max","index","parse","PLAYER_SHAPE_VALUES","DEFAULT_PLAYER_SHAPE","PLAYER_FILL_VALUES","PLAYER_RADIUS_YARDS","isPlayerShape","isPlayerFill","parseFieldPosition","lateralYard","downfieldYard","normalizePlayer","normalizePlayers","clonePlayer","player","MIN_LINE_PX","YARD_LINE_WIDTH_PER_W","HASH_TICK_LENGTH_PER_U","NUMBER_HEIGHT_PER_U","NUMBER_MIN_PX","MIDFIELD_LINE_WIDTH_PER_YARD_LINE","PYLON_SIZE_PER_U","PYLON_MIN_PX","GOALPOST_CROSSBAR_HALF_PER_U","GOALPOST_UPRIGHT_LENGTH_PER_U","GOALPOST_STROKE_PER_D","GOALPOST_STROKE_MIN_PX","MARKER_LABEL_PER_D","MARKER_LABEL_MIN_PX","ROUTE_WIDTH_PER_D","ROUTE_WIDTH_MIN_PX","BLOCK_CAP_LENGTH_PER_D","ARROW_LENGTH_PER_D","ARROW_HALF_WIDTH_PER_D","MOTION_DASH_ON_PER_U","MOTION_DASH_OFF_PER_U","GRID_DOT_RADIUS_PER_U","GRID_DOT_RADIUS_MIN_PX","computeFieldMetrics","w","fieldPixelWidth","u","unitPx","yardLineWidth","d","routeWidth","EDITOR_TOOL_VALUES","NO_SELECTION","isSameSelection","a","b","isToolAvailable","tool","state","canLoadFormation","formation","Emitter","onListenerError","listener","err","CommandService","model","history","changed","command","UndoRedoService","revert","reapply","from","run","to","TEAM_SIDE_VALUES","isTeamSide","deepFreeze","child","toFormationPlayer","_id","template","normalizeFormation","players","instantiateFormation","taken","takenIds","id","ids","IdFactory","prefix","LINE_KIND_VALUES","LINE_INTERPOLATION_VALUES","DEFAULT_LINE_KIND","DEFAULT_LINE_INTERPOLATION","isLineKind","isLineInterpolation","isLineThickness","normalizeLine","validPlayerIds","normalizeLines","entry","cloneLine","line","p","lineAnchorPoints","start","playersById","indexPlayersById","map","FIELD_ZONE_VALUES","DEFAULT_FIELD_ZONE","FIELD_ZONE_LABELS","LOS_YARD_BY_ZONE","CURRENT_PLAY_DATA_VERSION","isFieldZone","fieldStateForZone","zone","clonePlayData","data","resolvePlayData","source","makeIdsUnique","items","seen","n","toV2Position","rest","absoluteYard","losYard","toV2Player","toV2Line","migrateV1ToV2","l","PLAY_DATA_MIGRATIONS","readDeclaredVersion","applyPlayDataMigrations","acc","step","steps","declaredVersion","migratePlayData","insertAt","what","assertNewId","message","assertWithinLimit","count","limit","assertWithinLimits","PlayModel","initialData","target","removedLines","lines","i","removal","remaining","removals","prev","next","requireApplied","SetFieldZoneCommand","LoadFormationCommand","asRecord","applyPatch","current","key","patch","patchChangesAnything","applyLinePatch","AddLineCommand","RemoveLineCommand","lineId","UpdateLineCommand","applyPlayerPatch","AddPlayerCommand","RemovePlayerCommand","playerId","UpdatePlayerCommand","FIELD_WIDTH_YARDS","HASH_CENTER_OFFSET_YARDS_BY_LEAGUE","DEFAULT_FIELD_LEAGUE","fieldZoneWindow","zoneWindowLength","endYard","startYard","fieldWindowAspect","clampToZoneWindow","displayYardNumber","yardLinesInWindow","stepYards","first","y","FieldGeometry","viewportWidth","viewportHeight","windowLength","h","x","point","segments","previous","points","polylineLength","total","path","trimPolylineEnd","distance","maxFraction","segLen","t","cubicBezierPoint","p0","c1","c2","p1","catmullRomBezierControls","p2","p3","dedupeConsecutive","out","last","sampleLinePath","pts","interpolation","samplesPerSegment","result","s","hitTestPlayer","radiusYards","r2","dx","dy","LINE_HIT_TOLERANCE_YARDS","distanceToSegment","abx","aby","apx","apy","lenSq","distanceToPolyline","only","pointDistance","min","hitTestLine","toleranceYards","byId","anchors","WAYPOINT_HANDLE_RADIUS_YARDS","hitLineHandle","within","VIEW_STATE_EQUALS","VIEW_STATE_KEYS","isSameEntry","isSameView","EditorNotifier","readView","action","view","isSamePosition","MAX_DRAFT_POINTS","startDrag","origin","pointer","dragPosition","drag","dragPatch","scene","splitDraftPoints","startDrawing","addDraftPoint","accepts","draw","committableDraft","split","anchor","draftToLine","shape","DRAFT_LINE_ID","applyDragPatch","drop","draftLine","composePreview","interaction","computeOverlay","selection","EditorController","commands","pos","moved","added","loaded","handle","hit","openDocument","store","PlaySession","normalized","opened","controller","DEFAULT_EXPORT_WIDTH","MAX_EXPORT_WIDTH","resolveImageExportWidth","width","resolveImageExportSize","options","pt","shortOf","lateral","beside","OL","FRONT_4","END_OVER_TE","FRONT_3","LB_43","LB_NICKEL","cornerSpots","depth","offensiveLine","front4","strongEnd","front3","corners","left","right","iFormation","singlebackAce","shotgunSpread","trips","empty","pistol","bunch","defense43","defense34","nickelFront","NICKEL_BASE","nickel","shell","dime","defense425","defense335","FORMATION_PRESETS","getFormationPreset","f","ARROW_TRIM_MAX_FRACTION","lineEnd","tip","length","trimForArrowHead","arrowLength","arrowHeadVertices","baseX","ux","baseY","uy","halfWidth","blockCapEndpoints","half","capLength","CIRCLE_DRAW_SCALE","playerMarkerOutline","radius","resolveKeyAction","mod","stroke","ln","play","COVER_2_SHELL","COVER_3_SHELL","PLAY_PRESETS","getPlayPreset","PLAYMAKER_LOCALE_VALUES","MESSAGES","resolveMessages","locale","isFormControl","PointerInput","canvas","surface","toYard","activePointerId","onPointerDown","onPointerMove","endPress","onPointerUp","onPointerAbort","onDoubleClick","onKeyDown","listeners","root","playmaker_saira_subset_default","FIELD_FONT_NAME","FIELD_FONT_FAMILY","fieldFont","sizePx","loadFieldFont","registerFieldFont","face","context","resolveContext","ctx","normalizeCssColor","resolved","fromBlack","WHITE","BLACK","RED","BLUE","GREEN","ORANGE","GREY_100","GREY_200","GREY_400","GREY_500","THEME_TOKENS","themeReaderFrom","token","readProperty","property","fallback","createThemeReader","styles","element","ARROW_LENGTH_YARDS","ARROW_HALF_WIDTH_YARDS","ARROW_OFFSET_FROM_NUMBER_YARDS","NUMBER_FROM_SIDELINE_YARDS","NUMBER_DIGIT_SPACING_PER_HEIGHT","fieldTheme","read","FieldRenderer","frame","theme","geometry","bounds","yardWindow","metrics","yTop","yBottom","offset","center","hashLeft","hashRight","tick","goalPath","midPath","minorPath","top","bottom","leftX","rightX","arrowBaseHalfPx","halfArrowLength","gap","drawRotatedLabel","head","tail","label","rotation","drawArrow","xc","tipY","numberY","towardGoal","arrowCenterYard","pylon","drawPylon","goalY","endY","cx","crossHalf","uprightLen","dir","uprights","lineY","GridRenderer","gridDots","dots","size","LINE_COLOR_TOKENS","LineRenderer","isBlock","strokePath","kind","vertices","endpoints","OverlayRenderer","overlay","drawSelectedPlayer","r","drawLineHandles","outline","handleHalf","paintHandle","waypoint","handles","PlayerRenderer","defaultColor","grass","fontPx","isSolid","halfSide","tm","labelY","createDefaultLayers","renderLayers","layer","layers","CanvasSurface","parent","observer","rect","clientX","clientY","height","blob","resolve","reject","dpr","clientWidth","clientHeight","editorFrame","replaceKeepingFocus","doc","container","hadFocus","update","SWATCH_TOKENS","createSwatch","btn","name","PropertyPanel","focusFallback","messages","selected","shown","hint","setLabel","v","setShape","choices","setPressed","choice","setKind","setInterpolation","setThickness","text","labelText","control","onReset","group","span","row","children","reset","input","onChange","swatches","isPressed","select","opt","o","optionLabels","setEnabled","button","enabled","setTitle","reason","title","Toolbar","onClick","sep","placeholder","side","option","preset","isAnyUnavailable","isDevelopment","Playmaker","stage","mode","method","ui"],"sources":["../src/browser/i18n/ja.ts","../src/common/base/lifecycle.ts","../src/common/geometry/grid.ts","../src/common/model/guards.ts","../src/common/model/player.ts","../src/common/design/metrics.ts","../src/common/editing/editor.ts","../src/common/base/event.ts","../src/common/commands/command-service.ts","../src/common/commands/undo-redo-service.ts","../src/common/presets/shared.ts","../src/common/formations/formation.ts","../src/common/model/id-factory.ts","../src/common/model/line.ts","../src/common/model/play-data.ts","../src/common/model/migration-v2.ts","../src/common/model/migration.ts","../src/common/model/play-model.ts","../src/common/commands/command.ts","../src/common/commands/field-commands.ts","../src/common/commands/formation-commands.ts","../src/common/commands/patch.ts","../src/common/commands/line-commands.ts","../src/common/commands/player-commands.ts","../src/common/geometry/field.ts","../src/common/geometry/polyline.ts","../src/common/geometry/bezier.ts","../src/common/geometry/hit-test.ts","../src/common/editing/editor-notifier.ts","../src/common/editing/interaction.ts","../src/common/editing/preview.ts","../src/common/editing/editor-controller.ts","../src/common/editing/play-session.ts","../src/common/export/image-export.ts","../src/common/presets/alignments.ts","../src/common/formations/presets.ts","../src/common/geometry/line-decoration.ts","../src/common/geometry/player-marker.ts","../src/common/input/keymap.ts","../src/common/plays/presets.ts","../src/browser/i18n/messages.ts","../src/browser/i18n/index.ts","../src/browser/input/pointer-input.ts","../src/assets/playmaker-saira-subset.woff2","../src/browser/theme/field-font.ts","../src/browser/theme/field-font-face.ts","../src/browser/theme/css-color.ts","../src/browser/theme/tokens.ts","../src/browser/theme/theme-reader.ts","../src/browser/rendering/field-renderer.ts","../src/browser/rendering/grid-renderer.ts","../src/browser/rendering/line-renderer.ts","../src/browser/rendering/overlay-renderer.ts","../src/browser/rendering/player-renderer.ts","../src/browser/rendering/layer.ts","../src/browser/rendering/canvas-surface.ts","../src/browser/ui/focus.ts","../src/browser/theme/palette.ts","../src/browser/ui/property-panel.ts","../src/browser/ui/toolbar.ts","../src/playmaker.ts"],"sourcesContent":["import type { Messages } from \"./messages.js\";\n\nexport const ja: Messages = {\n tool: { select: \"選択\", \"add-player\": \"選手を追加\", \"draw-line\": \"線を描く\" },\n undo: \"元に戻す\",\n redo: \"やり直す\",\n delete: \"削除\",\n finishLine: \"線を確定\",\n cancel: \"取消\",\n zone: { \"own-redzone\": \"自陣RZ\", middle: \"中央\", redzone: \"相手RZ\" },\n formation: \"フォーメーション\",\n loadFormation: \"フォーメーション読込…\",\n side: { offense: \"オフェンス\", defense: \"ディフェンス\" },\n formationLimit: (maxPlayers) => `置くと ${maxPlayers} 人を超えるフォーメーションは読み込めません`,\n playerLimit: (maxPlayers) => `選手は ${maxPlayers} 人までです`,\n lineLimit: (maxLines) => `線は ${maxLines} 本までです`,\n selectionHint: \"対象を選択するとプロパティを編集できます\",\n player: \"選手\",\n line: \"線\",\n label: \"ラベル\",\n shape: \"形状\",\n color: \"色\",\n kind: \"種別\",\n interpolation: \"補間\",\n width: \"太さ\",\n shapes: { circle: \"丸\", square: \"四角\" },\n fills: { outline: \"枠線\", solid: \"塗りつぶし\" },\n kinds: { route: \"ルート\", block: \"ブロック\", motion: \"モーション\" },\n interpolations: { straight: \"直線\", bezier: \"曲線\" },\n colors: { swatch1: \"黒\", swatch2: \"赤\", swatch3: \"青\", swatch4: \"緑\" },\n swatch: (color, fill) => `${color}の${fill}`,\n reset: \"既定\",\n resetField: (field) => `${field}を既定に戻す`,\n};\n","export interface IDisposable {\n dispose(): void;\n}\n\nexport function toDisposable(fn: () => void): IDisposable {\n return { dispose: fn };\n}\n\n/** Disposes items together. An item added after disposal is disposed on the spot. */\nexport class DisposableStore implements IDisposable {\n private readonly items = new Set<IDisposable>();\n private disposed = false;\n\n /** Lets async work that finishes after disposal check for it and do nothing. */\n get isDisposed(): boolean {\n return this.disposed;\n }\n\n add<T extends IDisposable>(item: T): T {\n if (this.disposed) {\n item.dispose();\n return item;\n }\n this.items.add(item);\n return item;\n }\n\n dispose(): void {\n if (this.disposed) {\n return;\n }\n this.disposed = true;\n for (const item of this.items) {\n item.dispose();\n }\n this.items.clear();\n }\n}\n\n/** Disposes everything passed to `_register` together with itself on dispose. */\nexport abstract class Disposable implements IDisposable {\n protected readonly _store = new DisposableStore();\n\n protected get isDisposed(): boolean {\n return this._store.isDisposed;\n }\n\n protected _register<T extends IDisposable>(item: T): T {\n return this._store.add(item);\n }\n\n dispose(): void {\n this._store.dispose();\n }\n}\n","import type { FieldPosition } from \"../model/player.js\";\n\n/**\n * Step of the grid (in yards) that positions can be placed on. The origin is the sideline\n * laterally and the LOS vertically. Every position in the diagram sits on a grid point, so\n * elements line up vertically and horizontally without an explicit align operation.\n */\nexport const GRID_STEP_YARDS = 0.5;\n\nfunction snapYard(yard: number): number {\n // Rounding -0.2 gives -0, which toEqual (Object.is) distinguishes from 0, so coerce it to 0.\n return Math.round(yard / GRID_STEP_YARDS) * GRID_STEP_YARDS + 0;\n}\n\n/** Snaps a position to the nearest grid point, for loaded values and drag or click positions. */\nexport function snapToGrid(position: FieldPosition): FieldPosition {\n return {\n lateralYard: snapYard(position.lateralYard),\n downfieldYard: snapYard(position.downfieldYard),\n };\n}\n","export function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nexport function isFiniteNumber(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value);\n}\n\n/** A string of only whitespace also counts as empty. */\nexport function isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.trim() !== \"\";\n}\n\nexport function isOneOf<const T extends string>(value: unknown, values: readonly T[]): value is T {\n return values.some((candidate) => candidate === value);\n}\n\n/**\n * Reads only the first `max` entries of the array and drops those for which `parse` returns null.\n * Unrecoverable entries also count toward the number read, so at most `max` entries are ever\n * read for any input.\n */\nexport function parseBoundedArray<T>(\n raw: unknown,\n max: number,\n parse: (entry: unknown, index: number) => T | null,\n): T[] {\n if (!Array.isArray(raw)) {\n return [];\n }\n const parsed: T[] = [];\n const end = Math.min(raw.length, max);\n for (let index = 0; index < end; index += 1) {\n const value = parse(raw[index], index);\n if (value !== null) {\n parsed.push(value);\n }\n }\n return parsed;\n}\n","import { snapToGrid } from \"../geometry/grid.js\";\nimport {\n isFiniteNumber,\n isNonEmptyString,\n isOneOf,\n isRecord,\n parseBoundedArray,\n} from \"./guards.js\";\n\n/**\n * Shape of a player marker. Many shapes would clutter the diagram, so only circle and square.\n * Both can use the circumscribed circle as the hit area, so the hit test does not branch on shape.\n */\nexport const PLAYER_SHAPE_VALUES = [\"circle\", \"square\"] as const;\n\nexport type PlayerShape = (typeof PLAYER_SHAPE_VALUES)[number];\n\n/** Default when no shape is given. */\nexport const DEFAULT_PLAYER_SHAPE: PlayerShape = \"circle\";\n\n/**\n * Fill of a player marker: outline only (`outline`) or filled with the color (`solid`).\n * Held independently of the shape, it tells offense and defense or roles apart. The panel lists\n * its swatches in this order.\n */\nexport const PLAYER_FILL_VALUES = [\"outline\", \"solid\"] as const;\n\nexport type PlayerFill = (typeof PLAYER_FILL_VALUES)[number];\n\n/** Default when no fill is given. Outline only, like a diagram drawn on a chalkboard. */\nexport const DEFAULT_PLAYER_FILL: PlayerFill = \"outline\";\n\n/**\n * Limit on the players received from outside. A real play diagram has about 22, so it is never\n * reached. Normalization drops the excess so that broken or malicious data cannot stall rendering.\n */\nexport const MAX_PLAYERS = 64;\n\n/**\n * Radius of a player marker (in yards). The drawn size and the hit test radius share this value.\n * The field uses one scale in both directions, so a circle in yards is a circle on screen too.\n */\nexport const PLAYER_RADIUS_YARDS = 0.93;\n\n/**\n * Position on the field (in yards). The vertical value is the distance from the LOS\n * (`field.losYard`), so when switching zones moves the LOS, the diagram keeps its shape and\n * moves along with it.\n * - lateralYard: 0 = left sideline ... FIELD_WIDTH_YARDS = right sideline\n * - downfieldYard: 0 = LOS. Positive is the offense direction (screen top), negative the backfield\n *\n * Both are multiples of GRID_STEP_YARDS; the model holds no position off the grid.\n */\nexport interface FieldPosition {\n readonly lateralYard: number;\n readonly downfieldYard: number;\n}\n\n/**\n * A single player. `color` is a CSS color string; when omitted, the theme color is used.\n * When `fill` is omitted, DEFAULT_PLAYER_FILL is used.\n */\nexport interface Player {\n readonly id: string;\n readonly position: FieldPosition;\n readonly shape: PlayerShape;\n readonly label: string;\n readonly color?: string;\n readonly fill?: PlayerFill;\n}\n\nfunction isPlayerShape(value: unknown): value is PlayerShape {\n return isOneOf(value, PLAYER_SHAPE_VALUES);\n}\n\nfunction isPlayerFill(value: unknown): value is PlayerFill {\n return isOneOf(value, PLAYER_FILL_VALUES);\n}\n\n/**\n * Returns null when a coordinate is not a finite number, since it cannot be restored. The result\n * is not shared with the input. Values snap to grid points, so a position from outside lands on\n * the same grid as one set by dragging, and no position in the diagram is off the grid.\n */\nexport function parseFieldPosition(raw: unknown): FieldPosition | null {\n if (!isRecord(raw)) {\n return null;\n }\n const { lateralYard, downfieldYard } = raw;\n if (!isFiniteNumber(lateralYard) || !isFiniteNumber(downfieldYard)) {\n return null;\n }\n return snapToGrid({ lateralYard, downfieldYard });\n}\n\n/**\n * Normalizes one entry received from outside into a Player. An entry whose position is not valid\n * coordinates returns null and is dropped. A missing shape, label, color, or fill is filled with\n * the default instead of dropping the entry, so data saved by an older version stays readable.\n */\nfunction normalizePlayer(raw: unknown, index: number): Player | null {\n if (!isRecord(raw)) {\n return null;\n }\n const position = parseFieldPosition(raw.position);\n if (position === null) {\n return null;\n }\n\n // Without an id, assign one from the array position so the same data always yields the same id.\n const id = isNonEmptyString(raw.id) ? raw.id : `p${index}`;\n return {\n id,\n position,\n shape: isPlayerShape(raw.shape) ? raw.shape : DEFAULT_PLAYER_SHAPE,\n label: typeof raw.label === \"string\" ? raw.label : \"\",\n // exactOptionalPropertyTypes rejects undefined, so color and fill keys are set only if present.\n ...(isNonEmptyString(raw.color) ? { color: raw.color } : {}),\n ...(isPlayerFill(raw.fill) ? { fill: raw.fill } : {}),\n };\n}\n\n/**\n * Normalizes players received from outside into Player[]. Returns an empty array for a non-array.\n * Entries after the first MAX_PLAYERS are dropped unread.\n */\nexport function normalizePlayers(raw: unknown): Player[] {\n return parseBoundedArray(raw, MAX_PLAYERS, normalizePlayer);\n}\n\n/** Copies down to the position, sharing no references with the original player. */\nexport function clonePlayer(player: Player): Player {\n return {\n id: player.id,\n position: { ...player.position },\n shape: player.shape,\n label: player.label,\n ...(player.color === undefined ? {} : { color: player.color }),\n ...(player.fill === undefined ? {} : { fill: player.fill }),\n };\n}\n","// Sizes are not fixed in px; they are ratios of the field width W and the px per yard U.\n// The field's white lines, hashes, and numbers follow the ratios of the real dimensions.\n// Players and play lines are drawn larger than real for legibility, as ratios of marker diameter D.\n\nimport { PLAYER_RADIUS_YARDS } from \"../model/player.js\";\n\n// Keep at least 1 px so lines do not vanish when drawn small.\nconst MIN_LINE_PX = 1;\n// The real line width of 4 inches divided by the field width of 160 feet.\nconst YARD_LINE_WIDTH_PER_W = 0.0021;\n// The real hash length of 24 inches.\nconst HASH_TICK_LENGTH_PER_U = 0.375;\n// Real numbers are 6 feet (2 yards) tall, but slightly larger keeps them readable when drawn small.\nconst NUMBER_HEIGHT_PER_U = 2.4;\nconst NUMBER_MIN_PX = 10;\n// The midfield yard line is slightly thicker than the other yard lines to serve as a landmark.\nconst MIDFIELD_LINE_WIDTH_PER_YARD_LINE = 1.5;\n// End zone corner pylons: 4 inches square for real, drawn a bit larger to stay visible when small.\nconst PYLON_SIZE_PER_U = 0.45;\nconst PYLON_MIN_PX = 3;\n// Goalposts are drawn as seen from directly above: only the crossbar and the two uprights extending\n// into the field. The crossbar half is half of the real 18.5 feet.\nconst GOALPOST_CROSSBAR_HALF_PER_U = 1.85;\nconst GOALPOST_UPRIGHT_LENGTH_PER_U = 1.2;\nconst GOALPOST_STROKE_PER_D = 0.2;\nconst GOALPOST_STROKE_MIN_PX = 2;\n\n// Large enough for a two-character label to fit inside the marker.\nconst MARKER_LABEL_PER_D = 0.5;\nconst MARKER_LABEL_MIN_PX = 8;\nconst ROUTE_WIDTH_PER_D = 0.11;\nconst ROUTE_WIDTH_MIN_PX = 1.5;\nconst BLOCK_CAP_LENGTH_PER_D = 0.4;\n// The arrowhead base is about 4 times the line width, so it reads as an arrow rather than a barb\n// at the end of the line. The tip angle is about 37 degrees.\nconst ARROW_LENGTH_PER_D = 0.72;\nconst ARROW_HALF_WIDTH_PER_D = 0.24;\nconst MOTION_DASH_ON_PER_U = 1.1;\nconst MOTION_DASH_OFF_PER_U = 0.7;\n// The grid dots laid out while editing. They only need to be a guide, so they are thinner than the\n// hashes, with a minimum size that keeps them from vanishing.\nconst GRID_DOT_RADIUS_PER_U = 0.08;\nconst GRID_DOT_RADIUS_MIN_PX = 0.75;\n\n/** Dimensions used for drawing (px). */\nexport interface FieldMetrics {\n readonly yardLineWidth: number;\n /** Width of the goal lines and of the window's outer frame, including the sidelines. */\n readonly goalLineWidth: number;\n /** Width of the midfield (50 yard) yard line. */\n readonly midfieldLineWidth: number;\n readonly hashTickLength: number;\n readonly numberHeight: number;\n /** Side length of the pylons placed at the end zone corners. */\n readonly pylonSize: number;\n readonly goalpostCrossbarHalfLength: number;\n readonly goalpostUprightLength: number;\n readonly goalpostStroke: number;\n /** Player marker diameter D. Player and play line dimensions are ratios of this value. */\n readonly tokenDiameter: number;\n readonly markerStroke: number;\n readonly markerLabelFont: number;\n readonly routeWidth: number;\n readonly blockWidth: number;\n readonly blockCapLength: number;\n readonly arrowLength: number;\n readonly arrowHalfWidth: number;\n /** The motion dash pattern as [drawn length, gap length]. */\n readonly motionDash: readonly [number, number];\n /** Radius of the grid dots laid out while editing. */\n readonly gridDotRadius: number;\n}\n\n/** Does not return NaN even for a viewport of size 0. */\nexport function computeFieldMetrics(fieldPixelWidth: number, unitPx: number): FieldMetrics {\n const w = Math.max(0, fieldPixelWidth);\n const u = Math.max(0, unitPx);\n const yardLineWidth = Math.max(MIN_LINE_PX, YARD_LINE_WIDTH_PER_W * w);\n const d = 2 * PLAYER_RADIUS_YARDS * u;\n const routeWidth = Math.max(ROUTE_WIDTH_MIN_PX, ROUTE_WIDTH_PER_D * d);\n return {\n yardLineWidth,\n goalLineWidth: 2 * yardLineWidth,\n midfieldLineWidth: MIDFIELD_LINE_WIDTH_PER_YARD_LINE * yardLineWidth,\n hashTickLength: HASH_TICK_LENGTH_PER_U * u,\n numberHeight: Math.max(NUMBER_MIN_PX, NUMBER_HEIGHT_PER_U * u),\n pylonSize: Math.max(PYLON_MIN_PX, PYLON_SIZE_PER_U * u),\n goalpostCrossbarHalfLength: GOALPOST_CROSSBAR_HALF_PER_U * u,\n goalpostUprightLength: GOALPOST_UPRIGHT_LENGTH_PER_U * u,\n goalpostStroke: Math.max(GOALPOST_STROKE_MIN_PX, GOALPOST_STROKE_PER_D * d),\n tokenDiameter: d,\n // For an outline-only player the outline is the whole look, so it uses the play line width.\n markerStroke: routeWidth,\n markerLabelFont: Math.max(MARKER_LABEL_MIN_PX, MARKER_LABEL_PER_D * d),\n routeWidth,\n blockWidth: routeWidth,\n blockCapLength: BLOCK_CAP_LENGTH_PER_D * d,\n arrowLength: ARROW_LENGTH_PER_D * d,\n arrowHalfWidth: ARROW_HALF_WIDTH_PER_D * d,\n motionDash: [MOTION_DASH_ON_PER_U * u, MOTION_DASH_OFF_PER_U * u],\n gridDotRadius: Math.max(GRID_DOT_RADIUS_MIN_PX, GRID_DOT_RADIUS_PER_U * u),\n };\n}\n","import type { Event } from \"../base/event.js\";\nimport type { LinePatch } from \"../commands/line-commands.js\";\nimport type { PlayerPatch } from \"../commands/player-commands.js\";\nimport type { Formation } from \"../formations/formation.js\";\nimport type { Line } from \"../model/line.js\";\nimport type { FieldZone, PlayData } from \"../model/play-data.js\";\nimport type { FieldPosition, Player } from \"../model/player.js\";\n\nexport const EDITOR_TOOL_VALUES = [\"select\", \"add-player\", \"draw-line\"] as const;\n\nexport type EditorTool = (typeof EDITOR_TOOL_VALUES)[number];\n\n/**\n * A selection is a desired id; it is not cleared when the target disappears. Existence is\n * checked on read. Having nothing selected is expressed as `kind: \"none\"`, as in EditorOverlay.\n */\nexport type EditorSelection =\n | { readonly kind: \"player\"; readonly id: string }\n | { readonly kind: \"line\"; readonly id: string }\n | { readonly kind: \"none\" };\n\nexport const NO_SELECTION: EditorSelection = { kind: \"none\" };\n\nexport function isSameSelection(a: EditorSelection, b: EditorSelection): boolean {\n if (a.kind === \"none\" || b.kind === \"none\") {\n return a.kind === b.kind;\n }\n return a.kind === b.kind && a.id === b.id;\n}\n\n/** False when the count limit has been reached and the tool can place nothing. */\nexport function isToolAvailable(tool: EditorTool, state: EditorViewState): boolean {\n switch (tool) {\n case \"add-player\":\n return state.remainingPlayerSlots > 0;\n case \"draw-line\":\n return state.canStartLine;\n case \"select\":\n return true;\n }\n}\n\n/**\n * False for a formation that would exceed the player limit, since none of its players are placed.\n */\nexport function canLoadFormation(formation: Formation, state: EditorViewState): boolean {\n return formation.players.length <= state.remainingPlayerSlots;\n}\n\n/** The play diagram to draw. The version is needed only when saving, so it is left out. */\nexport type SceneData = Omit<PlayData, \"version\">;\n\n/** Where to draw the selection highlight and the handles (in yard space). */\nexport type EditorOverlay =\n | { readonly kind: \"none\" }\n | { readonly kind: \"player\"; readonly playerId: string }\n | {\n readonly kind: \"line\";\n readonly waypointHandles: readonly FieldPosition[];\n readonly endpointHandle: FieldPosition;\n };\n\nexport interface EditorViewState {\n readonly tool: EditorTool;\n readonly selection: EditorSelection;\n readonly canUndo: boolean;\n readonly canRedo: boolean;\n readonly fieldZone: FieldZone;\n /** Whether a line is being drawn (waiting for commit or cancel). */\n readonly isDrawing: boolean;\n /** How many more players can be placed. An operation placing more than this does nothing. */\n readonly remainingPlayerSlots: number;\n /** Whether the line count is below the limit, so that a new line can be started. */\n readonly canStartLine: boolean;\n}\n\n/** The overlay is computed from the same in-progress state as the scene. */\nexport interface EditorFrame {\n /** The committed play diagram with the in-progress drag or drawing overlaid on it. */\n readonly scene: SceneData;\n readonly overlay: EditorOverlay;\n}\n\n/** The read-only surface used to keep rendering and the UI in sync. */\nexport interface IEditorScene {\n /** Fires once per operation whenever the result of getFrame may have changed. */\n readonly onDidChangeScene: Event<void>;\n /**\n * Fires only when getViewState or the value of the selected player or line changes.\n * Does not fire for operations that change only the diagram, such as moving during a drag.\n */\n readonly onDidChangeViewState: Event<void>;\n getViewState(): EditorViewState;\n /** Undefined when no player is selected or the target has disappeared. */\n getSelectedPlayer(): Player | undefined;\n /** Undefined when no line is selected or the target has disappeared. */\n getSelectedLine(): Line | undefined;\n getFrame(): EditorFrame;\n}\n\n/** Pointer operations (in yard space). */\nexport interface IEditorGestures {\n pointerDown(pos: FieldPosition): void;\n pointerMove(pos: FieldPosition): void;\n pointerUp(pos: FieldPosition): void;\n}\n\n/** Edits invoked from the toolbar, the panels, and keyboard input. */\nexport interface IEditorActions {\n setTool(tool: EditorTool): void;\n commitLine(): void;\n cancelInteraction(): void;\n deleteSelection(): void;\n updateSelectedPlayer(patch: PlayerPatch): void;\n updateSelectedLine(patch: LinePatch): void;\n setFieldZone(zone: FieldZone): void;\n /**\n * Appends the players to the existing diagram and returns true once they are placed.\n * Does nothing and returns false when no player can be placed or the limit would be exceeded.\n */\n loadFormation(formation: Formation): boolean;\n /** While drawing, removes the last placed point and leaves the history untouched. */\n undo(): void;\n redo(): void;\n}\n\n/** The surface for UI such as a toolbar or a panel that reads the view and invokes edits. */\nexport type IEditorUi = IEditorActions & IEditorScene;\n\nexport interface IEditorController extends IEditorScene, IEditorGestures, IEditorActions {}\n","import { type IDisposable, toDisposable } from \"./lifecycle.js\";\n\n// common is type-checked against the ECMAScript lib only, so the host's console has no type.\n// Declare and use only console.error, which every host (browser, Node) has, inside this module.\ndeclare const console: { error(...data: unknown[]): void };\n\n/** Registers a listener and returns an IDisposable that ends the subscription. */\nexport type Event<T> = (listener: (e: T) => void) => IDisposable;\n\n/** Keeps notifying the remaining listeners even when a listener throws. */\nexport class Emitter<T> implements IDisposable {\n private listeners = new Set<(e: T) => void>();\n private disposed = false;\n private readonly onListenerError: (err: unknown) => void;\n\n /**\n * Receives the exceptions thrown by listeners. The default writes them to console.error.\n * They are not rethrown so that one failing listener does not stop the remaining listeners\n * and the caller of fire.\n */\n constructor(onListenerError: (err: unknown) => void = console.error) {\n this.onListenerError = onListenerError;\n }\n\n readonly event: Event<T> = (listener) => {\n if (this.disposed) {\n return toDisposable(() => {});\n }\n this.listeners.add(listener);\n return toDisposable(() => {\n this.listeners.delete(listener);\n });\n };\n\n fire(e: T): void {\n if (this.disposed) {\n return;\n }\n // Listeners added or removed during a notification do not change who is notified this round.\n for (const listener of [...this.listeners]) {\n try {\n listener(e);\n } catch (err) {\n this.onListenerError(err);\n }\n }\n }\n\n dispose(): void {\n this.disposed = true;\n this.listeners.clear();\n }\n}\n","import type { IPlayModel } from \"../model/play-model.js\";\nimport type { ICommand } from \"./command.js\";\nimport type { IUndoRedoService } from \"./undo-redo-service.js\";\n\n/**\n * Every edit goes through here. Applying, undoing, and redoing commands happen in one place\n * so the history and the Model never disagree.\n */\nexport interface ICommandService {\n readonly canUndo: boolean;\n readonly canRedo: boolean;\n /**\n * Applies the command. When it changes the Model, pushes it onto the history and returns true.\n * When nothing changed, returns false without pushing. Also does not push when apply throws.\n */\n execute(command: ICommand): boolean;\n /** If undo throws, the history stays as is. Does nothing when there is nothing to undo. */\n undo(): void;\n /** If redo throws, the history stays as is. Does nothing when there is nothing to redo. */\n redo(): void;\n}\n\nexport class CommandService implements ICommandService {\n private readonly model: IPlayModel;\n private readonly history: IUndoRedoService;\n\n constructor(model: IPlayModel, history: IUndoRedoService) {\n this.model = model;\n this.history = history;\n }\n\n get canUndo(): boolean {\n return this.history.canUndo;\n }\n\n get canRedo(): boolean {\n return this.history.canRedo;\n }\n\n execute(command: ICommand): boolean {\n const changed = command.apply(this.model);\n if (changed) {\n this.history.push(command);\n }\n return changed;\n }\n\n undo(): void {\n this.history.undo((command) => command.undo(this.model));\n }\n\n redo(): void {\n this.history.redo((command) => command.apply(this.model));\n }\n}\n","import type { ICommand } from \"./command.js\";\n\n/**\n * The history of applied commands. It holds no Model; running the commands is the caller's job.\n * The history never branches. Pushing a new command drops the redo history.\n */\nexport interface IUndoRedoService {\n readonly canUndo: boolean;\n readonly canRedo: boolean;\n /** Pushes a command that has already been run. */\n push(command: ICommand): void;\n /**\n * Passes the last pushed command to revert and moves it to the redo side once revert returns.\n * If revert throws, the history stays as is. Does nothing when there is nothing to undo.\n */\n undo(revert: (command: ICommand) => void): void;\n /** The reverse of undo. If reapply throws, the history stays as is. */\n redo(reapply: (command: ICommand) => void): void;\n}\n\nexport class UndoRedoService implements IUndoRedoService {\n private readonly undoStack: ICommand[] = [];\n private readonly redoStack: ICommand[] = [];\n\n get canUndo(): boolean {\n return this.undoStack.length > 0;\n }\n\n get canRedo(): boolean {\n return this.redoStack.length > 0;\n }\n\n push(command: ICommand): void {\n this.undoStack.push(command);\n this.redoStack.length = 0;\n }\n\n undo(revert: (command: ICommand) => void): void {\n this.move(this.undoStack, this.redoStack, revert);\n }\n\n redo(reapply: (command: ICommand) => void): void {\n this.move(this.redoStack, this.undoStack, reapply);\n }\n\n // Move only after the run succeeds. Popping first would lose the command if the run throws.\n private move(from: ICommand[], to: ICommand[], run: (command: ICommand) => void): void {\n const command = from.at(-1);\n if (command === undefined) {\n return;\n }\n run(command);\n from.pop();\n to.push(command);\n }\n}\n","import { isOneOf } from \"../model/guards.js\";\n\nexport const TEAM_SIDE_VALUES = [\"offense\", \"defense\"] as const;\n\nexport type TeamSide = (typeof TEAM_SIDE_VALUES)[number];\n\nexport function isTeamSide(value: unknown): value is TeamSide {\n return isOneOf(value, TEAM_SIDE_VALUES);\n}\n\n/**\n * Freezes the value and every object inside it, then returns it. The presets are shared by all\n * consumers, so one of them modifying a preset would change what the others load as well.\n */\nexport function deepFreeze<T>(value: T): T {\n if (typeof value === \"object\" && value !== null && !Object.isFrozen(value)) {\n Object.freeze(value);\n for (const child of Object.values(value)) {\n deepFreeze(child);\n }\n }\n return value;\n}\n","import { isNonEmptyString, isRecord } from \"../model/guards.js\";\nimport type { IIdFactory } from \"../model/id-factory.js\";\nimport { clonePlayer, normalizePlayers, type Player } from \"../model/player.js\";\nimport { isTeamSide, type TeamSide } from \"../presets/shared.js\";\n\n/**\n * One player of a formation. It has no `id`; one is assigned on every load. Even when the same\n * formation is loaded twice, ids collide neither with players in the diagram nor with players\n * that Undo brings back.\n */\nexport type FormationPlayer = Omit<Player, \"id\">;\n\n/**\n * Loading adds players to the current diagram. Besides the presets, a formation built by the\n * consumer can be passed as well.\n */\nexport interface Formation {\n readonly id: string;\n readonly name: string;\n /** Does not affect where the players are placed when loaded. */\n readonly side: TeamSide;\n /** At least one player. */\n readonly players: readonly FormationPlayer[];\n}\n\n/** Treats optional keys like clonePlayer, so adding a value to Player needs no change here. */\nexport function toFormationPlayer(player: Player): FormationPlayer {\n const { id: _id, ...template } = clonePlayer(player);\n return template;\n}\n\n/**\n * Normalizes an externally received formation into a Formation. Players are normalized by the\n * same rules as players in the diagram; null is returned if not a single player can be placed.\n * Missing id, name, and side are filled with defaults.\n * The return value is a new object that shares no references with the input.\n */\nexport function normalizeFormation(raw: unknown): Formation | null {\n if (!isRecord(raw)) {\n return null;\n }\n const players = normalizePlayers(raw.players).map(toFormationPlayer);\n if (players.length === 0) {\n return null;\n }\n return {\n id: isNonEmptyString(raw.id) ? raw.id : \"formation\",\n name: isNonEmptyString(raw.name) ? raw.name : \"Formation\",\n side: isTeamSide(raw.side) ? raw.side : \"offense\",\n players,\n };\n}\n\n/**\n * Assigns ids to the formation's players to make them Players. An id collides neither with\n * takenIds nor with the other players of the same formation.\n */\nexport function instantiateFormation(\n formation: Formation,\n ids: IIdFactory,\n takenIds: Iterable<string>,\n): Player[] {\n const taken = new Set(takenIds);\n return formation.players.map((template) => {\n const id = ids.next(\"player\", taken);\n taken.add(id);\n return { ...template, id };\n });\n}\n","export interface IIdFactory {\n /** Returns an unused id of the form `${prefix}-${n}` that is not in taken. */\n next(prefix: string, taken: ReadonlySet<string>): string;\n}\n\n/**\n * Issues ids from a counter per prefix. A number once used is never reused, even after the\n * element with that id is gone, so that an element restored by Undo does not collide with an\n * element added later.\n */\nexport class IdFactory implements IIdFactory {\n private readonly counters = new Map<string, number>();\n\n next(prefix: string, taken: ReadonlySet<string>): string {\n let n = this.counters.get(prefix) ?? 0;\n let id: string;\n do {\n n += 1;\n id = `${prefix}-${n}`;\n } while (taken.has(id));\n this.counters.set(prefix, n);\n return id;\n }\n}\n","import {\n isFiniteNumber,\n isNonEmptyString,\n isOneOf,\n isRecord,\n parseBoundedArray,\n} from \"./guards.js\";\nimport { type FieldPosition, type Player, parseFieldPosition } from \"./player.js\";\n\n/**\n * Kind of a line. Kinds are told apart by the end marker and the stroke style, not by color.\n * - `route`: the path of a receiver or runner. Drawn with an arrow at the end\n * - `block`: a blocking assignment. Same width as route, with a T bar at the end\n * - `motion`: a player's movement before the snap. Dashed, with an arrow at the end\n */\nexport const LINE_KIND_VALUES = [\"route\", \"block\", \"motion\"] as const;\n\nexport type LineKind = (typeof LINE_KIND_VALUES)[number];\n\n/**\n * Interpolation of a line. Applies to every kind of line.\n * - `straight`: joins the control points with straight segments\n * - `bezier`: joins the control points with a smooth curve that passes through all of them\n */\nexport const LINE_INTERPOLATION_VALUES = [\"straight\", \"bezier\"] as const;\n\nexport type LineInterpolation = (typeof LINE_INTERPOLATION_VALUES)[number];\n\n/** Default when no kind is given. */\nexport const DEFAULT_LINE_KIND: LineKind = \"route\";\n\n/** Default when no interpolation is given. */\nexport const DEFAULT_LINE_INTERPOLATION: LineInterpolation = \"straight\";\n\n/** Multiplier when no thickness is given. Draws at the default width of each kind as is. */\nexport const DEFAULT_LINE_THICKNESS = 1;\n\n/**\n * Limits on the lines received from outside and on the waypoints per line. A real play diagram\n * has about 20 lines with a few waypoints each, so these limits are never reached. Normalization\n * drops the excess so that broken or malicious data cannot stall rendering.\n */\nexport const MAX_LINES = 128;\nexport const MAX_WAYPOINTS_PER_LINE = 32;\n\n/**\n * A single line. The start is held as a player id rather than coordinates, so moving the player\n * carries the line's start along. Omitting `color` and `thickness` draws with the theme color\n * and the default width of the kind.\n */\nexport interface Line {\n readonly id: string;\n readonly kind: LineKind;\n /** Id of the player at the start. A reference to a missing player is dropped on restore. */\n readonly startPlayerId: string;\n /** Intermediate points between the start and the end (may be empty). */\n readonly waypoints: readonly FieldPosition[];\n /** End point (in yard space). */\n readonly end: FieldPosition;\n readonly interpolation: LineInterpolation;\n /** A CSS color string. */\n readonly color?: string;\n /** Multiplier on the default width of the kind. DEFAULT_LINE_THICKNESS when omitted. */\n readonly thickness?: number;\n}\n\nfunction isLineKind(value: unknown): value is LineKind {\n return isOneOf(value, LINE_KIND_VALUES);\n}\n\nfunction isLineInterpolation(value: unknown): value is LineInterpolation {\n return isOneOf(value, LINE_INTERPOLATION_VALUES);\n}\n\n/** Whether a value is accepted as a width multiplier. 0 or less hides the line and is rejected. */\nexport function isLineThickness(value: unknown): value is number {\n return isFiniteNumber(value) && value > 0;\n}\n\n/**\n * Normalizes one entry received from outside into a Line. A non-object, an entry whose start\n * player does not exist, or one whose end is not a position returns null and is dropped.\n * A missing kind, interpolation, or id is filled with the default instead of dropping the\n * entry, so that data saved by an older version stays readable.\n */\nfunction normalizeLine(\n raw: unknown,\n index: number,\n validPlayerIds: ReadonlySet<string>,\n): Line | null {\n if (!isRecord(raw)) {\n return null;\n }\n\n // A line without its start player can be neither drawn nor grabbed, so drop it.\n if (!isNonEmptyString(raw.startPlayerId) || !validPlayerIds.has(raw.startPlayerId)) {\n return null;\n }\n const end = parseFieldPosition(raw.end);\n if (end === null) {\n return null;\n }\n\n return {\n id: isNonEmptyString(raw.id) ? raw.id : `l${index}`,\n kind: isLineKind(raw.kind) ? raw.kind : DEFAULT_LINE_KIND,\n startPlayerId: raw.startPlayerId,\n // A non-numeric waypoint is dropped individually; the line itself is kept.\n waypoints: parseBoundedArray(raw.waypoints, MAX_WAYPOINTS_PER_LINE, parseFieldPosition),\n end,\n interpolation: isLineInterpolation(raw.interpolation)\n ? raw.interpolation\n : DEFAULT_LINE_INTERPOLATION,\n // exactOptionalPropertyTypes rejects undefined, so the key is set only when a value exists.\n ...(isNonEmptyString(raw.color) ? { color: raw.color } : {}),\n ...(isLineThickness(raw.thickness) ? { thickness: raw.thickness } : {}),\n };\n}\n\n/**\n * Normalizes lines received from outside into Line[]. Returns an empty array for a non-array.\n * Pass the ids of the already normalized players as `validPlayerIds`; a line starting at a\n * player not in that set is dropped. Entries after the first MAX_LINES are dropped unread.\n */\nexport function normalizeLines(raw: unknown, validPlayerIds: ReadonlySet<string>): Line[] {\n return parseBoundedArray(raw, MAX_LINES, (entry, index) =>\n normalizeLine(entry, index, validPlayerIds),\n );\n}\n\n/** Copies down to the waypoints and each position, sharing no references with the original line. */\nexport function cloneLine(line: Line): Line {\n return {\n id: line.id,\n kind: line.kind,\n startPlayerId: line.startPlayerId,\n waypoints: line.waypoints.map((p) => ({ ...p })),\n end: { ...line.end },\n interpolation: line.interpolation,\n ...(line.color === undefined ? {} : { color: line.color }),\n ...(line.thickness === undefined ? {} : { thickness: line.thickness }),\n };\n}\n\n/**\n * Builds the line's control point sequence [start player position, ...waypoints, end].\n * Returns undefined when the start player is not found.\n */\nexport function lineAnchorPoints(\n line: Line,\n playersById: ReadonlyMap<string, Player>,\n): FieldPosition[] | undefined {\n const start = playersById.get(line.startPlayerId);\n if (start === undefined) {\n return undefined;\n }\n return [{ ...start.position }, ...line.waypoints.map((p) => ({ ...p })), { ...line.end }];\n}\n\nexport function indexPlayersById(players: readonly Player[]): Map<string, Player> {\n const map = new Map<string, Player>();\n for (const player of players) {\n map.set(player.id, player);\n }\n return map;\n}\n","import { isOneOf, isRecord } from \"./guards.js\";\nimport { cloneLine, type Line, normalizeLines } from \"./line.js\";\nimport { clonePlayer, normalizePlayers, type Player } from \"./player.js\";\n\n/**\n * Which part of the field is shown.\n * - `middle`: around midfield\n * - `redzone`: the opponent's goal side\n * - `own-redzone`: the own goal side\n */\nexport const FIELD_ZONE_VALUES = [\"own-redzone\", \"middle\", \"redzone\"] as const;\n\nexport type FieldZone = (typeof FIELD_ZONE_VALUES)[number];\n\n/** Default when no zone is given. Many diagrams do not depend on where the LOS is, so middle. */\nexport const DEFAULT_FIELD_ZONE: FieldZone = \"middle\";\n\n/** English names of the zones. The `en` dictionary uses them; other locales carry their own. */\nexport const FIELD_ZONE_LABELS = {\n \"own-redzone\": \"Own red zone\",\n middle: \"Midfield\",\n redzone: \"Red zone\",\n} as const satisfies Record<FieldZone, string>;\n\n/**\n * Absolute yard of the LOS per zone (0 = own goal, 100 = opponent's goal).\n * Placed so that both the backfield and the downfield fit inside the window.\n * - `middle`: midfield\n * - `redzone`: the opponent's 15 yard line\n * - `own-redzone`: the own 10 yard line\n */\nexport const LOS_YARD_BY_ZONE = {\n \"own-redzone\": 10,\n middle: 50,\n redzone: 85,\n} as const satisfies Record<FieldZone, number>;\n\nexport interface FieldState {\n readonly zone: FieldZone;\n /**\n * Absolute yard of the LOS. For now it is derived from the zone, and the loaded value is not\n * used. It is kept as a value so the absolute player positions can be recovered from saved data.\n */\n readonly losYard: number;\n}\n\n/**\n * Current version of the PlayData schema. When the schema changes, raise it by 1 and add a step\n * to `PLAY_DATA_MIGRATIONS` that converts from the previous version.\n */\nexport const CURRENT_PLAY_DATA_VERSION = 2 as const;\n\n/**\n * Data of a play diagram. Passing saved data to `restorePlayData` loads it coerced to the\n * current version, whether it is an older version, has no version, or is a newer version.\n */\nexport interface PlayData {\n readonly version: typeof CURRENT_PLAY_DATA_VERSION;\n readonly field: FieldState;\n /** Later elements are drawn on top. */\n readonly players: readonly Player[];\n /** Later elements are drawn on top. */\n readonly lines: readonly Line[];\n}\n\n/**\n * A diagram built with types and loaded. The LOS position is derived again from the zone on\n * load, so `field.losYard` can be omitted.\n */\nexport interface PlayDataInput extends Omit<PlayData, \"field\"> {\n readonly field: Omit<FieldState, \"losYard\"> & { readonly losYard?: number | undefined };\n}\n\nexport function isFieldZone(value: unknown): value is FieldZone {\n return isOneOf(value, FIELD_ZONE_VALUES);\n}\n\nexport function fieldStateForZone(zone: FieldZone): FieldState {\n return { zone, losYard: LOS_YARD_BY_ZONE[zone] };\n}\n\n/** Copies down to the field, players, and lines, sharing no references with the original data. */\nexport function clonePlayData(data: PlayData): PlayData {\n return {\n version: CURRENT_PLAY_DATA_VERSION,\n field: { ...data.field },\n players: data.players.map(clonePlayer),\n lines: data.lines.map(cloneLine),\n };\n}\n\n/**\n * Normalizes a value shaped like the current version into a PlayData. It does not run the\n * version-based migration steps, so pass data of an older version to `migratePlayData`.\n * Missing fields are filled with defaults and unreadable entries are dropped. The return value\n * is a new object that shares no references with the input.\n */\nexport function resolvePlayData(data: unknown): PlayData {\n const source: Record<string, unknown> = isRecord(data) ? data : {};\n const zone = isRecord(source.field) ? source.field.zone : undefined;\n // Settle the players first and pass their id set, so lines with bad start references can be\n // rejected. Of duplicate player ids the later ones are renumbered, so a line pointing at that\n // id attaches to the first player.\n const players = makeIdsUnique(normalizePlayers(source.players));\n const validPlayerIds = new Set(players.map((p) => p.id));\n return {\n version: CURRENT_PLAY_DATA_VERSION,\n field: fieldStateForZone(isFieldZone(zone) ? zone : DEFAULT_FIELD_ZONE),\n players,\n lines: makeIdsUnique(normalizeLines(source.lines, validPlayerIds)),\n };\n}\n\n/**\n * Renumbers the second and later occurrences of an id to an unused id such as `${id}-2` (the\n * first keeps its id). With two equal ids, the hit test returns the last while an update hits\n * the first, so a different element gets edited. A renumbered id does not collide with any\n * other id in the input either.\n */\nfunction makeIdsUnique<T extends { readonly id: string }>(items: readonly T[]): T[] {\n const taken = new Set(items.map((item) => item.id));\n const seen = new Set<string>();\n return items.map((item) => {\n if (!seen.has(item.id)) {\n seen.add(item.id);\n return item;\n }\n let n = 2;\n while (taken.has(`${item.id}-${n}`)) {\n n += 1;\n }\n const id = `${item.id}-${n}`;\n taken.add(id);\n seen.add(id);\n return { ...item, id };\n });\n}\n","import { isFiniteNumber, isRecord } from \"./guards.js\";\nimport { MAX_LINES, MAX_WAYPOINTS_PER_LINE } from \"./line.js\";\nimport { DEFAULT_FIELD_ZONE, fieldStateForZone, isFieldZone } from \"./play-data.js\";\nimport { MAX_PLAYERS } from \"./player.js\";\n\n/**\n * In v1 the line thickness was in CSS px. The property panel showed an unspecified line as 2 px,\n * so v2 treats that value as multiplier 1 and divides by it.\n */\nexport const V1_DEFAULT_THICKNESS_PX = 2;\n\n/** A malformed value is returned as is; whether to drop it is left to the later normalization. */\nfunction toV2Position(raw: unknown, losYard: number): unknown {\n if (!isRecord(raw)) {\n return raw;\n }\n const { absoluteYard, ...rest } = raw;\n if (!isFiniteNumber(absoluteYard)) {\n return raw;\n }\n return { ...rest, downfieldYard: absoluteYard - losYard };\n}\n\nfunction toV2Player(raw: unknown, losYard: number): unknown {\n if (!isRecord(raw)) {\n return raw;\n }\n return { ...raw, position: toV2Position(raw.position, losYard) };\n}\n\nfunction toV2Line(raw: unknown, losYard: number): unknown {\n if (!isRecord(raw)) {\n return raw;\n }\n return {\n ...raw,\n waypoints: Array.isArray(raw.waypoints)\n ? raw.waypoints.slice(0, MAX_WAYPOINTS_PER_LINE).map((p) => toV2Position(p, losYard))\n : raw.waypoints,\n end: toV2Position(raw.end, losYard),\n thickness: isFiniteNumber(raw.thickness)\n ? raw.thickness / V1_DEFAULT_THICKNESS_PX\n : raw.thickness,\n };\n}\n\n/**\n * v1 held the vertical position as an absolute yard (`absoluteYard`). v2 holds it as the distance\n * from the zone's LOS (`downfieldYard`), so the LOS of the v1 zone is subtracted to carry the\n * position over without moving it on screen. Arrays are read only up to the limits, as in\n * normalization.\n */\nexport function migrateV1ToV2(data: Readonly<Record<string, unknown>>): Record<string, unknown> {\n const zone =\n isRecord(data.field) && isFieldZone(data.field.zone) ? data.field.zone : DEFAULT_FIELD_ZONE;\n const field = fieldStateForZone(zone);\n const { losYard } = field;\n return {\n ...data,\n field,\n players: Array.isArray(data.players)\n ? data.players.slice(0, MAX_PLAYERS).map((p) => toV2Player(p, losYard))\n : data.players,\n lines: Array.isArray(data.lines)\n ? data.lines.slice(0, MAX_LINES).map((l) => toV2Line(l, losYard))\n : data.lines,\n };\n}\n","import { isFiniteNumber, isRecord } from \"./guards.js\";\nimport { migrateV1ToV2 } from \"./migration-v2.js\";\nimport { type PlayData, resolvePlayData } from \"./play-data.js\";\n\n/**\n * A step that coerces data of an older version into the shape of the next version.\n * resolvePlayData tidies the shape after the steps run, so a step only has to convert the fields\n * whose meaning changes in the next version.\n */\nexport interface PlayDataMigration {\n /** The version after this step has run. */\n readonly to: number;\n migrate(data: Readonly<Record<string, unknown>>): Record<string, unknown>;\n}\n\n/** Ordered by ascending `to`. */\nexport const PLAY_DATA_MIGRATIONS: readonly PlayDataMigration[] = [\n { to: 2, migrate: migrateV1ToV2 },\n];\n\n/**\n * Reads the version declared in the data. Returns 0 when it is not a finite number, so the data\n * runs through every step as the earliest data, which had no version.\n */\nexport function readDeclaredVersion(raw: unknown): number {\n if (isRecord(raw) && isFiniteNumber(raw.version)) {\n return raw.version;\n }\n return 0;\n}\n\n/**\n * Runs only the steps newer than the declared version, in the given order. A non-object value is\n * returned as is without running any step. Data newer than the last step is also returned as is.\n */\nexport function applyPlayDataMigrations(\n raw: unknown,\n declaredVersion: number,\n steps: readonly PlayDataMigration[],\n): unknown {\n if (!isRecord(raw)) {\n return raw;\n }\n let acc = raw;\n for (const step of steps) {\n if (step.to > declaredVersion) {\n acc = step.migrate(acc);\n }\n }\n return acc;\n}\n\n/**\n * Turns data saved by any version into a PlayData of the current version. Does not throw on\n * broken data. The return value is a new object that shares no references with the input.\n * Players, lines, and waypoints are read only up to MAX_PLAYERS, MAX_LINES, and\n * MAX_WAYPOINTS_PER_LINE.\n */\nexport function migratePlayData(raw: unknown): PlayData {\n const declared = readDeclaredVersion(raw);\n const migrated = applyPlayDataMigrations(raw, declared, PLAY_DATA_MIGRATIONS);\n // For data of a newer version too, resolvePlayData rewrites version to the current one.\n return resolvePlayData(migrated);\n}\n","import { Emitter, type Event } from \"../base/event.js\";\nimport { Disposable } from \"../base/lifecycle.js\";\nimport { type Line, MAX_LINES, MAX_WAYPOINTS_PER_LINE } from \"./line.js\";\nimport { migratePlayData } from \"./migration.js\";\nimport { clonePlayData, type FieldZone, fieldStateForZone, type PlayData } from \"./play-data.js\";\nimport { MAX_PLAYERS, type Player } from \"./player.js\";\n\n/**\n * What is needed to undo the removal of one player. Holds the lines removed with the player,\n * each with its original index, so they are inserted back into the same order.\n */\nexport interface PlayerRemoval {\n readonly player: Player;\n readonly index: number;\n readonly removedLines: readonly LineRemoval[];\n}\n\n/** What is needed to undo the removal of one line. */\nexport interface LineRemoval {\n readonly line: Line;\n readonly index: number;\n}\n\n/**\n * State of the play diagram. An implementation never keeps a line whose start player is gone.\n * Removals and replacements return the previous values needed to undo them.\n */\nexport interface IPlayModel {\n /** Fires once after any change, with the same value as getSnapshot. */\n readonly onDidChange: Event<PlayData>;\n /** Deep copy of the current state. Each call copies the whole diagram. */\n getData(): PlayData;\n /**\n * Returns the current state without copying. The state is replaced with a new object on every\n * change, so the value received is not rewritten by later changes.\n */\n getSnapshot(): PlayData;\n getFieldZone(): FieldZone;\n /** undefined when absent. Returns the internal value as is, like getSnapshot. */\n findPlayer(id: string): Player | undefined;\n /** undefined when absent. Returns the internal value as is, like getSnapshot. */\n findLine(id: string): Line | undefined;\n /** Same as findPlayer, but throws for an unknown id. */\n getPlayer(id: string): Player;\n /** Same as findLine, but throws for an unknown id. */\n getLine(id: string): Line;\n /**\n * Also moves the LOS to the zone's default position. Players and lines are positioned\n * relative to the LOS, so the whole diagram moves along with it.\n */\n setFieldZone(zone: FieldZone): void;\n /**\n * Throws when given a player whose id already exists.\n * Also throws when the addition would exceed MAX_PLAYERS players.\n */\n addPlayer(player: Player): void;\n /**\n * However many players are added, onDidChange fires only once at the end.\n * Throws without adding anyone if an id duplicates an existing one or MAX_PLAYERS is exceeded.\n */\n addPlayers(players: readonly Player[]): void;\n /** Also removes the lines that start at that player. */\n removePlayer(id: string): PlayerRemoval;\n /**\n * Also removes the lines that start at the removed players; onDidChange fires only once at\n * the end. Throws without removing anyone when an id is unknown or repeated.\n */\n removePlayers(ids: readonly string[]): PlayerRemoval[];\n /**\n * Puts a player and lines removed by removePlayer back into their original order.\n * Throws when a player with the same id already exists, restoring would exceed a limit, or an\n * index points outside the current order.\n */\n restorePlayer(removal: PlayerRemoval): void;\n /** Replaces the player with the same id and returns the player it replaced. */\n updatePlayer(player: Player): Player;\n /**\n * Throws when given a line whose id already exists. Also throws when the lines would exceed\n * MAX_LINES, and for a line whose waypoints exceed MAX_WAYPOINTS_PER_LINE.\n */\n addLine(line: Line): void;\n /** index ranges from 0 to the line count; throws outside it, and otherwise as addLine does. */\n insertLine(line: Line, index: number): void;\n removeLine(id: string): LineRemoval;\n /**\n * Replaces the line with the same id and returns the line it replaced. Throws when the\n * waypoints exceed MAX_WAYPOINTS_PER_LINE.\n */\n updateLine(line: Line): Line;\n}\n\n// Inserting at an index not from a removal record lets slice silently clamp to the end, hiding an\n// order broken by Undo.\nfunction insertAt<T>(items: readonly T[], index: number, item: T, what: string): T[] {\n if (!Number.isInteger(index) || index < 0 || index > items.length) {\n throw new Error(`PlayModel: ${what} index out of range: ${index} (0..${items.length})`);\n }\n return [...items.slice(0, index), item, ...items.slice(index)];\n}\n\n// Selection and editing pick their target by id, so two elements never share an id.\nfunction assertNewId(items: readonly { id: string }[], id: string, message: string): void {\n if (items.some((item) => item.id === id)) {\n throw new Error(`${message} \"${id}\"`);\n }\n}\n\n// The count limits match those of external data normalization. If editing could exceed them,\n// a diagram written out with getData would be silently truncated when read back.\nfunction assertWithinLimit(count: number, limit: number, what: string): void {\n if (count > limit) {\n throw new Error(`PlayModel: too many ${what}: ${count} > ${limit}`);\n }\n}\n\nfunction assertWithinLimits(data: PlayData): void {\n assertWithinLimit(data.players.length, MAX_PLAYERS, \"players\");\n assertWithinLimit(data.lines.length, MAX_LINES, \"lines\");\n for (const line of data.lines) {\n assertWithinLimit(line.waypoints.length, MAX_WAYPOINTS_PER_LINE, \"waypoints\");\n }\n}\n\n/**\n * Mutating methods replace the state with a new object and then fire onDidChange exactly once.\n * Inputs are taken in without copying. The types are read-only, so neither the input nor the\n * state can be rewritten afterward. An operation on an unknown id is a mistake in how the edit\n * was assembled, so it throws on the spot.\n */\nexport class PlayModel extends Disposable implements IPlayModel {\n private readonly _onDidChange = this._register(new Emitter<PlayData>());\n readonly onDidChange = this._onDidChange.event;\n private state: PlayData;\n\n constructor(initialData?: unknown) {\n super();\n this.state = migratePlayData(initialData);\n }\n\n getData(): PlayData {\n return clonePlayData(this.state);\n }\n\n getSnapshot(): PlayData {\n return this.state;\n }\n\n getFieldZone(): FieldZone {\n return this.state.field.zone;\n }\n\n findPlayer(id: string): Player | undefined {\n return this.state.players.find((p) => p.id === id);\n }\n\n findLine(id: string): Line | undefined {\n return this.state.lines.find((l) => l.id === id);\n }\n\n getPlayer(id: string): Player {\n const player = this.findPlayer(id);\n if (player === undefined) {\n throw new Error(`PlayModel: unknown player id \"${id}\"`);\n }\n return player;\n }\n\n getLine(id: string): Line {\n const line = this.findLine(id);\n if (line === undefined) {\n throw new Error(`PlayModel: unknown line id \"${id}\"`);\n }\n return line;\n }\n\n setFieldZone(zone: FieldZone): void {\n // Fires even for the same zone. The editing side decides not to push a no-op onto the history.\n this.commit({ ...this.state, field: fieldStateForZone(zone) });\n }\n\n addPlayer(player: Player): void {\n this.addPlayers([player]);\n }\n\n addPlayers(players: readonly Player[]): void {\n // Throw without adding anyone if even one id is a duplicate. Throwing after adding some would\n // leave a change with neither a notification nor a history entry.\n const taken = new Set(this.state.players.map((p) => p.id));\n for (const player of players) {\n if (taken.has(player.id)) {\n throw new Error(`PlayModel.addPlayers: duplicate player id \"${player.id}\"`);\n }\n taken.add(player.id);\n }\n this.commit({ ...this.state, players: [...this.state.players, ...players] });\n }\n\n private removePlayerCore(id: string): PlayerRemoval {\n const target = this.state.players.find((p) => p.id === id);\n if (target === undefined) {\n throw new Error(`PlayModel.removePlayer: unknown player id \"${id}\"`);\n }\n const index = this.state.players.indexOf(target);\n const removedLines: LineRemoval[] = [];\n const lines: Line[] = [];\n this.state.lines.forEach((line, i) => {\n if (line.startPlayerId === id) {\n // A line losing its start cannot be drawn; remove it too, noting its index for restore.\n removedLines.push({ line, index: i });\n } else {\n lines.push(line);\n }\n });\n // Removal cannot exceed a limit, so commit is skipped. The caller emits a single notification.\n this.state = {\n ...this.state,\n players: this.state.players.filter((p) => p !== target),\n lines,\n };\n return { player: target, index, removedLines };\n }\n\n removePlayer(id: string): PlayerRemoval {\n const removal = this.removePlayerCore(id);\n this.emitChange();\n return removal;\n }\n\n removePlayers(ids: readonly string[]): PlayerRemoval[] {\n // Throw without removing anyone if even one id cannot be removed. Throwing after removing\n // some would leave a change with neither a notification nor a history entry.\n const remaining = new Set(this.state.players.map((p) => p.id));\n for (const id of ids) {\n if (!remaining.delete(id)) {\n throw new Error(`PlayModel.removePlayers: unknown or repeated player id \"${id}\"`);\n }\n }\n const removals = ids.map((id) => this.removePlayerCore(id));\n this.emitChange();\n return removals;\n }\n\n restorePlayer(removal: PlayerRemoval): void {\n assertNewId(\n this.state.players,\n removal.player.id,\n \"PlayModel.restorePlayer: duplicate player id\",\n );\n const players = insertAt(this.state.players, removal.index, removal.player, \"player\");\n // Inserting in ascending order of the original index restores the order before removal.\n let lines = this.state.lines;\n for (const { line, index } of [...removal.removedLines].sort((a, b) => a.index - b.index)) {\n lines = insertAt(lines, index, line, \"line\");\n }\n this.commit({ ...this.state, players, lines });\n }\n\n updatePlayer(player: Player): Player {\n const prev = this.getPlayer(player.id);\n this.commit({\n ...this.state,\n players: this.state.players.map((p) => (p === prev ? player : p)),\n });\n return prev;\n }\n\n addLine(line: Line): void {\n this.insertLine(line, this.state.lines.length);\n }\n\n insertLine(line: Line, index: number): void {\n assertNewId(this.state.lines, line.id, \"PlayModel: duplicate line id\");\n this.commit({ ...this.state, lines: insertAt(this.state.lines, index, line, \"line\") });\n }\n\n removeLine(id: string): LineRemoval {\n const target = this.state.lines.find((l) => l.id === id);\n if (target === undefined) {\n throw new Error(`PlayModel.removeLine: unknown line id \"${id}\"`);\n }\n const index = this.state.lines.indexOf(target);\n this.commit({ ...this.state, lines: this.state.lines.filter((l) => l !== target) });\n return { line: target, index };\n }\n\n updateLine(line: Line): Line {\n const prev = this.getLine(line.id);\n this.commit({\n ...this.state,\n lines: this.state.lines.map((l) => (l === prev ? line : l)),\n });\n return prev;\n }\n\n // Every change that can grow a count goes through here, so no state ever exceeds a limit.\n private commit(next: PlayData): void {\n assertWithinLimits(next);\n this.state = next;\n this.emitChange();\n }\n\n private emitChange(): void {\n this._onDidChange.fire(this.state);\n }\n}\n","import type { IPlayModel } from \"../model/play-model.js\";\n\n/**\n * A single editing operation. Redo is expressed by calling apply again.\n * Each command records by itself, on every apply, the prior state that undo needs.\n */\nexport interface ICommand {\n /** Short English name of the command, such as \"Add line\"; it appears in error messages. */\n readonly label: string;\n /**\n * Returns true when it changed the Model. When nothing changes, returns false without touching\n * the Model, so that no empty entry is pushed onto the history.\n */\n apply(model: IPlayModel): boolean;\n undo(model: IPlayModel): void;\n}\n\n/** Retrieves a value recorded by apply for use in undo. Throws if undo runs before apply. */\nexport function requireApplied<T>(value: T | undefined, command: ICommand): T {\n /* v8 ignore start -- Only applied commands enter the history, so edits never get here. */\n if (value === undefined) {\n throw new Error(`${command.label}: undone before apply`);\n }\n /* v8 ignore stop */\n return value;\n}\n","import type { FieldZone } from \"../model/play-data.js\";\nimport type { IPlayModel } from \"../model/play-model.js\";\nimport { type ICommand, requireApplied } from \"./command.js\";\n\nexport class SetFieldZoneCommand implements ICommand {\n readonly label = \"Switch field zone\";\n private readonly zone: FieldZone;\n private previous: FieldZone | undefined;\n\n constructor(zone: FieldZone) {\n this.zone = zone;\n }\n\n apply(model: IPlayModel): boolean {\n this.previous = model.getFieldZone();\n if (this.previous === this.zone) {\n return false;\n }\n model.setFieldZone(this.zone);\n return true;\n }\n\n undo(model: IPlayModel): void {\n model.setFieldZone(requireApplied(this.previous, this));\n }\n}\n","import type { IPlayModel } from \"../model/play-model.js\";\nimport type { Player } from \"../model/player.js\";\nimport type { ICommand } from \"./command.js\";\n\n/**\n * Adds a batch of players that already have ids assigned. Existing players and lines are kept,\n * so an offense formation and a defense formation can be layered one after the other.\n * The change to the Model is made in one go, so only one notification is sent.\n */\nexport class LoadFormationCommand implements ICommand {\n readonly label = \"Load formation\";\n private readonly players: readonly Player[];\n\n constructor(players: readonly Player[]) {\n this.players = players;\n }\n\n apply(model: IPlayModel): boolean {\n if (this.players.length === 0) {\n return false;\n }\n model.addPlayers(this.players);\n return true;\n }\n\n undo(model: IPlayModel): void {\n // Lines starting from the added players come from later commands, so they are already undone.\n model.removePlayers(this.players.map((p) => p.id));\n }\n}\n","type OptionalKeys<T> = {\n [K in keyof T]-?: Record<never, never> extends Pick<T, K> ? K : never;\n}[keyof T];\n\n/**\n * A patch that replaces only the specified keys. Unspecified keys keep their current values.\n * Only optional keys accept null, which removes the value and restores the default.\n */\nexport type Patch<T> = {\n readonly [K in keyof T]?: K extends OptionalKeys<T> ? T[K] | null : T[K];\n};\n\n// Line and Player are interfaces without an index signature, so their keys cannot be looked up\n// by string as is. To write the patch rules in one place without depending on the list of keys,\n// the view is changed only inside these two functions.\nfunction asRecord(value: object): Readonly<Record<string, unknown>> {\n return value as Record<string, unknown>;\n}\n\n/** Returns a new object with the patch applied to current. Does not modify current. */\nexport function applyPatch<T extends object>(current: T, patch: Patch<T>): T {\n const next = { ...asRecord(current) };\n for (const [key, value] of Object.entries(patch)) {\n if (value === null) {\n delete next[key];\n } else {\n next[key] = value;\n }\n }\n // Patch<T> types each value to match the key's type in T, so the patched result is also a T.\n return next as T;\n}\n\n/** Whether applying the patch changes any value in current. Objects are compared by reference. */\nexport function patchChangesAnything<T extends object>(current: T, patch: Patch<T>): boolean {\n const values = asRecord(current);\n return Object.entries(patch).some(([key, value]) => (value ?? undefined) !== values[key]);\n}\n","import type { Line } from \"../model/line.js\";\nimport type { IPlayModel, LineRemoval } from \"../model/play-model.js\";\nimport { type ICommand, requireApplied } from \"./command.js\";\nimport { applyPatch, type Patch, patchChangesAnything } from \"./patch.js\";\n\n/** The id and the starting player identify a line, so a patch never changes them. */\nexport type LinePatch = Patch<Omit<Line, \"id\" | \"startPlayerId\">>;\n\nexport function applyLinePatch(current: Line, patch: LinePatch): Line {\n return applyPatch<Line>(current, patch);\n}\n\nexport class AddLineCommand implements ICommand {\n readonly label = \"Add line\";\n private readonly line: Line;\n\n constructor(line: Line) {\n this.line = line;\n }\n\n apply(model: IPlayModel): boolean {\n model.addLine(this.line);\n return true;\n }\n\n undo(model: IPlayModel): void {\n model.removeLine(this.line.id);\n }\n}\n\n/** Undo puts the line back at its original position in the order. */\nexport class RemoveLineCommand implements ICommand {\n readonly label = \"Delete line\";\n private readonly lineId: string;\n private removal: LineRemoval | undefined;\n\n constructor(lineId: string) {\n this.lineId = lineId;\n }\n\n apply(model: IPlayModel): boolean {\n this.removal = model.removeLine(this.lineId);\n return true;\n }\n\n undo(model: IPlayModel): void {\n const { line, index } = requireApplied(this.removal, this);\n model.insertLine(line, index);\n }\n}\n\n/** Edits the kind, interpolation, waypoints, end point, color, and thickness together. */\nexport class UpdateLineCommand implements ICommand {\n readonly label = \"Update line\";\n private readonly lineId: string;\n private readonly patch: LinePatch;\n private previous: Line | undefined;\n\n constructor(lineId: string, patch: LinePatch) {\n this.lineId = lineId;\n this.patch = { ...patch };\n }\n\n apply(model: IPlayModel): boolean {\n const current = model.getLine(this.lineId);\n if (!patchChangesAnything(current, this.patch)) {\n return false;\n }\n this.previous = model.updateLine(applyLinePatch(current, this.patch));\n return true;\n }\n\n undo(model: IPlayModel): void {\n model.updateLine(requireApplied(this.previous, this));\n }\n}\n","import type { IPlayModel, PlayerRemoval } from \"../model/play-model.js\";\nimport type { Player } from \"../model/player.js\";\nimport { type ICommand, requireApplied } from \"./command.js\";\nimport { applyPatch, type Patch, patchChangesAnything } from \"./patch.js\";\n\n/** The id identifies a player, so a patch never changes it. */\nexport type PlayerPatch = Patch<Omit<Player, \"id\">>;\n\nexport function applyPlayerPatch(current: Player, patch: PlayerPatch): Player {\n return applyPatch<Player>(current, patch);\n}\n\nexport class AddPlayerCommand implements ICommand {\n readonly label = \"Add player\";\n private readonly player: Player;\n\n constructor(player: Player) {\n this.player = player;\n }\n\n apply(model: IPlayModel): boolean {\n model.addPlayer(this.player);\n return true;\n }\n\n undo(model: IPlayModel): void {\n // Lines starting from the added player come from later commands, so they are already undone.\n // The dependent lines recorded by the removal are therefore empty and are dropped.\n model.removePlayer(this.player.id);\n }\n}\n\n/**\n * Also removes the lines that start from the player. Undo puts the player and the lines back at\n * their original positions in the order.\n */\nexport class RemovePlayerCommand implements ICommand {\n readonly label = \"Delete player\";\n private readonly playerId: string;\n private removal: PlayerRemoval | undefined;\n\n constructor(playerId: string) {\n this.playerId = playerId;\n }\n\n apply(model: IPlayModel): boolean {\n this.removal = model.removePlayer(this.playerId);\n return true;\n }\n\n undo(model: IPlayModel): void {\n model.restorePlayer(requireApplied(this.removal, this));\n }\n}\n\n/** Edits the position, label, shape, color, and fill together. */\nexport class UpdatePlayerCommand implements ICommand {\n readonly label = \"Update player\";\n private readonly playerId: string;\n private readonly patch: PlayerPatch;\n private previous: Player | undefined;\n\n constructor(playerId: string, patch: PlayerPatch) {\n this.playerId = playerId;\n this.patch = { ...patch };\n }\n\n apply(model: IPlayModel): boolean {\n const current = model.getPlayer(this.playerId);\n if (!patchChangesAnything(current, this.patch)) {\n return false;\n }\n this.previous = model.updatePlayer(applyPlayerPatch(current, this.patch));\n return true;\n }\n\n undo(model: IPlayModel): void {\n model.updatePlayer(requireApplied(this.previous, this));\n }\n}\n","import type { FieldState, FieldZone } from \"../model/play-data.js\";\nimport type { FieldPosition } from \"../model/player.js\";\n\n/**\n * Width between the sidelines. The rule says 160 feet (53⅓ yards), but it is rounded to 53 yards\n * so that the center (26.5) lands on a 0.5 yard grid point. The difference from the real\n * dimension is 1 foot, which does not show in the diagram.\n */\nexport const FIELD_WIDTH_YARDS = 53;\n\n/**\n * Vertical length shown in the middle zone. The red zone windows are deeper than this so that\n * the yard numbers are not cut off at the edge. zoneWindowLength gives the length per window.\n */\nexport const ZONE_WINDOW_LENGTH_YARDS = 30;\n\n/**\n * Distance (in yards) from the center of the field to the hash marks, per league.\n * The gap between the left and right hashes is 40 feet in the NCAA, 18.5 feet in the NFL, and\n * 53⅓ feet in the NFHS. The values are rounded to grid points (0.5 yard steps) so that players\n * can be placed on the hashes. There is no API for the consumer to pick a league; the diagram\n * is drawn with DEFAULT_FIELD_LEAGUE.\n */\nexport const HASH_CENTER_OFFSET_YARDS_BY_LEAGUE = {\n ncaa: 6.5,\n nfl: 3,\n nfhs: 9,\n} as const;\n\nexport type FieldLeague = keyof typeof HASH_CENTER_OFFSET_YARDS_BY_LEAGUE;\n\n/** Fields in Japan are usually lined with the NCAA hash spacing. */\nexport const DEFAULT_FIELD_LEAGUE: FieldLeague = \"ncaa\";\n\n/**\n * Depth shown in the red zone windows, from the goal line back. It is wider than the actual red\n * zone (20 yards) so the 20 yard line does not sit on the window edge with its numbers cut off.\n */\nexport const RED_ZONE_DEPTH_YARDS = 25;\n\n/** Yard line interval. browser draws the lines; shared so grid dots are not placed on them. */\nexport const YARD_LINE_INTERVAL_YARDS = 5;\n\n/** End zone depth. Absolute yards run from -10 at the own end line to 110 at the opponent's. */\nexport const END_ZONE_DEPTH_YARDS = 10;\n\nexport interface YardWindow {\n /** Absolute yard of the near side of the window (screen bottom, behind the offense). */\n readonly startYard: number;\n /** Absolute yard of the far side of the window (screen top, ahead of the offense). */\n readonly endYard: number;\n}\n\n/**\n * Range of absolute yards shown for a zone. Absolute yards put the own goal line at 0 and the\n * opponent's goal line at 100, growing toward the offense direction (screen top). The red zone\n * windows show the end zone as well.\n */\nexport function fieldZoneWindow(zone: FieldZone): YardWindow {\n switch (zone) {\n case \"own-redzone\":\n return { startYard: -END_ZONE_DEPTH_YARDS, endYard: RED_ZONE_DEPTH_YARDS };\n case \"redzone\":\n return { startYard: 100 - RED_ZONE_DEPTH_YARDS, endYard: 100 + END_ZONE_DEPTH_YARDS };\n case \"middle\": {\n const half = ZONE_WINDOW_LENGTH_YARDS / 2;\n return { startYard: 50 - half, endYard: 50 + half };\n }\n }\n}\n\nexport function zoneWindowLength(zone: FieldZone): number {\n const { startYard, endYard } = fieldZoneWindow(zone);\n return endYard - startYard;\n}\n\n/** Width to vertical length ratio of the zone window. Red zone windows are taller than middle. */\nexport function fieldWindowAspect(zone: FieldZone): number {\n return FIELD_WIDTH_YARDS / zoneWindowLength(zone);\n}\n\n/**\n * Clamps a position into the zone window (between the sidelines laterally, edge to edge\n * vertically). Keeps a drag released off screen or a click in the margin from placing a\n * player or point at an invisible position.\n */\nexport function clampToZoneWindow(position: FieldPosition, field: FieldState): FieldPosition {\n const { startYard, endYard } = fieldZoneWindow(field.zone);\n return {\n lateralYard: Math.min(Math.max(position.lateralYard, 0), FIELD_WIDTH_YARDS),\n downfieldYard: Math.min(\n Math.max(position.downfieldYard, startYard - field.losYard),\n endYard - field.losYard,\n ),\n };\n}\n\n/**\n * Number painted on the field (1..50) for an absolute yard. Counted from both goals (...40, 50,\n * 40...). Returns null for the goal lines and the end zones, where no number is painted.\n */\nexport function displayYardNumber(absoluteYard: number): number | null {\n if (absoluteYard <= 0 || absoluteYard >= 100) {\n return null;\n }\n return 50 - Math.abs(50 - absoluteYard);\n}\n\n/** Returns the absolute yards inside the zone window that are multiples of stepYards, ascending. */\nexport function yardLinesInWindow(zone: FieldZone, stepYards: number): number[] {\n if (stepYards <= 0) {\n throw new Error(\"Playmaker: stepYards must be a positive number.\");\n }\n const { startYard, endYard } = fieldZoneWindow(zone);\n const lines: number[] = [];\n // A non-integer stepYards accumulates error over repeated additions, so a little slack\n // keeps the line at the far edge of the window from being dropped.\n const first = Math.ceil(startYard / stepYards) * stepYards;\n for (let y = first; y <= endYard + 1e-9; y += stepYards) {\n lines.push(y);\n }\n return lines;\n}\n\nexport interface CanvasPoint {\n readonly x: number;\n readonly y: number;\n}\n\n/**\n * Fits the zone window into the center of the viewport, keeping its aspect ratio, and converts\n * between yards and Canvas px. Field lines and numbers use absolute yards; players and lines\n * use positions relative to the LOS (FieldPosition).\n */\nexport class FieldGeometry {\n readonly viewportWidth: number;\n readonly viewportHeight: number;\n readonly zone: FieldZone;\n /** Absolute yard of the LOS. The downfieldYard of a FieldPosition is the distance from here. */\n readonly losYard: number;\n readonly yardWindow: YardWindow;\n /** Pixels per yard. The same value vertically and horizontally. */\n readonly scale: number;\n readonly fieldPixelWidth: number;\n readonly fieldPixelHeight: number;\n /** Canvas coordinates of the field's top left; the margin left by the aspect ratio mismatch. */\n readonly offsetX: number;\n readonly offsetY: number;\n\n constructor(viewportWidth: number, viewportHeight: number, field: FieldState) {\n this.viewportWidth = viewportWidth;\n this.viewportHeight = viewportHeight;\n this.zone = field.zone;\n this.losYard = field.losYard;\n this.yardWindow = fieldZoneWindow(field.zone);\n const windowLength = this.yardWindow.endYard - this.yardWindow.startYard;\n const w = Math.max(0, viewportWidth);\n const h = Math.max(0, viewportHeight);\n this.scale = Math.min(w / FIELD_WIDTH_YARDS, h / windowLength);\n this.fieldPixelWidth = FIELD_WIDTH_YARDS * this.scale;\n this.fieldPixelHeight = windowLength * this.scale;\n this.offsetX = (w - this.fieldPixelWidth) / 2;\n this.offsetY = (h - this.fieldPixelHeight) / 2;\n }\n\n xForLateralYard(lateralYard: number): number {\n return this.offsetX + lateralYard * this.scale;\n }\n\n yForAbsoluteYard(absoluteYard: number): number {\n return this.offsetY + (this.yardWindow.endYard - absoluteYard) * this.scale;\n }\n\n toCanvas(position: FieldPosition): CanvasPoint {\n return {\n x: this.xForLateralYard(position.lateralYard),\n y: this.yForAbsoluteYard(this.losYard + position.downfieldYard),\n };\n }\n\n // With a size 0 viewport, scale is 0 and cannot divide; return the field's top left, not NaN.\n lateralYardForX(x: number): number {\n return this.scale > 0 ? (x - this.offsetX) / this.scale : 0;\n }\n\n absoluteYardForY(y: number): number {\n return this.scale > 0\n ? this.yardWindow.endYard - (y - this.offsetY) / this.scale\n : this.yardWindow.endYard;\n }\n\n fromCanvas(point: CanvasPoint): FieldPosition {\n return {\n lateralYard: this.lateralYardForX(point.x),\n downfieldYard: this.absoluteYardForY(point.y) - this.losYard,\n };\n }\n\n /** Inclusive of both edges. */\n containsYard(absoluteYard: number): boolean {\n return absoluteYard >= this.yardWindow.startYard && absoluteYard <= this.yardWindow.endYard;\n }\n}\n","import type { CanvasPoint } from \"./field.js\";\n\n/** Yields adjacent point pairs in order from the start. Yields nothing for fewer than 2 points. */\nexport function* segments<T>(points: readonly T[]): Generator<readonly [T, T]> {\n // Box the previous point so \"no previous point\" stays distinct even when T includes undefined.\n let previous: { readonly point: T } | undefined;\n for (const point of points) {\n if (previous !== undefined) {\n yield [previous.point, point];\n }\n previous = { point };\n }\n}\n\n/** Total length as the sum of distances between adjacent points. 0 for fewer than 2 points. */\nexport function polylineLength(path: readonly CanvasPoint[]): number {\n let total = 0;\n for (const [a, b] of segments(path)) {\n total += Math.hypot(b.x - a.x, b.y - a.y);\n }\n return total;\n}\n\n/**\n * Returns a new polyline that ends at the point reached by walking back distance from the end\n * along the polyline. It does not walk back within the last segment only, because on a finely\n * sampled curve one segment is shorter than distance. maxFraction limits the trimmed length as\n * a fraction of the total length. The default of 1 sets no limit, and a distance at or beyond\n * the total length returns only the start point.\n */\nexport function trimPolylineEnd(\n path: readonly CanvasPoint[],\n distance: number,\n maxFraction = 1,\n): CanvasPoint[] {\n if (path.length < 2 || distance <= 0) {\n return [...path];\n }\n let remaining =\n maxFraction >= 1 ? distance : Math.min(distance, polylineLength(path) * maxFraction);\n if (remaining <= 0) {\n return [...path];\n }\n\n // The arrowhead is much shorter than the total length and usually stops within a few segments\n // from the end, so walk back without copying the array.\n for (let i = path.length - 1; i > 0; i--) {\n const a = path[i - 1];\n const b = path[i];\n /* v8 ignore start -- i runs from 1 to path.length - 1, so both a and b are inside the range. */\n if (a === undefined || b === undefined) {\n break;\n }\n /* v8 ignore stop */\n const segLen = Math.hypot(b.x - a.x, b.y - a.y);\n // Cutting on equality would add a point equal to a, leaving a zero-length segment at the end\n // (a round cap would draw a dot).\n if (segLen > remaining) {\n const t = (segLen - remaining) / segLen;\n return [...path.slice(0, i), { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t }];\n }\n remaining -= segLen;\n }\n return path.slice(0, 1);\n}\n","import type { LineInterpolation } from \"../model/line.js\";\nimport type { FieldPosition } from \"../model/player.js\";\nimport { segments } from \"./polyline.js\";\n\n/** Number of subdivisions per bezier segment. At play diagram sizes, no corners show with this. */\nexport const DEFAULT_BEZIER_SAMPLES_PER_SEGMENT = 16;\n\n/** Point at t (0 to 1) on the cubic bezier from p0 to p1, bent by the control points c1 and c2. */\nexport function cubicBezierPoint(\n p0: FieldPosition,\n c1: FieldPosition,\n c2: FieldPosition,\n p1: FieldPosition,\n t: number,\n): FieldPosition {\n const u = 1 - t;\n const a = u * u * u;\n const b = 3 * u * u * t;\n const c = 3 * u * t * t;\n const d = t * t * t;\n return {\n lateralYard: a * p0.lateralYard + b * c1.lateralYard + c * c2.lateralYard + d * p1.lateralYard,\n downfieldYard:\n a * p0.downfieldYard + b * c1.downfieldYard + c * c2.downfieldYard + d * p1.downfieldYard,\n };\n}\n\n/**\n * Control points [c1, c2] of the cubic bezier through the segment p1 to p2, with directions set\n * by the neighbors p0 and p3. The shape matches a uniform Catmull-Rom curve. At an end segment,\n * pass the end point itself for the missing neighbor. Waypoints are not used directly as bezier\n * control points because the curve would then no longer pass through them.\n */\nexport function catmullRomBezierControls(\n p0: FieldPosition,\n p1: FieldPosition,\n p2: FieldPosition,\n p3: FieldPosition,\n): [FieldPosition, FieldPosition] {\n return [\n {\n lateralYard: p1.lateralYard + (p2.lateralYard - p0.lateralYard) / 6,\n downfieldYard: p1.downfieldYard + (p2.downfieldYard - p0.downfieldYard) / 6,\n },\n {\n lateralYard: p2.lateralYard - (p3.lateralYard - p1.lateralYard) / 6,\n downfieldYard: p2.downfieldYard - (p3.downfieldYard - p1.downfieldYard) / 6,\n },\n ];\n}\n\n/** Consecutive equal points give a zero direction and break the curve, so they fold into one. */\nfunction dedupeConsecutive(points: readonly FieldPosition[]): FieldPosition[] {\n const out: FieldPosition[] = [];\n for (const p of points) {\n const last = out[out.length - 1];\n if (\n last === undefined ||\n last.lateralYard !== p.lateralYard ||\n last.downfieldYard !== p.downfieldYard\n ) {\n out.push({ lateralYard: p.lateralYard, downfieldYard: p.downfieldYard });\n }\n }\n return out;\n}\n\n/**\n * Turns a sequence of control points into the polyline used for drawing and hit testing. With\n * `straight`, or when folding equal points leaves 2 or fewer, the control points are joined as\n * they are. Returns a new array. samplesPerSegment is rounded to at least 1.\n */\nexport function sampleLinePath(\n points: readonly FieldPosition[],\n interpolation: LineInterpolation,\n samplesPerSegment: number = DEFAULT_BEZIER_SAMPLES_PER_SEGMENT,\n): FieldPosition[] {\n const pts = dedupeConsecutive(points);\n if (interpolation === \"straight\" || pts.length < 3) {\n return pts;\n }\n const steps = Math.max(1, Math.floor(samplesPerSegment));\n\n const result: FieldPosition[] = pts.slice(0, 1);\n for (const [i, [p1, p2]] of [...segments(pts)].entries()) {\n const p0 = pts[i - 1] ?? p1;\n const p3 = pts[i + 2] ?? p2;\n const [c1, c2] = catmullRomBezierControls(p0, p1, p2, p3);\n // The point at t=0 equals the previous segment's end point, so t is stepped to 1 excluding 0.\n for (let s = 1; s <= steps; s++) {\n result.push(cubicBezierPoint(p1, c1, c2, p2, s / steps));\n }\n }\n return result;\n}\n","import { indexPlayersById, type Line, lineAnchorPoints } from \"../model/line.js\";\nimport type { FieldPosition, Player } from \"../model/player.js\";\nimport { PLAYER_RADIUS_YARDS } from \"../model/player.js\";\nimport { sampleLinePath } from \"./bezier.js\";\nimport { segments } from \"./polyline.js\";\n\n/**\n * Returns the topmost drawn player overlapping target. Later elements are drawn on top, so the\n * search runs from the end. Square players are also hit by their circumscribed circle; the\n * difference is too small to justify a per-shape hit test.\n */\nexport function hitTestPlayer(\n players: readonly Player[],\n target: FieldPosition,\n radiusYards: number = PLAYER_RADIUS_YARDS,\n): Player | undefined {\n if (radiusYards <= 0) {\n throw new Error(\"Playmaker: radiusYards must be a positive number.\");\n }\n const r2 = radiusYards * radiusYards;\n for (const player of [...players].reverse()) {\n const dx = player.position.lateralYard - target.lateralYard;\n const dy = player.position.downfieldYard - target.downfieldYard;\n if (dx * dx + dy * dy <= r2) {\n return player;\n }\n }\n return undefined;\n}\n\n/**\n * Distance (in yards) within which a line counts as hit. The line width is too thin to grab, so\n * this is kept separate from it. It is smaller than the player radius, which makes lines harder\n * to grab when pressing near a player.\n */\nexport const LINE_HIT_TOLERANCE_YARDS = 0.6;\n\nexport function distanceToSegment(p: FieldPosition, a: FieldPosition, b: FieldPosition): number {\n const abx = b.lateralYard - a.lateralYard;\n const aby = b.downfieldYard - a.downfieldYard;\n const apx = p.lateralYard - a.lateralYard;\n const apy = p.downfieldYard - a.downfieldYard;\n const lenSq = abx * abx + aby * aby;\n // If a and b are the same point, use the distance to a.\n const t = lenSq > 0 ? Math.max(0, Math.min(1, (apx * abx + apy * aby) / lenSq)) : 0;\n const dx = apx - t * abx;\n const dy = apy - t * aby;\n return Math.sqrt(dx * dx + dy * dy);\n}\n\n/** +Infinity when there are no points. */\nfunction distanceToPolyline(p: FieldPosition, points: readonly FieldPosition[]): number {\n const only = points.length === 1 ? points[0] : undefined;\n if (only !== undefined) {\n return pointDistance(p, only);\n }\n let min = Number.POSITIVE_INFINITY;\n for (const [a, b] of segments(points)) {\n min = Math.min(min, distanceToSegment(p, a, b));\n }\n return min;\n}\n\n/**\n * Returns the topmost drawn line overlapping target. The distance is measured to the same\n * polyline used for drawing, so curves are hit exactly as they appear. A line without its start\n * player is not drawn, so it is not hit either.\n */\nexport function hitTestLine(\n lines: readonly Line[],\n players: readonly Player[],\n target: FieldPosition,\n toleranceYards: number = LINE_HIT_TOLERANCE_YARDS,\n): Line | undefined {\n if (toleranceYards <= 0) {\n throw new Error(\"Playmaker: toleranceYards must be a positive number.\");\n }\n const byId = indexPlayersById(players);\n for (const line of [...lines].reverse()) {\n const anchors = lineAnchorPoints(line, byId);\n if (anchors === undefined) {\n continue;\n }\n const polyline = sampleLinePath(anchors, line.interpolation);\n if (distanceToPolyline(target, polyline) <= toleranceYards) {\n return line;\n }\n }\n return undefined;\n}\n\n/** Radius (in yards) for grabbing the waypoint and end point handles of the selected line. */\nexport const WAYPOINT_HANDLE_RADIUS_YARDS = 0.9;\n\nexport function pointDistance(a: FieldPosition, b: FieldPosition): number {\n return Math.hypot(a.lateralYard - b.lateralYard, a.downfieldYard - b.downfieldYard);\n}\n\n/** The grabbed handle and its position when a line handle is hit. */\nexport type LineHandleHit =\n | { readonly kind: \"endpoint\"; readonly point: FieldPosition }\n | { readonly kind: \"waypoint\"; readonly index: number; readonly point: FieldPosition };\n\n/**\n * Returns the line handle hit by target. The end point is tested before the waypoints so that\n * an attempt to move the tip is not stolen by the last waypoint. Later waypoints are drawn in\n * front, so the search runs from the end.\n */\nexport function hitLineHandle(\n line: Pick<Line, \"waypoints\" | \"end\">,\n target: FieldPosition,\n): LineHandleHit | undefined {\n const within = (point: FieldPosition) =>\n pointDistance(target, point) <= WAYPOINT_HANDLE_RADIUS_YARDS;\n if (within(line.end)) {\n return { kind: \"endpoint\", point: line.end };\n }\n for (const [index, point] of [...line.waypoints.entries()].reverse()) {\n if (within(point)) {\n return { kind: \"waypoint\", index, point };\n }\n }\n return undefined;\n}\n","import { Emitter } from \"../base/event.js\";\nimport { Disposable } from \"../base/lifecycle.js\";\nimport type { Line } from \"../model/line.js\";\nimport type { Player } from \"../model/player.js\";\nimport { type EditorViewState, isSameSelection } from \"./editor.js\";\n\n/**\n * A view state notification is sent only when this set of values differs from the one at the\n * last notification (or at construction, if nothing has been notified yet).\n */\ninterface ViewSnapshot {\n readonly state: EditorViewState;\n readonly player: Player | undefined;\n readonly line: Line | undefined;\n}\n\n// How each entry of the view state is compared. Adding an entry makes this a type error until\n// its comparison is written.\nconst VIEW_STATE_EQUALS: {\n readonly [K in keyof EditorViewState]: (a: EditorViewState[K], b: EditorViewState[K]) => boolean;\n} = {\n tool: Object.is,\n selection: isSameSelection,\n canUndo: Object.is,\n canRedo: Object.is,\n fieldZone: Object.is,\n isDrawing: Object.is,\n remainingPlayerSlots: Object.is,\n canStartLine: Object.is,\n};\n\nconst VIEW_STATE_KEYS = Object.keys(VIEW_STATE_EQUALS) as (keyof EditorViewState)[];\n\nfunction isSameEntry<K extends keyof EditorViewState>(\n key: K,\n a: EditorViewState,\n b: EditorViewState,\n): boolean {\n return VIEW_STATE_EQUALS[key](a[key], b[key]);\n}\n\n// Players and lines are replaced on every change, so a reference comparison detects value changes.\nfunction isSameView(a: ViewSnapshot, b: ViewSnapshot): boolean {\n return (\n VIEW_STATE_KEYS.every((key) => isSameEntry(key, a.state, b.state)) &&\n a.player === b.player &&\n a.line === b.line\n );\n}\n\n/**\n * Sends separate notifications for the drawn diagram and the view state. No matter how many\n * changes happen inside a batch, the notifications are coalesced into one of each at the end of\n * the batch. The Model notifies inside a command's apply, so forwarding that as is would let\n * subscribers read canUndo / canRedo before they are updated.\n */\nexport class EditorNotifier extends Disposable {\n private readonly _onDidChangeScene = this._register(new Emitter<void>());\n readonly onDidChangeScene = this._onDidChangeScene.event;\n private readonly _onDidChangeViewState = this._register(new Emitter<void>());\n readonly onDidChangeViewState = this._onDidChangeViewState.event;\n\n private readonly readView: () => ViewSnapshot;\n private depth = 0;\n private sceneChanged = false;\n private lastView: ViewSnapshot;\n\n constructor(readView: () => ViewSnapshot) {\n super();\n this.readView = readView;\n this.lastView = readView();\n }\n\n batch<T>(action: () => T): T {\n this.depth++;\n let result: T;\n try {\n result = action();\n } finally {\n this.depth--;\n }\n this.flushIfIdle();\n return result;\n }\n\n markSceneChanged(): void {\n this.sceneChanged = true;\n this.flushIfIdle();\n }\n\n private flushIfIdle(): void {\n if (this.depth === 0) {\n this.flush();\n }\n }\n\n private flush(): void {\n if (this.sceneChanged) {\n this.sceneChanged = false;\n this._onDidChangeScene.fire();\n }\n const view = this.readView();\n if (!isSameView(view, this.lastView)) {\n this.lastView = view;\n this._onDidChangeViewState.fire();\n }\n }\n}\n","import type { LinePatch } from \"../commands/line-commands.js\";\nimport type { PlayerPatch } from \"../commands/player-commands.js\";\nimport {\n DEFAULT_LINE_INTERPOLATION,\n DEFAULT_LINE_KIND,\n type Line,\n MAX_WAYPOINTS_PER_LINE,\n} from \"../model/line.js\";\nimport type { FieldPosition, Player } from \"../model/player.js\";\nimport type { SceneData } from \"./editor.js\";\n\n/** The target moved by a drag. */\nexport type DragTarget =\n | { readonly kind: \"player\"; readonly playerId: string }\n | { readonly kind: \"waypoint\"; readonly lineId: string; readonly index: number }\n | { readonly kind: \"endpoint\"; readonly lineId: string };\n\nexport interface DragInteraction {\n readonly type: \"drag\";\n readonly target: DragTarget;\n /** The position of the target when it was grabbed. */\n readonly origin: FieldPosition;\n /** The target position minus the pointer. Kept until release so the grabbed point stays put. */\n readonly grabOffset: FieldPosition;\n /** The current position of the target, after being clamped into the window. */\n readonly current: FieldPosition;\n}\n\nexport interface DrawInteraction {\n readonly type: \"draw-line\";\n readonly startPlayerId: string;\n /**\n * The starting player's position when drawing began. Used to check whether the first placed\n * point is at the same position.\n */\n readonly start: FieldPosition;\n /** The placed points. The last one becomes the end point and the rest become waypoints. */\n readonly points: readonly FieldPosition[];\n readonly cursor: FieldPosition;\n}\n\n/** The in-progress state of a drag or drawing. Not put into the Model; overlaid when rendering. */\nexport type Interaction = DragInteraction | DrawInteraction;\n\n/** Whether the coordinates match exactly. For grid points, this is the \"same point\" test. */\nexport function isSamePosition(a: FieldPosition, b: FieldPosition): boolean {\n return a.lateralYard === b.lateralYard && a.downfieldYard === b.downfieldYard;\n}\n\n/**\n * How many points can be placed while drawing. One more than the waypoint limit, because the\n * last point becomes the end point.\n */\nexport const MAX_DRAFT_POINTS = MAX_WAYPOINTS_PER_LINE + 1;\n\nexport function startDrag(\n target: DragTarget,\n origin: FieldPosition,\n pointer: FieldPosition,\n): DragInteraction {\n return {\n type: \"drag\",\n target,\n origin,\n grabOffset: {\n lateralYard: origin.lateralYard - pointer.lateralYard,\n downfieldYard: origin.downfieldYard - pointer.downfieldYard,\n },\n current: origin,\n };\n}\n\n/** Where to put the target while keeping the offset between the grabbed point and the pointer. */\nexport function dragPosition(drag: DragInteraction, pointer: FieldPosition): FieldPosition {\n return {\n lateralYard: pointer.lateralYard + drag.grabOffset.lateralYard,\n downfieldYard: pointer.downfieldYard + drag.grabOffset.downfieldYard,\n };\n}\n\n/**\n * The change that puts the target at the drag destination. Both the preview and the commit apply\n * it, so what is shown never differs from the result.\n */\nexport type DragPatch =\n | { readonly kind: \"player\"; readonly playerId: string; readonly patch: PlayerPatch }\n | { readonly kind: \"line\"; readonly lineId: string; readonly patch: LinePatch };\n\n/** The change that puts the target at to. Undefined if the target is not in the scene. */\nexport function dragPatch(\n scene: SceneData,\n target: DragTarget,\n to: FieldPosition,\n): DragPatch | undefined {\n switch (target.kind) {\n case \"player\":\n return scene.players.some((p) => p.id === target.playerId)\n ? { kind: \"player\", playerId: target.playerId, patch: { position: to } }\n : undefined;\n case \"endpoint\":\n return scene.lines.some((l) => l.id === target.lineId)\n ? { kind: \"line\", lineId: target.lineId, patch: { end: to } }\n : undefined;\n case \"waypoint\": {\n const line = scene.lines.find((l) => l.id === target.lineId);\n return line === undefined\n ? undefined\n : {\n kind: \"line\",\n lineId: line.id,\n patch: { waypoints: line.waypoints.map((w, i) => (i === target.index ? to : w)) },\n };\n }\n }\n}\n\n/**\n * Splits the placed points into the waypoints and end point of the line to commit. Undefined if\n * there are no points.\n */\nexport function splitDraftPoints(\n points: readonly FieldPosition[],\n): { readonly waypoints: readonly FieldPosition[]; readonly end: FieldPosition } | undefined {\n const end = points.at(-1);\n return end === undefined ? undefined : { waypoints: points.slice(0, -1), end };\n}\n\n/** A line always starts at a player, so drawing begins from a player. */\nexport function startDrawing(player: Player): DrawInteraction {\n return {\n type: \"draw-line\",\n startPlayerId: player.id,\n start: player.position,\n points: [],\n cursor: player.position,\n };\n}\n\n/**\n * Places a point at point. When it is at the same position as the previous point (or the starting\n * player if there are no points yet), or the point limit has been reached, only the cursor advances\n * and no point is placed. Same positions are rejected because committing with a double-click\n * delivers pointerdown twice, which would place another point right on top of the previous one.\n */\nexport function addDraftPoint(draw: DrawInteraction, point: FieldPosition): DrawInteraction {\n const last = draw.points.at(-1) ?? draw.start;\n const accepts = !isSamePosition(last, point) && draw.points.length < MAX_DRAFT_POINTS;\n return { ...draw, points: accepts ? [...draw.points, point] : draw.points, cursor: point };\n}\n\n/**\n * The waypoints and end point of the line to commit. anchor is the current position of the starting\n * player. Undefined when there are no points, or when every placed point equals anchor and the\n * length is 0. A zero-length line is invisible and cannot be selected.\n */\nexport function committableDraft(\n draw: DrawInteraction,\n anchor: FieldPosition,\n): { readonly waypoints: readonly FieldPosition[]; readonly end: FieldPosition } | undefined {\n const split = splitDraftPoints(draw.points);\n if (split === undefined) {\n return undefined;\n }\n return draw.points.every((p) => isSamePosition(p, anchor)) ? undefined : split;\n}\n\n/** Builds a line from the drawing. The preview and the commit use the same defaults. */\nexport function draftToLine(\n draw: DrawInteraction,\n id: string,\n shape: { readonly waypoints: readonly FieldPosition[]; readonly end: FieldPosition },\n): Line {\n return {\n id,\n kind: DEFAULT_LINE_KIND,\n startPlayerId: draw.startPlayerId,\n waypoints: shape.waypoints,\n end: shape.end,\n interpolation: DEFAULT_LINE_INTERPOLATION,\n };\n}\n","import { applyLinePatch } from \"../commands/line-commands.js\";\nimport { applyPlayerPatch } from \"../commands/player-commands.js\";\nimport type { Line } from \"../model/line.js\";\nimport type { EditorOverlay, EditorSelection, SceneData } from \"./editor.js\";\nimport {\n type DragPatch,\n type DrawInteraction,\n draftToLine,\n dragPatch,\n type Interaction,\n MAX_DRAFT_POINTS,\n splitDraftPoints,\n} from \"./interaction.js\";\n\n/** The id of the line being drawn. Never enters the Model; used only inside the preview. */\nconst DRAFT_LINE_ID = \"__playmaker_draft_line__\";\n\n// A line starts at a player, so replacing the player is enough for the line to follow.\nfunction applyDragPatch(data: SceneData, drop: DragPatch): SceneData {\n switch (drop.kind) {\n case \"player\":\n return {\n ...data,\n players: data.players.map((p) =>\n p.id === drop.playerId ? applyPlayerPatch(p, drop.patch) : p,\n ),\n };\n case \"line\":\n return {\n ...data,\n lines: data.lines.map((l) => (l.id === drop.lineId ? applyLinePatch(l, drop.patch) : l)),\n };\n }\n}\n\nfunction draftLine(draw: DrawInteraction): Line {\n // Once the point limit is reached, draw the last placed point as the end point, just as the\n // committed line will have it.\n const full = draw.points.length >= MAX_DRAFT_POINTS ? splitDraftPoints(draw.points) : undefined;\n return draftToLine(draw, DRAFT_LINE_ID, full ?? { waypoints: draw.points, end: draw.cursor });\n}\n\n/** Overlays the in-progress drag or drawing on the committed diagram. Does not modify data. */\nexport function composePreview(data: SceneData, interaction: Interaction | undefined): SceneData {\n if (interaction === undefined) {\n return data;\n }\n switch (interaction.type) {\n case \"drag\": {\n const drop = dragPatch(data, interaction.target, interaction.current);\n return drop === undefined ? data : applyDragPatch(data, drop);\n }\n case \"draw-line\":\n return { ...data, lines: [...data.lines, draftLine(interaction)] };\n }\n}\n\n/**\n * The selection highlight and handle positions. Pass as scene the result of composePreview, with\n * the in-progress state overlaid. That way a handle being dragged is drawn at the drag destination.\n */\nexport function computeOverlay(scene: SceneData, selection: EditorSelection): EditorOverlay {\n if (selection.kind === \"player\") {\n return { kind: \"player\", playerId: selection.id };\n }\n const line =\n selection.kind === \"line\" ? scene.lines.find((l) => l.id === selection.id) : undefined;\n if (line === undefined) {\n return { kind: \"none\" };\n }\n return { kind: \"line\", waypointHandles: line.waypoints, endpointHandle: line.end };\n}\n","import type { Event } from \"../base/event.js\";\nimport { Disposable } from \"../base/lifecycle.js\";\nimport type { ICommand } from \"../commands/command.js\";\nimport type { ICommandService } from \"../commands/command-service.js\";\nimport { SetFieldZoneCommand } from \"../commands/field-commands.js\";\nimport { LoadFormationCommand } from \"../commands/formation-commands.js\";\nimport {\n AddLineCommand,\n type LinePatch,\n RemoveLineCommand,\n UpdateLineCommand,\n} from \"../commands/line-commands.js\";\nimport {\n AddPlayerCommand,\n type PlayerPatch,\n RemovePlayerCommand,\n UpdatePlayerCommand,\n} from \"../commands/player-commands.js\";\nimport { type Formation, instantiateFormation } from \"../formations/formation.js\";\nimport { clampToZoneWindow } from \"../geometry/field.js\";\nimport { snapToGrid } from \"../geometry/grid.js\";\nimport { hitLineHandle, hitTestLine, hitTestPlayer } from \"../geometry/hit-test.js\";\nimport type { IIdFactory } from \"../model/id-factory.js\";\nimport { type Line, MAX_LINES } from \"../model/line.js\";\nimport type { FieldZone } from \"../model/play-data.js\";\nimport type { IPlayModel } from \"../model/play-model.js\";\nimport {\n DEFAULT_PLAYER_SHAPE,\n type FieldPosition,\n MAX_PLAYERS,\n type Player,\n} from \"../model/player.js\";\nimport {\n canLoadFormation,\n type EditorFrame,\n type EditorSelection,\n type EditorTool,\n type EditorViewState,\n type IEditorController,\n isSameSelection,\n isToolAvailable,\n NO_SELECTION,\n} from \"./editor.js\";\nimport { EditorNotifier } from \"./editor-notifier.js\";\nimport {\n addDraftPoint,\n committableDraft,\n type DragInteraction,\n type DragTarget,\n type DrawInteraction,\n draftToLine,\n dragPatch,\n dragPosition,\n type Interaction,\n isSamePosition,\n startDrag,\n startDrawing,\n} from \"./interaction.js\";\nimport { composePreview, computeOverlay } from \"./preview.js\";\n\n/**\n * Takes the tool, the selection, and gestures, and issues commands. It never writes to the\n * Model directly. An in-progress drag or drawing is not put into the Model but is overlaid by\n * getFrame, so neither the Model's onDidChange nor the history moves until the edit is committed.\n */\nexport class EditorController extends Disposable implements IEditorController {\n private readonly model: IPlayModel;\n private readonly commands: ICommandService;\n private readonly ids: IIdFactory;\n\n private readonly notifier: EditorNotifier;\n readonly onDidChangeScene: Event<void>;\n readonly onDidChangeViewState: Event<void>;\n\n private tool: EditorTool = \"select\";\n private selection: EditorSelection = NO_SELECTION;\n private interaction: Interaction | undefined;\n\n constructor(model: IPlayModel, commands: ICommandService, ids: IIdFactory) {\n super();\n this.model = model;\n this.commands = commands;\n this.ids = ids;\n this.notifier = this._register(\n new EditorNotifier(() => ({\n state: this.getViewState(),\n player: this.getSelectedPlayer(),\n line: this.getSelectedLine(),\n })),\n );\n this.onDidChangeScene = this.notifier.onDidChangeScene;\n this.onDidChangeViewState = this.notifier.onDidChangeViewState;\n this._register(this.model.onDidChange(() => this.notifier.markSceneChanged()));\n }\n\n getViewState(): EditorViewState {\n const { players, lines } = this.model.getSnapshot();\n return {\n tool: this.tool,\n selection:\n this.getSelectedPlayer() === undefined && this.getSelectedLine() === undefined\n ? NO_SELECTION\n : this.selection,\n canUndo: this.commands.canUndo,\n canRedo: this.commands.canRedo,\n fieldZone: this.model.getFieldZone(),\n isDrawing: this.interaction?.type === \"draw-line\",\n remainingPlayerSlots: MAX_PLAYERS - players.length,\n canStartLine: lines.length < MAX_LINES,\n };\n }\n\n getSelectedPlayer(): Player | undefined {\n const s = this.selection;\n return s.kind === \"player\" ? this.model.findPlayer(s.id) : undefined;\n }\n\n getSelectedLine(): Line | undefined {\n const s = this.selection;\n return s.kind === \"line\" ? this.model.findLine(s.id) : undefined;\n }\n\n getFrame(): EditorFrame {\n const scene = composePreview(this.model.getSnapshot(), this.interaction);\n return { scene, overlay: computeOverlay(scene, this.selection) };\n }\n\n setTool(tool: EditorTool): void {\n if (tool === this.tool) {\n return;\n }\n this.notifier.batch(() => {\n this.tool = tool;\n this.setInteraction(undefined);\n });\n }\n\n pointerDown(pos: FieldPosition): void {\n this.notifier.batch(() => {\n switch (this.tool) {\n case \"add-player\":\n this.addPlayerAt(pos);\n return;\n case \"draw-line\":\n this.drawLinePointerDown(pos);\n return;\n case \"select\":\n this.selectPointerDown(pos);\n return;\n }\n });\n }\n\n pointerMove(pos: FieldPosition): void {\n const interaction = this.interaction;\n if (interaction === undefined) {\n return;\n }\n this.notifier.batch(() =>\n this.setInteraction(\n interaction.type === \"draw-line\"\n ? { ...interaction, cursor: this.clampToField(pos) }\n : { ...interaction, current: this.clampToField(dragPosition(interaction, pos)) },\n ),\n );\n }\n\n pointerUp(pos: FieldPosition): void {\n const interaction = this.interaction;\n // Drawing places points on click (pointerDown), so up does nothing.\n if (interaction?.type !== \"drag\") {\n return;\n }\n this.notifier.batch(() => {\n this.setInteraction(undefined);\n const moved = dragPosition(interaction, pos);\n // Compare the position before snapping. Merely clicking a player that sits outside the window\n // must not move it to the window's edge.\n if (isSamePosition(moved, interaction.origin)) {\n return;\n }\n const to = this.clampToField(moved);\n // A move that lands on the same grid point does not change the diagram, so it is put into\n // neither the history nor a notification.\n if (isSamePosition(to, interaction.origin)) {\n return;\n }\n const command = this.dropCommand(interaction, to);\n if (command !== undefined) {\n this.commands.execute(command);\n }\n });\n }\n\n cancelInteraction(): void {\n this.notifier.batch(() => this.setInteraction(undefined));\n }\n\n commitLine(): void {\n const interaction = this.interaction;\n if (interaction?.type !== \"draw-line\") {\n return;\n }\n this.notifier.batch(() => {\n this.setInteraction(undefined);\n const line = this.lineFromDraft(interaction);\n if (line === undefined) {\n return;\n }\n this.commands.execute(new AddLineCommand(line));\n // Right after drawing, switch back to the select tool and select the drawn line. Being able\n // to edit it right away is preferred over drawing the next line in a row.\n this.setTool(\"select\");\n this.setSelection({ kind: \"line\", id: line.id });\n });\n }\n\n deleteSelection(): void {\n const player = this.getSelectedPlayer();\n const line = this.getSelectedLine();\n this.notifier.batch(() => {\n if (player !== undefined) {\n this.commands.execute(new RemovePlayerCommand(player.id));\n } else if (line !== undefined) {\n this.commands.execute(new RemoveLineCommand(line.id));\n } else {\n return;\n }\n this.setSelection(NO_SELECTION);\n });\n }\n\n updateSelectedPlayer(patch: PlayerPatch): void {\n const player = this.getSelectedPlayer();\n if (player === undefined) {\n return;\n }\n this.notifier.batch(() => this.commands.execute(new UpdatePlayerCommand(player.id, patch)));\n }\n\n updateSelectedLine(patch: LinePatch): void {\n const line = this.getSelectedLine();\n if (line === undefined) {\n return;\n }\n this.notifier.batch(() => this.commands.execute(new UpdateLineCommand(line.id, patch)));\n }\n\n setFieldZone(zone: FieldZone): void {\n this.notifier.batch(() => {\n // If the same zone was merely selected again, keep the in-progress drag or drawing.\n if (this.commands.execute(new SetFieldZoneCommand(zone))) {\n this.setInteraction(undefined);\n }\n });\n }\n\n loadFormation(formation: Formation): boolean {\n if (!canLoadFormation(formation, this.getViewState())) {\n return false;\n }\n const { players } = this.model.getSnapshot();\n const added = instantiateFormation(\n formation,\n this.ids,\n players.map((p) => p.id),\n );\n return this.notifier.batch(() => {\n const loaded = this.commands.execute(new LoadFormationCommand(added));\n if (loaded) {\n // After loading, the in-progress operation and old selection mean nothing, so clear them.\n this.setInteraction(undefined);\n this.setSelection(NO_SELECTION);\n }\n return loaded;\n });\n }\n\n undo(): void {\n this.notifier.batch(() => {\n const interaction = this.interaction;\n if (interaction?.type === \"draw-line\") {\n this.setInteraction(\n interaction.points.length === 0\n ? undefined\n : { ...interaction, points: interaction.points.slice(0, -1) },\n );\n return;\n }\n this.setInteraction(undefined);\n this.commands.undo();\n });\n }\n\n redo(): void {\n this.notifier.batch(() => {\n this.setInteraction(undefined);\n this.commands.redo();\n });\n }\n\n private setSelection(next: EditorSelection): void {\n if (isSameSelection(this.selection, next)) {\n return;\n }\n this.selection = next;\n this.notifier.markSceneChanged();\n }\n\n private setInteraction(next: Interaction | undefined): void {\n if (next === this.interaction) {\n return;\n }\n this.interaction = next;\n this.notifier.markSceneChanged();\n }\n\n // Every window edge (the sidelines horizontally, whole yards from the LOS vertically) is a grid\n // point, so clamping and then rounding never leaves the window.\n private clampToField(pos: FieldPosition): FieldPosition {\n return snapToGrid(clampToZoneWindow(pos, this.model.getSnapshot().field));\n }\n\n // Returns undefined and issues no command if the target disappeared during the drag.\n private dropCommand(drag: DragInteraction, to: FieldPosition): ICommand | undefined {\n const drop = dragPatch(this.model.getSnapshot(), drag.target, to);\n if (drop === undefined) {\n return undefined;\n }\n return drop.kind === \"player\"\n ? new UpdatePlayerCommand(drop.playerId, drop.patch)\n : new UpdateLineCommand(drop.lineId, drop.patch);\n }\n\n // Does not commit if the starting player has disappeared.\n private lineFromDraft(draw: DrawInteraction): Line | undefined {\n const start = this.model.findPlayer(draw.startPlayerId);\n const split = start === undefined ? undefined : committableDraft(draw, start.position);\n if (split === undefined) {\n return undefined;\n }\n const id = this.ids.next(\"line\", new Set(this.model.getSnapshot().lines.map((l) => l.id)));\n return draftToLine(draw, id, split);\n }\n\n private selectPointerDown(pos: FieldPosition): void {\n const data = this.model.getSnapshot();\n // Grab a handle of the selected line before anything else, so it can be edited even when it\n // overlaps a player or another line.\n const handle = this.hitSelectedLineHandle(pos);\n if (handle !== undefined) {\n this.setInteraction(handle);\n return;\n }\n // Selecting a player also starts a drag. Releasing without moving is just a selection.\n const player = hitTestPlayer(data.players, pos);\n if (player !== undefined) {\n this.setSelection({ kind: \"player\", id: player.id });\n this.setInteraction(startDrag({ kind: \"player\", playerId: player.id }, player.position, pos));\n return;\n }\n const line = hitTestLine(data.lines, data.players, pos);\n this.setSelection(line === undefined ? NO_SELECTION : { kind: \"line\", id: line.id });\n }\n\n private hitSelectedLineHandle(pos: FieldPosition): DragInteraction | undefined {\n const line = this.getSelectedLine();\n const hit = line === undefined ? undefined : hitLineHandle(line, pos);\n if (line === undefined || hit === undefined) {\n return undefined;\n }\n const target: DragTarget =\n hit.kind === \"endpoint\"\n ? { kind: \"endpoint\", lineId: line.id }\n : { kind: \"waypoint\", lineId: line.id, index: hit.index };\n return startDrag(target, hit.point, pos);\n }\n\n private drawLinePointerDown(pos: FieldPosition): void {\n const interaction = this.interaction;\n if (interaction?.type === \"draw-line\") {\n this.setInteraction(addDraftPoint(interaction, this.clampToField(pos)));\n return;\n }\n if (!isToolAvailable(\"draw-line\", this.getViewState())) {\n return;\n }\n const data = this.model.getSnapshot();\n // Ignore an attempt to start anywhere other than on a player.\n const player = hitTestPlayer(data.players, pos);\n if (player === undefined) {\n return;\n }\n this.setInteraction(startDrawing(player));\n }\n\n private addPlayerAt(pos: FieldPosition): void {\n if (!isToolAvailable(\"add-player\", this.getViewState())) {\n return;\n }\n const { players } = this.model.getSnapshot();\n const id = this.ids.next(\"player\", new Set(players.map((p) => p.id)));\n this.commands.execute(\n new AddPlayerCommand({\n id,\n position: this.clampToField(pos),\n shape: DEFAULT_PLAYER_SHAPE,\n label: \"\",\n }),\n );\n this.setSelection({ kind: \"player\", id });\n }\n}\n","import { Emitter, type Event } from \"../base/event.js\";\nimport { Disposable, DisposableStore } from \"../base/lifecycle.js\";\nimport { CommandService } from \"../commands/command-service.js\";\nimport { UndoRedoService } from \"../commands/undo-redo-service.js\";\nimport { type Formation, normalizeFormation } from \"../formations/formation.js\";\nimport { IdFactory } from \"../model/id-factory.js\";\nimport type { FieldZone, PlayData } from \"../model/play-data.js\";\nimport { PlayModel } from \"../model/play-model.js\";\nimport type { IEditorController } from \"./editor.js\";\nimport { EditorController } from \"./editor-controller.js\";\n\ninterface PlayDocument {\n readonly store: DisposableStore;\n readonly model: PlayModel;\n readonly controller: EditorController;\n}\n\nfunction openDocument(data: unknown): PlayDocument {\n const store = new DisposableStore();\n const model = store.add(new PlayModel(data));\n const commands = new CommandService(model, new UndoRedoService());\n const controller = store.add(new EditorController(model, commands, new IdFactory()));\n return { store, model, controller };\n}\n\n/**\n * Handles the editing of a single play diagram.\n * setPlayData rebuilds everything, history included, so the controller becomes a new object too.\n */\nexport class PlaySession extends Disposable {\n private readonly _onDidReset = this._register(new Emitter<void>());\n /** Fires after setPlayData has rebuilt the controller. */\n readonly onDidReset = this._onDidReset.event;\n\n private readonly _onDidEdit = this._register(new Emitter<void>());\n /**\n * Passes a deep copy of the latest diagram once per committed edit. Does not fire at\n * construction or on setPlayData (loading is not an edit).\n * A copy is made per listener, so a receiver that modifies it affects neither other listeners\n * nor this diagram.\n */\n readonly onDidChange: Event<PlayData> = (listener) =>\n this._onDidEdit.event(() => listener(this.getPlayData()));\n\n private readonly _onDidChangeScene = this._register(new Emitter<void>());\n /**\n * Forwards onDidChangeScene of the current controller. Also fires once after onDidReset when\n * setPlayData reloads the diagram. No need to resubscribe when the controller is rebuilt.\n */\n readonly onDidChangeScene = this._onDidChangeScene.event;\n\n private readonly _onDidChangeViewState = this._register(new Emitter<void>());\n /**\n * Forwards onDidChangeViewState of the current controller. Also fires once after onDidReset\n * when setPlayData reloads the diagram, whether or not the view state changed.\n */\n readonly onDidChangeViewState = this._onDidChangeViewState.event;\n\n private document: PlayDocument;\n\n constructor(data: unknown) {\n super();\n this.document = this.open(data);\n }\n\n get controller(): IEditorController {\n return this.document.controller;\n }\n\n get fieldZone(): FieldZone {\n return this.document.model.getFieldZone();\n }\n\n /** A deep copy of the current diagram. Modifying the returned value does not affect this one. */\n getPlayData(): PlayData {\n return this.document.model.getData();\n }\n\n /** Returns the current diagram without copying. */\n getSnapshot(): PlayData {\n return this.document.model.getSnapshot();\n }\n\n setFieldZone(zone: FieldZone): void {\n this.document.controller.setFieldZone(zone);\n }\n\n /**\n * Normalizes an external formation before loading it. Does nothing and returns false if not a\n * single player can be placed.\n */\n loadFormation(formation: Formation): boolean {\n const normalized = normalizeFormation(formation);\n return normalized !== null && this.document.controller.loadFormation(normalized);\n }\n\n setPlayData(data: unknown): void {\n this.document.store.dispose();\n this.document = this.open(data);\n this._onDidReset.fire();\n this._onDidChangeScene.fire();\n this._onDidChangeViewState.fire();\n }\n\n private open(data: unknown): PlayDocument {\n const opened = openDocument(data);\n const { store, model, controller } = opened;\n store.add(model.onDidChange(() => this._onDidEdit.fire()));\n store.add(controller.onDidChangeScene(() => this._onDidChangeScene.fire()));\n store.add(controller.onDidChangeViewState(() => this._onDidChangeViewState.fire()));\n return opened;\n }\n\n override dispose(): void {\n this.document.store.dispose();\n super.dispose();\n }\n}\n","import { fieldWindowAspect } from \"../geometry/field.js\";\nimport { isFiniteNumber } from \"../model/guards.js\";\nimport type { FieldZone } from \"../model/play-data.js\";\n\n/** PNG width (px) when unspecified. Big enough not to look coarse when pasted into a document. */\nexport const DEFAULT_EXPORT_WIDTH = 1600;\n\n/**\n * The export width limit (px). Even the tall red zone window stays within about 11 million\n * pixels, so browsers that cap the canvas area at about 16.78 million pixels (Safari on iOS)\n * can still draw it.\n */\nexport const MAX_EXPORT_WIDTH = 4096;\n\nexport interface ImageExportOptions {\n /**\n * The PNG width (px). The height is derived from the aspect ratio of the zone's window.\n * Unspecified, non-finite, or below 1 becomes 1600; a width above 4096 is clamped to 4096.\n */\n width?: number | undefined;\n}\n\nexport interface ImageExportSize {\n readonly width: number;\n readonly height: number;\n}\n\n/**\n * Turns an externally given width into integer px. Non-finite values and values below 1 become\n * the default width; a width above the limit is clamped to the limit.\n */\nexport function resolveImageExportWidth(width: number | undefined): number {\n if (!isFiniteNumber(width) || width < 1) {\n return DEFAULT_EXPORT_WIDTH;\n }\n return Math.min(Math.round(width), MAX_EXPORT_WIDTH);\n}\n\n/**\n * The height is derived from the aspect ratio of the zone's window, so the image has no margins.\n * The window's aspect ratio is under 2, so a width of at least 1 rounds to a height of at least 1.\n */\nexport function resolveImageExportSize(\n zone: FieldZone,\n options: ImageExportOptions = {},\n): ImageExportSize {\n const width = resolveImageExportWidth(options.width);\n return { width, height: Math.round(width / fieldWindowAspect(zone)) };\n}\n","// Alignments shared by the formation presets and the play presets. Vertical is the position from\n// the LOS (the LOS is 0 and the offense's direction is positive), and horizontal puts the center\n// at 26.5 yards (the middle of the field). Every position is written on a 0.5-yard grid point.\n// They fit in the window of whichever zone they are loaded into. Names are English only; they are\n// not localized through the ja dictionary.\n//\n// The OL sit 1 yard behind the LOS and the defensive line 1.5 yards in front of it, leaving a gap\n// so the markers do not overlap. Players on the LOS (OL, TE, outside WRs) are at -1, players off\n// the LOS (slot, flanker) at -2, and a QB under center at -3. The defensive line aligns on the\n// OL's shoulders (shades), so it is readable which gap each defender has. Against a formation\n// with a TE, the front aligns over, with the strong-side E on the TE's outside shoulder\n// (9-technique). Every defensive player gets a label.\n//\n// Players have ids, because play lines refer to their starting player by id. The ids are stripped\n// when they are turned into formations.\n\nimport type { FieldPosition, Player, PlayerShape } from \"../model/player.js\";\n\nexport function pt(lateralYard: number, downfieldYard: number): FieldPosition {\n return { lateralYard, downfieldYard };\n}\n\nexport function player(\n id: string,\n label: string,\n position: FieldPosition,\n shape: PlayerShape = \"circle\",\n): Player {\n return { id, position, shape, label };\n}\n\n/**\n * 1 yard short of the opponent. Used as a block's end point, so the T bar touches the edge of the\n * opponent's marker. lateral shifts it toward the opponent's shoulder (reach or down block).\n */\nexport function shortOf(target: FieldPosition, lateral = 0): FieldPosition {\n return pt(target.lateralYard + lateral, target.downfieldYard - 1);\n}\n\n/**\n * Beside the opponent's shoulder. In a combo block, the blocker climbing to the LB goes through\n * here. lateral is kept at least 1 yard away.\n */\nexport function beside(target: FieldPosition, lateral: number): FieldPosition {\n return pt(target.lateralYard + lateral, target.downfieldYard);\n}\n\nexport const OL = {\n lt: pt(21.5, -1),\n lg: pt(24, -1),\n c: pt(26.5, -1),\n rg: pt(29, -1),\n rt: pt(31.5, -1),\n} as const;\n\n// Four-down over front. With the right as the strong side, the E aligns on the LT's outside\n// shoulder, the N on the C's weak-side shoulder, and the T on the RG's outside shoulder. With no\n// TE, the strong-side E aligns on the RT's outside shoulder.\nexport const FRONT_4 = {\n eL: pt(20.5, 1.5),\n n: pt(25.5, 1.5),\n t: pt(30, 1.5),\n eR: pt(32.5, 1.5),\n} as const;\n\n/** The strong-side E when the TE is on the right: the TE's outside shoulder (9-technique). */\nexport const END_OVER_TE = pt(35.5, 1.5);\n\n// Three-down odd front. N head up on the C, Es on the inside shoulder of each tackle.\nexport const FRONT_3 = {\n eL: pt(22.5, 1.5),\n n: pt(26.5, 1.5),\n eR: pt(30.5, 1.5),\n} as const;\n\n// 4-3 over LBs. The S aligns head up on the TE.\nexport const LB_43 = { w: pt(22.5, 5), m: pt(27, 5), s: pt(34.5, 5) } as const;\n\n// The two nickel ILBs and the nickel back covering the slot.\nexport const LB_NICKEL = { m: pt(24, 5), w: pt(30, 5), nb: pt(14, 5) } as const;\n\nexport function cornerSpots(depth: number): { left: FieldPosition; right: FieldPosition } {\n return { left: pt(6.5, depth), right: pt(46.5, depth) };\n}\n\nexport function offensiveLine(): Player[] {\n return [\n player(\"lt\", \"LT\", OL.lt, \"square\"),\n player(\"lg\", \"LG\", OL.lg, \"square\"),\n player(\"c\", \"C\", OL.c, \"square\"),\n player(\"rg\", \"RG\", OL.rg, \"square\"),\n player(\"rt\", \"RT\", OL.rt, \"square\"),\n ];\n}\n\nexport function front4(strongEnd: FieldPosition = FRONT_4.eR): Player[] {\n return [\n player(\"de-l\", \"E\", FRONT_4.eL),\n player(\"dt-l\", \"N\", FRONT_4.n),\n player(\"dt-r\", \"T\", FRONT_4.t),\n player(\"de-r\", \"E\", strongEnd),\n ];\n}\n\nexport function front3(): Player[] {\n return [\n player(\"de-l\", \"E\", FRONT_3.eL),\n player(\"nt\", \"N\", FRONT_3.n),\n player(\"de-r\", \"E\", FRONT_3.eR),\n ];\n}\n\nexport function corners(depth: number): Player[] {\n const { left, right } = cornerSpots(depth);\n return [player(\"cb-l\", \"CB\", left), player(\"cb-r\", \"CB\", right)];\n}\n\n// ---- Offensive formations ----\n\nexport function iFormation(): Player[] {\n return [\n ...offensiveLine(),\n player(\"y\", \"Y\", pt(34.5, -1), \"square\"),\n player(\"qb\", \"QB\", pt(26.5, -3)),\n player(\"fb\", \"FB\", pt(26.5, -6)),\n player(\"rb\", \"RB\", pt(26.5, -8.5)),\n player(\"x\", \"X\", pt(6.5, -1)),\n player(\"z\", \"Z\", pt(46.5, -2)),\n ];\n}\n\nexport function singlebackAce(): Player[] {\n return [\n ...offensiveLine(),\n player(\"y\", \"Y\", pt(34.5, -1), \"square\"),\n player(\"qb\", \"QB\", pt(26.5, -3)),\n player(\"rb\", \"RB\", pt(26.5, -7.5)),\n player(\"x\", \"X\", pt(6, -1)),\n player(\"h\", \"H\", pt(40, -2)),\n player(\"z\", \"Z\", pt(47, -1)),\n ];\n}\n\nexport function shotgunSpread(): Player[] {\n return [\n ...offensiveLine(),\n player(\"qb\", \"QB\", pt(26.5, -5)),\n player(\"rb\", \"RB\", pt(29.5, -5)),\n player(\"x\", \"X\", pt(5.5, -1)),\n player(\"y\", \"Y\", pt(13, -2)),\n player(\"h\", \"H\", pt(40, -2)),\n player(\"z\", \"Z\", pt(47.5, -1)),\n ];\n}\n\nexport function trips(): Player[] {\n return [\n ...offensiveLine(),\n player(\"qb\", \"QB\", pt(26.5, -5)),\n player(\"rb\", \"RB\", pt(24, -5)),\n player(\"x\", \"X\", pt(5.5, -1)),\n player(\"y\", \"Y\", pt(34.5, -1), \"square\"),\n player(\"h\", \"H\", pt(41, -2)),\n player(\"z\", \"Z\", pt(47.5, -1)),\n ];\n}\n\nexport function empty(): Player[] {\n return [\n ...offensiveLine(),\n player(\"qb\", \"QB\", pt(26.5, -5)),\n player(\"x\", \"X\", pt(5, -1)),\n player(\"f\", \"F\", pt(12, -2)),\n player(\"y\", \"Y\", pt(34.5, -1), \"square\"),\n player(\"h\", \"H\", pt(41, -2)),\n player(\"z\", \"Z\", pt(48, -1)),\n ];\n}\n\nexport function pistol(): Player[] {\n return [\n ...offensiveLine(),\n player(\"y\", \"Y\", pt(34.5, -1), \"square\"),\n player(\"qb\", \"QB\", pt(26.5, -4.5)),\n player(\"rb\", \"RB\", pt(26.5, -7.5)),\n player(\"x\", \"X\", pt(6, -1)),\n player(\"h\", \"H\", pt(40, -2)),\n player(\"z\", \"Z\", pt(47, -1)),\n ];\n}\n\nexport function bunch(): Player[] {\n return [\n ...offensiveLine(),\n player(\"qb\", \"QB\", pt(26.5, -5)),\n player(\"rb\", \"RB\", pt(24, -5)),\n player(\"x\", \"X\", pt(5.5, -1)),\n player(\"z\", \"Z\", pt(37, -1)),\n player(\"h\", \"H\", pt(34.5, -2)),\n player(\"y\", \"Y\", pt(39.5, -2)),\n ];\n}\n\n// ---- Defensive formations ----\n\n/** 4-3 over. Aligned against a formation with the TE on the right. */\nexport function defense43(): Player[] {\n return [\n ...front4(END_OVER_TE),\n player(\"wlb\", \"W\", LB_43.w),\n player(\"mlb\", \"M\", LB_43.m),\n player(\"slb\", \"S\", LB_43.s),\n ...corners(6),\n player(\"fs\", \"FS\", pt(23, 11)),\n player(\"ss\", \"SS\", pt(32, 9)),\n ];\n}\n\nexport function defense34(): Player[] {\n return [\n ...front3(),\n player(\"wlb\", \"W\", pt(19, 2.5)),\n player(\"mlb\", \"M\", pt(24.5, 5)),\n player(\"tlb\", \"T\", pt(29, 5)),\n player(\"slb\", \"S\", pt(34.5, 2.5)),\n ...corners(6),\n player(\"fs\", \"FS\", pt(23, 11)),\n player(\"ss\", \"SS\", pt(30, 11)),\n ];\n}\n\n/** The six front players of the nickel (four down linemen and two ILBs). */\nexport function nickelFront(strongEnd: FieldPosition = FRONT_4.eR): Player[] {\n return [...front4(strongEnd), player(\"mlb\", \"M\", LB_NICKEL.m), player(\"wlb\", \"W\", LB_NICKEL.w)];\n}\n\n/** How the five back-end players of the nickel are placed. Varies by coverage and pressure. */\nexport interface NickelShell {\n readonly cornerDepth: number;\n readonly fs: FieldPosition;\n readonly ss: FieldPosition;\n}\n\n/** Two-high shell with the CBs at 6 yards and the safeties at 11. */\nexport const NICKEL_BASE: NickelShell = { cornerDepth: 6, fs: pt(22, 11), ss: pt(31, 11) };\n\nexport function nickel(shell: NickelShell, strongEnd: FieldPosition = FRONT_4.eR): Player[] {\n return [\n ...nickelFront(strongEnd),\n player(\"nb\", \"NB\", LB_NICKEL.nb),\n ...corners(shell.cornerDepth),\n player(\"fs\", \"FS\", shell.fs),\n player(\"ss\", \"SS\", shell.ss),\n ];\n}\n\nexport function dime(): Player[] {\n return [\n ...front4(),\n player(\"mlb\", \"M\", pt(27, 5)),\n player(\"nb\", \"NB\", LB_NICKEL.nb),\n player(\"db\", \"$\", pt(39, 5)),\n ...corners(6),\n player(\"fs\", \"FS\", pt(22, 11)),\n player(\"ss\", \"SS\", pt(31, 11)),\n ];\n}\n\nexport function defense425(): Player[] {\n return [\n ...front4(),\n player(\"mlb\", \"M\", pt(24.5, 5)),\n player(\"wlb\", \"W\", LB_NICKEL.w),\n player(\"nb\", \"NB\", pt(39, 5)),\n ...corners(6),\n player(\"fs\", \"FS\", pt(23, 11)),\n player(\"ss\", \"SS\", pt(31, 9)),\n ];\n}\n\nexport function defense335(): Player[] {\n return [\n ...front3(),\n player(\"wlb\", \"W\", pt(20.5, 5)),\n player(\"mlb\", \"M\", pt(26.5, 5)),\n player(\"slb\", \"S\", pt(32.5, 5)),\n player(\"nb\", \"NB\", LB_NICKEL.nb),\n ...corners(6),\n player(\"fs\", \"FS\", pt(23, 11)),\n player(\"ss\", \"SS\", pt(31, 9)),\n ];\n}\n","// The alignments live in presets/alignments.ts and are shared with the play presets.\n// Here the ids are stripped to turn them into formations.\n\nimport type { Player } from \"../model/player.js\";\nimport {\n bunch,\n defense34,\n defense43,\n defense335,\n defense425,\n dime,\n empty,\n iFormation,\n NICKEL_BASE,\n nickel,\n pistol,\n shotgunSpread,\n singlebackAce,\n trips,\n} from \"../presets/alignments.js\";\nimport { deepFreeze, type TeamSide } from \"../presets/shared.js\";\nimport { type Formation, toFormationPlayer } from \"./formation.js\";\n\nfunction formation(id: string, name: string, side: TeamSide, players: Player[]): Formation {\n return { id, name, side, players: players.map(toFormationPlayer) };\n}\n\nexport const FORMATION_PRESETS: readonly Formation[] = /* @__PURE__ */ deepFreeze([\n formation(\"i-formation\", \"I-Formation\", \"offense\", iFormation()),\n formation(\"singleback-ace\", \"Singleback Ace\", \"offense\", singlebackAce()),\n formation(\"shotgun-spread\", \"Shotgun Spread\", \"offense\", shotgunSpread()),\n formation(\"trips\", \"Trips\", \"offense\", trips()),\n formation(\"empty\", \"Empty\", \"offense\", empty()),\n formation(\"pistol\", \"Pistol\", \"offense\", pistol()),\n formation(\"bunch\", \"Bunch\", \"offense\", bunch()),\n formation(\"defense-4-3\", \"4-3 Over\", \"defense\", defense43()),\n formation(\"defense-3-4\", \"3-4\", \"defense\", defense34()),\n formation(\"defense-nickel\", \"Nickel\", \"defense\", nickel(NICKEL_BASE)),\n formation(\"defense-dime\", \"Dime\", \"defense\", dime()),\n formation(\"defense-4-2-5\", \"4-2-5\", \"defense\", defense425()),\n formation(\"defense-3-3-5\", \"3-3-5\", \"defense\", defense335()),\n]);\n\nexport function getFormationPreset(id: string): Formation | undefined {\n return FORMATION_PRESETS.find((f) => f.id === id);\n}\n","import type { CanvasPoint } from \"./field.js\";\nimport { trimPolylineEnd } from \"./polyline.js\";\n\n// Trimming stops at this fraction of the total length so a line shorter than the arrowhead does not\n// vanish (the arrowhead covers the rest).\nconst ARROW_TRIM_MAX_FRACTION = 0.9;\n\ninterface LineEnd {\n readonly tip: CanvasPoint;\n /** Unit vector pointing toward the end point. */\n readonly ux: number;\n readonly uy: number;\n}\n\n/**\n * The end point and the direction toward it. The tail of a curve can hold several points at the\n * end coordinates, so the direction comes from the nearest point whose coordinates differ from\n * the end. undefined when all points share the same coordinates.\n */\nfunction lineEnd(path: readonly CanvasPoint[]): LineEnd | undefined {\n const tip = path.at(-1);\n if (tip === undefined) {\n return undefined;\n }\n // Called per line every frame, so search from the end side without copying the array.\n for (let i = path.length - 2; i >= 0; i--) {\n const from = path[i];\n if (from !== undefined && (from.x !== tip.x || from.y !== tip.y)) {\n const dx = tip.x - from.x;\n const dy = tip.y - from.y;\n const length = Math.hypot(dx, dy);\n return { tip, ux: dx / length, uy: dy / length };\n }\n }\n return undefined;\n}\n\n/** The line cut at the arrowhead base. One shorter than the arrowhead keeps 10% of its length. */\nexport function trimForArrowHead(path: readonly CanvasPoint[], arrowLength: number): CanvasPoint[] {\n return trimPolylineEnd(path, arrowLength, ARROW_TRIM_MAX_FRACTION);\n}\n\n/**\n * Vertices of the arrowhead triangle (tip, both base corners) with the end point as the tip,\n * pointed along the travel direction. undefined for a line whose direction cannot be determined.\n */\nexport function arrowHeadVertices(\n path: readonly CanvasPoint[],\n arrowLength: number,\n halfWidth: number,\n): readonly [CanvasPoint, CanvasPoint, CanvasPoint] | undefined {\n const end = lineEnd(path);\n if (end === undefined) {\n return undefined;\n }\n const { tip, ux, uy } = end;\n const baseX = tip.x - arrowLength * ux;\n const baseY = tip.y - arrowLength * uy;\n return [\n tip,\n { x: baseX - halfWidth * uy, y: baseY + halfWidth * ux },\n { x: baseX + halfWidth * uy, y: baseY - halfWidth * ux },\n ];\n}\n\n/**\n * Both ends of the T bar centered on the end point and perpendicular to the travel direction.\n * undefined for a line whose direction cannot be determined.\n */\nexport function blockCapEndpoints(\n path: readonly CanvasPoint[],\n capLength: number,\n): readonly [CanvasPoint, CanvasPoint] | undefined {\n const end = lineEnd(path);\n if (end === undefined) {\n return undefined;\n }\n const { tip, ux, uy } = end;\n const half = capLength / 2;\n return [\n { x: tip.x + half * uy, y: tip.y - half * ux },\n { x: tip.x - half * uy, y: tip.y + half * ux },\n ];\n}\n","import type { PlayerShape } from \"../model/player.js\";\n\n// A circle drawn at the circumscribed radius looks a size larger than a square, whose sides sit\n// inside that circle, and covers block lines near the LOS. Only the drawn radius is brought toward\n// the square's side width; a circle looks smaller by illusion, so it stays a bit larger than that.\n// The hit test keeps the circumscribed circle regardless of shape.\nconst CIRCLE_DRAW_SCALE = 0.82;\n\nexport type PlayerMarkerOutline =\n | { readonly kind: \"circle\"; readonly radius: number }\n | { readonly kind: \"square\"; readonly halfSide: number };\n\n/**\n * Outline of a player marker. radius is the circumscribed radius shared with the hit test; a\n * square is sized so that its corners lie on that circle.\n */\nexport function playerMarkerOutline(shape: PlayerShape, radius: number): PlayerMarkerOutline {\n switch (shape) {\n case \"circle\":\n return { kind: \"circle\", radius: radius * CIRCLE_DRAW_SCALE };\n case \"square\":\n return { kind: \"square\", halfSide: radius / Math.SQRT2 };\n }\n}\n","/** Edits invoked by keyboard input. */\nexport type KeyAction = \"cancel-interaction\" | \"commit-line\" | \"undo\" | \"redo\";\n\n/** The values of a KeyboardEvent that are read to resolve the binding. */\nexport interface KeyStroke {\n readonly key: string;\n readonly metaKey: boolean;\n readonly ctrlKey: boolean;\n readonly shiftKey: boolean;\n}\n\n/**\n * Returns the edit bound to the key. Null for a key with no binding.\n * Cmd on Mac and Ctrl on other operating systems are treated as the same modifier.\n * With Shift held, key arrives in uppercase, so uppercase z and y count as the same keys.\n */\nexport function resolveKeyAction(stroke: KeyStroke): KeyAction | null {\n const mod = stroke.metaKey || stroke.ctrlKey;\n const key = stroke.key.toLowerCase();\n if (stroke.key === \"Escape\") {\n return \"cancel-interaction\";\n }\n if (stroke.key === \"Enter\") {\n return \"commit-line\";\n }\n if (mod && key === \"z\") {\n return stroke.shiftKey ? \"redo\" : \"undo\";\n }\n if (mod && key === \"y\") {\n return \"redo\";\n }\n return null;\n}\n","// The alignments live in presets/alignments.ts and are shared with the formation presets. Every\n// play fits in the window of the middle zone (15 yards on either side of the LOS). Only the\n// featured side has routes, blocks, and motions; the opposing side has player alignments only.\n//\n// Rules for lines. A block ends 1 yard short of the opponent (shortOf), so the T bar touches the\n// edge of the opponent's marker. In a combo block, one blocker stops short of the opponent and the\n// other goes through the opponent's shoulder (beside) and climbs to just short of the LB. A pull\n// that leads through a hole aims at the hole rather than an opponent, so it is written as\n// coordinates. Paths and pulls stay at least 1 yard from the center of any other player's marker\n// (the OL are 2.5 yards apart, so a line through a gap touches the edges). A defensive rush stops\n// at the LOS (0), and a zone drop is drawn away from the LOS (in the + direction).\n\nimport type { Line, LineInterpolation, LineKind } from \"../model/line.js\";\nimport { CURRENT_PLAY_DATA_VERSION, fieldStateForZone } from \"../model/play-data.js\";\nimport type { FieldPosition, Player } from \"../model/player.js\";\nimport {\n beside,\n cornerSpots,\n defense43,\n END_OVER_TE,\n FRONT_4,\n iFormation,\n LB_43,\n LB_NICKEL,\n NICKEL_BASE,\n type NickelShell,\n nickel,\n nickelFront,\n offensiveLine,\n player,\n pt,\n shortOf,\n shotgunSpread,\n singlebackAce,\n trips,\n} from \"../presets/alignments.js\";\nimport { deepFreeze, type TeamSide } from \"../presets/shared.js\";\nimport type { PlayCategory, PlayPreset } from \"./play-preset.js\";\n\n/**\n * No color is set, so lines use the theme's default color. Offense and defense are told apart by\n * the players' shapes and labels.\n */\nfunction ln(\n id: string,\n kind: LineKind,\n startPlayerId: string,\n waypoints: FieldPosition[],\n end: FieldPosition,\n interpolation: LineInterpolation,\n): Line {\n return { id, kind, startPlayerId, waypoints, end, interpolation };\n}\n\nfunction play(\n id: string,\n name: string,\n side: TeamSide,\n category: PlayCategory,\n personnel: string,\n summary: string,\n players: Player[],\n lines: Line[],\n): PlayPreset {\n return {\n id,\n name,\n side,\n category,\n personnel,\n summary,\n data: {\n version: CURRENT_PLAY_DATA_VERSION,\n field: fieldStateForZone(\"middle\"),\n players,\n lines,\n },\n };\n}\n\n// Defenses paired with offensive plays, in which CB depth and safety placement vary by coverage.\nconst COVER_2_SHELL = { cornerDepth: 5, fs: pt(18, 12), ss: pt(35, 12) } satisfies NickelShell;\nconst COVER_3_SHELL = { cornerDepth: 7, fs: pt(26.5, 12), ss: pt(34, 6) } satisfies NickelShell;\n\nconst INSIDE_ZONE = play(\n \"play-inside-zone\",\n \"Inside Zone\",\n \"offense\",\n \"run-zone\",\n \"11 pers\",\n \"Zone blocking, hitting inside in one motion. The RB aims inside the RG and cuts to the B gap or the backside A gap off the 3-technique's movement.\",\n [\n ...offensiveLine(),\n player(\"y\", \"Y\", pt(34.5, -1), \"square\"),\n player(\"qb\", \"QB\", pt(26.5, -5)),\n player(\"rb\", \"RB\", pt(24, -5)),\n player(\"x\", \"X\", pt(6, -1)),\n player(\"h\", \"H\", pt(40, -2)),\n player(\"z\", \"Z\", pt(47, -1)),\n ...defense43(),\n ],\n [\n ln(\"bl-lt\", \"block\", \"lt\", [], shortOf(FRONT_4.eL), \"straight\"),\n ln(\"bl-lg\", \"block\", \"lg\", [], shortOf(FRONT_4.n), \"straight\"),\n ln(\"bl-c\", \"block\", \"c\", [beside(FRONT_4.n, 1)], shortOf(LB_43.m), \"bezier\"),\n ln(\"bl-rg\", \"block\", \"rg\", [], shortOf(FRONT_4.t), \"straight\"),\n ln(\"bl-rt\", \"block\", \"rt\", [beside(FRONT_4.t, 1.5)], shortOf(LB_43.s), \"bezier\"),\n ln(\"bl-y\", \"block\", \"y\", [], shortOf(END_OVER_TE), \"straight\"),\n ln(\"run-rb\", \"route\", \"rb\", [pt(27.5, -2)], pt(28, 3.5), \"bezier\"),\n ],\n);\n\nconst OUTSIDE_ZONE = play(\n \"play-outside-zone\",\n \"Outside Zone\",\n \"offense\",\n \"run-zone\",\n \"11 pers\",\n \"Wide zone, stretching the defense sideways. The RB aims outside the TE and bounces out or cuts upfield off the E and S.\",\n [...singlebackAce(), ...defense43()],\n [\n ln(\"bl-lt\", \"block\", \"lt\", [], shortOf(FRONT_4.eL), \"straight\"),\n ln(\"bl-lg\", \"block\", \"lg\", [], shortOf(FRONT_4.n), \"straight\"),\n ln(\"bl-c\", \"block\", \"c\", [beside(FRONT_4.n, 1)], shortOf(LB_43.m, 0.5), \"bezier\"),\n ln(\"bl-rg\", \"block\", \"rg\", [], shortOf(FRONT_4.t, 0.5), \"straight\"),\n ln(\"bl-rt\", \"block\", \"rt\", [beside(FRONT_4.t, 1.5)], shortOf(LB_43.s, 0.5), \"bezier\"),\n ln(\"bl-y\", \"block\", \"y\", [], shortOf(END_OVER_TE, 0.5), \"straight\"),\n ln(\"run-rb\", \"route\", \"rb\", [pt(31, -5), pt(35.5, -2)], pt(37.5, 3), \"bezier\"),\n ],\n);\n\nconst POWER = play(\n \"play-power\",\n \"Power\",\n \"offense\",\n \"run-gap\",\n \"21 pers\",\n \"Down blocks on the play side, the FB kicks out the 9-technique E, and the backside guard pulls to lead through the C gap.\",\n [...iFormation(), ...defense43()],\n [\n ln(\"bl-y\", \"block\", \"y\", [], shortOf(LB_43.s), \"straight\"),\n ln(\"bl-rt\", \"block\", \"rt\", [], shortOf(FRONT_4.t, 0.5), \"straight\"),\n ln(\"bl-rg\", \"block\", \"rg\", [beside(FRONT_4.t, -1.5)], shortOf(LB_43.m), \"bezier\"),\n ln(\"bl-c\", \"block\", \"c\", [], shortOf(FRONT_4.n), \"straight\"),\n ln(\"bl-lt\", \"block\", \"lt\", [], shortOf(FRONT_4.eL), \"straight\"),\n ln(\n \"lead-fb\",\n \"block\",\n \"fb\",\n [pt(31, -3.5), pt(33.5, -0.5)],\n shortOf(END_OVER_TE, -0.5),\n \"bezier\",\n ),\n ln(\n \"pull-lg\",\n \"block\",\n \"lg\",\n [pt(25, -4), pt(31, -4), pt(32.5, -2), pt(32.5, 1)],\n pt(31.5, 3.5),\n \"bezier\",\n ),\n ln(\"run-rb\", \"route\", \"rb\", [pt(30, -6), pt(33, -3)], pt(33.5, 4), \"bezier\"),\n ],\n);\n\nconst COUNTER = play(\n \"play-counter\",\n \"Counter (GT)\",\n \"offense\",\n \"run-gap\",\n \"21 pers\",\n \"The backside guard kicks out the 9-technique E, the tackle wraps through the C gap, and the FB fills where the tackle left (GT counter).\",\n [...iFormation(), ...defense43()],\n [\n ln(\"bl-y\", \"block\", \"y\", [], shortOf(LB_43.s), \"straight\"),\n ln(\"bl-rt\", \"block\", \"rt\", [], shortOf(FRONT_4.t, 0.5), \"straight\"),\n ln(\"bl-rg\", \"block\", \"rg\", [beside(FRONT_4.t, -1.5)], shortOf(LB_43.m), \"bezier\"),\n ln(\"bl-c\", \"block\", \"c\", [], shortOf(FRONT_4.n), \"straight\"),\n ln(\n \"pull-lg\",\n \"block\",\n \"lg\",\n [pt(25, -4), pt(32, -4), pt(33.5, -0.5)],\n shortOf(END_OVER_TE, -0.5),\n \"straight\",\n ),\n ln(\n \"pull-lt\",\n \"block\",\n \"lt\",\n [pt(23, -5), pt(31, -5), pt(32.5, -2), pt(32.5, 1)],\n pt(31.5, 3.5),\n \"straight\",\n ),\n ln(\"fill-fb\", \"block\", \"fb\", [pt(24, -3), pt(22.5, -0.5)], shortOf(FRONT_4.eL, 1), \"bezier\"),\n ln(\"run-rb\", \"route\", \"rb\", [pt(24, -7.5), pt(30, -6.5), pt(33, -3)], pt(33.5, 4), \"bezier\"),\n ],\n);\n\nconst FOUR_VERTICALS = play(\n \"play-four-verticals\",\n \"Four Verticals\",\n \"offense\",\n \"pass-deep\",\n \"10 pers\",\n \"Four verticals from trips, attacking the seams against a single-high safety.\",\n [...trips(), ...nickel(COVER_3_SHELL, END_OVER_TE)],\n [\n ln(\"go-x\", \"route\", \"x\", [pt(4, 4)], pt(4, 13), \"straight\"),\n ln(\"seam-y\", \"route\", \"y\", [pt(33, 5)], pt(31, 13), \"bezier\"),\n ln(\"seam-h\", \"route\", \"h\", [pt(40, 5)], pt(38, 13), \"bezier\"),\n ln(\"go-z\", \"route\", \"z\", [pt(49, 4)], pt(49, 13), \"straight\"),\n ln(\"chk-rb\", \"route\", \"rb\", [], pt(20, -3), \"straight\"),\n ],\n);\n\nconst MESH = play(\n \"play-mesh\",\n \"Mesh\",\n \"offense\",\n \"pass-dropback\",\n \"10 pers\",\n \"Shallow crossers that mesh. Works against man and zone by running to the open spot.\",\n [...shotgunSpread(), ...nickel(NICKEL_BASE)],\n [\n ln(\"cross-y\", \"route\", \"y\", [pt(20, 3.5)], pt(38, 4), \"straight\"),\n ln(\"cross-h\", \"route\", \"h\", [pt(33, 3)], pt(15, 3.5), \"straight\"),\n ln(\"corner-x\", \"route\", \"x\", [pt(5, 5)], pt(2, 9), \"bezier\"),\n ln(\"sit-z\", \"route\", \"z\", [], pt(47, 5), \"straight\"),\n ln(\"swing-rb\", \"route\", \"rb\", [pt(33, -4)], pt(41, -3), \"straight\"),\n ],\n);\n\nconst SMASH = play(\n \"play-smash\",\n \"Smash\",\n \"offense\",\n \"pass-dropback\",\n \"11 pers\",\n \"A corner and hitch high-low that attacks the corner against Cover 2.\",\n [...singlebackAce(), ...nickel(COVER_2_SHELL, END_OVER_TE)],\n [\n ln(\"hitch-z\", \"route\", \"z\", [], pt(47, 3), \"straight\"),\n ln(\"corner-h\", \"route\", \"h\", [pt(41, 4)], pt(46, 10), \"bezier\"),\n ln(\"dig-y\", \"route\", \"y\", [pt(34.5, 8)], pt(27, 9), \"straight\"),\n ln(\"hitch-x\", \"route\", \"x\", [], pt(6, 3), \"straight\"),\n ln(\"flat-rb\", \"route\", \"rb\", [], pt(22, -4), \"straight\"),\n ],\n);\n\nconst STICK = play(\n \"play-stick\",\n \"Stick\",\n \"offense\",\n \"pass-quick\",\n \"11 pers\",\n \"A three-step quick game. Stick (sit down) and flat for a fast distribution.\",\n [...singlebackAce(), ...nickel(NICKEL_BASE, END_OVER_TE)],\n [\n ln(\"stick-y\", \"route\", \"y\", [], pt(34.5, 4), \"straight\"),\n ln(\"flat-h\", \"route\", \"h\", [], pt(45, 0), \"straight\"),\n ln(\"hitch-z\", \"route\", \"z\", [], pt(47, 3), \"straight\"),\n ln(\"slant-x\", \"route\", \"x\", [pt(6, 2)], pt(10, 3), \"bezier\"),\n ln(\"chk-rb\", \"route\", \"rb\", [], pt(21, -5), \"straight\"),\n ],\n);\n\nconst RPO_BUBBLE = play(\n \"play-rpo-bubble\",\n \"RPO (IZ + Bubble)\",\n \"offense\",\n \"rpo\",\n \"11 pers\",\n \"Reads the W on the bubble side: bubble if the W enters the box, inside zone if he stays.\",\n [...shotgunSpread(), ...nickel(NICKEL_BASE)],\n [\n ln(\"bl-lt\", \"block\", \"lt\", [], shortOf(FRONT_4.eL), \"straight\"),\n ln(\"bl-lg\", \"block\", \"lg\", [beside(FRONT_4.n, -1)], shortOf(LB_NICKEL.m), \"bezier\"),\n ln(\"bl-c\", \"block\", \"c\", [], shortOf(FRONT_4.n), \"straight\"),\n ln(\"bl-rg\", \"block\", \"rg\", [], shortOf(FRONT_4.t), \"straight\"),\n ln(\"bl-rt\", \"block\", \"rt\", [], shortOf(FRONT_4.eR), \"straight\"),\n ln(\"run-rb\", \"route\", \"rb\", [pt(26, -2.5)], pt(23, 3), \"straight\"),\n ln(\"bubble-h\", \"route\", \"h\", [pt(43, -3)], pt(46, -0.5), \"bezier\"),\n ln(\"bl-z\", \"block\", \"z\", [], shortOf(cornerSpots(NICKEL_BASE.cornerDepth).right), \"straight\"),\n ln(\"glance-y\", \"route\", \"y\", [pt(15, 1)], pt(19, 3), \"bezier\"),\n ],\n);\n\nconst PA_BOOT = play(\n \"play-pa-boot\",\n \"PA Boot (Sail)\",\n \"offense\",\n \"pa\",\n \"11 pers\",\n \"Play action with the QB booting out, attacking in three levels: flat, sail, and vertical.\",\n [...singlebackAce(), ...defense43()],\n [\n ln(\"fake-rb\", \"motion\", \"rb\", [pt(24, -6)], pt(20.5, -4.5), \"straight\"),\n ln(\"boot-qb\", \"route\", \"qb\", [pt(31, -4)], pt(36.5, -5), \"bezier\"),\n ln(\"bl-lt\", \"block\", \"lt\", [], shortOf(FRONT_4.eL), \"straight\"),\n ln(\"bl-c\", \"block\", \"c\", [], shortOf(FRONT_4.n), \"straight\"),\n ln(\"bl-rg\", \"block\", \"rg\", [], shortOf(FRONT_4.t), \"straight\"),\n ln(\"flat-y\", \"route\", \"y\", [pt(37, -0.5)], pt(43, 0.5), \"bezier\"),\n ln(\"sail-h\", \"route\", \"h\", [pt(41, 3)], pt(46, 8), \"bezier\"),\n ln(\"go-z\", \"route\", \"z\", [pt(49, 3)], pt(49, 13), \"straight\"),\n ln(\"comeback-x\", \"route\", \"x\", [pt(5, 9)], pt(7.5, 7.5), \"bezier\"),\n ],\n);\n\nconst COVER_1 = play(\n \"play-cover-1\",\n \"Cover 1 (Man Free)\",\n \"defense\",\n \"coverage\",\n \"Nickel\",\n \"One deep free safety, everyone else in man. The W waits in the hole (rat).\",\n [...nickel({ cornerDepth: 6, fs: pt(26.5, 11), ss: pt(38, 6) }), ...shotgunSpread()],\n [\n ln(\"man-cbl\", \"motion\", \"cb-l\", [], pt(5.5, 0.5), \"straight\"),\n ln(\"man-cbr\", \"motion\", \"cb-r\", [], pt(47.5, 0.5), \"straight\"),\n ln(\"man-nb\", \"motion\", \"nb\", [], pt(13, -0.5), \"straight\"),\n ln(\"man-ss\", \"motion\", \"ss\", [], pt(40, -0.5), \"straight\"),\n ln(\"man-mlb\", \"motion\", \"mlb\", [pt(27.5, 2.5), pt(27.5, -2.5)], pt(29, -3.5), \"straight\"),\n ln(\"rat-wlb\", \"motion\", \"wlb\", [], pt(27.5, 3.5), \"straight\"),\n ln(\"free-fs\", \"motion\", \"fs\", [], pt(26.5, 13), \"straight\"),\n ],\n);\n\nconst COVER_2 = play(\n \"play-cover-2\",\n \"Cover 2\",\n \"defense\",\n \"coverage\",\n \"Nickel\",\n \"Two-deep, five-under zone. Two safeties split the deep halves, and the CBs sit in the flats.\",\n [...nickel({ cornerDepth: 5, fs: pt(15, 11), ss: pt(38, 11) }), ...shotgunSpread()],\n [\n ln(\"half-fs\", \"motion\", \"fs\", [], pt(13, 14), \"straight\"),\n ln(\"half-ss\", \"motion\", \"ss\", [], pt(40, 14), \"straight\"),\n ln(\"flat-cbl\", \"motion\", \"cb-l\", [], pt(9.5, 4), \"straight\"),\n ln(\"flat-cbr\", \"motion\", \"cb-r\", [], pt(43.5, 4), \"straight\"),\n ln(\"hook-mlb\", \"motion\", \"mlb\", [], pt(21.5, 10), \"straight\"),\n ln(\"hook-wlb\", \"motion\", \"wlb\", [], pt(32.5, 10), \"straight\"),\n ln(\"curl-nb\", \"motion\", \"nb\", [], pt(16, 9), \"straight\"),\n ],\n);\n\nconst COVER_3 = play(\n \"play-cover-3\",\n \"Cover 3\",\n \"defense\",\n \"coverage\",\n \"Nickel\",\n \"Three-deep, four-under. The single-high zone that is today's baseline.\",\n [...nickel(COVER_3_SHELL), ...shotgunSpread()],\n [\n ln(\"deep-cbl\", \"motion\", \"cb-l\", [], pt(7, 13), \"straight\"),\n ln(\"deep-cbr\", \"motion\", \"cb-r\", [], pt(46, 13), \"straight\"),\n ln(\"deep-fs\", \"motion\", \"fs\", [], pt(26.5, 14), \"straight\"),\n ln(\"curl-nb\", \"motion\", \"nb\", [], pt(15.5, 9), \"straight\"),\n ln(\"hook-mlb\", \"motion\", \"mlb\", [], pt(22, 10), \"straight\"),\n ln(\"hook-wlb\", \"motion\", \"wlb\", [], pt(31, 10), \"straight\"),\n ln(\"flat-ss\", \"motion\", \"ss\", [], pt(39, 4), \"straight\"),\n ],\n);\n\nconst COVER_4 = play(\n \"play-cover-4\",\n \"Cover 4 (Quarters)\",\n \"defense\",\n \"coverage\",\n \"Nickel\",\n \"Four-deep quarters, stamping out vertical passes from above.\",\n [...nickel({ cornerDepth: 7, fs: pt(20, 11), ss: pt(33, 11) }), ...shotgunSpread()],\n [\n ln(\"qtr-cbl\", \"motion\", \"cb-l\", [], pt(6, 13), \"straight\"),\n ln(\"qtr-cbr\", \"motion\", \"cb-r\", [], pt(47, 13), \"straight\"),\n ln(\"qtr-fs\", \"motion\", \"fs\", [], pt(18, 14), \"straight\"),\n ln(\"qtr-ss\", \"motion\", \"ss\", [], pt(35, 14), \"straight\"),\n ln(\"under-nb\", \"motion\", \"nb\", [], pt(16, 8), \"straight\"),\n ln(\"under-mlb\", \"motion\", \"mlb\", [], pt(23, 9), \"straight\"),\n ln(\"under-wlb\", \"motion\", \"wlb\", [], pt(31, 9), \"straight\"),\n ],\n);\n\nconst FIRE_ZONE = play(\n \"play-fire-zone\",\n \"Fire Zone\",\n \"defense\",\n \"pressure\",\n \"Nickel\",\n \"A five-man blitz with one lineman dropping, covered by three-deep, three-under.\",\n [...nickel(COVER_3_SHELL), ...shotgunSpread()],\n [\n ln(\"rush-dtl\", \"route\", \"dt-l\", [], pt(25, 0), \"straight\"),\n ln(\"rush-dtr\", \"route\", \"dt-r\", [], pt(28.5, 0), \"straight\"),\n ln(\"rush-der\", \"route\", \"de-r\", [], pt(33, 0), \"straight\"),\n ln(\"blz-wlb\", \"route\", \"wlb\", [pt(31.5, 2.5)], pt(31, 0), \"bezier\"),\n ln(\"blz-nb\", \"route\", \"nb\", [pt(17, 2.5)], pt(20, 0), \"bezier\"),\n ln(\"drop-del\", \"motion\", \"de-l\", [pt(19, 4)], pt(17, 7), \"straight\"),\n ln(\"deep-cbl\", \"motion\", \"cb-l\", [], pt(7, 13), \"straight\"),\n ln(\"deep-cbr\", \"motion\", \"cb-r\", [], pt(46, 13), \"straight\"),\n ln(\"deep-fs\", \"motion\", \"fs\", [], pt(26.5, 14), \"straight\"),\n ln(\"hook-mlb\", \"motion\", \"mlb\", [], pt(23, 9), \"straight\"),\n ln(\"flat-ss\", \"motion\", \"ss\", [], pt(39, 4), \"straight\"),\n ],\n);\n\nconst COVER_0 = play(\n \"play-cover-0\",\n \"Cover 0 Blitz\",\n \"defense\",\n \"pressure\",\n \"Nickel\",\n \"All-out man with no safety deep, six rushers for maximum pressure. The FS takes the RB in man.\",\n [...nickel({ cornerDepth: 5, fs: pt(22, 9), ss: pt(31, 9) }), ...shotgunSpread()],\n [\n ln(\"rush-del\", \"route\", \"de-l\", [], pt(20, 0), \"straight\"),\n ln(\"rush-dtl\", \"route\", \"dt-l\", [], pt(25, 0), \"straight\"),\n ln(\"rush-dtr\", \"route\", \"dt-r\", [], pt(30.5, 0), \"straight\"),\n ln(\"rush-der\", \"route\", \"de-r\", [], pt(33, 0), \"straight\"),\n ln(\"blz-mlb\", \"route\", \"mlb\", [pt(27, 2.5)], pt(27.5, 0), \"bezier\"),\n ln(\"blz-wlb\", \"route\", \"wlb\", [pt(31.5, 2.5)], pt(31, 0), \"bezier\"),\n ln(\"man-cbl\", \"motion\", \"cb-l\", [], pt(5.5, 0.5), \"straight\"),\n ln(\"man-cbr\", \"motion\", \"cb-r\", [], pt(47.5, 0.5), \"straight\"),\n ln(\"man-nb\", \"motion\", \"nb\", [], pt(13, -0.5), \"straight\"),\n ln(\"man-fs\", \"motion\", \"fs\", [pt(22, 2.5), pt(23, -2.5)], pt(28, -4), \"straight\"),\n ln(\"man-ss\", \"motion\", \"ss\", [pt(37, 4)], pt(39.5, -0.5), \"straight\"),\n ],\n);\n\n// With LBs standing in both A gaps, both DTs widen out to 3-technique.\nconst DOUBLE_A = play(\n \"play-double-a\",\n \"Double-A Gap\",\n \"defense\",\n \"pressure\",\n \"Nickel\",\n \"Two defenders shown in both A gaps. It can turn into a real pressure or a simulated one.\",\n [\n player(\"de-l\", \"E\", FRONT_4.eL),\n player(\"dt-l\", \"T\", pt(23, 1.5)),\n player(\"dt-r\", \"T\", FRONT_4.t),\n player(\"de-r\", \"E\", FRONT_4.eR),\n player(\"mlb\", \"M\", pt(25.5, 3)),\n player(\"wlb\", \"W\", pt(27.5, 3)),\n player(\"nb\", \"NB\", LB_NICKEL.nb),\n player(\"cb-l\", \"CB\", cornerSpots(7).left),\n player(\"cb-r\", \"CB\", cornerSpots(7).right),\n player(\"fs\", \"FS\", COVER_3_SHELL.fs),\n player(\"ss\", \"SS\", COVER_3_SHELL.ss),\n ...shotgunSpread(),\n ],\n [\n ln(\"aim-mlb\", \"route\", \"mlb\", [], pt(25.5, 0.5), \"straight\"),\n ln(\"aim-wlb\", \"route\", \"wlb\", [], pt(27.5, 0.5), \"straight\"),\n ln(\"rush-del\", \"route\", \"de-l\", [], pt(20, 0), \"straight\"),\n ln(\"rush-dtl\", \"route\", \"dt-l\", [], pt(22.5, 0), \"straight\"),\n ln(\"rush-dtr\", \"route\", \"dt-r\", [], pt(30.5, 0), \"straight\"),\n ln(\"rush-der\", \"route\", \"de-r\", [], pt(33, 0), \"straight\"),\n ln(\"bail-cbl\", \"motion\", \"cb-l\", [], pt(7, 12), \"straight\"),\n ln(\"bail-cbr\", \"motion\", \"cb-r\", [], pt(46, 12), \"straight\"),\n ln(\"deep-fs\", \"motion\", \"fs\", [], pt(26.5, 14), \"straight\"),\n ln(\"curl-nb\", \"motion\", \"nb\", [], pt(15.5, 9), \"straight\"),\n ln(\"flat-ss\", \"motion\", \"ss\", [], pt(39, 4), \"straight\"),\n ],\n);\n\n// The field side (right) plays quarters and the boundary side (left) plays Cover 2, so the CB\n// depth differs between the two sides, and the nickel back aligns on the field-side slot.\nconst COVER_6 = play(\n \"play-cover-6\",\n \"Cover 6\",\n \"defense\",\n \"coverage\",\n \"Nickel\",\n \"Split coverage: two quarters plus a half. Quarters to the field, Cover 2 to the boundary.\",\n [\n ...nickelFront(),\n player(\"nb\", \"NB\", pt(39, 5)),\n player(\"cb-l\", \"CB\", cornerSpots(5).left),\n player(\"cb-r\", \"CB\", cornerSpots(7).right),\n player(\"fs\", \"FS\", pt(15, 11)),\n player(\"ss\", \"SS\", pt(35, 10)),\n ...shotgunSpread(),\n ],\n [\n ln(\"flat-cbl\", \"motion\", \"cb-l\", [], pt(10, 4), \"straight\"),\n ln(\"half-fs\", \"motion\", \"fs\", [], pt(12, 14), \"straight\"),\n ln(\"qtr-cbr\", \"motion\", \"cb-r\", [], pt(47, 13), \"straight\"),\n ln(\"qtr-ss\", \"motion\", \"ss\", [], pt(38, 14), \"straight\"),\n ln(\"curl-nb\", \"motion\", \"nb\", [], pt(41, 9), \"straight\"),\n ln(\"hook-mlb\", \"motion\", \"mlb\", [], pt(22, 10), \"straight\"),\n ln(\"hook-wlb\", \"motion\", \"wlb\", [], pt(31, 10), \"straight\"),\n ],\n);\n\nexport const PLAY_PRESETS: readonly PlayPreset[] = /* @__PURE__ */ deepFreeze([\n INSIDE_ZONE,\n OUTSIDE_ZONE,\n POWER,\n COUNTER,\n FOUR_VERTICALS,\n MESH,\n SMASH,\n STICK,\n RPO_BUBBLE,\n PA_BOOT,\n COVER_1,\n COVER_2,\n COVER_3,\n COVER_4,\n FIRE_ZONE,\n COVER_0,\n DOUBLE_A,\n COVER_6,\n]);\n\nexport function getPlayPreset(id: string): PlayPreset | undefined {\n return PLAY_PRESETS.find((p) => p.id === id);\n}\n","import {\n type EditorTool,\n FIELD_ZONE_LABELS,\n type FieldZone,\n type LineInterpolation,\n type LineKind,\n type PlayerFill,\n type PlayerShape,\n type TeamSide,\n} from \"../../common/index.js\";\nimport type { SwatchToken } from \"../theme/palette.js\";\n\n/** Languages the built-in UI ships. The host picks one at construction. */\nexport const PLAYMAKER_LOCALE_VALUES = [\"en\", \"ja\"] as const;\n\nexport type PlaymakerLocale = (typeof PLAYMAKER_LOCALE_VALUES)[number];\n\n/**\n * Every string the toolbar and the property panel show. Sentences whose word order differs\n * between languages are functions, so each dictionary decides the order itself.\n */\nexport interface Messages {\n readonly tool: Readonly<Record<EditorTool, string>>;\n readonly undo: string;\n readonly redo: string;\n readonly delete: string;\n readonly finishLine: string;\n readonly cancel: string;\n readonly zone: Readonly<Record<FieldZone, string>>;\n readonly formation: string;\n readonly loadFormation: string;\n readonly side: Readonly<Record<TeamSide, string>>;\n readonly formationLimit: (maxPlayers: number) => string;\n readonly playerLimit: (maxPlayers: number) => string;\n readonly lineLimit: (maxLines: number) => string;\n readonly selectionHint: string;\n readonly player: string;\n readonly line: string;\n readonly label: string;\n readonly shape: string;\n readonly color: string;\n readonly kind: string;\n readonly interpolation: string;\n readonly width: string;\n readonly shapes: Readonly<Record<PlayerShape, string>>;\n readonly fills: Readonly<Record<PlayerFill, string>>;\n readonly kinds: Readonly<Record<LineKind, string>>;\n readonly interpolations: Readonly<Record<LineInterpolation, string>>;\n readonly colors: Readonly<Record<SwatchToken, string>>;\n /** Name of a player swatch, from the color name and the fill name. */\n readonly swatch: (color: string, fill: string) => string;\n readonly reset: string;\n /** Name of the button that clears a field back to the theme default. */\n readonly resetField: (field: string) => string;\n}\n\nexport const en: Messages = {\n tool: { select: \"Select\", \"add-player\": \"Add player\", \"draw-line\": \"Draw line\" },\n undo: \"Undo\",\n redo: \"Redo\",\n delete: \"Delete\",\n finishLine: \"Finish line\",\n cancel: \"Cancel\",\n zone: FIELD_ZONE_LABELS,\n formation: \"Formation\",\n loadFormation: \"Load formation…\",\n side: { offense: \"Offense\", defense: \"Defense\" },\n formationLimit: (maxPlayers) =>\n `Formations that would put more than ${maxPlayers} players on the field cannot be loaded`,\n playerLimit: (maxPlayers) => `Up to ${maxPlayers} players`,\n lineLimit: (maxLines) => `Up to ${maxLines} lines`,\n selectionHint: \"Select a player or a line to edit its properties\",\n player: \"Player\",\n line: \"Line\",\n label: \"Label\",\n shape: \"Shape\",\n color: \"Color\",\n kind: \"Kind\",\n interpolation: \"Interpolation\",\n width: \"Width\",\n shapes: { circle: \"Circle\", square: \"Square\" },\n fills: { outline: \"Outline\", solid: \"Solid\" },\n kinds: { route: \"Route\", block: \"Block\", motion: \"Motion\" },\n interpolations: { straight: \"Straight\", bezier: \"Curved\" },\n colors: { swatch1: \"Black\", swatch2: \"Red\", swatch3: \"Blue\", swatch4: \"Green\" },\n swatch: (color, fill) => `${color} ${fill.toLowerCase()}`,\n reset: \"Default\",\n resetField: (field) => `Reset ${field.toLowerCase()} to default`,\n};\n","import { ja } from \"./ja.js\";\nimport { en, type Messages, type PlaymakerLocale } from \"./messages.js\";\n\nexport { type Messages, PLAYMAKER_LOCALE_VALUES, type PlaymakerLocale } from \"./messages.js\";\n\nconst MESSAGES: Readonly<Record<PlaymakerLocale, Messages>> = { en, ja };\n\nexport function resolveMessages(locale: PlaymakerLocale): Messages {\n return MESSAGES[locale];\n}\n","import {\n Disposable,\n type FieldPosition,\n type IEditorActions,\n type IEditorGestures,\n type IEditorScene,\n resolveKeyAction,\n toDisposable,\n} from \"../../common/index.js\";\n\n/** The canvas that receives the pointer, and a way to map a position on it to field yards. */\nexport interface IPointerSurface {\n readonly canvas: HTMLCanvasElement;\n clientToYard(clientX: number, clientY: number): FieldPosition;\n}\n\n// Keys that reach a form control are left to the control's default behavior (text undo and so on).\nfunction isFormControl(target: EventTarget | null): boolean {\n return (\n target instanceof HTMLInputElement ||\n target instanceof HTMLSelectElement ||\n target instanceof HTMLTextAreaElement\n );\n}\n\nexport class PointerInput extends Disposable {\n /**\n * @param root The element that receives the shortcuts. It is the element that wraps the whole\n * UI rather than the canvas, so that Cmd+Z works right after a toolbar button is pressed.\n */\n constructor(\n root: HTMLElement,\n surface: IPointerSurface,\n controller: IEditorGestures & IEditorActions & Pick<IEditorScene, \"getViewState\">,\n ) {\n super();\n const canvas = surface.canvas;\n // The canvas takes no focus by default, so Esc, Enter, undo, and redo keys would not reach it.\n canvas.tabIndex = 0;\n\n const toYard = (e: PointerEvent) => surface.clientToYard(e.clientX, e.clientY);\n // The pressed pointer. Up and cancel from any other (second finger, right button) are ignored.\n let activePointerId: number | null = null;\n\n const onPointerDown = (e: PointerEvent) => {\n if (e.button !== 0 || !e.isPrimary) {\n return;\n }\n activePointerId = e.pointerId;\n // If focusing scrolls the page, the canvas shifts away from the pressed position.\n canvas.focus({ preventScroll: true });\n canvas.setPointerCapture(e.pointerId);\n controller.pointerDown(toYard(e));\n };\n // The line being drawn follows the pointer without a button held, so every move is passed on.\n const onPointerMove = (e: PointerEvent) => {\n if (e.isPrimary) {\n controller.pointerMove(toYard(e));\n }\n };\n // Clears the record and returns true when this ends the pressed pointer. Right after up, the\n // browser drops the capture and fires lostpointercapture, so the record is cleared on up first\n // and that event is not mistaken for an abort.\n const endPress = (e: PointerEvent): boolean => {\n if (e.pointerId !== activePointerId) {\n return false;\n }\n activePointerId = null;\n return true;\n };\n const onPointerUp = (e: PointerEvent) => {\n if (endPress(e)) {\n controller.pointerUp(toYard(e));\n }\n };\n // A press that ended without an up, because a touch scroll started or the window changed.\n // Left alone, the drag stays active and the player follows the next move.\n // Drawing spans presses, so aborting a single press does not discard the points placed so far.\n const onPointerAbort = (e: PointerEvent) => {\n if (endPress(e) && !controller.getViewState().isDrawing) {\n controller.cancelInteraction();\n }\n };\n const onDoubleClick = () => controller.commitLine();\n const onKeyDown = (e: KeyboardEvent) => {\n if (isFormControl(e.target)) {\n return;\n }\n switch (resolveKeyAction(e)) {\n case \"cancel-interaction\":\n controller.cancelInteraction();\n return;\n case \"commit-line\":\n // Enter on a button presses that button, so it does not also commit the line.\n if (!(e.target instanceof HTMLButtonElement)) {\n controller.commitLine();\n }\n return;\n case \"undo\":\n e.preventDefault();\n controller.undo();\n return;\n case \"redo\":\n e.preventDefault();\n controller.redo();\n return;\n case null:\n return;\n }\n };\n\n const listeners = new AbortController();\n const { signal } = listeners;\n canvas.addEventListener(\"pointerdown\", onPointerDown, { signal });\n canvas.addEventListener(\"pointermove\", onPointerMove, { signal });\n canvas.addEventListener(\"pointerup\", onPointerUp, { signal });\n canvas.addEventListener(\"pointercancel\", onPointerAbort, { signal });\n canvas.addEventListener(\"lostpointercapture\", onPointerAbort, { signal });\n canvas.addEventListener(\"dblclick\", onDoubleClick, { signal });\n root.addEventListener(\"keydown\", onKeyDown, { signal });\n this._register(\n toDisposable(() => {\n listeners.abort();\n canvas.removeAttribute(\"tabindex\");\n }),\n );\n }\n}\n","export default \"data:font/woff2;base64,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\"","// Yard numbers and player labels are fixed to the bundled font, and the theme cannot replace it.\n// ctx.font cannot interpret CSS var(), so the value is built from these constants, not a variable.\n// The name is specific to the library so it does not collide with the fonts of the host page.\nexport const FIELD_FONT_NAME = \"Playmaker Saira\";\nexport const FIELD_FONT_WEIGHT = 700;\nexport const FIELD_FONT_FAMILY = `\"${FIELD_FONT_NAME}\", sans-serif`;\n\n/** The ctx.font value that draws with the bundled font at sizePx. */\nexport function fieldFont(sizePx: number): string {\n return `${FIELD_FONT_WEIGHT} ${sizePx}px ${FIELD_FONT_FAMILY}`;\n}\n","// The source font is Saira Condensed Bold (SIL OFL 1.1, library/assets/fonts/OFL.txt).\n// Rebuild it with make font.\nimport fontUrl from \"../../assets/playmaker-saira-subset.woff2\";\nimport { FIELD_FONT_NAME, FIELD_FONT_WEIGHT } from \"./field-font.js\";\n\nlet loaded: Promise<void> | undefined;\n\n/**\n * Registers the bundled font in document.fonts and resolves once it has loaded. However many times\n * it is called, the font is registered only once. Declared through CSS @font-face, the font would\n * stay invisible until the host loads the CSS, and numbers and labels drawn in the meantime would\n * use the fallback font. A load failure still draws with the fallback font, so it never rejects.\n */\nexport function loadFieldFont(): Promise<void> {\n loaded ??= registerFieldFont();\n return loaded;\n}\n\nasync function registerFieldFont(): Promise<void> {\n try {\n const face = new FontFace(FIELD_FONT_NAME, `url(\"${fontUrl}\")`, {\n weight: String(FIELD_FONT_WEIGHT),\n });\n document.fonts.add(face);\n await face.load();\n } catch {\n // Keep drawing with the fallback font.\n }\n}\n","/** A 2D context just to read colors. Failing to get one is remembered too, so it is not retried. */\ntype ColorContext =\n | { readonly status: \"unresolved\" }\n | { readonly status: \"unavailable\" }\n | { readonly status: \"ready\"; readonly ctx: CanvasRenderingContext2D };\n\nlet context: ColorContext = { status: \"unresolved\" };\n\nfunction resolveContext(): ColorContext {\n if (context.status === \"unresolved\") {\n const ctx = document.createElement(\"canvas\").getContext(\"2d\");\n context = ctx === null ? { status: \"unavailable\" } : { status: \"ready\", ctx };\n }\n return context;\n}\n\n/**\n * Normalizes a CSS color to the format that canvas fillStyle returns.\n * The same color in any notation gives the same string. Opaque colors become lowercase `#rrggbb`.\n * A value that is not a color gives undefined. Without a 2D context, the value is returned as is.\n */\nexport function normalizeCssColor(value: string): string | undefined {\n const resolved = resolveContext();\n if (resolved.status !== \"ready\") {\n return value;\n }\n const { ctx } = resolved;\n // Assigning an unreadable value leaves fillStyle at its previous value.\n // Assign twice from different colors; if the results agree, the value was read as a color.\n ctx.fillStyle = \"#000000\";\n ctx.fillStyle = value;\n const fromBlack = ctx.fillStyle;\n ctx.fillStyle = \"#ffffff\";\n ctx.fillStyle = value;\n return typeof fromBlack === \"string\" && ctx.fillStyle === fromBlack ? fromBlack : undefined;\n}\n","// The Material Design palette. The reasoning behind the colors is in \"Theme\" in docs/design.md.\nconst WHITE = \"#ffffff\";\nconst BLACK = \"#212121\";\nconst RED = \"#d32f2f\";\nconst BLUE = \"#1976d2\";\nconst GREEN = \"#388e3c\";\nconst ORANGE = \"#ff9800\";\nconst GREY_100 = \"#f5f5f5\";\nconst GREY_200 = \"#eeeeee\";\nconst GREY_400 = \"#bdbdbd\";\nconst GREY_500 = \"#9e9e9e\";\nconst GREY_700 = \"#616161\";\n\ninterface ThemeToken {\n readonly property: `--playmaker-${string}`;\n readonly fallback: string;\n}\n\n/**\n * The theme variables for the colors drawn on the canvas, with defaults for when the host does not\n * declare them. The toolbar and panel colors are decided by CSS alone, so they are not listed here.\n */\nexport const THEME_TOKENS = {\n fieldGrass: { property: \"--playmaker-field-grass\", fallback: WHITE },\n fieldOob: { property: \"--playmaker-field-oob\", fallback: GREY_200 },\n fieldEndzone: { property: \"--playmaker-field-endzone\", fallback: GREY_100 },\n fieldLine: { property: \"--playmaker-field-line\", fallback: GREY_500 },\n fieldNumber: { property: \"--playmaker-field-number\", fallback: GREY_400 },\n fieldGoalLine: { property: \"--playmaker-field-goal-line\", fallback: GREY_700 },\n fieldPylon: { property: \"--playmaker-field-pylon\", fallback: GREY_500 },\n fieldGoalpost: { property: \"--playmaker-field-goalpost\", fallback: GREY_500 },\n fieldGrid: { property: \"--playmaker-field-grid\", fallback: \"rgba(0, 0, 0, 0.1)\" },\n /**\n * The color of a player that omits its color. It is the outline and label of an outline player,\n * and the fill of a solid player.\n */\n playerColor: { property: \"--playmaker-player-color\", fallback: BLACK },\n /** The label color of a solid player. The label of an outline player uses the player's color. */\n playerLabel: { property: \"--playmaker-player-label\", fallback: WHITE },\n lineRoute: { property: \"--playmaker-line-route\", fallback: BLACK },\n lineBlock: { property: \"--playmaker-line-block\", fallback: BLACK },\n lineMotion: { property: \"--playmaker-line-motion\", fallback: BLACK },\n /** The four colors the panel offers for players and lines. */\n swatch1: { property: \"--playmaker-swatch-1\", fallback: BLACK },\n swatch2: { property: \"--playmaker-swatch-2\", fallback: RED },\n swatch3: { property: \"--playmaker-swatch-3\", fallback: BLUE },\n swatch4: { property: \"--playmaker-swatch-4\", fallback: GREEN },\n selection: { property: \"--playmaker-selection\", fallback: ORANGE },\n selectionOutline: { property: \"--playmaker-selection-outline\", fallback: WHITE },\n} as const satisfies Record<string, ThemeToken>;\n\nexport type ThemeTokenName = keyof typeof THEME_TOKENS;\n\nexport type ThemeReader = (token: ThemeTokenName) => string;\n\n/**\n * Builds a ThemeReader from a function that reads a variable's value. A token whose value is empty\n * or whitespace only draws with the default.\n */\nexport function themeReaderFrom(readProperty: (property: string) => string): ThemeReader {\n return (token) => {\n const { property, fallback } = THEME_TOKENS[token];\n const value = readProperty(property).trim();\n return value === \"\" ? fallback : value;\n };\n}\n","import { normalizeCssColor } from \"./css-color.js\";\nimport { THEME_TOKENS, type ThemeReader, themeReaderFrom } from \"./tokens.js\";\n\n/**\n * Reads the theme variables as resolved on element. canvas cannot interpret var(), so the colors\n * are made concrete before drawing. Every variable is read at creation, so if the host changes one\n * later, the colors this reader returns stay the same. Colors come back in a normalized notation,\n * and a value that is not a color is treated as undeclared and falls back to the default color.\n */\nexport function createThemeReader(element: Element): ThemeReader {\n const styles = getComputedStyle(element);\n const values = new Map<string, string>();\n for (const { property } of Object.values(THEME_TOKENS)) {\n values.set(property, normalizeCssColor(styles.getPropertyValue(property).trim()) ?? \"\");\n }\n return themeReaderFrom((property) => values.get(property) ?? \"\");\n}\n","import {\n DEFAULT_FIELD_LEAGUE,\n displayYardNumber,\n FIELD_WIDTH_YARDS,\n type FieldGeometry,\n type FieldMetrics,\n HASH_CENTER_OFFSET_YARDS_BY_LEAGUE,\n YARD_LINE_INTERVAL_YARDS,\n yardLinesInWindow,\n} from \"../../common/index.js\";\nimport { fieldFont } from \"../theme/field-font.js\";\nimport type { ThemeReader } from \"../theme/tokens.js\";\nimport type { ILayerRenderer, RenderFrame } from \"./layer.js\";\n\ninterface FieldTheme {\n readonly fieldColor: string;\n /** Outside the sidelines, and the margin left over when the aspect ratio does not match. */\n readonly oobColor: string;\n readonly endzoneColor: string;\n readonly lineColor: string;\n readonly goalLineColor: string;\n readonly numberColor: string;\n readonly pylonColor: string;\n readonly goalpostColor: string;\n}\n\n/**\n * The outer frame (px) of the field shown in the window. The sidelines are left and right, and the\n * top and bottom edges of the window are top and bottom.\n */\ninterface FieldBounds {\n readonly left: number;\n readonly right: number;\n readonly top: number;\n readonly bottom: number;\n}\n\n/**\n * The direction the goalpost uprights extend. The own end zone is at the bottom of the screen, so\n * its uprights extend upward, and the opponent's extend downward.\n */\ntype UprightDirection = \"toward-top\" | \"toward-bottom\";\n\n// Size (yards) of the triangle beside each number that points to the nearer goal. It is kept small\n// so that it stands out less than the number.\nconst ARROW_LENGTH_YARDS = 0.8;\nconst ARROW_HALF_WIDTH_YARDS = 0.3;\nconst ARROW_OFFSET_FROM_NUMBER_YARDS = 2.5;\n// Distance of the numbers from the sideline. The triangles share the column to line up vertically.\nconst NUMBER_FROM_SIDELINE_YARDS = 6;\n// Two-digit numbers straddle the yard line, so the digits are spaced apart to let the line pass\n// between them, as on a real field.\nconst NUMBER_DIGIT_SPACING_PER_HEIGHT = 0.18;\n\nfunction fieldTheme(read: ThemeReader): FieldTheme {\n return {\n fieldColor: read(\"fieldGrass\"),\n oobColor: read(\"fieldOob\"),\n endzoneColor: read(\"fieldEndzone\"),\n lineColor: read(\"fieldLine\"),\n goalLineColor: read(\"fieldGoalLine\"),\n numberColor: read(\"fieldNumber\"),\n pylonColor: read(\"fieldPylon\"),\n goalpostColor: read(\"fieldGoalpost\"),\n };\n}\n\nexport class FieldRenderer implements ILayerRenderer {\n /** Draws one frame of the field according to the window of geometry. */\n draw(ctx: CanvasRenderingContext2D, frame: RenderFrame): void {\n const { geometry, metrics } = frame;\n const theme = fieldTheme(frame.theme);\n const { viewportWidth, viewportHeight, yardWindow } = geometry;\n\n const bounds: FieldBounds = {\n left: geometry.xForLateralYard(0),\n right: geometry.xForLateralYard(FIELD_WIDTH_YARDS),\n top: geometry.yForAbsoluteYard(yardWindow.endYard),\n bottom: geometry.yForAbsoluteYard(yardWindow.startYard),\n };\n\n ctx.fillStyle = theme.oobColor;\n ctx.fillRect(0, 0, viewportWidth, viewportHeight);\n ctx.fillStyle = theme.fieldColor;\n ctx.fillRect(bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top);\n\n this.drawEndZones(ctx, geometry, bounds, theme);\n this.drawHashes(ctx, geometry, bounds, metrics, theme);\n this.drawYardLines(ctx, geometry, bounds, metrics, theme);\n this.drawNumbers(ctx, geometry, metrics, theme);\n this.drawEndZoneMarkers(ctx, geometry, bounds, metrics, theme);\n }\n\n /** Fills the part of the end zones that falls in the window. No lines are drawn inside them. */\n private drawEndZones(\n ctx: CanvasRenderingContext2D,\n geometry: FieldGeometry,\n { left, right }: FieldBounds,\n theme: FieldTheme,\n ): void {\n const { startYard, endYard } = geometry.yardWindow;\n ctx.fillStyle = theme.endzoneColor;\n if (startYard < 0) {\n const yTop = geometry.yForAbsoluteYard(0);\n const yBottom = geometry.yForAbsoluteYard(startYard);\n ctx.fillRect(left, yTop, right - left, yBottom - yTop);\n }\n if (endYard > 100) {\n const yTop = geometry.yForAbsoluteYard(endYard);\n const yBottom = geometry.yForAbsoluteYard(100);\n ctx.fillRect(left, yTop, right - left, yBottom - yTop);\n }\n }\n\n /**\n * Draws the hash marks at every yard and the ticks on the sidelines. Multiples of 5 yards are\n * skipped because the yard lines cover them, and nothing is drawn in the end zones.\n */\n private drawHashes(\n ctx: CanvasRenderingContext2D,\n geometry: FieldGeometry,\n { left, right }: FieldBounds,\n metrics: FieldMetrics,\n theme: FieldTheme,\n ): void {\n const offset = HASH_CENTER_OFFSET_YARDS_BY_LEAGUE[DEFAULT_FIELD_LEAGUE];\n const center = FIELD_WIDTH_YARDS / 2;\n const hashLeft = geometry.xForLateralYard(center - offset);\n const hashRight = geometry.xForLateralYard(center + offset);\n const tick = metrics.hashTickLength;\n const half = tick / 2;\n\n ctx.strokeStyle = theme.lineColor;\n ctx.lineWidth = metrics.yardLineWidth;\n ctx.beginPath();\n for (const yard of yardLinesInWindow(geometry.zone, 1)) {\n if (yard < 0 || yard > 100 || yard % YARD_LINE_INTERVAL_YARDS === 0) {\n continue;\n }\n const y = geometry.yForAbsoluteYard(yard);\n ctx.moveTo(left, y);\n ctx.lineTo(left + tick, y);\n ctx.moveTo(right - tick, y);\n ctx.lineTo(right, y);\n ctx.moveTo(hashLeft - half, y);\n ctx.lineTo(hashLeft + half, y);\n ctx.moveTo(hashRight - half, y);\n ctx.lineTo(hashRight + half, y);\n }\n ctx.stroke();\n }\n\n /**\n * Draws the yard lines every 5 yards and the outer frame of the window. The goal lines are as\n * thick as the frame and are told apart by color.\n */\n private drawYardLines(\n ctx: CanvasRenderingContext2D,\n geometry: FieldGeometry,\n { left, right, top, bottom }: FieldBounds,\n metrics: FieldMetrics,\n theme: FieldTheme,\n ): void {\n const goalPath = new Path2D();\n const midPath = new Path2D();\n const minorPath = new Path2D();\n for (const yard of yardLinesInWindow(geometry.zone, YARD_LINE_INTERVAL_YARDS)) {\n if (yard < 0 || yard > 100) {\n continue;\n }\n const y = geometry.yForAbsoluteYard(yard);\n const target = yard === 0 || yard === 100 ? goalPath : yard === 50 ? midPath : minorPath;\n target.moveTo(left, y);\n target.lineTo(right, y);\n }\n\n ctx.strokeStyle = theme.lineColor;\n ctx.lineWidth = metrics.yardLineWidth;\n ctx.stroke(minorPath);\n ctx.lineWidth = metrics.midfieldLineWidth;\n ctx.stroke(midPath);\n\n // The sidelines and the top and bottom edges of the window.\n ctx.lineWidth = metrics.goalLineWidth;\n ctx.beginPath();\n ctx.moveTo(left, top);\n ctx.lineTo(left, bottom);\n ctx.moveTo(right, top);\n ctx.lineTo(right, bottom);\n ctx.moveTo(left, top);\n ctx.lineTo(right, top);\n ctx.moveTo(left, bottom);\n ctx.lineTo(right, bottom);\n ctx.stroke();\n\n ctx.strokeStyle = theme.goalLineColor;\n ctx.lineWidth = metrics.goalLineWidth;\n ctx.stroke(goalPath);\n }\n\n /**\n * Draws the number and the direction triangle at every 10-yard line. The numbers are flipped so\n * they read from both sidelines, and the triangles point to the nearer goal. No triangle is drawn\n * at the 50, and nothing at the goal lines and end zones, where displayYardNumber returns null.\n */\n private drawNumbers(\n ctx: CanvasRenderingContext2D,\n geometry: FieldGeometry,\n metrics: FieldMetrics,\n theme: FieldTheme,\n ): void {\n const leftX = geometry.xForLateralYard(NUMBER_FROM_SIDELINE_YARDS);\n const rightX = geometry.xForLateralYard(FIELD_WIDTH_YARDS - NUMBER_FROM_SIDELINE_YARDS);\n const arrowBaseHalfPx = ARROW_HALF_WIDTH_YARDS * geometry.scale;\n const halfArrowLength = ARROW_LENGTH_YARDS / 2;\n\n ctx.font = fieldFont(metrics.numberHeight);\n ctx.textBaseline = \"middle\";\n ctx.textAlign = \"center\";\n\n // The center of the gap between the digits sits on the yard line, and each digit is placed from\n // the edge of the gap by its measured width. ctx.letterSpacing is not used for the gap because\n // it also adds space after the last glyph, which shifts the centering to one side.\n const gap = metrics.numberHeight * NUMBER_DIGIT_SPACING_PER_HEIGHT;\n const drawRotatedLabel = (label: string, x: number, y: number, rotation: number): void => {\n const [head, tail] = [...label];\n ctx.save();\n ctx.translate(x, y);\n ctx.rotate(rotation);\n if (label.length === 2 && head !== undefined && tail !== undefined) {\n ctx.fillText(head, -gap / 2 - ctx.measureText(head).width / 2, 0);\n ctx.fillText(tail, gap / 2 + ctx.measureText(tail).width / 2, 0);\n } else {\n ctx.fillText(label, 0, 0);\n }\n ctx.restore();\n };\n const drawArrow = (xc: number, tipY: number, baseY: number): void => {\n ctx.beginPath();\n ctx.moveTo(xc, tipY);\n ctx.lineTo(xc - arrowBaseHalfPx, baseY);\n ctx.lineTo(xc + arrowBaseHalfPx, baseY);\n ctx.closePath();\n ctx.fill();\n };\n\n for (const yard of yardLinesInWindow(geometry.zone, 10)) {\n const n = displayYardNumber(yard);\n if (n === null) {\n continue;\n }\n const numberY = geometry.yForAbsoluteYard(yard);\n ctx.fillStyle = theme.numberColor;\n drawRotatedLabel(String(n), leftX, numberY, Math.PI / 2);\n drawRotatedLabel(String(n), rightX, numberY, -Math.PI / 2);\n\n if (yard === 50) {\n continue;\n }\n const towardGoal = yard < 50 ? -1 : 1;\n const arrowCenterYard = yard + towardGoal * ARROW_OFFSET_FROM_NUMBER_YARDS;\n const tipY = geometry.yForAbsoluteYard(arrowCenterYard + towardGoal * halfArrowLength);\n const baseY = geometry.yForAbsoluteYard(arrowCenterYard - towardGoal * halfArrowLength);\n ctx.fillStyle = theme.lineColor;\n drawArrow(leftX, tipY, baseY);\n drawArrow(rightX, tipY, baseY);\n }\n }\n\n /** Draws pylons at the end zone's four corners and a goalpost at the center of the end line. */\n private drawEndZoneMarkers(\n ctx: CanvasRenderingContext2D,\n geometry: FieldGeometry,\n { left, right }: FieldBounds,\n metrics: FieldMetrics,\n theme: FieldTheme,\n ): void {\n const { startYard, endYard } = geometry.yardWindow;\n const pylon = metrics.pylonSize;\n const drawPylon = (x: number, y: number): void => {\n ctx.fillStyle = theme.pylonColor;\n ctx.fillRect(x - pylon / 2, y - pylon / 2, pylon, pylon);\n };\n\n if (startYard < 0) {\n const goalY = geometry.yForAbsoluteYard(0);\n const endY = geometry.yForAbsoluteYard(startYard);\n drawPylon(left, goalY);\n drawPylon(right, goalY);\n drawPylon(left, endY);\n drawPylon(right, endY);\n this.drawGoalpost(ctx, geometry, endY, \"toward-top\", metrics, theme);\n }\n if (endYard > 100) {\n const goalY = geometry.yForAbsoluteYard(100);\n const endY = geometry.yForAbsoluteYard(endYard);\n drawPylon(left, goalY);\n drawPylon(right, goalY);\n drawPylon(left, endY);\n drawPylon(right, endY);\n this.drawGoalpost(ctx, geometry, endY, \"toward-bottom\", metrics, theme);\n }\n }\n\n /** Draws the crossbar and two uprights centered on the end line, uprights toward the field. */\n private drawGoalpost(\n ctx: CanvasRenderingContext2D,\n geometry: FieldGeometry,\n lineY: number,\n uprights: UprightDirection,\n metrics: FieldMetrics,\n theme: FieldTheme,\n ): void {\n const cx = geometry.xForLateralYard(FIELD_WIDTH_YARDS / 2);\n const crossHalf = metrics.goalpostCrossbarHalfLength;\n const uprightLen = metrics.goalpostUprightLength;\n const dir = uprights === \"toward-top\" ? -1 : 1;\n\n ctx.save();\n ctx.strokeStyle = theme.goalpostColor;\n ctx.lineWidth = metrics.goalpostStroke;\n ctx.lineCap = \"round\";\n ctx.beginPath();\n ctx.moveTo(cx - crossHalf, lineY);\n ctx.lineTo(cx + crossHalf, lineY);\n ctx.moveTo(cx - crossHalf, lineY);\n ctx.lineTo(cx - crossHalf, lineY + dir * uprightLen);\n ctx.moveTo(cx + crossHalf, lineY);\n ctx.lineTo(cx + crossHalf, lineY + dir * uprightLen);\n ctx.stroke();\n ctx.restore();\n }\n}\n","import {\n FIELD_WIDTH_YARDS,\n type FieldGeometry,\n GRID_STEP_YARDS,\n YARD_LINE_INTERVAL_YARDS,\n yardLinesInWindow,\n} from \"../../common/index.js\";\nimport type { EditorRenderFrame, ILayerRenderer } from \"./layer.js\";\n\n/**\n * Lays faint dots on the grid points where a position can go. It is an editing-only layer, so it\n * does not appear in the PNG or in view mode. The grid's origin is the sideline laterally and the\n * LOS lengthwise, but the LOS is on a whole yard, so stepping absolute yards by GRID_STEP_YARDS\n * gives the same points.\n */\nexport class GridRenderer implements ILayerRenderer<EditorRenderFrame> {\n // There are thousands of dots, and rebuilding them on every drag takes most of the drawing time.\n // While the window and scale stay the same, so does the path, so the previous frame's is reused.\n private cached: { readonly key: string; readonly dots: Path2D } | undefined;\n\n draw(ctx: CanvasRenderingContext2D, frame: EditorRenderFrame): void {\n const { geometry, metrics } = frame;\n const key = [\n geometry.zone,\n geometry.offsetX,\n geometry.offsetY,\n geometry.scale,\n metrics.gridDotRadius,\n ].join(\":\");\n if (this.cached?.key !== key) {\n this.cached = { key, dots: gridDots(geometry, metrics.gridDotRadius) };\n }\n ctx.fillStyle = frame.theme(\"fieldGrid\");\n ctx.fill(this.cached.dots);\n }\n}\n\n// The dots are 1 to 2 px, so squares are indistinguishable from circles and cheaper to fill.\nfunction gridDots(geometry: FieldGeometry, radius: number): Path2D {\n const dots = new Path2D();\n const size = 2 * radius;\n for (const yard of yardLinesInWindow(geometry.zone, GRID_STEP_YARDS)) {\n // No dots on the yard lines or the window edges. Every window edge is a multiple of 5 yards,\n // but the end zones (before 0 and beyond 100) have no yard lines, so dots do go there.\n const { startYard, endYard } = geometry.yardWindow;\n const onYardLine = yard >= 0 && yard <= 100 && yard % YARD_LINE_INTERVAL_YARDS === 0;\n if (onYardLine || yard === startYard || yard === endYard) {\n continue;\n }\n const y = geometry.yForAbsoluteYard(yard);\n for (let lateral = GRID_STEP_YARDS; lateral < FIELD_WIDTH_YARDS; lateral += GRID_STEP_YARDS) {\n dots.rect(geometry.xForLateralYard(lateral) - radius, y - radius, size, size);\n }\n }\n return dots;\n}\n","import {\n arrowHeadVertices,\n blockCapEndpoints,\n type CanvasPoint,\n DEFAULT_LINE_THICKNESS,\n type FieldMetrics,\n indexPlayersById,\n type LineKind,\n lineAnchorPoints,\n sampleLinePath,\n trimForArrowHead,\n} from \"../../common/index.js\";\nimport type { ThemeTokenName } from \"../theme/tokens.js\";\nimport type { ILayerRenderer, RenderFrame } from \"./layer.js\";\n\nconst LINE_COLOR_TOKENS = {\n route: \"lineRoute\",\n block: \"lineBlock\",\n motion: \"lineMotion\",\n} as const satisfies Record<LineKind, ThemeTokenName>;\n\nexport class LineRenderer implements ILayerRenderer {\n /** A line whose origin player cannot be found is not drawn. */\n draw(ctx: CanvasRenderingContext2D, frame: RenderFrame): void {\n const { geometry, metrics } = frame;\n const { lines, players } = frame.scene;\n if (geometry.scale <= 0 || lines.length === 0) {\n return;\n }\n const byId = indexPlayersById(players);\n\n for (const line of lines) {\n const anchors = lineAnchorPoints(line, byId);\n if (anchors === undefined) {\n continue;\n }\n const path = sampleLinePath(anchors, line.interpolation).map((p) => geometry.toCanvas(p));\n if (path.length < 2) {\n continue;\n }\n\n const color = line.color ?? frame.theme(LINE_COLOR_TOKENS[line.kind]);\n // thickness is a factor on the default width, so the look matches on screen and in the PNG.\n const width = this.widthFor(line.kind, metrics) * (line.thickness ?? DEFAULT_LINE_THICKNESS);\n const isBlock = line.kind === \"block\";\n\n ctx.save();\n ctx.strokeStyle = color;\n ctx.lineWidth = width;\n ctx.lineJoin = \"round\";\n ctx.lineCap = \"round\";\n if (line.kind === \"motion\") {\n ctx.setLineDash([...metrics.motionDash]);\n }\n\n // A line with an arrow stops at the arrowhead's base. If drawn to the tip, its round cap\n // would poke out past the narrowing tip of the triangle and look like a dot at the end.\n // A block line gets a T bar, so it runs to the endpoint. The arrowhead's direction and\n // position come from the path before trimming.\n const strokePath = isBlock ? path : trimForArrowHead(path, metrics.arrowLength);\n ctx.beginPath();\n // The first lineTo on an empty path acts as a moveTo.\n for (const pt of strokePath) {\n ctx.lineTo(pt.x, pt.y);\n }\n ctx.stroke();\n ctx.setLineDash([]);\n\n if (isBlock) {\n this.drawBlockCap(ctx, path, color, metrics, width);\n } else {\n this.drawArrowHead(ctx, path, color, metrics);\n }\n ctx.restore();\n }\n }\n\n private widthFor(kind: LineKind, metrics: FieldMetrics): number {\n return kind === \"block\" ? metrics.blockWidth : metrics.routeWidth;\n }\n\n /** Draws a triangular arrowhead at the endpoint, pointing in the direction of travel. */\n private drawArrowHead(\n ctx: CanvasRenderingContext2D,\n path: readonly CanvasPoint[],\n color: string,\n metrics: FieldMetrics,\n ): void {\n const vertices = arrowHeadVertices(path, metrics.arrowLength, metrics.arrowHalfWidth);\n if (vertices === undefined) {\n return;\n }\n const [tip, left, right] = vertices;\n ctx.beginPath();\n ctx.moveTo(tip.x, tip.y);\n ctx.lineTo(left.x, left.y);\n ctx.lineTo(right.x, right.y);\n ctx.closePath();\n ctx.fillStyle = color;\n ctx.fill();\n }\n\n /** Draws a T bar at the endpoint, perpendicular to the direction of travel. */\n private drawBlockCap(\n ctx: CanvasRenderingContext2D,\n path: readonly CanvasPoint[],\n color: string,\n metrics: FieldMetrics,\n width: number,\n ): void {\n const endpoints = blockCapEndpoints(path, metrics.blockCapLength);\n if (endpoints === undefined) {\n return;\n }\n const [from, to] = endpoints;\n ctx.beginPath();\n ctx.strokeStyle = color;\n ctx.lineWidth = width * 1.2;\n ctx.lineCap = \"round\";\n ctx.moveTo(from.x, from.y);\n ctx.lineTo(to.x, to.y);\n ctx.stroke();\n }\n}\n","import {\n type EditorOverlay,\n PLAYER_RADIUS_YARDS,\n WAYPOINT_HANDLE_RADIUS_YARDS,\n} from \"../../common/index.js\";\nimport type { EditorRenderFrame, ILayerRenderer } from \"./layer.js\";\n\n/**\n * Draws the selection highlight and the handles for a line's waypoints and endpoint.\n *\n * The selection color sits outside the target rather than over it (a ring around the player marker,\n * handles on a line). Line colors come from the palette, so painting over would blend with the line\n * color and hide which color the line is.\n */\nexport class OverlayRenderer implements ILayerRenderer<EditorRenderFrame> {\n draw(ctx: CanvasRenderingContext2D, frame: EditorRenderFrame): void {\n const { overlay } = frame;\n switch (overlay.kind) {\n case \"none\":\n return;\n case \"player\":\n drawSelectedPlayer(ctx, frame, overlay.playerId);\n return;\n case \"line\":\n drawLineHandles(ctx, frame, overlay);\n return;\n }\n }\n}\n\nfunction drawSelectedPlayer(\n ctx: CanvasRenderingContext2D,\n frame: EditorRenderFrame,\n playerId: string,\n): void {\n const player = frame.scene.players.find((p) => p.id === playerId);\n if (player === undefined) {\n return;\n }\n const { x, y } = frame.geometry.toCanvas(player.position);\n const r = PLAYER_RADIUS_YARDS * frame.geometry.scale + 4;\n ctx.beginPath();\n ctx.arc(x, y, r, 0, Math.PI * 2);\n ctx.strokeStyle = frame.theme(\"selection\");\n ctx.lineWidth = 2;\n ctx.stroke();\n}\n\nfunction drawLineHandles(\n ctx: CanvasRenderingContext2D,\n frame: EditorRenderFrame,\n handles: Extract<EditorOverlay, { kind: \"line\" }>,\n): void {\n const { geometry } = frame;\n const fill = frame.theme(\"selection\");\n const outline = frame.theme(\"selectionOutline\");\n // The icon covers only part of the hit area (WAYPOINT_HANDLE_RADIUS_YARDS). Drawn to the full\n // area it would be as big as a marker, so it stays small and grabbing is left to the hit area.\n const handleHalf = Math.max(3, 0.45 * WAYPOINT_HANDLE_RADIUS_YARDS * geometry.scale);\n // Finishes the current path (after beginPath) with the fill and outline shared by all handles.\n const paintHandle = (): void => {\n ctx.fillStyle = fill;\n ctx.fill();\n ctx.lineWidth = 1.5;\n ctx.strokeStyle = outline;\n ctx.stroke();\n };\n for (const waypoint of handles.waypointHandles) {\n const { x, y } = geometry.toCanvas(waypoint);\n ctx.beginPath();\n ctx.rect(x - handleHalf, y - handleHalf, handleHalf * 2, handleHalf * 2);\n paintHandle();\n }\n // The endpoint handle is a circle so it can be told apart from the waypoint squares.\n const { x, y } = geometry.toCanvas(handles.endpointHandle);\n ctx.beginPath();\n ctx.arc(x, y, handleHalf + 1, 0, Math.PI * 2);\n paintHandle();\n}\n","import {\n DEFAULT_PLAYER_FILL,\n PLAYER_RADIUS_YARDS,\n playerMarkerOutline,\n} from \"../../common/index.js\";\nimport { fieldFont } from \"../theme/field-font.js\";\nimport type { ILayerRenderer, RenderFrame } from \"./layer.js\";\n\nexport class PlayerRenderer implements ILayerRenderer {\n draw(ctx: CanvasRenderingContext2D, frame: RenderFrame): void {\n const { geometry, metrics } = frame;\n const { players } = frame.scene;\n const defaultColor = frame.theme(\"playerColor\");\n const grass = frame.theme(\"fieldGrass\");\n const label = frame.theme(\"playerLabel\");\n // The outline has the same radius as the hit area, and the stroke sits on top of it.\n const r = PLAYER_RADIUS_YARDS * geometry.scale;\n if (r <= 0) {\n return;\n }\n const fontPx = metrics.markerLabelFont;\n\n for (const player of players) {\n const center = geometry.toCanvas(player.position);\n const color = player.color ?? defaultColor;\n const isSolid = (player.fill ?? DEFAULT_PLAYER_FILL) === \"solid\";\n\n ctx.beginPath();\n const outline = playerMarkerOutline(player.shape, r);\n if (outline.kind === \"circle\") {\n ctx.arc(center.x, center.y, outline.radius, 0, Math.PI * 2);\n } else {\n const { halfSide } = outline;\n ctx.rect(center.x - halfSide, center.y - halfSide, halfSide * 2, halfSide * 2);\n }\n // Outline-only players get the grass color inside too, to hide lines that pass underneath.\n // Players in the end zone get the grass color as well. By default the end zone and the grass\n // differ too little to justify a fill that depends on position.\n ctx.fillStyle = isSolid ? color : grass;\n ctx.fill();\n ctx.lineWidth = metrics.markerStroke;\n ctx.strokeStyle = color;\n ctx.stroke();\n\n if (player.label !== \"\") {\n ctx.fillStyle = isSolid ? label : color;\n ctx.font = fieldFont(fontPx);\n ctx.textAlign = \"center\";\n // textBaseline middle aligns to the center of the em box, so it drifts up or down by font.\n // The center of the measured glyph bounds is aligned to the marker center instead.\n ctx.textBaseline = \"alphabetic\";\n const tm = ctx.measureText(player.label);\n const labelY = center.y + (tm.actualBoundingBoxAscent - tm.actualBoundingBoxDescent) / 2;\n ctx.fillText(player.label, center.x, labelY);\n }\n }\n }\n}\n","import type { EditorOverlay, FieldGeometry, FieldMetrics, SceneData } from \"../../common/index.js\";\nimport type { ThemeReader } from \"../theme/tokens.js\";\nimport { FieldRenderer } from \"./field-renderer.js\";\nimport { GridRenderer } from \"./grid-renderer.js\";\nimport { LineRenderer } from \"./line-renderer.js\";\nimport { OverlayRenderer } from \"./overlay-renderer.js\";\nimport { PlayerRenderer } from \"./player-renderer.js\";\n\n/** The input shared by the diagram layers in one rendering pass. */\nexport interface RenderFrame {\n readonly geometry: FieldGeometry;\n readonly metrics: FieldMetrics;\n readonly scene: SceneData;\n readonly theme: ThemeReader;\n}\n\n/**\n * The input only the editor layers receive. The overlay is never passed to the diagram layers, so\n * no selection highlight or handle can slip into a diagram exported as PNG.\n */\nexport interface EditorRenderFrame extends RenderFrame {\n readonly overlay: EditorOverlay;\n}\n\n/**\n * Draws one layer of the diagram. ctx already has the DPR transform applied, so drawing can use\n * CSS px coordinates. Each layer is wrapped in save and restore, so it need not reset ctx state.\n */\nexport interface ILayerRenderer<F extends RenderFrame = RenderFrame> {\n // With method syntax the parameter is checked bivariantly, and putting an editor layer among the\n // diagram layers would not be a type error.\n readonly draw: (ctx: CanvasRenderingContext2D, frame: F) => void;\n}\n\n/**\n * The layers that draw the diagram (field and play) and the layers inserted only while editing\n * (backdrop and editor). The drawing order is field, backdrop, play, editor. PNG export and view\n * mode draw only the diagram layers, so the grid, selection highlight, and handles never reach the\n * exported image or the read-only screen.\n */\nexport interface SurfaceLayers {\n /** The field. The diagram layers drawn below the editing-time backdrop. */\n readonly field: readonly ILayerRenderer[];\n /** Editing-only layers between the field and the lines and players. Holds the position grid. */\n readonly backdrop: readonly ILayerRenderer<EditorRenderFrame>[];\n /** The lines and players. */\n readonly play: readonly ILayerRenderer[];\n /** Layers overlaid on the diagram only while editing. */\n readonly editor: readonly ILayerRenderer<EditorRenderFrame>[];\n}\n\n/** Lines go under the players. On top, the root of a line would overlap the player's marker. */\nexport function createDefaultLayers(): SurfaceLayers {\n return {\n field: [new FieldRenderer()],\n backdrop: [new GridRenderer()],\n play: [new LineRenderer(), new PlayerRenderer()],\n editor: [new OverlayRenderer()],\n };\n}\n\nexport function renderLayers<F extends RenderFrame>(\n ctx: CanvasRenderingContext2D,\n frame: F,\n layers: readonly ILayerRenderer<F>[],\n): void {\n for (const layer of layers) {\n ctx.save();\n layer.draw(ctx, frame);\n ctx.restore();\n }\n}\n","import {\n computeFieldMetrics,\n Disposable,\n type EditorOverlay,\n FieldGeometry,\n type FieldPosition,\n type ImageExportOptions,\n resolveImageExportSize,\n type SceneData,\n toDisposable,\n} from \"../../common/index.js\";\nimport { loadFieldFont } from \"../theme/field-font-face.js\";\nimport { createThemeReader } from \"../theme/theme-reader.js\";\nimport type { ThemeReader } from \"../theme/tokens.js\";\nimport {\n createDefaultLayers,\n type EditorRenderFrame,\n type RenderFrame,\n renderLayers,\n type SurfaceLayers,\n} from \"./layer.js\";\n\n/**\n * Owns the canvas size, the DPR, and when to redraw, and draws the given diagram layer by layer.\n * What to draw (preview composition, selection highlight) is decided by whoever passes the diagram.\n */\nexport class CanvasSurface extends Disposable {\n readonly canvas: HTMLCanvasElement;\n private readonly ctx: CanvasRenderingContext2D;\n private readonly layers: SurfaceLayers;\n private scene: SceneData;\n private overlay: EditorOverlay | undefined;\n private frameRequest: number | undefined;\n private dpr = 0;\n // Removes the DPR watchers all at once on dispose.\n private readonly lifetime = new AbortController();\n\n constructor(\n parent: HTMLElement,\n scene: SceneData,\n overlay: EditorOverlay | undefined,\n layers: SurfaceLayers = createDefaultLayers(),\n ) {\n super();\n this.scene = scene;\n this.overlay = overlay;\n this.layers = layers;\n\n this.canvas = document.createElement(\"canvas\");\n this.canvas.className = \"playmaker-canvas\";\n parent.appendChild(this.canvas);\n this._register(toDisposable(() => this.canvas.remove()));\n\n const ctx = this.canvas.getContext(\"2d\");\n if (!ctx) {\n throw new Error(\"Playmaker: could not get a 2D canvas context.\");\n }\n this.ctx = ctx;\n\n const observer = new ResizeObserver(() => this.resize());\n observer.observe(parent);\n this._register(\n toDisposable(() => {\n observer.disconnect();\n this.lifetime.abort();\n this.cancelFrame();\n }),\n );\n\n // The first draw is left to the ResizeObserver notification after observe. When constructed,\n // the host has not placed the toolbar or panel, so a draw here would be thrown away by the\n // resize that follows.\n this.watchDevicePixelRatio();\n this.redrawWhenFontLoads();\n }\n\n /** Theme variables are re-read per draw, so a call after the host changes them takes effect. */\n refresh(): void {\n this.invalidate();\n }\n\n /**\n * Replaces the diagram and the overlay. Drawing happens on the next frame, so\n * replacing them any number of times within the same frame draws only once.\n * Omitting overlay skips editing-only layers like the grid and selection highlight (view mode).\n */\n setScene(scene: SceneData, overlay: EditorOverlay | undefined): void {\n this.scene = scene;\n this.overlay = overlay;\n this.invalidate();\n }\n\n /** Converts clientX / clientY of a mouse event into a position in yards on the field. */\n clientToYard(clientX: number, clientY: number): FieldPosition {\n const rect = this.canvas.getBoundingClientRect();\n return this.measure().fromCanvas({ x: clientX - rect.left, y: clientY - rect.top });\n }\n\n /**\n * Exports the given play diagram as a PNG (Blob). Only the diagram layers are drawn, so the grid,\n * selection highlight, and handles stay out. The colors come from the screen's theme variables.\n * Drawing waits for the bundled font, so numbers and labels use it even right after construction.\n */\n async exportToPngBlob(data: SceneData, options?: ImageExportOptions): Promise<Blob> {\n const { width, height } = resolveImageExportSize(data.field.zone, options);\n // Colors are fixed at call time. If the host reverts the variables while the font loads, or if\n // it disposes and the canvas leaves the host, the exported colors do not change.\n const theme = createThemeReader(this.host);\n await loadFieldFont();\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n throw new Error(\"Playmaker: could not get a 2D canvas context for the export.\");\n }\n const geometry = new FieldGeometry(width, height, data.field);\n this.paint(ctx, this.frameFor(geometry, data, theme), undefined);\n return new Promise<Blob>((resolve, reject) => {\n canvas.toBlob((blob) => {\n if (blob) {\n resolve(blob);\n } else {\n reject(new Error(\"Playmaker: could not convert the canvas to a PNG.\"));\n }\n }, \"image/png\");\n });\n }\n\n /**\n * Draws with the fallback font first to avoid a blank screen, then redraws once the bundled font\n * has loaded.\n */\n private redrawWhenFontLoads(): void {\n void loadFieldFont().then(() => {\n if (!this.isDisposed) {\n this.invalidate();\n }\n });\n }\n\n /** Rebuilds the drawing buffer to match the container size and the DPR. */\n private resize(): void {\n const dpr = window.devicePixelRatio || 1;\n const { clientWidth, clientHeight } = this.host;\n const width = Math.max(1, Math.round(clientWidth * dpr));\n const height = Math.max(1, Math.round(clientHeight * dpr));\n // Even if the element resized, the drawn diagram is reusable if the buffer size and DPR match.\n if (width === this.canvas.width && height === this.canvas.height && dpr === this.dpr) {\n return;\n }\n this.dpr = dpr;\n this.canvas.width = width;\n this.canvas.height = height;\n // From here on, drawing uses CSS px coordinates. The geometry is computed in CSS px as well.\n this.ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n // Rebuilding the buffer clears its contents, so draw now instead of waiting for the next frame.\n this.cancelFrame();\n this.render();\n }\n\n // A resolution media query fires change only once, when it no longer matches the current DPR.\n // Each time the browser zoom or the monitor changes, it is re-attached with the new DPR.\n private watchDevicePixelRatio(): void {\n const query = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);\n query.addEventListener(\n \"change\",\n () => {\n this.resize();\n this.watchDevicePixelRatio();\n },\n { once: true, signal: this.lifetime.signal },\n );\n }\n\n private invalidate(): void {\n // No drawing before the initial size is known. resize draws once it is.\n if (this.frameRequest !== undefined || this.dpr === 0) {\n return;\n }\n this.frameRequest = requestAnimationFrame(() => {\n this.frameRequest = undefined;\n this.render();\n });\n }\n\n private cancelFrame(): void {\n if (this.frameRequest !== undefined) {\n cancelAnimationFrame(this.frameRequest);\n this.frameRequest = undefined;\n }\n }\n\n private render(): void {\n const frame = this.frameFor(this.measure(), this.scene, createThemeReader(this.host));\n this.paint(this.ctx, frame, this.overlay);\n }\n\n /**\n * Draws the layers in the order field, backdrop, play, editor. Without an overlay, editing-only\n * layers (backdrop and editor) are skipped, so view mode and PNG export use only diagram layers.\n */\n private paint(\n ctx: CanvasRenderingContext2D,\n frame: RenderFrame,\n overlay: EditorOverlay | undefined,\n ): void {\n const editorFrame: EditorRenderFrame | undefined =\n overlay === undefined ? undefined : { ...frame, overlay };\n renderLayers(ctx, frame, this.layers.field);\n if (editorFrame !== undefined) {\n renderLayers(ctx, editorFrame, this.layers.backdrop);\n }\n renderLayers(ctx, frame, this.layers.play);\n if (editorFrame !== undefined) {\n renderLayers(ctx, editorFrame, this.layers.editor);\n }\n }\n\n private frameFor(geometry: FieldGeometry, scene: SceneData, theme: ThemeReader): RenderFrame {\n return {\n geometry,\n metrics: computeFieldMetrics(geometry.fieldPixelWidth, geometry.scale),\n scene,\n theme,\n };\n }\n\n private measure(): FieldGeometry {\n const { clientWidth, clientHeight } = this.host;\n return new FieldGeometry(clientWidth, clientHeight, this.scene.field);\n }\n\n // The size and the theme variables are read from the element the canvas was placed in.\n private get host(): HTMLElement {\n return this.canvas.parentElement ?? this.canvas;\n }\n}\n","/**\n * Rebuilds the contents of container with update. If the focus that was in container before the\n * rebuild vanished along with a removed element, it is moved to fallback. Lost focus falls to body,\n * and keys would no longer reach the element that receives the shortcuts.\n */\nexport function replaceKeepingFocus(\n container: HTMLElement,\n fallback: HTMLElement,\n update: () => void,\n): void {\n const doc = container.ownerDocument;\n const hadFocus = container.contains(doc.activeElement);\n update();\n if (hadFocus && !container.contains(doc.activeElement)) {\n fallback.focus({ preventScroll: true });\n }\n}\n","import type { ThemeTokenName } from \"./tokens.js\";\n\n/**\n * Players and lines do not get a free color choice; the choice is limited to 4 colors that are\n * easy to tell apart on white grass. The saved color is the concrete color resolved from the\n * theme variable, because the canvas cannot interpret var(). The names shown for the swatches\n * come from the messages of the chosen locale.\n */\nexport const SWATCH_TOKENS = [\n \"swatch1\",\n \"swatch2\",\n \"swatch3\",\n \"swatch4\",\n] as const satisfies readonly ThemeTokenName[];\n\nexport type SwatchToken = (typeof SWATCH_TOKENS)[number];\n","import {\n DEFAULT_LINE_THICKNESS,\n DEFAULT_PLAYER_FILL,\n Disposable,\n type IEditorUi,\n isLineThickness,\n isOneOf,\n LINE_INTERPOLATION_VALUES,\n LINE_KIND_VALUES,\n type Line,\n PLAYER_FILL_VALUES,\n PLAYER_SHAPE_VALUES,\n type Player,\n type PlayerFill,\n toDisposable,\n} from \"../../common/index.js\";\nimport type { Messages } from \"../i18n/index.js\";\nimport { normalizeCssColor } from \"../theme/css-color.js\";\nimport { SWATCH_TOKENS } from \"../theme/palette.js\";\nimport { createThemeReader } from \"../theme/theme-reader.js\";\nimport { replaceKeepingFocus } from \"./focus.js\";\n\n/** One round color button. An outline swatch colors only its rim; a solid one fills the inside. */\ninterface SwatchChoice {\n readonly name: string;\n readonly color: string;\n readonly fill: PlayerFill;\n}\n\nfunction createSwatch({ name, color, fill }: SwatchChoice): HTMLButtonElement {\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = \"playmaker-panel__swatch\";\n btn.title = name;\n btn.setAttribute(\"aria-label\", name);\n if (fill === \"outline\") {\n btn.classList.add(\"playmaker-panel__swatch--outline\");\n btn.style.borderColor = color;\n } else {\n btn.style.backgroundColor = color;\n }\n return btn;\n}\n\n/** The selected item. Selection is single, so the type never has a player and a line at once. */\ntype SelectedItem =\n | { readonly kind: \"player\"; readonly player: Player }\n | { readonly kind: \"line\"; readonly line: Line }\n | { readonly kind: \"none\" };\n\n/**\n * The values passed to an input and the function called when the value changes. Passing onReset\n * adds a button that restores the default.\n */\ninterface InputOptions<T> {\n readonly label: string;\n readonly onChange: (value: T) => void;\n readonly onReset?: () => void;\n}\n\n/** The inputs on display, and the function that writes the selected item's values into them. */\ntype ShownFields =\n | { readonly kind: \"player\"; readonly id: string; readonly update: (player: Player) => void }\n | { readonly kind: \"line\"; readonly id: string; readonly update: (line: Line) => void }\n | { readonly kind: \"hint\" };\n\nexport class PropertyPanel extends Disposable {\n readonly element: HTMLElement;\n private readonly controller: IEditorUi;\n private readonly focusFallback: HTMLElement;\n private readonly messages: Messages;\n // While the selected item stays the same, the inputs are kept and only their values are written.\n // Rebuilding them would remove the input Tab just moved to after a commit, and drop the focus.\n private shown: ShownFields | undefined;\n\n /**\n * @param focusFallback Where the focus goes when a selection change rebuilds the inputs and\n * removes the input that had it.\n */\n constructor(\n parent: HTMLElement,\n controller: IEditorUi,\n focusFallback: HTMLElement,\n messages: Messages,\n ) {\n super();\n this.controller = controller;\n this.focusFallback = focusFallback;\n this.messages = messages;\n this.element = document.createElement(\"div\");\n this.element.className = \"playmaker-panel\";\n\n parent.appendChild(this.element);\n this._register(toDisposable(() => this.element.remove()));\n this._register(controller.onDidChangeViewState(() => this.sync()));\n this.sync();\n }\n\n /**\n * The default colors and swatches are read from the theme variables, so a call after the host\n * changes the variables picks up the change.\n */\n refresh(): void {\n this.shown = undefined;\n this.sync();\n }\n\n private sync(): void {\n const selected = this.selectedItem();\n const shown = this.shown;\n if (selected.kind === \"player\" && shown?.kind === \"player\" && shown.id === selected.player.id) {\n shown.update(selected.player);\n return;\n }\n if (selected.kind === \"line\" && shown?.kind === \"line\" && shown.id === selected.line.id) {\n shown.update(selected.line);\n return;\n }\n if (selected.kind === \"none\" && shown?.kind === \"hint\") {\n return;\n }\n replaceKeepingFocus(this.element, this.focusFallback, () => {\n this.shown = this.rebuild(selected);\n });\n }\n\n private selectedItem(): SelectedItem {\n const player = this.controller.getSelectedPlayer();\n if (player !== undefined) {\n return { kind: \"player\", player };\n }\n const line = this.controller.getSelectedLine();\n if (line !== undefined) {\n return { kind: \"line\", line };\n }\n return { kind: \"none\" };\n }\n\n private rebuild(selected: SelectedItem): ShownFields {\n this.element.replaceChildren();\n switch (selected.kind) {\n case \"player\": {\n const update = this.addPlayerFields();\n update(selected.player);\n return { kind: \"player\", id: selected.player.id, update };\n }\n case \"line\": {\n const update = this.addLineFields();\n update(selected.line);\n return { kind: \"line\", id: selected.line.id, update };\n }\n case \"none\": {\n const hint = document.createElement(\"p\");\n hint.className = \"playmaker-panel__hint\";\n hint.textContent = this.messages.selectionHint;\n this.element.appendChild(hint);\n return { kind: \"hint\" };\n }\n }\n }\n\n private addPlayerFields(): (player: Player) => void {\n const { controller, messages } = this;\n this.addTitle(messages.player);\n const setLabel = this.addText({\n label: messages.label,\n onChange: (v) => controller.updateSelectedPlayer({ label: v }),\n });\n const setShape = this.addSelect(messages.shape, PLAYER_SHAPE_VALUES, messages.shapes, (v) =>\n controller.updateSelectedPlayer({ shape: v }),\n );\n // Eight swatches, outline and solid for each of the four colors. The top row is outline, the\n // bottom row solid. Instead of a separate \"solid or outline\" choice, both are picked at once.\n const read = createThemeReader(this.element);\n const choices = PLAYER_FILL_VALUES.flatMap((fill) =>\n SWATCH_TOKENS.map((token) => ({\n name: messages.swatch(messages.colors[token], messages.fills[fill]),\n color: read(token),\n fill,\n })),\n );\n const setPressed = this.addSwatches(messages.color, choices, {\n onChange: ({ color, fill }) => controller.updateSelectedPlayer({ color, fill }),\n onReset: () => controller.updateSelectedPlayer({ color: null, fill: null }),\n });\n // A player with no color shows the outline swatch of the theme's default color as pressed.\n const defaultColor = read(\"playerColor\");\n return (player) => {\n setLabel(player.label);\n setShape(player.shape);\n const color = player.color === undefined ? defaultColor : normalizeCssColor(player.color);\n const fill = player.fill ?? DEFAULT_PLAYER_FILL;\n setPressed((choice) => choice.color === color && choice.fill === fill);\n };\n }\n\n private addLineFields(): (line: Line) => void {\n const { controller, messages } = this;\n this.addTitle(messages.line);\n const setKind = this.addSelect(messages.kind, LINE_KIND_VALUES, messages.kinds, (v) =>\n controller.updateSelectedLine({ kind: v }),\n );\n const setInterpolation = this.addSelect(\n messages.interpolation,\n LINE_INTERPOLATION_VALUES,\n messages.interpolations,\n (v) => controller.updateSelectedLine({ interpolation: v }),\n );\n const read = createThemeReader(this.element);\n const choices = SWATCH_TOKENS.map((token) => ({\n name: messages.colors[token],\n color: read(token),\n fill: \"solid\" as const,\n }));\n const setPressed = this.addSwatches(messages.color, choices, {\n onChange: ({ color }) => controller.updateSelectedLine({ color }),\n onReset: () => controller.updateSelectedLine({ color: null }),\n });\n const setThickness = this.addNumber({\n label: messages.width,\n onChange: (v) => controller.updateSelectedLine({ thickness: v }),\n onReset: () => controller.updateSelectedLine({ thickness: null }),\n });\n return (line) => {\n setKind(line.kind);\n setInterpolation(line.interpolation);\n // A saved color may come from outside, so it is normalized like the theme colors to compare.\n const color = line.color === undefined ? undefined : normalizeCssColor(line.color);\n setPressed((choice) => choice.color === color);\n setThickness(line.thickness ?? DEFAULT_LINE_THICKNESS);\n };\n }\n\n private addTitle(text: string): void {\n const h = document.createElement(\"h2\");\n h.className = \"playmaker-panel__title\";\n h.textContent = text;\n this.element.appendChild(h);\n }\n\n private addRow(labelText: string, control: HTMLElement, onReset?: () => void): void {\n const label = document.createElement(\"label\");\n label.className = \"playmaker-panel__label\";\n label.append(this.createRowText(labelText), control);\n this.appendRow(labelText, [label], onReset);\n }\n\n /**\n * A row of buttons. Wrapping it in a label would press the first button when the text is clicked,\n * so the group of buttons is named instead.\n */\n private addGroupRow(\n labelText: string,\n group: HTMLElement,\n onReset: (() => void) | undefined,\n ): void {\n group.setAttribute(\"role\", \"group\");\n group.setAttribute(\"aria-label\", labelText);\n this.appendRow(labelText, [this.createRowText(labelText), group], onReset);\n }\n\n private createRowText(text: string): HTMLElement {\n const span = document.createElement(\"span\");\n span.className = \"playmaker-panel__row-text\";\n span.textContent = text;\n return span;\n }\n\n /**\n * With onReset, a button that clears the value back to the theme default ends the row.\n * It stays enabled even at the default; disabling it right after a press drops the focus to body.\n */\n private appendRow(\n labelText: string,\n children: readonly HTMLElement[],\n onReset: (() => void) | undefined,\n ): void {\n const row = document.createElement(\"div\");\n row.className = \"playmaker-panel__row\";\n row.append(...children);\n if (onReset !== undefined) {\n const reset = document.createElement(\"button\");\n reset.type = \"button\";\n reset.className = \"playmaker-panel__reset\";\n reset.textContent = this.messages.reset;\n reset.setAttribute(\"aria-label\", this.messages.resetField(labelText));\n reset.addEventListener(\"click\", onReset);\n row.appendChild(reset);\n }\n this.element.appendChild(row);\n }\n\n private addText({ label, onChange, onReset }: InputOptions<string>): (value: string) => void {\n const input = document.createElement(\"input\");\n input.type = \"text\";\n input.addEventListener(\"change\", () => onChange(input.value));\n this.addRow(label, input, onReset);\n return (value) => {\n input.value = value;\n };\n }\n\n private addNumber({ label, onChange, onReset }: InputOptions<number>): (value: number) => void {\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.min = \"0.25\";\n input.step = \"0.25\";\n input.addEventListener(\"change\", () => {\n const n = Number(input.value);\n if (isLineThickness(n)) {\n onChange(n);\n }\n });\n this.addRow(label, input, onReset);\n return (value) => {\n input.value = String(value);\n };\n }\n\n /**\n * A row of color swatches. The chosen choice is passed to onChange.\n * The returned function takes a predicate for the pressed choice, to match the selected item.\n */\n private addSwatches<T extends SwatchChoice>(\n label: string,\n choices: readonly T[],\n { onChange, onReset }: Omit<InputOptions<T>, \"label\">,\n ): (isPressed: (choice: T) => boolean) => void {\n const group = document.createElement(\"div\");\n group.className = \"playmaker-panel__swatches\";\n if (choices.some((choice) => choice.fill === \"outline\")) {\n group.classList.add(\"playmaker-panel__swatches--rows\");\n }\n const swatches = choices.map((choice) => {\n const btn = createSwatch(choice);\n btn.addEventListener(\"click\", () => onChange(choice));\n group.appendChild(btn);\n return { btn, choice };\n });\n this.addGroupRow(label, group, onReset);\n return (isPressed) => {\n for (const { btn, choice } of swatches) {\n btn.setAttribute(\"aria-pressed\", String(isPressed(choice)));\n }\n };\n }\n\n private addSelect<T extends string>(\n labelText: string,\n options: readonly T[],\n optionLabels: Readonly<Record<T, string>>,\n onChange: (v: T) => void,\n ): (value: T) => void {\n const select = document.createElement(\"select\");\n for (const opt of options) {\n const o = document.createElement(\"option\");\n o.value = opt;\n o.textContent = optionLabels[opt];\n select.appendChild(o);\n }\n select.addEventListener(\"change\", () => {\n if (isOneOf(select.value, options)) {\n onChange(select.value);\n }\n });\n this.addRow(labelText, select);\n return (value) => {\n select.value = value;\n };\n }\n}\n","import {\n canLoadFormation,\n Disposable,\n EDITOR_TOOL_VALUES,\n type EditorTool,\n type EditorViewState,\n FIELD_ZONE_VALUES,\n type FieldZone,\n FORMATION_PRESETS,\n getFormationPreset,\n type IEditorUi,\n isToolAvailable,\n MAX_LINES,\n MAX_PLAYERS,\n TEAM_SIDE_VALUES,\n toDisposable,\n} from \"../../common/index.js\";\nimport type { Messages } from \"../i18n/index.js\";\n\n/**\n * Disabled state is shown with aria-disabled, not disabled. With disabled, focus falls to the\n * body from a button that became disabled right after it was pressed, and the shortcuts\n * received inside the editing UI stop working.\n */\nfunction setEnabled(button: HTMLButtonElement, enabled: boolean, reason?: string): void {\n button.setAttribute(\"aria-disabled\", String(!enabled));\n setTitle(button, enabled ? undefined : reason);\n}\n\nfunction setTitle(element: HTMLElement, title: string | undefined): void {\n if (title === undefined) {\n element.removeAttribute(\"title\");\n } else {\n element.title = title;\n }\n}\n\nexport class Toolbar extends Disposable {\n readonly element: HTMLElement;\n private readonly messages: Messages;\n private readonly toolButtons = new Map<EditorTool, HTMLButtonElement>();\n private readonly zoneButtons = new Map<FieldZone, HTMLButtonElement>();\n private readonly undoButton: HTMLButtonElement;\n private readonly redoButton: HTMLButtonElement;\n private readonly deleteButton: HTMLButtonElement;\n private readonly commitButton: HTMLButtonElement;\n private readonly cancelButton: HTMLButtonElement;\n private readonly formationPicker: HTMLSelectElement;\n // Text shown as the reason on parts that cannot be pressed because a count limit is reached.\n private readonly toolUnavailableReasons: Readonly<Partial<Record<EditorTool, string>>>;\n\n constructor(parent: HTMLElement, controller: IEditorUi, messages: Messages) {\n super();\n this.messages = messages;\n this.toolUnavailableReasons = {\n \"add-player\": messages.playerLimit(MAX_PLAYERS),\n \"draw-line\": messages.lineLimit(MAX_LINES),\n };\n this.element = document.createElement(\"div\");\n this.element.className = \"playmaker-toolbar\";\n\n for (const tool of EDITOR_TOOL_VALUES) {\n const btn = this.addButton(messages.tool[tool], () => controller.setTool(tool));\n this.toolButtons.set(tool, btn);\n }\n this.addSeparator();\n this.undoButton = this.addButton(messages.undo, () => controller.undo());\n this.redoButton = this.addButton(messages.redo, () => controller.redo());\n this.deleteButton = this.addButton(messages.delete, () => controller.deleteSelection());\n this.addSeparator();\n for (const zone of FIELD_ZONE_VALUES) {\n const btn = this.addButton(messages.zone[zone], () => controller.setFieldZone(zone));\n this.zoneButtons.set(zone, btn);\n }\n this.addSeparator();\n this.formationPicker = this.addFormationPicker(controller);\n this.addSeparator();\n this.commitButton = this.addButton(messages.finishLine, () => controller.commitLine());\n this.cancelButton = this.addButton(messages.cancel, () => controller.cancelInteraction());\n\n parent.appendChild(this.element);\n this._register(toDisposable(() => this.element.remove()));\n this._register(controller.onDidChangeViewState(() => this.sync(controller)));\n this.sync(controller);\n }\n\n private addButton(label: string, onClick: () => void): HTMLButtonElement {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.textContent = label;\n button.className = \"playmaker-toolbar__button\";\n button.addEventListener(\"click\", () => {\n if (button.getAttribute(\"aria-disabled\") !== \"true\") {\n onClick();\n }\n });\n this.element.appendChild(button);\n return button;\n }\n\n private addSeparator(): void {\n const sep = document.createElement(\"span\");\n sep.className = \"playmaker-toolbar__sep\";\n sep.setAttribute(\"aria-hidden\", \"true\");\n this.element.appendChild(sep);\n }\n\n /**\n * Returns to the placeholder after a choice. If the chosen formation stayed displayed,\n * choosing the same formation again would not fire change, so it could not be layered again.\n */\n private addFormationPicker(controller: IEditorUi): HTMLSelectElement {\n const select = document.createElement(\"select\");\n select.className = \"playmaker-toolbar__select\";\n select.setAttribute(\"aria-label\", this.messages.formation);\n\n const placeholder = document.createElement(\"option\");\n placeholder.value = \"\";\n placeholder.textContent = this.messages.loadFormation;\n select.appendChild(placeholder);\n\n for (const side of TEAM_SIDE_VALUES) {\n const group = document.createElement(\"optgroup\");\n group.label = this.messages.side[side];\n for (const formation of FORMATION_PRESETS.filter((f) => f.side === side)) {\n const option = document.createElement(\"option\");\n option.value = formation.id;\n option.textContent = formation.name;\n group.appendChild(option);\n }\n select.appendChild(group);\n }\n\n select.addEventListener(\"change\", () => {\n const preset = getFormationPreset(select.value);\n if (preset !== undefined) {\n controller.loadFormation(preset);\n }\n select.value = \"\";\n });\n\n this.element.appendChild(select);\n return select;\n }\n\n private sync(controller: IEditorUi): void {\n const state = controller.getViewState();\n for (const [tool, btn] of this.toolButtons) {\n btn.setAttribute(\"aria-pressed\", String(tool === state.tool));\n setEnabled(btn, isToolAvailable(tool, state), this.toolUnavailableReasons[tool]);\n }\n for (const [zone, btn] of this.zoneButtons) {\n btn.setAttribute(\"aria-pressed\", String(zone === state.fieldZone));\n }\n setEnabled(this.undoButton, state.canUndo);\n setEnabled(this.redoButton, state.canRedo);\n setEnabled(this.deleteButton, state.selection.kind !== \"none\");\n setEnabled(this.commitButton, state.isDrawing);\n setEnabled(this.cancelButton, state.isDrawing);\n this.syncFormationPicker(state);\n }\n\n private syncFormationPicker(state: EditorViewState): void {\n let isAnyUnavailable = false;\n for (const option of this.formationPicker.options) {\n const formation = getFormationPreset(option.value);\n option.disabled = formation !== undefined && !canLoadFormation(formation, state);\n isAnyUnavailable ||= option.disabled;\n }\n setTitle(\n this.formationPicker,\n isAnyUnavailable ? this.messages.formationLimit(MAX_PLAYERS) : undefined,\n );\n }\n}\n","import {\n CanvasSurface,\n type Messages,\n PLAYMAKER_LOCALE_VALUES,\n type PlaymakerLocale,\n PointerInput,\n PropertyPanel,\n replaceKeepingFocus,\n resolveMessages,\n Toolbar,\n} from \"./browser/index.js\";\nimport {\n DisposableStore,\n type EditorOverlay,\n type Event,\n type FieldZone,\n type Formation,\n type IDisposable,\n type ImageExportOptions,\n type PlayData,\n type PlayDataInput,\n PlaySession,\n toDisposable,\n} from \"./common/index.js\";\nimport \"./styles.css\";\n\nexport type {\n Event,\n FieldPosition,\n FieldState,\n FieldZone,\n Formation,\n FormationPlayer,\n IDisposable,\n ImageExportOptions,\n Line,\n LineInterpolation,\n LineKind,\n PlayCategory,\n PlayData,\n PlayDataInput,\n Player,\n PlayerFill,\n PlayerShape,\n PlayPreset,\n TeamSide,\n} from \"./common/index.js\";\nexport {\n CURRENT_PLAY_DATA_VERSION,\n FIELD_WIDTH_YARDS,\n FIELD_ZONE_LABELS,\n FIELD_ZONE_VALUES,\n FORMATION_PRESETS,\n GRID_STEP_YARDS,\n getFormationPreset,\n getPlayPreset,\n migratePlayData,\n PLAY_PRESETS,\n} from \"./common/index.js\";\n\nexport type PlaymakerMode = \"view\" | \"edit\";\n\nexport { PLAYMAKER_LOCALE_VALUES, type PlaymakerLocale };\n\n// The bundler replaces process.env.NODE_ENV with a string literal. Loaded in a browser without\n// that replacement, `process` does not exist and throws a ReferenceError; treat that as production.\ndeclare const process: { readonly env: { readonly NODE_ENV?: string } };\n\nfunction isDevelopment(): boolean {\n try {\n return process.env.NODE_ENV !== \"production\";\n } catch {\n return false;\n }\n}\n\nexport interface PlaymakerOptions {\n /**\n * The initial mode. The default is \"edit\". \"view\" places no editing UI and accepts no pointer\n * or keyboard input. `setMode` switches the mode later.\n */\n mode?: PlaymakerMode | undefined;\n /**\n * The first diagram to show. Like `restorePlayData`, it is accepted without checking its shape\n * and coerced to the current schema.\n */\n initialData?: unknown;\n /** A listener added to `onDidChange` at construction. Dispose the Playmaker to remove it. */\n onChange?: ((data: PlayData) => void) | undefined;\n /**\n * Language of the built-in UI (the toolbar and the property panel). The default is \"en\".\n * Fixed at construction; to switch, construct again with the same data.\n * Error messages are English regardless of the locale, because they are for developers.\n */\n locale?: PlaymakerLocale | undefined;\n}\n\n/**\n * Draws the play diagram inside container and, in edit mode, also places the editing UI.\n * dispose removes every element it placed.\n * Changes after dispose do nothing instead of throwing; in development, console.warn reports it.\n * getPlayData and fieldZone return the diagram as it was at dispose.\n */\nexport class Playmaker implements IDisposable {\n /**\n * Fires once per committed edit command or Undo / Redo, with a deep copy of the latest diagram.\n * `version` is always the current one, so the received value can be persisted as is.\n * Does not fire at construction, on loads through `setPlayData` and `restorePlayData`, or on PNG\n * export (a load is not an edit).\n * The copy is made per listener, so mutating it affects neither this diagram nor other listeners.\n * Disposing the return value ends the subscription.\n */\n readonly onDidChange: Event<PlayData>;\n private readonly store = new DisposableStore();\n private readonly root: HTMLElement;\n private readonly surface: CanvasSurface;\n private readonly session: PlaySession;\n private currentMode: PlaymakerMode;\n private readonly messages: Messages;\n private panel: PropertyPanel | undefined;\n // UI and input attached to the current controller. Reattached when it or the mode changes.\n private ui: DisposableStore;\n\n constructor(container: HTMLElement, options: PlaymakerOptions = {}) {\n this.currentMode = options.mode ?? \"edit\";\n const locale = options.locale ?? \"en\";\n this.messages = resolveMessages(locale);\n this.session = this.store.add(new PlaySession(options.initialData));\n this.onDidChange = this.session.onDidChange;\n if (options.onChange !== undefined) {\n this.store.add(this.session.onDidChange(options.onChange));\n }\n\n this.root = document.createElement(\"div\");\n this.root.className = \"playmaker-root\";\n this.root.dataset.mode = this.currentMode;\n // Tells assistive technology and font fallback which language the UI text is in.\n this.root.lang = locale;\n container.appendChild(this.root);\n this.store.add(toDisposable(() => this.root.remove()));\n\n const stage = document.createElement(\"div\");\n stage.className = \"playmaker-stage\";\n this.root.appendChild(stage);\n const { scene, overlay } = this.session.controller.getFrame();\n this.surface = this.store.add(new CanvasSurface(stage, scene, this.surfaceOverlay(overlay)));\n // CanvasSurface draws the current diagram once its first size is known, so no draw here.\n this.ui = this.createUi();\n this.store.add(this.session.onDidChangeScene(() => this.draw()));\n // After a reload the session notifies a redraw itself, so only the UI is reattached here.\n this.store.add(this.session.onDidReset(() => this.replaceUi()));\n }\n\n get mode(): PlaymakerMode {\n return this.currentMode;\n }\n\n /**\n * Switches the mode. The diagram and Undo history stay; only the editing UI and the pointer and\n * key input are attached or detached. View cancels a drag or drawing in progress and draws no\n * selection highlight.\n */\n setMode(mode: PlaymakerMode): void {\n if (this.store.isDisposed) {\n this.warnIgnoredAfterDispose(\"setMode\");\n return;\n }\n if (mode === this.currentMode) {\n return;\n }\n this.currentMode = mode;\n this.root.dataset.mode = mode;\n if (mode === \"view\") {\n this.session.controller.cancelInteraction();\n }\n this.replaceUi();\n this.draw();\n }\n\n get fieldZone(): FieldZone {\n return this.session.fieldZone;\n }\n\n /**\n * Moves the LOS to the default position of that zone, and moves the players and lines with it.\n * This is an edit, so Undo and onDidChange cover it. Callable in view mode as well.\n */\n setFieldZone(zone: FieldZone): void {\n if (this.store.isDisposed) {\n this.warnIgnoredAfterDispose(\"setFieldZone\");\n return;\n }\n this.session.setFieldZone(zone);\n }\n\n /**\n * Appends the formation's players to the current diagram and returns true once placed. Offense\n * and defense formations can be layered in turn. Player positions are relative to the LOS\n * (`lateralYard` and `downfieldYard`). A formation passed from outside is normalized before it is\n * read, so players whose position cannot be read are dropped. When no player can be placed, or\n * placing them would exceed 64 players, places none and returns false.\n * This is an edit, so Undo and onDidChange cover it. Callable in view mode as well.\n */\n loadFormation(formation: Formation): boolean {\n if (this.store.isDisposed) {\n this.warnIgnoredAfterDispose(\"loadFormation\");\n return false;\n }\n return this.session.loadFormation(formation);\n }\n\n /**\n * Loads a diagram built with the types and replaces the current one. The return value of\n * `getPlayData` and a preset's `data` can be passed as is.\n * Even when the input matches the types, duplicate ids and entries past the count limits are\n * normalized as in `restorePlayData`.\n * A load is not an edit, so the Undo history is cleared and onDidChange does not fire.\n */\n setPlayData(data: PlayDataInput): void {\n if (this.store.isDisposed) {\n this.warnIgnoredAfterDispose(\"setPlayData\");\n return;\n }\n this.session.setPlayData(data);\n }\n\n /**\n * Accepts a persisted value unchecked, loads it, and replaces the current diagram.\n * Old versions, unversioned values, future versions, and broken data are coerced to the current\n * schema without throwing; only the elements that cannot be read are dropped.\n * Anything past 64 players, 128 lines, or 32 waypoints per line is dropped, keeping the first\n * entries up to the limit.\n * A load is not an edit, so the Undo history is cleared and onDidChange does not fire.\n */\n restorePlayData(raw: unknown): void {\n if (this.store.isDisposed) {\n this.warnIgnoredAfterDispose(\"restorePlayData\");\n return;\n }\n this.session.setPlayData(raw);\n }\n\n /**\n * A deep copy of the current diagram. `version` is always the current one, so it can be turned\n * into JSON and persisted as is. Passing it back through `restorePlayData` or `initialData`\n * yields the same diagram. Edits cannot exceed the count limits either: at a limit, adding a\n * player, loading a formation, and drawing a line do nothing.\n */\n getPlayData(): PlayData {\n return this.session.getPlayData();\n }\n\n /**\n * Exports the play diagram as committed at the time of the call as a PNG image (Blob).\n * The selection highlight, waypoint handles, the shape of a drag or drawing in progress, and the\n * toolbar and panel are not included. Usable in both view and edit mode.\n * Draws after the bundled font has finished loading, so even a call right after construction\n * renders the yard numbers and player labels in the bundled font. If the font fails to load,\n * draws with a fallback font. Rejects without throwing when a canvas cannot be obtained, the\n * PNG conversion fails, or the Playmaker is disposed.\n */\n exportToPng(options?: ImageExportOptions): Promise<Blob> {\n if (this.store.isDisposed) {\n return Promise.reject(new Error(\"Playmaker: cannot export a PNG after dispose.\"));\n }\n return this.surface.exportToPngBlob(this.session.getSnapshot(), options);\n }\n\n /**\n * Re-reads the colors of the diagram and the panel from the theme CSS variables. The browser does\n * not report CSS variable changes, so the host calls this after changing them on an ancestor.\n */\n refresh(): void {\n if (this.store.isDisposed) {\n this.warnIgnoredAfterDispose(\"refresh\");\n return;\n }\n this.surface.refresh();\n this.panel?.refresh();\n }\n\n dispose(): void {\n this.ui.dispose();\n this.store.dispose();\n }\n\n /** Calls after dispose do nothing, so report that in development only. */\n private warnIgnoredAfterDispose(method: string): void {\n if (isDevelopment()) {\n console.warn(`Playmaker: ${method} was called after dispose, so it did nothing.`);\n }\n }\n\n private replaceUi(): void {\n // In view the canvas does not take focus, so focus that was lost stays on the body.\n replaceKeepingFocus(this.root, this.surface.canvas, () => {\n this.ui.dispose();\n this.ui = this.createUi();\n });\n }\n\n private createUi(): DisposableStore {\n const ui = new DisposableStore();\n const controller = this.session.controller;\n this.panel = undefined;\n if (this.currentMode === \"edit\") {\n ui.add(new Toolbar(this.root, controller, this.messages));\n this.panel = ui.add(\n new PropertyPanel(this.root, controller, this.surface.canvas, this.messages),\n );\n ui.add(new PointerInput(this.root, this.surface, controller));\n }\n return ui;\n }\n\n private draw(): void {\n const { scene, overlay } = this.session.controller.getFrame();\n this.surface.setScene(scene, this.surfaceOverlay(overlay));\n }\n\n /** Undefined in view mode, so edit-only layers (the grid, selection highlight) are not drawn. */\n private surfaceOverlay(overlay: EditorOverlay): EditorOverlay | undefined {\n return this.currentMode === \"edit\" ? overlay : undefined;\n }\n}\n"],"mappings":"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"}
|