Document Gariff influence workbook and pack timeline lanes by time span.
Add Inputs/gariff_influential_painters_influences.xlsx for curator import, document Inputs/Output conventions, and let non-overlapping movements share horizontal lanes on the timeline. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
8005252881
commit
6656f91f25
@@ -45,7 +45,6 @@ interface BranchSegment {
|
||||
const MAX_STREAM_STROKE_PX = 54;
|
||||
const MIN_STREAM_STROKE_PX = 28;
|
||||
const LANE_GAP_PX = 10;
|
||||
const DEPTH_GAP_PX = 14;
|
||||
const CANVAS_TOP_PAD = 38;
|
||||
const CANVAS_BOTTOM_PAD = 24;
|
||||
const DEFAULT_CANVAS_HEIGHT = 360;
|
||||
@@ -961,51 +960,29 @@ export default function MovementBands({
|
||||
const nameToId = new Map(movements.map((m) => [m.name, m.id]));
|
||||
const lineageParents = buildLineageParentMap(visibleMovements, nameToId);
|
||||
const depths = assignDepths(visibleMovements, lineageParents);
|
||||
const maxDepth = Math.max(...depths.values());
|
||||
const layoutHeight = Math.max(200, canvasHeight);
|
||||
const usableHeight = layoutHeight - CANVAS_TOP_PAD - CANVAS_BOTTOM_PAD;
|
||||
|
||||
const byDepth = new Map<number, ArtMovement[]>();
|
||||
// Pack all visible movements into shared horizontal lanes whenever their
|
||||
// clipped time spans do not overlap (lineage depth is kept for branch curves only).
|
||||
const laneIndex = assignTemporalLanes(visibleMovements, viewStart, viewEnd);
|
||||
let maxLanes = 0;
|
||||
const laneOccupancy = new Map<number, number>();
|
||||
for (const movement of visibleMovements) {
|
||||
const depth = depths.get(movement.id) ?? 0;
|
||||
const list = byDepth.get(depth) || [];
|
||||
list.push(movement);
|
||||
byDepth.set(depth, list);
|
||||
const lane = laneIndex.get(movement.id) ?? 0;
|
||||
maxLanes = Math.max(maxLanes, lane + 1);
|
||||
laneOccupancy.set(lane, (laneOccupancy.get(lane) ?? 0) + 1);
|
||||
}
|
||||
maxLanes = Math.max(1, maxLanes);
|
||||
|
||||
const laneIndex = new Map<number, number>();
|
||||
const maxLanesByDepth = new Map<number, number>();
|
||||
for (const [depth, group] of byDepth.entries()) {
|
||||
const temporalLanes = assignTemporalLanes(group, viewStart, viewEnd);
|
||||
let maxLane = 0;
|
||||
for (const movement of group) {
|
||||
const lane = temporalLanes.get(movement.id) ?? 0;
|
||||
laneIndex.set(movement.id, lane);
|
||||
maxLane = Math.max(maxLane, lane + 1);
|
||||
}
|
||||
maxLanesByDepth.set(depth, Math.max(1, maxLane));
|
||||
}
|
||||
|
||||
const depthCount = maxDepth + 1;
|
||||
const depthGaps = Math.max(0, depthCount - 1) * DEPTH_GAP_PX;
|
||||
const totalLaneSlots = [...maxLanesByDepth.values()].reduce((sum, n) => sum + n, 0);
|
||||
const minLaneStep = MIN_STREAM_STROKE_PX + LANE_GAP_PX;
|
||||
const laneStep = Math.max(minLaneStep, (usableHeight - depthGaps) / Math.max(1, totalLaneSlots));
|
||||
const laneStep = Math.max(minLaneStep, usableHeight / maxLanes);
|
||||
|
||||
let streamStrokePx = Math.min(MAX_STREAM_STROKE_PX, laneStep - LANE_GAP_PX);
|
||||
streamStrokePx = Math.max(MIN_STREAM_STROKE_PX, streamStrokePx);
|
||||
const portraitSizePx = Math.max(28, Math.min(52, streamStrokePx * 0.96));
|
||||
const streamCurveOffset = Math.min(12, Math.max(2, (laneStep - streamStrokePx) * 0.35));
|
||||
|
||||
const depthBaseY = new Map<number, number>();
|
||||
let yCursor = CANVAS_TOP_PAD;
|
||||
for (let depth = 0; depth <= maxDepth; depth++) {
|
||||
depthBaseY.set(depth, yCursor);
|
||||
const bandLanes = maxLanesByDepth.get(depth) ?? 1;
|
||||
yCursor += bandLanes * laneStep;
|
||||
if (depth < maxDepth) yCursor += DEPTH_GAP_PX;
|
||||
}
|
||||
|
||||
const layoutById = new Map<number, MovementLayout>();
|
||||
const branchList: BranchSegment[] = [];
|
||||
const childIdsByParent = new Map<number, number[]>();
|
||||
@@ -1017,10 +994,8 @@ export default function MovementBands({
|
||||
|
||||
const depth = depths.get(movement.id) ?? 0;
|
||||
const lane = laneIndex.get(movement.id) ?? 0;
|
||||
const lanesAtDepth = maxLanesByDepth.get(depth) ?? 1;
|
||||
const bandTop = depthBaseY.get(depth) ?? CANVAS_TOP_PAD;
|
||||
const y = bandTop + lane * laneStep + laneStep / 2;
|
||||
const allowDrift = lanesAtDepth === 1 && laneStep >= 80;
|
||||
const y = CANVAS_TOP_PAD + lane * laneStep + laneStep / 2;
|
||||
const allowDrift = (laneOccupancy.get(lane) ?? 0) === 1 && laneStep >= 80;
|
||||
const yEnd = y + (allowDrift ? organicDrift(movement.id + 1000) * 0.22 : 0);
|
||||
const parentIds = lineageParents.get(movement.id) || [];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user