Compare commits

..

No commits in common. "54e75eed31abcf1eee7327ff63cc786b1111f940" and "6b8b20d2868935e23591cc9dfcb4932549ce6c53" have entirely different histories.

25 changed files with 228 additions and 717 deletions

10
TODO.md
View File

@ -1,10 +0,0 @@
# TODO
- ellipse
- smooth/nosmooth
- curves
- lerp
- clipping
- text

View File

@ -1,25 +1,27 @@
import { artworld } from "./world.js";
import { Vector2 } from "./math.js"; import { Vector2 } from "./math.js";
import { Color } from "./color.js"; import { Color } from "./color.js";
import { Random } from "./random.js"; import { Random } from "./random.js";
import { World } from "./world.js";
import { getVal } from "./variables.js"; function cloneObject(obj) {
for (let [k, v] of Object.entries(obj)) {
console.log(k, v);
}
}
export class Drawable { export class Drawable {
constructor(parent) { constructor(parent) {
this._parent = null; this._parent = parent;
this._world = null; this._updates = [];
// if parent is provided, then delegate to parent class
if (parent instanceof World) {
parent.registerDrawable(this);
} else if (parent) {
parent.addChild(this);
}
this._fill = parent ? parent._fill : null; this._fill = parent ? parent._fill : null;
this._stroke = parent ? parent._stroke : null; this._stroke = parent ? parent._stroke : null;
this._strokeWeight = parent ? parent._strokeWeight : null; this._strokeWeight = parent ? parent._strokeWeight : null;
this._zIndex = parent ? parent._zIndex : null; this._z_index = parent ? parent._z_index : null;
this._posVec = new Vector2(0, 0); this._posVec = new Vector2(0, 0);
this._animations = []; if (this._parent) {
this._parent.addChild(this);
}
artworld.register(this);
} }
copy() { copy() {
@ -30,22 +32,7 @@ export class Drawable {
throw new Error("draw() must be implemented on child class"); throw new Error("draw() must be implemented on child class");
} }
animate(setter, func, args) {
this._animations.push({ setter, func, args });
return this;
}
update(t) {
for (let anim of this._animations) {
this[anim.setter](anim.func(t), anim.args);
}
}
// setters // setters
world(world) {
this._world = world;
return this;
}
fill(color) { fill(color) {
this._fill = color; this._fill = color;
@ -78,7 +65,7 @@ export class Drawable {
} }
z(scalar) { z(scalar) {
this._zIndex = scalar; this._z_index = scalar;
return this; return this;
} }
@ -90,7 +77,10 @@ export class Drawable {
} }
random() { random() {
this._posVec = new Vector2(Random.under(100), Random.under(100)); this._posVec = new Vector2(
Random.under(artworld.width),
Random.under(artworld.height),
);
this._stroke = new Color( this._stroke = new Color(
Random.under(360), Random.under(360),
Random.between(30, 60), Random.between(30, 60),
@ -109,14 +99,14 @@ export class Drawable {
get worldPos() { get worldPos() {
// offset from parent if needed // offset from parent if needed
return this._parent return this._parent
? this._parent.worldPos.add(getVal(this._posVec)) ? this._parent.worldPos.add(this._posVec)
: getVal(this._posVec); : this._posVec;
} }
} }
export class Group extends Drawable { export class Group extends Drawable {
constructor(parent) { constructor() {
super(parent); super();
this._children = []; this._children = [];
} }
@ -137,9 +127,6 @@ export class Group extends Drawable {
newGroup._parent = this._parent; newGroup._parent = this._parent;
newGroup._parent.addChild(newGroup); newGroup._parent.addChild(newGroup);
} }
if (this._world) {
this._world.registerDrawable(newGroup);
}
return newGroup; return newGroup;
} }
@ -148,11 +135,7 @@ export class Group extends Drawable {
addChild(drawable) { addChild(drawable) {
this._children.push(drawable); this._children.push(drawable);
// probably alraedy true, but make sure
drawable._parent = this; drawable._parent = this;
if (this._world) {
this._world.registerDrawable(drawable);
}
return this; return this;
} }

View File

@ -1,7 +1,6 @@
export { artworld } from "./world.js";
export { Color, Pico8 } from "./color.js"; export { Color, Pico8 } from "./color.js";
export { Vector2, degToRad, radToDeg } from "./math.js"; export { Vector2, degToRad, radToDeg } from "./math.js";
export { Line, Rect, Circle, Arc, Polygon } from "./shapes.js"; export { Line, Rect, Circle, Arc } from "./shapes.js";
export { Group } from "./drawable.js"; export { Group } from "./drawable.js";
export { Random } from "./random.js"; export { Random } from "./random.js";
export { Var } from "./variables.js";
export { World } from "./world.js";

View File

@ -27,16 +27,6 @@ export class Vector2 {
return new Vector2(s * this.x, s * this.y); return new Vector2(s * this.x, s * this.y);
} }
distance(other) {
return Math.sqrt(
Math.pow(this.x - other.x, 2) + Math.pow(this.y - other.y, 2),
);
}
get magnitude() {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
static random(x, y) { static random(x, y) {
let theta = Random.radians(); let theta = Random.radians();
// if neither specified, use (1, 1) // if neither specified, use (1, 1)

View File

@ -16,8 +16,4 @@ export class Random {
static radians() { static radians() {
return Math.random() * Math.PI * 2; return Math.random() * Math.PI * 2;
} }
static choice(array) {
return array[Math.floor(Random.under(array.length))];
}
} }

View File

@ -1,7 +1,6 @@
import { Vector2 } from "./math.js"; import { Vector2 } from "./math.js";
import { Drawable } from "./drawable.js"; import { Drawable } from "./drawable.js";
import { Random } from "./random.js"; import { Random } from "./random.js";
import { getVal } from "./variables.js";
function makeCopy(Cls, obj, override) { function makeCopy(Cls, obj, override) {
// make new object, will be registered with world, but no _parent yet // make new object, will be registered with world, but no _parent yet
@ -12,10 +11,6 @@ function makeCopy(Cls, obj, override) {
if (newObj._parent) { if (newObj._parent) {
newObj._parent.addChild(newObj); newObj._parent.addChild(newObj);
} }
// attach to world
if (newObj._world) {
newObj._world.registerDrawable(newObj);
}
return newObj; return newObj;
} }
@ -29,18 +24,10 @@ export class Line extends Drawable {
return makeCopy(Line, this, overrides); return makeCopy(Line, this, overrides);
} }
random(range = 100) {
super.random();
this._offsetVec = Vector2.random(range, range);
return this;
}
draw() { draw() {
this._world.prepareDraw(this); artworld.setStrokeColor(this._stroke);
this._world.drawLine( artworld.setStrokeWeight(this._strokeWeight);
this.worldPos, artworld.drawLine(this.worldPos, this.worldPos.add(this._offsetVec));
this.worldPos.add(getVal(this._offsetVec)),
);
} }
to(vec) { to(vec) {
@ -61,26 +48,13 @@ export class Arc extends Drawable {
this._startAngle = 0; this._startAngle = 0;
this._endAngle = 180; this._endAngle = 180;
} }
copy(overrides) { copy(overrides) {
return makeCopy(Arc, this, overrides); return makeCopy(Arc, this, overrides);
} }
random(range = 100) {
super.random();
this._r = Random.between(20, range);
this._startAngle = Random.radians();
this._endAngle = Random.radians();
return this;
}
draw() { draw() {
this._world.prepareDraw(this); artworld.prepareDraw(this);
this._world.drawArc( artworld.drawArc(this.worldPos, this._r, this._startAngle, this._endAngle);
this.worldPos,
this._r,
this._startAngle,
this._endAngle,
);
} }
radius(scalar) { radius(scalar) {
@ -106,15 +80,9 @@ export class Circle extends Drawable {
return makeCopy(Circle, this, overrides); return makeCopy(Circle, this, overrides);
} }
random(range = 100) {
super.random();
this._r = Random.between(20, range);
return this;
}
draw() { draw() {
this._world.prepareDraw(this); artworld.prepareDraw(this);
this._world.drawArc(this.worldPos, this._r, 0, 2 * Math.PI); artworld.drawArc(this.worldPos, this._r, 0, 2 * Math.PI);
} }
radius(scalar) { radius(scalar) {
@ -130,7 +98,7 @@ export class Rect extends Drawable {
this._height = 50; this._height = 50;
} }
random(range = 100) { random(range) {
super.random(); super.random();
this._width = Random.between(20, range); this._width = Random.between(20, range);
this._height = Random.between(20, range); this._height = Random.between(20, range);
@ -141,8 +109,8 @@ export class Rect extends Drawable {
} }
draw() { draw() {
this._world.prepareDraw(this); artworld.prepareDraw(this);
this._world.drawRect(this.worldPos, this._width, this._height); artworld.drawRect(this.worldPos, this._width, this._height);
} }
size(w, h) { size(w, h) {
@ -171,67 +139,3 @@ export class Rect extends Drawable {
// ); // );
// } // }
} }
export class Polygon extends Drawable {
constructor(parent) {
super(parent);
this._points = [];
}
random(n = 7) {
super.random();
let points = [];
let i;
// generate points
for (i = 0; i < n; i++) {
points.push(
new Vector2(Random.between(50, 200), Random.between(50, 200)),
);
}
// sort points by distance for mostly convex (but not always) polygons
this._points.push(points.shift()); // take first element
while (points.length) {
let closestIdx = 0;
let closestDist = 999999999;
let dist;
for (let j = 0; j < points.length; j++) {
dist = this._points[this._points.length - 1].distance(points[j]);
if (dist < closestDist) {
closestDist = dist;
closestIdx = j;
}
}
this._points.push(points[closestIdx]);
points.splice(closestIdx, 1);
}
return this;
}
copy(overrides) {
return makeCopy(Polygon, this, overrides);
}
draw() {
this._world.prepareDraw(this);
this._world.drawPolygon(this.worldPos, this._points);
}
point(vector, to_modify = null) {
if (to_modify !== null) {
this._points[to_modify] = vector;
} else {
this._points.push(point);
}
return this;
}
// TODO
// contains(x, y) {
// return (
// x > this.x &&
// x < this.x + this._width &&
// y > this.y &&
// y < this.pos.y + this._height
// );
// }
}

View File

@ -1,34 +0,0 @@
export class Timer {
constructor() {
this._pausedAt = null;
this._elapsed = 0;
this._lastStarted = performance.now();
}
pause() {
if (!this.isPaused) {
this._pausedAt = performance.now();
// store time since last pause
this._elapsed += this._pausedAt - this._lastStarted;
}
}
unpause() {
if (this.isPaused) {
this.lastStarted = performance.now();
this._pausedAt = null;
}
}
get isPaused() {
return this._pausedAt !== null;
}
time() {
if (this.isPaused) {
return this._elapsed;
} else {
return this._elapsed + (performance.now() - this._lastStarted);
}
}
}

View File

@ -1,29 +0,0 @@
export function getVal(x) {
return x && x.getValue ? x.getValue() : x;
}
export class Var {
constructor(value) {
if (typeof value === "function") {
this._updateFunc = value;
this._value = this._updateFunc(0);
} else {
this._value = value;
this._updateFunc = null;
}
}
updateFunc(func) {
this._updateFunc = func;
}
update(t) {
if (this._updateFunc) {
this._value = this._updateFunc(t);
}
}
getValue() {
return this._value;
}
}

View File

@ -1,20 +1,11 @@
import { Timer } from "./timer.js";
import { getVal, Var } from "./variables.js";
export class World { export class World {
constructor(canvasId) { constructor() {
this.ctx = null;
this.drawables = []; this.drawables = [];
this.variables = []; this.ctx = null;
this.backgroundColor = "white"; }
this.timer = new Timer();
this.targetFrameRate = 60; bindCanvas(id) {
this.stepSize = 1000 / this.targetFrameRate; this.ctx = document.getElementById(id).getContext("2d");
this.lastTick = this.timer.time();
this.numTicks = 0;
this.ctx = document.getElementById(canvasId).getContext("2d");
this.ctx.canvas.width = 1000;
this.ctx.canvas.height = 1000;
} }
get width() { get width() {
@ -25,55 +16,14 @@ export class World {
return this.ctx.canvas.height; return this.ctx.canvas.height;
} }
var(value) { register(drawable) {
let v = new Var(value); this.drawables.push(drawable);
this.variables.push(v);
return v;
}
registerDrawable(thing) {
this.drawables.push(thing);
thing._world = this;
return thing;
} }
draw() { draw() {
this.ctx.fillStyle = this.backgroundColor; for (let d of this.drawables.sort((a, b) => a._z_index - b._z_index)) {
this.ctx.beginPath();
this.ctx.fillRect(0, 0, this.width, this.height);
this.ctx.fill();
this.ctx.save();
this.ctx.translate(500, 500);
for (let d of this.drawables.sort((a, b) => a._zIndex - b._zIndex)) {
d.draw(); d.draw();
} }
this.ctx.restore();
}
tick() {
this.numTicks++;
for (let d of this.drawables) {
d.update(this.numTicks);
}
for (let v of this.variables) {
v.update(this.numTicks);
}
}
get updatesPerSecond() {
return (this.numTicks / this.timer.time()) * 1000;
}
loopStep() {
window.requestAnimationFrame(() => this.loopStep());
let curTime = this.timer.time();
// tick appropriate number of times
while (curTime - this.lastTick > this.stepSize) {
this.lastTick += this.stepSize;
this.tick();
}
this.draw();
} }
// Canvas 2D ///////////////// // Canvas 2D /////////////////
@ -81,21 +31,21 @@ export class World {
prepareDraw(drawable) { prepareDraw(drawable) {
this._stroke = false; this._stroke = false;
this._fill = false; this._fill = false;
let stroke = getVal(drawable._stroke); if (drawable._stroke) {
let fill = getVal(drawable._fill); this.ctx.lineWidth = drawable._strokeWeight;
if (stroke) { this.ctx.strokeStyle = drawable._stroke.toStr
this.ctx.lineWidth = getVal(drawable._strokeWeight); ? drawable._stroke.toStr()
this.ctx.strokeStyle = stroke.toStr ? stroke.toStr() : stroke; : drawable._stroke;
this._stroke = true; this._stroke = true;
} else if (fill) { } else if (drawable._fill) {
this.ctx.fillStyle = fill.toStr ? fill.toStr() : fill; this.ctx.fillStyle = drawable._fill.toStr
? drawable._fill.toStr()
: drawable._fill;
this._fill = true; this._fill = true;
} }
} }
drawLine(from, to) { drawLine(from, to) {
from = getVal(from);
to = getVal(to);
this.ctx.beginPath(); this.ctx.beginPath();
this.ctx.moveTo(from.x, from.y); this.ctx.moveTo(from.x, from.y);
this.ctx.lineTo(to.x, to.y); this.ctx.lineTo(to.x, to.y);
@ -103,38 +53,19 @@ export class World {
} }
drawArc(center, radius, fromAngle, toAngle) { drawArc(center, radius, fromAngle, toAngle) {
center = getVal(center);
this.ctx.beginPath(); this.ctx.beginPath();
this.ctx.arc( this.ctx.arc(center.x, center.y, radius, fromAngle, toAngle);
center.x,
center.y,
getVal(radius),
getVal(fromAngle),
getVal(toAngle),
);
if (this._stroke) this.ctx.stroke(); if (this._stroke) this.ctx.stroke();
if (this._fill) this.ctx.fill(); if (this._fill) this.ctx.fill();
} }
drawRect(center, width, height) { drawRect(center, width, height) {
center = getVal(center);
width = getVal(width);
height = getVal(height);
this.ctx.beginPath(); this.ctx.beginPath();
this.ctx.rect(center.x - width / 2, center.y - height / 2, width, height); this.ctx.rect(center.x - width / 2, center.y - height / 2, width, height);
if (this._stroke) this.ctx.stroke(); if (this._stroke) this.ctx.stroke();
if (this._fill) this.ctx.fill(); if (this._fill) this.ctx.fill();
} }
drawPolygon(center, points) {
center = getVal(center);
this.ctx.beginPath();
this.ctx.moveTo(center.x + points[0].x, center.y + points[0].y);
for (let i = 1; i < points.length; i++) {
this.ctx.lineTo(center.x + points[i].x, center.y + points[i].y);
}
this.ctx.lineTo(center.x + points[0].x, center.y + points[0].y);
if (this._stroke) this.ctx.stroke();
if (this._fill) this.ctx.fill();
}
} }
// singleton (for now)
export const artworld = new World();

View File

@ -1,55 +0,0 @@
import {
World,
Color,
Pico8,
Group,
Circle,
Random,
Vector2,
degToRad,
} from "../artworld/index.js";
let world = new World("mainCanvas");
window.world = world;
class Ball extends Circle {
constructor() {
super(world);
this.speed = new Vector2(0, Random.between(9, 15));
}
update() {
this.move(this.speed);
if (this.worldPos.y > world.height + 20) {
this.y(-20);
}
}
}
class GravityBall extends Circle {
constructor() {
super(world);
this.accel = new Vector2(0, 0.5);
this.speed = Vector2.random(0, 5);
}
update() {
this.speed = this.speed.add(this.accel);
this.move(this.speed);
if (this.worldPos.y > world.height - 10) {
this.speed = this.speed.scale(-0.98); // bounce w/ dampening
this.y(world.height - 10.01);
}
}
}
for (let i = 0; i < 21; i++) {
new Ball()
.pos(new Vector2(40 * i, 0))
.radius(10)
.fill(Pico8.BLUE);
new GravityBall()
.pos(new Vector2(20 + 40 * i, 0))
.radius(10)
.fill(Pico8.PURPLE);
}
world.loopStep();

38
examples/circles.html Normal file
View File

@ -0,0 +1,38 @@
<html>
<head> </head>
<body>
<canvas id="mainCanvas" width="500" height="500"></canvas>
<script type="module">
import {
artworld,
Color,
Pico8,
Group,
Circle,
Random,
Vector2,
degToRad,
} from "../artworld/index.js";
artworld.bindCanvas("mainCanvas");
window.artworld = artworld;
function* color_cycle() {
while (true) {
yield Pico8.RED;
yield Pico8.ORANGE;
yield Pico8.YELLOW;
}
}
let cycle = color_cycle();
let g = new Group().pos(new Vector2(250, 250));
for (let r = 20; r < 100; r += 12) {
new Circle(g).radius(r).fill(cycle.next().value).z(-r).strokeWeight(0);
}
for (let r = 100; r < 250; r += 12) {
new Circle(g).radius(r).fill(cycle.next().value).z(-r).strokeWeight(0);
}
artworld.draw();
</script>
</body>
</html>

View File

@ -1,18 +0,0 @@
import { World, Pico8, Group, Circle, Vector2 } from "../artworld/index.js";
let world = new World("mainCanvas");
window.world = world;
function* color_cycle() {
while (true) {
yield Pico8.RED;
yield Pico8.ORANGE;
yield Pico8.YELLOW;
}
}
let cycle = color_cycle();
let g = new Group(world);
for (let r = 20; r < 500; r += 20) {
new Circle(g).radius(r).fill(cycle.next().value).z(-r).strokeWeight(0);
}
world.draw();

View File

@ -1,51 +0,0 @@
<html>
<head> </head>
<body>
<canvas id="mainCanvas" width="500" height="500"></canvas>
<script type="module">
import {
artworld,
Color,
Pico8,
Group,
Rect,
Random,
Vector2,
Line,
Circle,
degToRad,
} from "../artworld/index.js";
artworld.bindCanvas("mainCanvas");
window.artworld = artworld;
function colorFunc(t) {
return [Pico8.RED, Pico8.ORANGE, Pico8.GREEN, Pico8.BLUE][
Math.floor(t / 60) % 4
];
}
function sizeFuncFactory(minSize, factor) {
return (t) => Math.sin(t / 100) * factor + minSize;
}
function timeAngle(t) {
return (t / 6000) * Math.PI * 2;
}
let g = new Group().pos(new Vector2(250, 250));
new Circle(g)
.fill(Pico8.BLACK)
.z(1)
.animate("radius", sizeFuncFactory(200, 50));
new Circle(g)
.z(10)
.animate("fill", colorFunc)
.animate("radius", sizeFuncFactory(190, 50));
new Circle(g).radius(20).fill(Pico8.BLACK).z(50);
new Line(g).to(Vector2.polar(200, 0)).z(100).animate("angle", timeAngle);
artworld.loopStep();
</script>
</body>
</html>

28
examples/copies.html Normal file
View File

@ -0,0 +1,28 @@
<html>
<head> </head>
<body>
<canvas id="mainCanvas" width="500" height="500"></canvas>
<script type="module">
import {
artworld,
Color,
Pico8,
Group,
Circle,
Random,
Vector2,
} from "../artworld/index.js";
artworld.bindCanvas("mainCanvas");
window.artworld = artworld;
let g = new Group().pos(new Vector2(0, 0));
let c = new Circle(g).radius(80).stroke(Pico8.RED).strokeWeight(5);
for (let i = 0; i < 15; i++) {
c = c.copy().move(new Vector2(45, 45));
}
g.copy().move(new Vector2(200, 0)).stroke(Pico8.GREEN);
g.copy().move(new Vector2(-200, 0)).stroke(Pico8.BLUE);
artworld.draw();
</script>
</body>
</html>

View File

@ -1,20 +0,0 @@
import {
World,
Color,
Pico8,
Group,
Circle,
Random,
Vector2,
} from "../artworld/index.js";
let world = new World("mainCanvas");
window.world = world;
let g = new Group(world).pos(new Vector2(0, 0));
let c = new Circle(g).radius(80).stroke(Pico8.RED).strokeWeight(5);
for (let i = 0; i < 15; i++) {
c = c.copy().move(new Vector2(45, 45));
}
g.copy().move(new Vector2(200, 0)).stroke(Pico8.GREEN);
g.copy().move(new Vector2(-200, 0)).stroke(Pico8.BLUE);
world.draw();

View File

@ -1,41 +0,0 @@
import {
World,
Pico8,
Group,
Vector2,
Line,
Circle,
} from "../artworld/index.js";
let world = new World("mainCanvas");
window.world = world;
// This is an experiment to separate the declaration of
// "what the data does" from "what it looks like".
// First we use `Var` to define "what the data does"
// variables are expected to regularly *vary*!
// Variables are defined as functions that update with time.
// It is also possible to define static variables.
let color = world.var(
(t) =>
[Pico8.RED, Pico8.ORANGE, Pico8.GREEN, Pico8.BLUE][Math.floor(t / 60) % 4],
);
// We can create dependencies between them: here the inner & outer
// radii should be 10 pixels apart.
let outerR = world.var((t) => Math.sin(t / 100) * 50 + 200);
let innerR = world.var(() => outerR.getValue() - 10);
// We take this composition a bit further here, the radius of the clock
// hand grows & shrinks with the edge.
let lineTo = world.var((t) => {
let angle = (t / 6000) * Math.PI * 2;
return Vector2.polar(innerR.getValue(), angle);
});
let g = new Group(world);
new Circle(g).fill(Pico8.BLACK).z(1).radius(outerR);
new Circle(g).z(10).fill(color).radius(innerR);
new Circle(g).radius(20).fill(Pico8.BLACK).z(50);
new Line(g).to(lineTo).z(100);
world.loopStep();

View File

@ -1,113 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>JS Module Loader</title>
<style>
body {
display: flex;
font-family: Arial, sans-serif;
height: 100vh;
margin: 0;
}
#sidebar {
width: 200px;
background: #f4f4f4;
padding: 10px;
overflow-y: auto;
}
#file-list button {
display: block;
width: 100%;
margin: 5px 0;
padding: 10px;
text-align: left;
border: none;
background: #ddd;
cursor: pointer;
}
#file-list button:hover {
background: #bbb;
}
#main {
flex-grow: 1;
display: flex;
flex-direction: column;
padding: 10px;
}
#mainCanvas {
width: 500px;
height: 500px;
background: #eee;
flex: 1;
}
#output {
white-space: pre-wrap;
background: #222;
color: #ffb300;
padding: 10px;
overflow: auto;
font-family: monospace;
flex: 1;
max-height: 50%;
}
</style>
</head>
<body>
<div id="sidebar">
<h3>Files</h3>
<div id="file-list"></div>
</div>
<div id="main">
<canvas id="mainCanvas"></canvas>
<div id="output">Script output will appear here...</div>
</div>
<script>
const jsFiles = [
"balls.js",
"circles.js",
"copies.js",
"dataclock.js",
"lines.js",
"liskov.js",
"polygons.js",
"rect.js",
"spiral.js",
];
async function loadAndRunModule(fileName) {
const canvas = document.getElementById("mainCanvas");
const outputDiv = document.getElementById("output");
// Clear the canvas
const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
outputDiv.textContent = `Loading ${fileName}...\n`;
try {
// import the module dynamically with cache busting
const module = await import(`./${fileName}?t=${Date.now()}`);
// TODO: get source code
} catch (err) {
outputDiv.textContent += `Error loading ${fileName}:\n${err}`;
}
}
function renderFileList() {
const fileListDiv = document.getElementById("file-list");
jsFiles.forEach((file) => {
const btn = document.createElement("button");
btn.textContent = file;
btn.onclick = () => loadAndRunModule(file);
fileListDiv.appendChild(btn);
});
}
renderFileList();
</script>
</body>
</html>

19
examples/lines.html Normal file
View File

@ -0,0 +1,19 @@
<html>
<head> </head>
<body>
<canvas id="mainCanvas" width="800" height="800"></canvas>
<script type="module">
import { artworld, Color, Line, Vector2 } from "../artworld/index.js";
artworld.bindCanvas("mainCanvas");
window.artworld = artworld;
for (let i = 0; i < 10; i++) {
new Line()
.pos(new Vector2(5 * i * 14, 5))
.to(new Vector2(5 * i * 14, 140))
.stroke(new Color(20 * i, 50, 50))
.strokeWeight(1 + i);
}
artworld.draw();
</script>
</body>
</html>

View File

@ -1,11 +0,0 @@
import { World, Color, Line, Vector2 } from "../artworld/index.js";
let world = new World("mainCanvas");
window.world = world;
for (let i = 0; i < 10; i++) {
new Line(world)
.pos(new Vector2(5 * i * 14, 5))
.to(new Vector2(5 * i * 14, 140))
.stroke(new Color(20 * i, 50, 50))
.strokeWeight(1 + i);
}
world.draw();

View File

@ -1,10 +0,0 @@
import { World, Rect, Arc, Line, Circle, Random } from "../artworld/index.js";
let world = new World("mainCanvas");
let types = [Rect, Line, Circle, Arc];
for (let i = 0; i < 100; i++) {
let Cls = Random.choice(types);
new Cls(world).random().strokeWeight(2);
}
world.draw();

View File

@ -1,9 +0,0 @@
import { World, Polygon } from "../artworld/index.js";
let world = new World("mainCanvas");
for (let i = 1; i < 8; i++) {
new Polygon(world).random(3 + i);
new Polygon(world).random(i * 10);
}
world.draw();

View File

@ -1,11 +0,0 @@
import { World, Pico8, Rect } from "../artworld/index.js";
let world = new World("mainCanvas");
window.world = world;
for (let i = 0; i < 50; i++) {
new Rect(world).random(200).fill(Pico8.BLACK).z(10);
new Rect(world).random(150).fill(Pico8.DARK_BLUE).z(15);
new Rect(world).random(100).fill(Pico8.DARK_GREY).z(20);
new Rect(world).random(50).fill(Pico8.LIGHT_GREY).z(30);
}
world.draw();

28
examples/rects.html Normal file
View File

@ -0,0 +1,28 @@
<html>
<head> </head>
<body>
<canvas id="mainCanvas" width="500" height="500"></canvas>
<script type="module">
import {
artworld,
Color,
Pico8,
Group,
Rect,
Random,
Vector2,
degToRad,
} from "../artworld/index.js";
artworld.bindCanvas("mainCanvas");
window.artworld = artworld;
for (let i = 0; i < 50; i++) {
new Rect().random(200).fill(Pico8.BLACK).z(10);
new Rect().random(150).fill(Pico8.DARK_BLUE).z(15);
new Rect().random(100).fill(Pico8.DARK_GREY).z(20);
new Rect().random(50).fill(Pico8.LIGHT_GREY).z(30);
}
artworld.draw();
</script>
</body>
</html>

58
examples/spiral.html Normal file
View File

@ -0,0 +1,58 @@
<html>
<head> </head>
<body>
<canvas id="mainCanvas" width="800" height="800"></canvas>
<script type="module">
import {
artworld,
Color,
Group,
Line,
Random,
Vector2,
degToRad,
} from "../artworld/index.js";
artworld.bindCanvas("mainCanvas");
window.artworld = artworld;
function* spirals() {
while (true) {
let g = new Group();
for (let d = 0; d < 180; d += 10) {
if (Random.chance(0.9)) {
new Line(g).to(Vector2.polar(190 - d, degToRad(d)));
}
}
yield g;
}
}
function makeGrid(iterable, rows, cols, opts) {
opts = opts || {};
let width = opts.width || 50;
let height = opts.height || 50;
let xOff = opts.xOff || 0;
let yOff = opts.yOff || 0;
for (let c = 0; c < cols; c++) {
for (let r = 0; r < rows; r++) {
let result = iterable.next().value;
if (result === undefined) {
return;
} else {
result.pos(new Vector2(width * c + xOff, height * r + yOff));
}
}
}
}
makeGrid(spirals(), 4, 3, {
width: 220,
height: 120,
xOff: 80,
yOff: 10,
});
artworld.draw();
</script>
</body>
</html>

View File

@ -1,51 +0,0 @@
import {
World,
Pico8,
Group,
Line,
Random,
Vector2,
degToRad,
} from "../artworld/index.js";
const world = new World("mainCanvas");
window.world = world;
function* spirals() {
while (true) {
let g = new Group(world);
let rc = Random.choice(Object.values(Pico8));
for (let d = 0; d < 180; d += 10) {
if (Random.chance(0.9)) {
new Line(g).to(Vector2.polar(190 - d, degToRad(d))).stroke(rc);
}
}
yield g;
}
}
function makeGrid(iterable, rows, cols, opts) {
opts = opts || {};
let width = opts.width || 50;
let height = opts.height || 50;
let xOff = opts.xOff || 0;
let yOff = opts.yOff || 0;
for (let c = 0; c < cols; c++) {
for (let r = 0; r < rows; r++) {
let result = iterable.next().value;
if (result === undefined) {
return;
} else {
result.pos(new Vector2(width * c + xOff, height * r + yOff));
}
}
}
}
makeGrid(spirals(), 4, 3, {
width: 220,
height: 120,
xOff: 80,
yOff: 10,
});
world.draw();