Skip to content

Commit

Permalink
refactor(examples): further simplification rotating-voronoi example
Browse files Browse the repository at this point in the history
  • Loading branch information
postspectacular committed Jul 17, 2019
1 parent 2834c90 commit 2de12da
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 106 deletions.
2 changes: 1 addition & 1 deletion examples/rotating-voronoi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ Please refer to the [example build instructions](https://github.com/thi-ng/umbre

## License

© 2019 Karsten Schmidt // Apache Software License 2.0
© 2019 Alberto Massa // Apache Software License 2.0
19 changes: 9 additions & 10 deletions examples/rotating-voronoi/src/download.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { createElement } from "@thi.ng/hdom";

export function download(filename: string, text: string) {
var element = document.createElement("a");
element.setAttribute(
"href",
"data:text/plain;charset=utf-8," + encodeURIComponent(text)
);
element.setAttribute("download", filename);
element.style.display = "none";
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
const link = <HTMLLinkElement>createElement(document.body, "a", {
href: "data:text/plain;charset=utf-8," + encodeURIComponent(text),
download: filename,
style: { display: "none" }
});
link.click();
document.body.removeChild(link);
}
145 changes: 57 additions & 88 deletions examples/rotating-voronoi/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,32 @@
import { canvas } from "@thi.ng/hdom-canvas";
import { DVMesh } from "@thi.ng/geom-voronoi";
import { simplify } from "@thi.ng/geom-resample";
import {
pathFromCubics,
asCubic,
svgDoc,
rect,
asSvg,
group,
pathFromCubics,
points,
polygon,
asSvg
rect,
svgDoc,
vertices
} from "@thi.ng/geom";
import { CubicOpts } from "@thi.ng/geom-api";
import { cartesian2, Vec } from "@thi.ng/vectors";
import { TAU, PI } from "@thi.ng/math";
import { clearDOM } from "@thi.ng/hdom";
import { simplify } from "@thi.ng/geom-resample";
import { DVMesh } from "@thi.ng/geom-voronoi";
import { canvas } from "@thi.ng/hdom-canvas";
import { PI, TAU } from "@thi.ng/math";
import { SYSTEM } from "@thi.ng/random";
import { map, mapcat, normRange } from "@thi.ng/transducers";
import { updateDOM } from "@thi.ng/transducers-hdom";
import { cartesian2, Vec } from "@thi.ng/vectors";
import { checkbox, slider } from "./controllers";
import { download } from "./download";
import {
AppState,
scaleStream,
animationStream,
AppState,
frameStreamConditional,
frameStream,
keyStream,
keyStreamConditional,
mainStream
mainStream,
scaleStream
} from "./stream-state";
import {
map,
iterator1,
normRange,
comp,
mapcat,
iterator
} from "@thi.ng/transducers";
import { SYSTEM } from "@thi.ng/random";
import { slider, checkbox } from "./controllers";
import { download } from "./download";

const edge = window.innerWidth * 0.7;
const width = edge;
Expand All @@ -59,70 +49,55 @@ const pointsInCircle = (
_num: number,
_angle: number
) => [
...iterator1(
map((index) =>
cartesian2([], [_radius, index * TAU + _angle], _center)
),
...map(
(index) => cartesian2(null, [_radius, index * TAU + _angle], _center),
normRange(_num, false)
)
];

scaleStream.next(1);
frameStreamConditional.next(0);
animationStream.next(false);
const startKeyEvent: KeyboardEvent = document.createEvent("KeyboardEvent");
startKeyEvent.initEvent("keyup");
keyStreamConditional.next(startKeyEvent);
keyStreamConditional.next(<any>{});

This comment has been minimized.

Copy link
@postspectacular

postspectacular Jul 17, 2019

Author Member

maybe somewhat of a hack, but KISS :)


const app = (...args: any) => (state: AppState) => appRender(state);
mainStream.transform(
map(app(scaleStream, animationStream, frameStream, keyStream)),
updateDOM()
);
mainStream.transform(map(appRender), updateDOM());

This comment has been minimized.

Copy link
@postspectacular

postspectacular Jul 17, 2019

Author Member

since neither of the 2 levels of higher order args are used, i've updated the stream transform to use appRender directly...


function computeVoronoi(state: AppState) {
const delta = state.frameValue / 100;
const doSave = state.keyValue === "s";

const opts: CubicOpts = {

This comment has been minimized.

Copy link
@postspectacular

postspectacular Jul 17, 2019

Author Member

the asCubic function only requires partial opts so we can get rid of this predeclaration. see usage further below...

breakPoints: false,
uniform: false,
scale: state.scaleValue
};

const startPoints = [
...iterator(
comp(
map(([rad, density, clockwise]) =>
pointsInCircle(
center,
rad,
density,
clockwise ? delta : PI - delta
)
...mapcat(
([rad, density, clockwise]) =>
pointsInCircle(
center,
rad,
density,
clockwise ? delta : PI - delta
),
mapcat((x) => x)
),
startingCircles
)
];

const bounds = rect([width, height], { fill: "black" });

This comment has been minimized.

Copy link
@postspectacular

postspectacular Jul 17, 2019

Author Member

use this rect for both drawing and as voronoi clip shape...

const mesh = new DVMesh();
mesh.addKeys(startPoints, 0.01);
const bounds = [[0, 0], [width, 0], [width, height], [0, height]];
const cells = mesh.voronoi(bounds);
const cells = mesh.voronoi(vertices(bounds));

This comment has been minimized.

Copy link
@postspectacular

postspectacular Jul 17, 2019

Author Member

vertices() returns array of vertices of the given shape


const voronoi = [
rect([width, height], { fill: "black" }),
bounds,

group(
{ fill: "white", "stroke-width": 1 },
cells.map((cell) =>
pathFromCubics(
asCubic(polygon(simplify(cell, 0.01, true)), opts)
asCubic(polygon(simplify(cell, 0.5, true)), {
scale: state.scaleValue
})

This comment has been minimized.

Copy link
@postspectacular

postspectacular Jul 17, 2019

Author Member

only need to supply scale cubic option in this case, rest is using defaults

This comment has been minimized.

Copy link
@nkint

nkint Jul 17, 2019

Contributor

I thought it is more performant not to create object each time if it always the same... but yes, micro optimization is the root of all evil.

This comment has been minimized.

Copy link
@postspectacular

postspectacular Jul 17, 2019

Author Member

maybe you're right, and if there'd be hundreds/thousands of cells, am sure this would be making a difference, but here I doubt it... :)

)
)
),

points(doSave ? [] : startPoints, {
size: 4,
shape: "circle",
Expand Down Expand Up @@ -150,36 +125,34 @@ function computeVoronoi(state: AppState) {

function appRender(state: AppState) {
return [
"div.ma3.flex.flex-column.flex-row-l.flex-row-m",
"div.ma3.flex.flex-column.flex-row-l.flex-row-m.sans-serif",
[
[
"div.pr3.w-100.w-30-l.w-30-m",
["h1", "Rotating voronoi"],
[
"p",
"Based on a M. Bostock",
[
"a",
{
href:
"https://observablehq.com/@mbostock/rotating-voronoi"
},
" observablehq sketch"
],
". ",

"Originally from an ",
[
["span", "Based on a M. Bostock"],

This comment has been minimized.

Copy link
@postspectacular

postspectacular Jul 17, 2019

Author Member

no need to wrap text content in spans, done by hdom automatically...

[
"a",
{
href:
"https://observablehq.com/@mbostock/rotating-voronoi"
},
" observablehq sketch"
],
["span", ". "],

["span", "Originally from an "],
[
"a",
{
href:
"https://www.flickr.com/photos/quasimondo/8254540763/"
},
"ornament"
],
["span", " by Mario Klingemann."]
]
"a",
{
href:
"https://www.flickr.com/photos/quasimondo/8254540763/"
},
"ornament"
],
" by Mario Klingemann."
],
["p", "Press `s` to save the SVG file."],
[
Expand Down Expand Up @@ -209,9 +182,5 @@ function appRender(state: AppState) {

if (process.env.NODE_ENV !== "production") {
const hot = (<any>module).hot;
hot &&
hot.dispose(() => {
const app = document.getElementById("app");
app && clearDOM(app);
});
hot && hot.dispose(() => mainStream.done());

This comment has been minimized.

Copy link
@postspectacular

postspectacular Jul 17, 2019

Author Member

clearing the DOM does NOT cancel existing streams, however calling mainStream.done() recursively stops all upstream inputs/streams...

}
17 changes: 10 additions & 7 deletions examples/rotating-voronoi/src/stream-state.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import {
stream,
fromDOMEvent,
fromRAF,
sidechainToggle,
fromDOMEvent,
stream,

This comment has been minimized.

Copy link
@nkint

nkint Jul 17, 2019

Contributor

@postspectacular but do you have any linter to alphabetically order imports?

This comment has been minimized.

This comment has been minimized.

Copy link
@postspectacular

postspectacular Jul 17, 2019

Author Member

Maybe I should add a wiki page about project / editor config?

sync
} from "@thi.ng/rstream";
import { map, mapcat, scan, add } from "@thi.ng/transducers";
import {
add,
map,
mapcat,
scan
} from "@thi.ng/transducers";

export const keyStream = fromDOMEvent(document, "keyup");
export const keyStreamConditional = keyStream.transform(
export const keyStreamConditional = fromDOMEvent(document, "keyup").transform(
mapcat((x) => [x.key, null])
);

export const scaleStream = stream<number>();
export const animationStream = stream<boolean>();
export const frameStream = fromRAF();
export const frameStreamConditional = frameStream
export const frameStreamConditional = fromRAF()
.subscribe(sidechainToggle<number, boolean>(animationStream))
.transform(map(() => 1), scan(add()));

Expand Down

0 comments on commit 2de12da

Please sign in to comment.