Download frontend/src/components/widgets/inputs/RulerInput.svelte from openfree/graphite2: direct link, hf CLI and curl.
- Browser
- Download file 4.69 kB
-
https://huggingface.co/spaces/openfree/graphite2/resolve/main/frontend/src/components/widgets/inputs/RulerInput.svelte
- Command line
-
hf download hf://spaces/openfree/graphite2/frontend/src/components/widgets/inputs/RulerInput.svelte
-
curl -L -o RulerInput.svelte https://huggingface.co/spaces/openfree/graphite2/resolve/main/frontend/src/components/widgets/inputs/RulerInput.svelte
4.69 kB
| <script lang="ts" context="module"> | |
| export type RulerDirection = "Horizontal" | "Vertical"; | |
| </script> | |
| <script lang="ts"> | |
| import { onMount } from "svelte"; | |
| const RULER_THICKNESS = 16; | |
| const MAJOR_MARK_THICKNESS = 16; | |
| const MINOR_MARK_THICKNESS = 6; | |
| const MICRO_MARK_THICKNESS = 3; | |
| export let direction: RulerDirection = "Vertical"; | |
| export let origin: number; | |
| export let numberInterval: number; | |
| export let majorMarkSpacing: number; | |
| export let minorDivisions = 5; | |
| export let microDivisions = 2; | |
| let rulerInput: HTMLDivElement | undefined; | |
| let rulerLength = 0; | |
| let svgBounds = { width: "0px", height: "0px" }; | |
| $: svgPath = computeSvgPath(direction, origin, majorMarkSpacing, minorDivisions, microDivisions, rulerLength); | |
| $: svgTexts = computeSvgTexts(direction, origin, majorMarkSpacing, numberInterval, rulerLength); | |
| function computeSvgPath(direction: RulerDirection, origin: number, majorMarkSpacing: number, minorDivisions: number, microDivisions: number, rulerLength: number): string { | |
| const isVertical = direction === "Vertical"; | |
| const lineDirection = isVertical ? "H" : "V"; | |
| const offsetStart = mod(origin, majorMarkSpacing); | |
| const shiftedOffsetStart = offsetStart - majorMarkSpacing; | |
| const divisions = majorMarkSpacing / minorDivisions / microDivisions; | |
| const majorMarksFrequency = minorDivisions * microDivisions; | |
| let dPathAttribute = ""; | |
| let i = 0; | |
| for (let location = shiftedOffsetStart; location < rulerLength; location += divisions) { | |
| let length; | |
| if (i % majorMarksFrequency === 0) length = MAJOR_MARK_THICKNESS; | |
| else if (i % microDivisions === 0) length = MINOR_MARK_THICKNESS; | |
| else length = MICRO_MARK_THICKNESS; | |
| i += 1; | |
| const destination = Math.round(location) + 0.5; | |
| const startPoint = isVertical ? `${RULER_THICKNESS - length},${destination}` : `${destination},${RULER_THICKNESS - length}`; | |
| dPathAttribute += `M${startPoint}${lineDirection}${RULER_THICKNESS} `; | |
| } | |
| return dPathAttribute; | |
| } | |
| function computeSvgTexts(direction: RulerDirection, origin: number, majorMarkSpacing: number, numberInterval: number, rulerLength: number): { transform: string; text: string }[] { | |
| const isVertical = direction === "Vertical"; | |
| const offsetStart = mod(origin, majorMarkSpacing); | |
| const shiftedOffsetStart = offsetStart - majorMarkSpacing; | |
| const svgTextCoordinates = []; | |
| let labelNumber = (Math.ceil(-origin / majorMarkSpacing) - 1) * numberInterval; | |
| for (let location = shiftedOffsetStart; location < rulerLength; location += majorMarkSpacing) { | |
| const destination = Math.round(location); | |
| const x = isVertical ? 9 : destination + 2; | |
| const y = isVertical ? destination + 1 : 9; | |
| let transform = `translate(${x} ${y})`; | |
| if (isVertical) transform += " rotate(270)"; | |
| const text = numberInterval >= 1 ? `${labelNumber}` : labelNumber.toFixed(Math.abs(Math.log10(numberInterval))).replace(/\.0+$/, ""); | |
| svgTextCoordinates.push({ transform, text }); | |
| labelNumber += numberInterval; | |
| } | |
| return svgTextCoordinates; | |
| } | |
| export function resize() { | |
| if (!rulerInput) return; | |
| const isVertical = direction === "Vertical"; | |
| const newLength = isVertical ? rulerInput.clientHeight : rulerInput.clientWidth; | |
| const roundedUp = (Math.floor(newLength / majorMarkSpacing) + 1) * majorMarkSpacing; | |
| if (roundedUp !== rulerLength) { | |
| rulerLength = roundedUp; | |
| const thickness = `${RULER_THICKNESS}px`; | |
| const length = `${roundedUp}px`; | |
| svgBounds = isVertical ? { width: thickness, height: length } : { width: length, height: thickness }; | |
| } | |
| } | |
| // Modulo function that works for negative numbers, unlike the JS `%` operator | |
| function mod(n: number, m: number): number { | |
| const remainder = n % m; | |
| return Math.floor(remainder >= 0 ? remainder : remainder + m); | |
| } | |
| onMount(resize); | |
| </script> | |
| <div class={`ruler-input ${direction.toLowerCase()}`} bind:this={rulerInput}> | |
| <svg style:width={svgBounds.width} style:height={svgBounds.height}> | |
| <path d={svgPath} /> | |
| {#each svgTexts as svgText} | |
| <text transform={svgText.transform}>{svgText.text}</text> | |
| {/each} | |
| </svg> | |
| </div> | |
| <style lang="scss" global> | |
| .ruler-input { | |
| flex: 1 1 100%; | |
| background: var(--color-2-mildblack); | |
| overflow: hidden; | |
| position: relative; | |
| box-sizing: border-box; | |
| &.horizontal { | |
| height: 16px; | |
| border-bottom: 1px solid var(--color-5-dullgray); | |
| } | |
| &.vertical { | |
| width: 16px; | |
| border-right: 1px solid var(--color-5-dullgray); | |
| svg text { | |
| text-anchor: end; | |
| } | |
| } | |
| svg { | |
| position: absolute; | |
| path { | |
| stroke-width: 1px; | |
| stroke: var(--color-5-dullgray); | |
| } | |
| text { | |
| font-size: 12px; | |
| fill: var(--color-8-uppergray); | |
| } | |
| } | |
| } | |
| </style> | |