diff --git a/apps/web/src/routes/kripke/components/frame-input.svelte b/apps/web/src/routes/kripke/components/frame-input.svelte index 57d7109..74fa781 100644 --- a/apps/web/src/routes/kripke/components/frame-input.svelte +++ b/apps/web/src/routes/kripke/components/frame-input.svelte @@ -9,7 +9,15 @@ import { worlds, } from "@cannorin/kripke"; import type { SVGAttributes } from "svelte/elements"; -import { type Vector, add, degree, rotate, sub, theta } from "../lib/vector"; +import { + type Radian, + type Vector, + add, + degree, + rotate, + sub, + theta, +} from "../lib/vector"; export interface FrameInputProps extends SVGAttributes { frame?: Frame | undefined; @@ -82,23 +90,32 @@ const center: Vector = { x: 125, y: 125 }; const radius: Vector = { x: 20, y: 0 }; -function getSelfPath(w: World) { +function tip(position: Vector, angle: Radian) { + const length = 8; + const offset = degree(20); + const lb = add(position, rotate({ x: length, y: 0 }, angle - offset)); + const rb = add(position, rotate({ x: length, y: 0 }, angle + offset)); + return { lb, rb }; +} + +function getSelfArrow(w: World) { const angle = theta(sub(center, positions[w])) + Math.PI; const offset = degree(45); const loopRadius = 20; const start = add(positions[w], rotate(radius, angle - offset)); const end = add(positions[w], rotate(radius, angle + offset)); + const { lb, rb } = tip(end, angle + offset - degree(10)); - return ` - M ${start.x} ${start.y} - A ${loopRadius} ${loopRadius} 0 1 1 ${end.x} ${end.y} - `; + return { + path: `M ${start.x} ${start.y} A ${loopRadius} ${loopRadius} 0 1 1 ${end.x} ${end.y}`, + tipPolygon: `${end.x} ${end.y} ${lb.x} ${lb.y} ${rb.x} ${rb.y}`, + }; } -function getPath(rel: Relation) { +function getArrow(rel: Relation) { const l = left(rel); const r = right(rel); - if (l === r) return getSelfPath(l); + if (l === r) return getSelfArrow(l); const angle = theta(sub(positions[r], positions[l])); @@ -106,62 +123,61 @@ function getPath(rel: Relation) { const dl = rotate(radius, angle + offset); const dr = rotate(radius, angle + Math.PI - offset); + const from = add(positions[l], dl); const to = add(positions[r], dr); + const { lb, rb } = tip(to, angle + Math.PI - offset); if (!frame.relations.has(reverse(rel))) - return `M ${from.x} ${from.y} L ${to.x} ${to.y}`; + return { + path: `M ${from.x} ${from.y} L ${to.x} ${to.y}`, + tipPolygon: `${to.x} ${to.y} ${lb.x} ${lb.y} ${rb.x} ${rb.y}`, + }; - return `M ${from.x} ${from.y} C ${from.x + dl.x * 2} ${from.y + dl.y * 2}, ${to.x + dr.x * 2} ${to.y + dr.y * 2}, ${to.x} ${to.y}`; + return { + path: `M ${from.x} ${from.y} C ${from.x + dl.x * 2} ${from.y + dl.y * 2}, ${to.x + dr.x * 2} ${to.y + dr.y * 2}, ${to.x} ${to.y}`, + tipPolygon: `${to.x} ${to.y} ${lb.x} ${lb.y} ${rb.x} ${rb.y}`, + }; } + + - - - - - - - {#each Array.from(frame.relations) as rel} - {#key rel} - !disabled && handleEdgeClick(rel, e)} - /> - {/key} + {@const { path, tipPolygon } = getArrow(rel)} + !disabled && handleEdgeClick(rel, e)} + /> + !disabled && handleEdgeClick(rel, e)} + /> {/each} {#each worlds as w} diff --git a/apps/web/src/routes/kripke/components/game.svelte b/apps/web/src/routes/kripke/components/game.svelte index 7c8dd67..75311c2 100644 --- a/apps/web/src/routes/kripke/components/game.svelte +++ b/apps/web/src/routes/kripke/components/game.svelte @@ -107,21 +107,8 @@ const colors: Record = { {#snippet sampleArrow()} - - - - - - + + " {/snippet}