diff --git a/lib/colors.ts b/lib/colors.ts index 568a1f1..bd469b2 100644 --- a/lib/colors.ts +++ b/lib/colors.ts @@ -2,18 +2,9 @@ export const STROKE_REPLACEMENT_COLOR = "#000000"; export const FILL_REPLACEMENT_COLOR = "#ffffff"; export const TAIL_ATTACHMENT_COLOR = "#ff0000"; -export const LEGS_ATTACHMENT_COLOR = "#ffff00"; -export const ARMS_ATTACHMENT_COLOR = "#00ff00"; -export const HORNS_ATTACHMENT_COLOR = "#00ffff"; +export const LEG_ATTACHMENT_COLOR = "#ffff00"; +export const ARM_ATTACHMENT_COLOR = "#00ff00"; +export const HORN_ATTACHMENT_COLOR = "#00ffff"; export const CROWN_ATTACHMENT_COLOR = "#0000ff"; export const NESTING_BOUNDING_BOX_COLOR = "#ff00ff"; - -export const NON_VISUAL_COLORS = new Set([ - TAIL_ATTACHMENT_COLOR, - LEGS_ATTACHMENT_COLOR, - ARMS_ATTACHMENT_COLOR, - HORNS_ATTACHMENT_COLOR, - CROWN_ATTACHMENT_COLOR, - NESTING_BOUNDING_BOX_COLOR, -]); diff --git a/lib/specs.ts b/lib/specs.ts new file mode 100644 index 0000000..f676d67 --- /dev/null +++ b/lib/specs.ts @@ -0,0 +1,114 @@ +import { Bbox } from "./bounding-box"; +import * as colors from "./colors"; +import type { SvgSymbolElement } from "./vocabulary"; + +const SPEC_LAYER_ID_RE = /^specs.*/i; + +export type Point = { + x: number; + y: number; +}; + +export type Specs = { + tail?: Point[]; + leg?: Point[]; + arm?: Point[]; + horn?: Point[]; + crown?: Point[]; + nesting?: Bbox[]; +}; + +function getPoints(path: string): Point[] { + // TODO: Implement this. + return []; +} + +function getBoundingBoxes(path: string): Bbox[] { + // TODO: Implement this. + return []; +} + +function concat(first: T[] | undefined, second: T[]): T[] { + return first ? [...first, ...second] : second; +} + +function updateSpecs(fill: string, path: string, specs: Specs): Specs { + switch (fill) { + case colors.TAIL_ATTACHMENT_COLOR: + return { ...specs, tail: concat(specs.tail, getPoints(path)) }; + case colors.LEG_ATTACHMENT_COLOR: + return { ...specs, leg: concat(specs.leg, getPoints(path)) }; + case colors.ARM_ATTACHMENT_COLOR: + return { ...specs, arm: concat(specs.arm, getPoints(path)) }; + case colors.HORN_ATTACHMENT_COLOR: + return { ...specs, horn: concat(specs.horn, getPoints(path)) }; + case colors.CROWN_ATTACHMENT_COLOR: + return { ...specs, crown: concat(specs.crown, getPoints(path)) }; + case colors.NESTING_BOUNDING_BOX_COLOR: + return { + ...specs, + nesting: concat(specs.nesting, getBoundingBoxes(path)), + }; + } + + throw new Error(`Not sure what to do with specs path with fill "${fill}"!`); +} + +function getSpecs(layers: SvgSymbolElement[]): Specs { + let specs: Specs = {}; + + for (let layer of layers) { + if (layer.tagName !== "path") { + throw new Error( + `Found an unexpected <${layer.tagName}> in the specs layer!` + ); + } + const { fill, d } = layer.props; + if (!(fill && d)) { + throw new Error( + `Specs layer does not contain 'fill' and/or 'd' attributes!` + ); + } + specs = updateSpecs(fill, d, specs); + } + + return specs; +} + +export function extractSpecs( + layers: SvgSymbolElement[] +): [Specs | undefined, SvgSymbolElement[]] { + const layersWithoutSpecs: SvgSymbolElement[] = []; + let specs: Specs | undefined = undefined; + + const setSpecs = (s: Specs | undefined) => { + if (s) { + if (specs) { + throw new Error("Duplicate specs layers found!"); + } + specs = s; + } + }; + + for (let layer of layers) { + switch (layer.tagName) { + case "g": + const { id } = layer.props; + if (id && SPEC_LAYER_ID_RE.test(id)) { + setSpecs(getSpecs(layer.children)); + } else { + let [s, children] = extractSpecs(layer.children); + setSpecs(s); + layersWithoutSpecs.push({ + ...layer, + children, + }); + } + break; + case "path": + layersWithoutSpecs.push(layer); + } + } + + return [specs, layersWithoutSpecs]; +} diff --git a/lib/vocabulary.ts b/lib/vocabulary.ts index 5a8a230..d738be8 100644 --- a/lib/vocabulary.ts +++ b/lib/vocabulary.ts @@ -3,11 +3,13 @@ import path from "path"; import cheerio from "cheerio"; import { SVGProps } from "react"; import { getSvgBoundingBox, Bbox } from "./bounding-box"; +import { Specs, extractSpecs } from "./specs"; export type SvgSymbolData = { name: string; bbox: Bbox; layers: SvgSymbolElement[]; + specs?: Specs; }; export type SvgSymbolElement = ( @@ -112,15 +114,17 @@ export function build() { const $ = cheerio.load(svgMarkup); const svgEl = $("svg"); const name = path.basename(filename, SVG_EXT); - const layers = onlyTags(svgEl.children()).map((ch) => + const rawLayers = onlyTags(svgEl.children()).map((ch) => serializeSvgSymbolElement($, ch) ); + const [specs, layers] = extractSpecs(rawLayers); const bbox = getSvgBoundingBox(layers); const symbol: SvgSymbolData = { name, bbox, layers, + specs, }; vocab.push(symbol); }