From 64693d34bcc52628295a6051fe5cdd875aa3aa67 Mon Sep 17 00:00:00 2001 From: Karsten Schmidt Date: Fri, 12 Jul 2019 12:57:46 +0100 Subject: [PATCH] feat(examples): add poly-spline example --- examples/README.md | 45 +++---- examples/poly-spline/.gitignore | 5 + examples/poly-spline/README.md | 13 ++ examples/poly-spline/index.html | 16 +++ examples/poly-spline/package.json | 29 +++++ examples/poly-spline/src/index.ts | 202 +++++++++++++++++++++++++++++ examples/poly-spline/tsconfig.json | 11 ++ 7 files changed, 299 insertions(+), 22 deletions(-) create mode 100644 examples/poly-spline/.gitignore create mode 100644 examples/poly-spline/README.md create mode 100644 examples/poly-spline/index.html create mode 100644 examples/poly-spline/package.json create mode 100644 examples/poly-spline/src/index.ts create mode 100644 examples/poly-spline/tsconfig.json diff --git a/examples/README.md b/examples/README.md index f1191ac313..1445cfd99d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -45,25 +45,26 @@ in touch via PR, issue tracker, email or twitter! | 34 | [markdown](./markdown) | Markdown parser & editor w/ live preview | fsm, rstream, transducers-hdom | advanced | | 35 | [package-stats](./package-stats) | CLI util to visualize umbrella pkg stats | hiccup-svg, transducers | intermediate | | 36 | [pointfree-svg](./pointfree-svg) | Generate SVG using pointfree DSL | hiccup, hiccup-svg, pointfree-lang | intermediate | -| 37 | [router-basics](./router-basics) | Complete mini SPA | atom, hdom, interceptors, router | advanced | -| 38 | [rstream-dataflow](./rstream-dataflow) | Dataflow graph | atom, hdom, rstream, rstream-gestures, rstream-graph, transducers | intermediate | -| 39 | [rstream-grid](./rstream-grid) | Dataflow graph SVG grid | atom, hdom, hiccup-svg, interceptors, rstream-graph, transducers | advanced | -| 40 | [rstream-hdom](./rstream-hdom) | rstream based UI updates & state handling | hdom, rstream, transducers | intermediate | -| 41 | [shader-ast-canvas2d](shader-ast-canvas2d) | 2D canvas shader emulation | shader-ast | basic | -| 42 | [shader-ast-noise](shader-ast-noise) | HOF shader function composition | shader-ast, webgl | basic | -| 43 | [shader-ast-raymarch](shader-ast-raymarch) | WebGL & Canvas2D raymarch shader | shader-ast, webgl | intermediate | -| 44 | [shader-ast-sdf2d](shader-ast-sdf2d) | WebGL & Canvas2D SDF | shader-ast, webgl | basic | -| 45 | [shader-ast-tunnel](shader-ast-tunnel) | WebGL & Canvas2D textured tunnel shader | shader-ast, webgl | basic | -| 46 | [svg-barchart](./svg-barchart) | hdom SVG barchart component | hdom, transducers | basic | -| 47 | [svg-particles](./svg-particles) | hdom SVG generation / animation | hdom, transducers | basic | -| 48 | [svg-waveform](./svg-waveform) | hdom SVG generation / undo history | atom, hdom, hiccup-svg, interceptors, iterators | intermediate | -| 49 | [talk-slides](./talk-slides) | Presentation slides from ClojureX 2018 | hdom, rstream, transducers-hdom | intermediate | -| 50 | [todo-list](./todo-list) | Canonical Todo list with undo/redo | atom, hdom, transducers | intermediate | -| 51 | [transducers-hdom](./transducers-hdom) | Transducer & rstream based hdom UI updates | hdom, rstream, transducers-hdom | basic | -| 52 | [triple-query](./triple-query) | Triple store query results & sortable table | atom, hdom, hdom-components, rstream-query, transducers | intermediate | -| 53 | [webgl-cubemap](./webgl-cubemap) | WebGL cubemap, async texture loading | hdom, webgl, shader-ast | intermediate | -| 54 | [webgl-gpgpu-basics](./webgl-gpgpu-basics) | Minimal GPGPU example | webgl, shader-ast | basic | -| 55 | [webgl-grid](./webgl-grid) | WebGL instancing | webgl, hdom | intermediate | -| 56 | [webgl-msdf](./webgl-msdf) | WebGL MSDF font rendering & particle system | webgl, webgl-msdf, shader-ast, hdom | intermediate | -| 57 | [webgl-ssao](./webgl-ssao) | WebGL screenspace ambient occlusion | webgl, shader-ast, rstream, hdom | advanced | -| 58 | [xml-converter](./xml-converter) | XML/HTML/SVG to hiccup conversion as you type | rstream, sax, transducers, transducers-hdom | advanced | +| 37 | [poly-spline](./poly-spline) | Polygon to cubic curve conversion & visualization | geom, hiccup-svg, hdom, rstream | intermediate | +| 38 | [router-basics](./router-basics) | Complete mini SPA | atom, hdom, interceptors, router | advanced | +| 39 | [rstream-dataflow](./rstream-dataflow) | Dataflow graph | atom, hdom, rstream, rstream-gestures, rstream-graph, transducers | intermediate | +| 40 | [rstream-grid](./rstream-grid) | Dataflow graph SVG grid | atom, hdom, hiccup-svg, interceptors, rstream-graph, transducers | advanced | +| 41 | [rstream-hdom](./rstream-hdom) | rstream based UI updates & state handling | hdom, rstream, transducers | intermediate | +| 42 | [shader-ast-canvas2d](shader-ast-canvas2d) | 2D canvas shader emulation | shader-ast | basic | +| 43 | [shader-ast-noise](shader-ast-noise) | HOF shader function composition | shader-ast, webgl | basic | +| 44 | [shader-ast-raymarch](shader-ast-raymarch) | WebGL & Canvas2D raymarch shader | shader-ast, webgl | intermediate | +| 45 | [shader-ast-sdf2d](shader-ast-sdf2d) | WebGL & Canvas2D SDF | shader-ast, webgl | basic | +| 46 | [shader-ast-tunnel](shader-ast-tunnel) | WebGL & Canvas2D textured tunnel shader | shader-ast, webgl | basic | +| 47 | [svg-barchart](./svg-barchart) | hdom SVG barchart component | hdom, transducers | basic | +| 48 | [svg-particles](./svg-particles) | hdom SVG generation / animation | hdom, transducers | basic | +| 49 | [svg-waveform](./svg-waveform) | hdom SVG generation / undo history | atom, hdom, hiccup-svg, interceptors, iterators | intermediate | +| 50 | [talk-slides](./talk-slides) | Presentation slides from ClojureX 2018 | hdom, rstream, transducers-hdom | intermediate | +| 51 | [todo-list](./todo-list) | Canonical Todo list with undo/redo | atom, hdom, transducers | intermediate | +| 52 | [transducers-hdom](./transducers-hdom) | Transducer & rstream based hdom UI updates | hdom, rstream, transducers-hdom | basic | +| 53 | [triple-query](./triple-query) | Triple store query results & sortable table | atom, hdom, hdom-components, rstream-query, transducers | intermediate | +| 54 | [webgl-cubemap](./webgl-cubemap) | WebGL cubemap, async texture loading | hdom, webgl, shader-ast | intermediate | +| 55 | [webgl-gpgpu-basics](./webgl-gpgpu-basics) | Minimal GPGPU example | webgl, shader-ast | basic | +| 56 | [webgl-grid](./webgl-grid) | WebGL instancing | webgl, hdom | intermediate | +| 57 | [webgl-msdf](./webgl-msdf) | WebGL MSDF font rendering & particle system | webgl, webgl-msdf, shader-ast, hdom | intermediate | +| 58 | [webgl-ssao](./webgl-ssao) | WebGL screenspace ambient occlusion | webgl, shader-ast, rstream, hdom | advanced | +| 59 | [xml-converter](./xml-converter) | XML/HTML/SVG to hiccup conversion as you type | rstream, sax, transducers, transducers-hdom | advanced | diff --git a/examples/poly-spline/.gitignore b/examples/poly-spline/.gitignore new file mode 100644 index 0000000000..0c5abcab62 --- /dev/null +++ b/examples/poly-spline/.gitignore @@ -0,0 +1,5 @@ +.cache +out +node_modules +yarn.lock +*.js diff --git a/examples/poly-spline/README.md b/examples/poly-spline/README.md new file mode 100644 index 0000000000..89971ee483 --- /dev/null +++ b/examples/poly-spline/README.md @@ -0,0 +1,13 @@ +# poly-spline + +[Live demo](http://demo.thi.ng/umbrella/poly-spline/) + +Please refer to the [example build instructions](https://github.com/thi-ng/umbrella/wiki/Example-build-instructions) on the wiki. + +## Authors + +- Karsten Schmidt + +## License + +© 2018 Karsten Schmidt // Apache Software License 2.0 diff --git a/examples/poly-spline/index.html b/examples/poly-spline/index.html new file mode 100644 index 0000000000..f5086aec1e --- /dev/null +++ b/examples/poly-spline/index.html @@ -0,0 +1,16 @@ + + + + + + + poly-spline + + + + +
+ + + diff --git a/examples/poly-spline/package.json b/examples/poly-spline/package.json new file mode 100644 index 0000000000..8377ef0bd5 --- /dev/null +++ b/examples/poly-spline/package.json @@ -0,0 +1,29 @@ +{ + "name": "poly-spline", + "version": "0.0.1", + "repository": "https://github.com/thi-ng/umbrella", + "author": "Karsten Schmidt ", + "license": "Apache-2.0", + "scripts": { + "clean": "rm -rf .cache build out", + "build": "yarn clean && parcel build index.html -d out --public-url ./ --no-source-maps --no-cache --detailed-report --experimental-scope-hoisting", + "build:webpack": "../../node_modules/.bin/webpack --mode production", + "start": "parcel index.html -p 8080 --open" + }, + "devDependencies": { + "parcel-bundler": "^1.12.3", + "terser": "^3.17.0", + "typescript": "^3.4.1" + }, + "dependencies": { + "@thi.ng/api": "latest", + "@thi.ng/rstream": "latest", + "@thi.ng/transducers-hdom": "latest" + }, + "browserslist": [ + "last 3 Chrome versions" + ], + "browser": { + "process": false + } +} diff --git a/examples/poly-spline/src/index.ts b/examples/poly-spline/src/index.ts new file mode 100644 index 0000000000..9ecb30d0d2 --- /dev/null +++ b/examples/poly-spline/src/index.ts @@ -0,0 +1,202 @@ +import { sin } from "@thi.ng/dsp"; +import { + asCubic, + circle, + group, + line, + pathFromCubics, + star, + svgDoc, + withAttribs +} from "@thi.ng/geom"; +import { convertTree } from "@thi.ng/hiccup-svg"; +import { + fromRAF, + stream, + Stream, + sync +} from "@thi.ng/rstream"; +import { + comp, + iterator, + map, + mapcat, + partition, + reducer, + scan +} from "@thi.ng/transducers"; +import { updateDOM } from "@thi.ng/transducers-hdom"; + +const BUTTONS = { + blue: "bg-blue white hover-bg-light-blue hover-navy", + green: "bg-green white hover-bg-light-green hover-dark-green" +}; + +// HOF event listener to emit a value on given stream +const emitOnStream = (stream: Stream, val: any) => () => stream.next(val); + +// button UI component +const button = ( + _: any, + clazz: string, + onclick: EventListener, + label: string +) => [ + "a", + { + href: "#", + onclick, + class: "dib w4 mr2 pa2 link " + clazz + }, + label +]; + +// slider UI component +const slider = ( + _: any, + attribs: any, + stream: Stream, + label: string +) => [ + "div.mv3", + ["span.dib.w4.mr2", label], + [ + "input.mr3", + { + type: "range", + value: stream.deref(), + oninput: (e: any) => stream.next(parseFloat(e.target.value)), + ...attribs + } + ], + stream.deref().toFixed(1) +]; + +// main app component / stream transformer +// attached to `main` stream sync and responsible to build full UI +// from current stream values +const app = ( + _mode: Stream, + _uniform: Stream, + _scale: Stream, + _uniScale: Stream +) => ({ poly, mode, uniform, scale, uniScale }: any) => { + // reconstruct poly as cubic curve segments + const cubics = asCubic(poly, { + breakPoints: mode, + scale: scale * (uniform ? uniScale : 1), + uniform + }); + // visualize control points as circles + const controlPoints = iterator( + comp(mapcat((x) => x.points), map((p) => circle(p, 0.75))), + cubics + ); + // visualize control point handles + const handles = iterator( + comp(mapcat((x) => x.points), partition(2), map(line)), + cubics + ); + return [ + "div.sans-serif.ma3", + // user controls + [ + "div", + [ + button, + BUTTONS.blue, + emitOnStream(_mode, true), + mode ? "break points" : "control points" + ], + [ + button, + BUTTONS.green, + emitOnStream(_uniform, true), + uniform ? "uniform" : "non-uniform" + ], + [ + slider, + { min: -1.3, max: 1.3, step: 0.1 }, + _scale, + "tangent scale" + ], + [ + slider, + { min: 0, max: 100, step: 1, disabled: !uniform }, + _uniScale, + "uniform scale" + ] + ], + [ + "div", + // all @thi.ng/geom shapes implement the `IToHiccup` + // interface and so can be used directly in + // @thi.ng/hdom-canvas visualizations. However, here we're + // using SVG and hence will need to call `convertTree()` to + // transform the hiccup format into a SVG compatible format + // see: + // https://github.com/thi-ng/umbrella/blob/master/packages/hiccup-svg/src/convert.ts#L34 + convertTree( + svgDoc( + { + width: 480, + height: 480, + viewBox: "-150 -150 300 300", + fill: "none", + stroke: "#ccc", + "stroke-width": 0.25 + }, + poly, + withAttribs(pathFromCubics(cubics), { + stroke: mode ? "blue" : "red", + "stroke-width": 1 + }), + group({ stroke: "#333" }, [...controlPoints, ...handles]) + ) + ) + ] + ]; +}; + +// stream of animated polygons +const poly = fromRAF().transform( + map((t) => star(100, 6, [sin(t, 0.01, 0.5, 0.8), 1])) +); + +// poly spline mode flag (control points vs break points) +const mode = stream(); +// flag for uniform tangent scaling +const uniform = stream(); +// tangent scale +const scale = stream(); +// uniform scale factor (only used if uniform scaling is enabled) +const uniScale = stream(); + +// re-usable transducer implementing a toggle switch +const toggle = scan(reducer(() => true, (acc) => !acc)); + +// main stream combinator +const main = sync({ + src: { + poly, + mode: mode.transform(toggle), + uniform: uniform.transform(toggle), + scale, + uniScale + } +}); + +// transform to create & apply UI +main.transform(map(app(mode, uniform, scale, uniScale)), updateDOM()); + +// seed all input streams to kick off +mode.next(false); +uniform.next(false); +scale.next(0.5); +uniScale.next(25); + +// HMR handling (dev builds only) +if (process.env.NODE_ENV !== "production") { + const hot = (module).hot; + hot && hot.dispose(() => main.done()); +} diff --git a/examples/poly-spline/tsconfig.json b/examples/poly-spline/tsconfig.json new file mode 100644 index 0000000000..bbf112cc18 --- /dev/null +++ b/examples/poly-spline/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": ".", + "target": "es6", + "sourceMap": true + }, + "include": [ + "./src/**/*.ts" + ] +}