From 131c1f31cb457c554ec5ceb7a73dd53df8db753d Mon Sep 17 00:00:00 2001 From: Atul Varma Date: Sun, 28 Mar 2021 07:24:16 -0400 Subject: [PATCH] Orient mandala symbols around their anchor point. (#60) This orients the mandala symbols around their anchor point, rather than the center of their bounding box (#24). --- lib/creature-symbol.tsx | 44 ++------------------------------------ lib/pages/mandala-page.tsx | 35 +++++++++++++++++++++++++----- lib/svg-symbol.tsx | 43 ++++++++++++++++++++++++++++++++++++- 3 files changed, 74 insertions(+), 48 deletions(-) diff --git a/lib/creature-symbol.tsx b/lib/creature-symbol.tsx index cd4fc3f..4d22e38 100644 --- a/lib/creature-symbol.tsx +++ b/lib/creature-symbol.tsx @@ -3,9 +3,10 @@ import { BBox, Point } from "../vendor/bezier-js"; import { getAttachmentTransforms } from "./attach"; import { getBoundingBoxCenter, uniformlyScaleToFit } from "./bounding-box"; import { scalePointXY, subtractPoints } from "./point"; -import { AttachmentPointType, PointWithNormal } from "./specs"; +import { AttachmentPointType } from "./specs"; import { createSvgSymbolContext, + safeGetAttachmentPoint, SvgSymbolContent, SvgSymbolContext, SvgSymbolData, @@ -21,47 +22,6 @@ import { const DEFAULT_ATTACHMENT_SCALE = 0.5; -function getAttachmentPoint( - s: SvgSymbolData, - type: AttachmentPointType, - idx: number = 0 -): PointWithNormal { - const { specs } = s; - if (!specs) { - throw new AttachmentPointError(`Symbol ${s.name} has no specs.`); - } - const points = specs[type]; - if (!(points && points.length > idx)) { - throw new AttachmentPointError( - `Expected symbol ${s.name} to have at least ${ - idx + 1 - } ${type} attachment point(s).` - ); - } - - return points[idx]; -} - -class AttachmentPointError extends Error {} - -function safeGetAttachmentPoint( - s: SvgSymbolData, - type: AttachmentPointType, - idx: number = 0 -): PointWithNormal | null { - try { - return getAttachmentPoint(s, type, idx); - } catch (e) { - if (e instanceof AttachmentPointError) { - console.log(e.message); - } else { - throw e; - } - } - - return null; -} - export type CreatureContextType = SvgSymbolContext & { attachmentScale: number; parent: SvgSymbolData | null; diff --git a/lib/pages/mandala-page.tsx b/lib/pages/mandala-page.tsx index b5c56b8..7d04a33 100644 --- a/lib/pages/mandala-page.tsx +++ b/lib/pages/mandala-page.tsx @@ -6,9 +6,9 @@ import { DEFAULT_BG_COLOR } from "../colors"; import { ExportSvgButton } from "../export-svg"; import { HoverDebugHelper } from "../hover-debug-helper"; import { NumericSlider } from "../numeric-slider"; -import { reversePoint } from "../point"; import { createSvgSymbolContext, + safeGetAttachmentPoint, SvgSymbolContent, SvgSymbolContext, SvgSymbolData, @@ -24,8 +24,25 @@ import { SvgVocabulary } from "../svg-vocabulary"; import { SymbolContextWidget } from "../symbol-context-widget"; import { NumericRange, range } from "../util"; import { Random } from "../random"; +import { PointWithNormal } from "../specs"; +import { getAttachmentTransforms } from "../attach"; -const EYE = SvgVocabulary.get("eye"); +const EYE = SvgVocabulary.get("eye_vertical"); + +/** + * Returns the anchor point of the given symbol; if it doesn't have + * an anchor point, return a reasonable default one by taking the + * center of the symbol and having the normal point along the positive + * x-axis. + */ +function getAnchorOrCenter(symbol: SvgSymbolData): PointWithNormal { + return ( + safeGetAttachmentPoint(symbol, "anchor") || { + point: getBoundingBoxCenter(symbol.bbox), + normal: { x: 1, y: 0 }, + } + ); +} const MandalaCircle: React.FC< { @@ -34,13 +51,21 @@ const MandalaCircle: React.FC< numSymbols: number; } & SvgSymbolContext > = (props) => { - const center = getBoundingBoxCenter(props.data.bbox); const degreesPerItem = 360 / props.numSymbols; + const { translation, rotation } = getAttachmentTransforms( + { + point: { x: 0, y: 0 }, + normal: { x: 1, y: 0 }, + }, + getAnchorOrCenter(props.data) + ); + const symbol = ( @@ -64,7 +89,7 @@ const RADIUS: NumericParams = { min: 0, max: 1000, step: 1, - default: 400, + default: 50, }; const NUM_SYMBOLS: NumericParams = { diff --git a/lib/svg-symbol.tsx b/lib/svg-symbol.tsx index 7eb321e..8c5dcf8 100644 --- a/lib/svg-symbol.tsx +++ b/lib/svg-symbol.tsx @@ -2,7 +2,7 @@ import React from "react"; import { SVGProps } from "react"; import { BBox } from "../vendor/bezier-js"; import { FILL_REPLACEMENT_COLOR, STROKE_REPLACEMENT_COLOR } from "./colors"; -import { Specs } from "./specs"; +import { AttachmentPointType, PointWithNormal, Specs } from "./specs"; import type { SvgSymbolMetadata } from "./svg-symbol-metadata"; import { VisibleSpecs } from "./visible-specs"; @@ -114,3 +114,44 @@ export const SvgSymbolContent: React.FC< ); }; + +export class AttachmentPointError extends Error {} + +export function getAttachmentPoint( + s: SvgSymbolData, + type: AttachmentPointType, + idx: number = 0 +): PointWithNormal { + const { specs } = s; + if (!specs) { + throw new AttachmentPointError(`Symbol ${s.name} has no specs.`); + } + const points = specs[type]; + if (!(points && points.length > idx)) { + throw new AttachmentPointError( + `Expected symbol ${s.name} to have at least ${ + idx + 1 + } ${type} attachment point(s).` + ); + } + + return points[idx]; +} + +export function safeGetAttachmentPoint( + s: SvgSymbolData, + type: AttachmentPointType, + idx: number = 0 +): PointWithNormal | null { + try { + return getAttachmentPoint(s, type, idx); + } catch (e) { + if (e instanceof AttachmentPointError) { + console.log(e.message); + } else { + throw e; + } + } + + return null; +}