diff --git a/lib/bounding-box.ts b/lib/bounding-box.ts
index 14c7fb2..4c5917b 100644
--- a/lib/bounding-box.ts
+++ b/lib/bounding-box.ts
@@ -1,7 +1,7 @@
import { Bezier, Point, BBox, MinMax } from "../vendor/bezier-js";
import { SVGProps } from "react";
-import type { SvgSymbolElement } from "./vocabulary";
+import type { SvgSymbolElement } from "./svg-symbol";
import { flatten, float } from "./util";
import { pathToShapes } from "./path";
diff --git a/lib/pages/creature-page.tsx b/lib/pages/creature-page.tsx
index d6ec0d7..5c06dee 100644
--- a/lib/pages/creature-page.tsx
+++ b/lib/pages/creature-page.tsx
@@ -1,10 +1,30 @@
import React from "react";
import { Random } from "../random";
import { SvgVocabulary } from "../svg-vocabulary";
+import {
+ createSvgSymbolContext,
+ SvgSymbolContent,
+ SvgSymbolData,
+} from "../svg-symbol";
+
+const SYMBOL_MAP = new Map(
+ SvgVocabulary.map((symbol) => [symbol.name, symbol])
+);
+
+function getSymbol(name: string): SvgSymbolData {
+ const symbol = SYMBOL_MAP.get(name);
+ if (!symbol) {
+ throw new Error(`Unable to find the symbol "${name}"!`);
+ }
+ return symbol;
+}
export const CreaturePage: React.FC<{}> = () => {
const rand = new Random(1);
const parts: string[] = [];
+ const ctx = createSvgSymbolContext();
+ const eye = getSymbol("eye");
+ const hand = getSymbol("hand");
for (let i = 0; i < 5; i++) {
parts.push(rand.choice(SvgVocabulary).name);
@@ -13,6 +33,12 @@ export const CreaturePage: React.FC<{}> = () => {
return (
<>
Creature!
+
TODO: Make a creature with maybe the following parts:
{parts.map((name, i) => (
diff --git a/lib/pages/vocabulary-page.tsx b/lib/pages/vocabulary-page.tsx
index da42b3f..4cf1ebf 100644
--- a/lib/pages/vocabulary-page.tsx
+++ b/lib/pages/vocabulary-page.tsx
@@ -1,18 +1,12 @@
import React, { useState } from "react";
-import { BBox } from "../../vendor/bezier-js";
import { dilateBoundingBox, getBoundingBoxSize } from "../bounding-box";
-import { FILL_REPLACEMENT_COLOR, STROKE_REPLACEMENT_COLOR } from "../colors";
-import * as colors from "../colors";
-import { iterAttachmentPoints, AttachmentPoint, Specs } from "../specs";
-
-import type { SvgSymbolData, SvgSymbolElement } from "../vocabulary";
+import {
+ createSvgSymbolContext,
+ SvgSymbolContent,
+ SvgSymbolData,
+} from "../svg-symbol";
import { SvgVocabulary } from "../svg-vocabulary";
-
-type SvgSymbolContext = {
- stroke: string;
- fill: string;
- showSpecs: boolean;
-};
+import { SvgSymbolContext } from "../svg-symbol";
type SvgSymbolProps = {
data: SvgSymbolData;
@@ -21,103 +15,6 @@ type SvgSymbolProps = {
const px = (value: number) => `${value}px`;
-function getColor(
- ctx: SvgSymbolContext,
- color: string | undefined
-): string | undefined {
- switch (color) {
- case STROKE_REPLACEMENT_COLOR:
- return ctx.stroke;
- case FILL_REPLACEMENT_COLOR:
- return ctx.fill;
- }
- return color;
-}
-
-function reactifySvgSymbolElement(
- ctx: SvgSymbolContext,
- el: SvgSymbolElement,
- key: number
-): JSX.Element {
- let { fill, stroke } = el.props;
- fill = getColor(ctx, fill);
- stroke = getColor(ctx, stroke);
- return React.createElement(
- el.tagName,
- {
- ...el.props,
- id: undefined,
- fill,
- stroke,
- key,
- },
- el.children.map(reactifySvgSymbolElement.bind(null, ctx))
- );
-}
-
-const ATTACHMENT_POINT_RADIUS = 20;
-
-const ATTACHMENT_POINT_NORMAL_LENGTH = 50;
-
-const ATTACHMENT_POINT_NORMAL_STROKE = 4;
-
-const VisibleAttachmentPoint: React.FC<{
- point: AttachmentPoint;
-}> = ({ point: ap }) => {
- const { x, y } = ap.point;
- const x2 = x + ap.normal.x * ATTACHMENT_POINT_NORMAL_LENGTH;
- const y2 = y + ap.normal.y * ATTACHMENT_POINT_NORMAL_LENGTH;
- const color = colors.ATTACHMENT_POINT_COLORS[ap.type];
-
- return (
- <>
-
-
- >
- );
-};
-
-const BoundingBoxes: React.FC<{ fill: string; bboxes: BBox[] }> = (props) => (
- <>
- {props.bboxes.map((b, i) => {
- const [width, height] = getBoundingBoxSize(b);
- return (
-
- );
- })}
- >
-);
-
-const SvgSymbolSpecs: React.FC<{ specs: Specs }> = ({ specs }) => {
- return (
- <>
- {Array.from(iterAttachmentPoints(specs)).map((point, i) => (
-
- ))}
- {specs.nesting && (
-
- )}
- >
- );
-};
-
const BBOX_DILATION = 100;
const SvgSymbol: React.FC = (props) => {
@@ -132,8 +29,7 @@ const SvgSymbol: React.FC = (props) => {
width={px(width * scale)}
height={px(height * scale)}
>
- {props.data.layers.map(reactifySvgSymbolElement.bind(null, props))}
- {props.showSpecs && d.specs && }
+
);
};
@@ -142,6 +38,7 @@ export const VocabularyPage: React.FC<{}> = () => {
const [stroke, setStroke] = useState("#000000");
const [fill, setFill] = useState("#ffffff");
const [showSpecs, setShowSpecs] = useState(false);
+ const ctx = createSvgSymbolContext({ stroke, fill, showSpecs });
return (
<>
@@ -189,13 +86,7 @@ export const VocabularyPage: React.FC<{}> = () => {
{symbolData.name}
-
+
))}
diff --git a/lib/specs.ts b/lib/specs.ts
index 3505410..ddad5c8 100644
--- a/lib/specs.ts
+++ b/lib/specs.ts
@@ -2,7 +2,7 @@ import { Point, BBox } from "../vendor/bezier-js";
import { getBoundingBoxForBeziers } from "./bounding-box";
import * as colors from "./colors";
import { pathToShapes } from "./path";
-import type { SvgSymbolElement } from "./vocabulary";
+import type { SvgSymbolElement } from "./svg-symbol";
const SPEC_LAYER_ID_RE = /^specs.*/i;
diff --git a/lib/svg-symbol.tsx b/lib/svg-symbol.tsx
new file mode 100644
index 0000000..bc3513a
--- /dev/null
+++ b/lib/svg-symbol.tsx
@@ -0,0 +1,94 @@
+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 { VisibleSpecs } from "./visible-specs";
+
+export type SvgSymbolData = {
+ name: string;
+ bbox: BBox;
+ layers: SvgSymbolElement[];
+ specs?: Specs;
+};
+
+export type SvgSymbolElement = (
+ | {
+ tagName: "g";
+ props: SVGProps;
+ }
+ | {
+ tagName: "path";
+ props: SVGProps;
+ }
+) & {
+ children: SvgSymbolElement[];
+};
+
+export type SvgSymbolContext = {
+ stroke: string;
+ fill: string;
+ showSpecs: boolean;
+};
+
+const DEFAULT_CONTEXT: SvgSymbolContext = {
+ stroke: "#000000",
+ fill: "#ffffff",
+ showSpecs: false,
+};
+
+export function createSvgSymbolContext(
+ ctx: Partial = {}
+): SvgSymbolContext {
+ return {
+ ...DEFAULT_CONTEXT,
+ ...ctx,
+ };
+}
+
+function getColor(
+ ctx: SvgSymbolContext,
+ color: string | undefined
+): string | undefined {
+ switch (color) {
+ case STROKE_REPLACEMENT_COLOR:
+ return ctx.stroke;
+ case FILL_REPLACEMENT_COLOR:
+ return ctx.fill;
+ }
+ return color;
+}
+
+function reactifySvgSymbolElement(
+ ctx: SvgSymbolContext,
+ el: SvgSymbolElement,
+ key: number
+): JSX.Element {
+ let { fill, stroke } = el.props;
+ fill = getColor(ctx, fill);
+ stroke = getColor(ctx, stroke);
+ return React.createElement(
+ el.tagName,
+ {
+ ...el.props,
+ id: undefined,
+ fill,
+ stroke,
+ key,
+ },
+ el.children.map(reactifySvgSymbolElement.bind(null, ctx))
+ );
+}
+
+export const SvgSymbolContent: React.FC<
+ { data: SvgSymbolData } & SvgSymbolContext
+> = (props) => {
+ const d = props.data;
+
+ return (
+ <>
+ {props.data.layers.map(reactifySvgSymbolElement.bind(null, props))}
+ {props.showSpecs && d.specs && }
+ >
+ );
+};
diff --git a/lib/svg-vocabulary.ts b/lib/svg-vocabulary.ts
index 55571eb..678f236 100644
--- a/lib/svg-vocabulary.ts
+++ b/lib/svg-vocabulary.ts
@@ -1,4 +1,4 @@
-import type { SvgSymbolData } from "./vocabulary";
+import type { SvgSymbolData } from "./svg-symbol";
import _SvgVocabulary from "./_svg-vocabulary.json";
export const SvgVocabulary: SvgSymbolData[] = _SvgVocabulary as any;
diff --git a/lib/visible-specs.tsx b/lib/visible-specs.tsx
new file mode 100644
index 0000000..2345770
--- /dev/null
+++ b/lib/visible-specs.tsx
@@ -0,0 +1,68 @@
+import React from "react";
+import { BBox } from "../vendor/bezier-js";
+import { getBoundingBoxSize } from "./bounding-box";
+import * as colors from "./colors";
+import { AttachmentPoint, iterAttachmentPoints, Specs } from "./specs";
+
+const ATTACHMENT_POINT_RADIUS = 20;
+
+const ATTACHMENT_POINT_NORMAL_LENGTH = 50;
+
+const ATTACHMENT_POINT_NORMAL_STROKE = 4;
+
+const VisibleAttachmentPoint: React.FC<{
+ point: AttachmentPoint;
+}> = ({ point: ap }) => {
+ const { x, y } = ap.point;
+ const x2 = x + ap.normal.x * ATTACHMENT_POINT_NORMAL_LENGTH;
+ const y2 = y + ap.normal.y * ATTACHMENT_POINT_NORMAL_LENGTH;
+ const color = colors.ATTACHMENT_POINT_COLORS[ap.type];
+
+ return (
+ <>
+
+
+ >
+ );
+};
+
+const BoundingBoxes: React.FC<{ fill: string; bboxes: BBox[] }> = (props) => (
+ <>
+ {props.bboxes.map((b, i) => {
+ const [width, height] = getBoundingBoxSize(b);
+ return (
+
+ );
+ })}
+ >
+);
+
+export const VisibleSpecs: React.FC<{ specs: Specs }> = ({ specs }) => {
+ return (
+ <>
+ {Array.from(iterAttachmentPoints(specs)).map((point, i) => (
+
+ ))}
+ {specs.nesting && (
+
+ )}
+ >
+ );
+};
diff --git a/lib/vocabulary.ts b/lib/vocabulary.ts
index 4192ca6..d4cb565 100644
--- a/lib/vocabulary.ts
+++ b/lib/vocabulary.ts
@@ -1,30 +1,9 @@
import fs from "fs";
import path from "path";
import cheerio from "cheerio";
-import { SVGProps } from "react";
-import { BBox } from "../vendor/bezier-js";
import { getSvgBoundingBox } from "./bounding-box";
-import { Specs, extractSpecs } from "./specs";
-
-export type SvgSymbolData = {
- name: string;
- bbox: BBox;
- layers: SvgSymbolElement[];
- specs?: Specs;
-};
-
-export type SvgSymbolElement = (
- | {
- tagName: "g";
- props: SVGProps;
- }
- | {
- tagName: "path";
- props: SVGProps;
- }
-) & {
- children: SvgSymbolElement[];
-};
+import { extractSpecs } from "./specs";
+import { SvgSymbolData, SvgSymbolElement } from "./svg-symbol";
const SUPPORTED_SVG_TAG_ARRAY: SvgSymbolElement["tagName"][] = ["g", "path"];
const SUPPORTED_SVG_TAGS = new Set(SUPPORTED_SVG_TAG_ARRAY);