diff --git a/src/main.ts b/src/main.ts index 7c97918..1a63996 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,32 +2,38 @@ import { line, type Line, group, translate, asSvg, svgDoc, scale, flip } from "@ import { vec2, type Vec2 } from "@thi.ng/vectors" import { $canvas } from "@thi.ng/rdom-canvas"; import { reactive, sync } from "@thi.ng/rstream"; -import { $compile, $input } from '@thi.ng/rdom' +import { $compile, $input, $inputNum } from '@thi.ng/rdom' import { cycle, comp, range, iterator, map, mapcat, reverse, zip } from "@thi.ng/transducers" -const cellSize_ = "12" -const xs_ = "101110100101100" -const ys_ = "101001111001110000101" +// initial settings -const parsePattern = (pattern: string) => pattern.split('').map(x => Number.parseInt(x, 10)) +// available size for a2: 420 * 594 +const width_ = 420 +const height_ = 594 +const cellSize_ = 12 -const xSeeds = reactive(xs_).map((pattern) => parsePattern(pattern)) -const ySeeds = reactive(ys_).map((pattern) => parsePattern(pattern)) -const cellSize = reactive(cellSize_).map(num => { - const next = Number.parseInt(num, 10) - return Number.isNaN(next)|| next < 5 ? 5 : next -}) +const xSeeds_ = "101110100101100" +const ySeeds_ = "101001111001110000101" +const unit_ = 'mm' + +// turn these into reactive values to trigger re-rendering on change + +const width = reactive(width_) +const height = reactive(height_) +const unit = reactive(unit_) +const size = sync({ src: { width, height } }).map(({ width, height }) => [width, height]) + +const toNumber = (v: any) => typeof v === 'number' ? v : Number.parseInt(v, 10) +const parsePattern = (pattern: string) => pattern.split('').map(toNumber) + +const xSeeds = reactive(xSeeds_).map((pattern) => parsePattern(pattern)) +const ySeeds = reactive(ySeeds_).map((pattern) => parsePattern(pattern)) +const cellSize = reactive(cellSize_).map(num => Number.isNaN(num) || num < 5 ? 5 : num) // restrict cells to be at least 5 $unit // const backgroundColor = getComputedStyle(document.documentElement).getPropertyValue('--background-color') const primaryColor = getComputedStyle(document.documentElement).getPropertyValue('--primary-color') const secondaryColor = getComputedStyle(document.documentElement).getPropertyValue('--secondary-color') -// available size for a2: 420 * 594 -const width = 420 -const height = 594 - -const size = [width, height] - // some explanation for what's happening below: // // - a coord is a position on a single axis @@ -71,8 +77,19 @@ const generateLines = (maxCoordOnAxis: number, maxCoordPerpendicular: number, se // now we can put the functions above together based on the current settings and generate a scene to be drawn const scene = sync({ - src: { cellSize, xSeeds, ySeeds, } -}).map(({ cellSize, xSeeds, ySeeds }) => { + src: { + size, + cellSize, + xSeeds, + ySeeds, + } +}).map(({ + size, + cellSize, + xSeeds, + ySeeds, +}) => { + const [width, height] = size const minPadding = cellSize // calculate available drawing area @@ -85,15 +102,13 @@ const scene = sync({ const xLines = [...map(l => scale(l, cellSize), generateLines(yCells, xCells, cycle(ySeeds), xLineGen))] const yLines = [...map(l => scale(l, cellSize), generateLines(xCells, yCells, cycle(xSeeds), yLineGen))] - const scene = translate( + return translate( group( { __background: primaryColor, stroke: secondaryColor }, [...yLines, ...xLines] ), [xPadding, yPadding] ) - - return scene }) const input = (label: string, attrs: any) => @@ -106,25 +121,47 @@ $compile( $canvas(scene, size)], ["aside", {}, ["h2", {}, "Settings"], + input("Canvas width:", { + type: "number", + min: "10", + value: width.deref()!, + oninput: $inputNum(width), + }), + input("Canvas height:", { + type: "number", + min: "10", + value: height.deref()!, + oninput: $inputNum(height), + }), input("Grid size: ", { type: "number", min: "5", value: cellSize.deref()!, - oninput: $input(cellSize) + oninput: $inputNum(cellSize), }), input("X Pattern: ", { type: "text", - value: xs_, + value: xSeeds_, oninput: $input(xSeeds) }), input("Y Pattern: ", { type: "text", - value: ys_, + value: ySeeds_, oninput: $input(ySeeds) }), ["h2", {}, "SVG Export"], ["div", {}, ["textarea", { - value: scene.map((scene) => asSvg(svgDoc({ viewBox: `0 0 ${size[0]} ${size[1]}` }, scene))), + value: sync({ + src: { + scene, + size, + unit, + } + }).map(({ + scene, + size, + unit + }) => asSvg(svgDoc({ width: `${size[0]}${unit}`, height: `${size[1]}${unit}`, viewBox: `0 0 ${size[0]} ${size[1]}` }, scene))), cols: 80, rows: 20 }]]]]