diff --git a/docs/screenshots/loop.png b/docs/screenshots/loop.png index 4e64e4c..d6de8dc 100644 Binary files a/docs/screenshots/loop.png and b/docs/screenshots/loop.png differ diff --git a/skills/diagram-design/assets/example-loop-dark.html b/skills/diagram-design/assets/example-loop-dark.html index 6c3add9..d73bdf6 100644 --- a/skills/diagram-design/assets/example-loop-dark.html +++ b/skills/diagram-design/assets/example-loop-dark.html @@ -49,13 +49,13 @@ - - - - - - - + + + + + + + diff --git a/skills/diagram-design/assets/example-loop-full.html b/skills/diagram-design/assets/example-loop-full.html index ee458c4..37f3f7f 100644 --- a/skills/diagram-design/assets/example-loop-full.html +++ b/skills/diagram-design/assets/example-loop-full.html @@ -69,13 +69,13 @@ - - - - - - - + + + + + + + diff --git a/skills/diagram-design/assets/example-loop.html b/skills/diagram-design/assets/example-loop.html index d04568c..060b73b 100644 --- a/skills/diagram-design/assets/example-loop.html +++ b/skills/diagram-design/assets/example-loop.html @@ -49,13 +49,13 @@ - - - - - - - + + + + + + + diff --git a/skills/diagram-design/references/type-loop.md b/skills/diagram-design/references/type-loop.md index 9df7b41..aaf2bce 100644 --- a/skills/diagram-design/references/type-loop.md +++ b/skills/diagram-design/references/type-loop.md @@ -32,7 +32,6 @@ hub_w: 200 hub_h: 104 radius: 240 margin: 64 -ring_style: "arc" # arc | rounded-orthogonal dark: false ``` @@ -61,51 +60,54 @@ station_x(k) = station_center_x(k) - station_w/2 station_y(k) = station_center_y(k) - station_h/2 ``` -Thus station 1 sits at the top, and increasing `k` moves clockwise. Round rendered coordinates to the nearest 4px grid point after computing the ideal geometry; preserve symmetry when rounding paired stations. +Thus station 1 sits at the top, and increasing `k` moves clockwise. Round station rectangles to the nearest 4px grid point after computing the ideal geometry; preserve symmetry when rounding paired stations. Keep ring-circle intersection points to three decimal places so every arc remains on the same circle. ### 2.2 Solid ring-flow endpoints -Ring connectors travel from station `k` to station `j = (k + 1) mod N`. They terminate on box edges, never at box centers. The clockwise unit tangent at station `k` is: +Ring connectors travel from station `k` to station `j = (k + 1) mod N` as circular SVG arcs on the station circle itself. Every segment uses the same center `C`, radius `R`, and clockwise sweep. The station boxes interrupt the circle; connectors begin at the circle's clockwise exit from the source box and end just before its counterclockwise entry into the destination box, so the marker tip lands on the destination stroke. + +Find the circle/rectangle intersections against all four edges of station `k`. For vertical edge `x = x_e`: ```text -t_k = (-sin(theta_k), cos(theta_k)) +y = cy +/- sqrt(R^2 - (x_e - cx)^2) ``` -The distance from the center of an axis-aligned station box to its boundary along a unit direction `v` is: +Keep only candidates whose `y` lies within the edge. For horizontal edge `y = y_e`: ```text -box_distance(v, a, b) = min(a / abs(v.x), b / abs(v.y)) +x = cx +/- sqrt(R^2 - (y_e - cy)^2) ``` -Ignore a division whose denominator is zero. Then: +Keep only candidates whose `x` lies within the edge. The two surviving points are classified by their normalized polar angles around `C`: ```text -d_exit = box_distance( t_k, a, b) -d_entry = box_distance(-t_j, a, b) - -ring_start(k) = P_k + d_exit * t_k -ring_end(k) = P_j - d_entry * t_j +q_entry(k) = circle/box intersection immediately before theta_k clockwise +q_exit(k) = circle/box intersection immediately after theta_k clockwise ``` -For `ring_style: arc`, connect those edge points with a cubic whose control vectors follow the local tangents: +Compensate for the marker tip before emitting the destination endpoint. With the canonical marker (`refX=7`, polygon tip at `x=8`) and ring stroke width `1.2`, `marker_overhang = 1.2`: ```text -delta = 2pi / N -lambda = 0.55 * R * delta +phi_entry = atan2(q_entry(j).y - cy, q_entry(j).x - cx) +phi_end = phi_entry - marker_overhang / R +q_end = C + R * (cos(phi_end), sin(phi_end)) -M ring_start -C ring_start + lambda*t_k, - ring_end - lambda*t_j, - ring_end +M q_exit(k).x q_exit(k).y +A R R 0 0 1 q_end.x q_end.y ``` -Reduce `lambda` only enough to keep the curve outside the hub and neighboring boxes. For `ring_style: rounded-orthogonal`, route both edge points to an outer rail and join them with `r=8` quarter-arcs. Every bend is rounded; no slanted straight segments. The closing connector from station `N-1` to station 0 uses the same formula and must remain visibly continuous with the other ring arrows. +The large-arc flag is `0` because adjacent-station gaps are less than 180 degrees; the sweep flag is always `1` for clockwise motion in SVG coordinates. The arrowhead overhang completes the final 1.2px to `q_entry(j)`, landing on the box edge without crossing its stroke. The closing connector from station `N-1` to station 0 uses the identical formula. + +Loop's circular ring arcs are a documented type-specific exception to SKILL.md §6 rule 1, following the same precedent as Medallion's promotion arcs. A Loop never mixes cubic, straight, or rounded-orthogonal segments into its ring: the six visible gaps must read as pieces of one continuous circle. ### 2.3 Dashed write-back spoke endpoints Each spoke runs inward from the station edge toward the hub. Use the same ray/box intersection formula, now on the radial vector `u_k`: ```text +box_distance(v, half_w, half_h) = min(half_w / abs(v.x), half_h / abs(v.y)) + # ignore a term whose denominator is zero + d_station = box_distance(u_k, a, b) d_hub = box_distance(u_k, A, B) marker_gap = 6 # 4..8px; 6px canonical @@ -133,7 +135,7 @@ viewBox_w = right - left viewBox_h = bottom - top ``` -If cubic controls or an outer orthogonal rail extend farther than those bounds, grow the corresponding margin. Never shrink the canvas until a station stroke, marker, or curved return clips. For the six-station canonical example, `viewBox="0 0 1040 680"`, `C=(520,340)`, `R=240`, station size `160×64`, and hub size `200×104` leave generous outer clearance. +Include the full circle extrema `cx +/- R`, `cy +/- R` plus station bounds and marker clearance when checking these limits. Never shrink the canvas until a station stroke, marker, or ring arc clips. For the six-station canonical example, `viewBox="0 0 1040 680"`, `C=(520,340)`, `R=240`, station size `160×64`, and hub size `200×104` leave generous outer clearance. --- @@ -144,7 +146,7 @@ If cubic controls or an outer orthogonal rail extend farther than those bounds, | Station | Standard node: `paper` fill, `ink` stroke, `radius-md`; name in `node-name`, sublabel in `sublabel` | | Hub | The one dark element: `ink` fill, `paper` text; slightly larger than a station | | Focal station | At most one: `accent-tint` fill, `accent` stroke; station name may use `accent` | -| Ring flow | Solid `muted` stroke, default arrowhead at the destination; clockwise only | +| Ring flow | Circular `A R R 0 0 1` arcs on the station circle, solid `muted` stroke, default arrowhead at the destination; clockwise only | | Write-back spoke | Dashed `soft` stroke at reduced emphasis, `stroke-dasharray="5,4"`, with a `soft` arrowhead | | Spoke label | `arrow-label` role, `soft`, uppercase, paper mask, 6–10px clear of the connector | @@ -156,14 +158,14 @@ The hub is not a seventh process step. It is accumulated state: memory, standard ## 4. Connector rules (mandatory) -SKILL.md §6 applies in full, with the radial-spoke exception stated in §2.3: +SKILL.md §6 applies in full except for the two Loop-specific connector primitives: circular ring arcs (§2.2) and straight radial spokes (§2.3). Like Medallion's promotion arcs, these replace §6 rule 1 for this diagram type: -- Ring arrows are solid and clockwise. Their destination markers land on station edges; no connector ends at a center point. +- Ring arrows are same-radius circular arcs, solid, and clockwise. Every path uses `A R R 0 0 1`; destination markers land on station edges and no connector ends at a center point. - Spokes are dashed and point inward. A solid spoke destroys the visual distinction between operating flow and write-back. - Labels use opaque masks and maintain a visible 6–10px connector gap. Never place text on the stroke. - No ring connector or spoke may overlap another connector. Ring paths remain outside the hub; spokes occupy distinct radial routes. - When two spokes must leave the same station edge, fan their attach points by the §6 formula with at least 12px separation. The normal Loop has one spoke per station; use a second only when the semantics cannot be merged. -- If a ring route would cross the hub, increase `R`, move to the outer rail, or split the diagram. Do not thread flow through shared state. +- If a ring route would cross the hub, increase `R` or split the diagram. Do not thread flow through shared state or substitute an orthogonal route. --- @@ -189,7 +191,7 @@ The semantic relationship stays unchanged in dark mode: one `ink`-filled hub, on 1. Station count is 5–8 and hub count is exactly one. 2. Station 0 is at `-90deg`; all others use equal `360/N` steps clockwise. -3. Every solid ring arrow connects adjacent stations and the last returns to the first. +3. Every solid ring arrow connects adjacent stations with `A R R 0 0 1`, using the same `R`; the last returns to the first. 4. Every ring marker lands on a station edge, not its center. 5. Every dashed spoke begins on a station's inner edge and stops `marker_gap` before the hub stroke. 6. Ring connectors stay outside the hub; spokes do not cross or overlap. @@ -206,6 +208,7 @@ The semantic relationship stays unchanged in dark mode: one `ink`-filled hub, on | Two hubs | Two accumulated states create two systems. Draw two diagrams. | | Solid spokes | They look like primary flow and kill the dashed return signal. Use dashed `soft` write-backs. | | Stations at uneven angles without reason | The ring stops reading as one operating cadence. Use equal `360/N` spacing unless a documented phase grouping requires a deliberate gap. | +| Mixed arc + orthogonal ring segments | The ring becomes a rounded rectangle. Every segment must be a circular arc of the same radius so the ring reads as one continuous circle. | | Connectors crossing the hub | Flow becomes confused with state. Route the ring outside or enlarge the radius. | | Accent on multiple stations | The editorial gate disappears. Keep one focal station at most. | | More than 8 stations | Labels and spokes crowd the hub. Split into overview + detail. |