switch to gallery

This commit is contained in:
James Turk 2025-03-12 23:51:24 -05:00
parent aa2885c930
commit 54e75eed31
21 changed files with 354 additions and 352 deletions

10
TODO.md Normal file
View File

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

View File

@ -13,6 +13,8 @@ export class World {
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() {
@ -40,9 +42,12 @@ export class World {
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();
}
this.ctx.restore();
}
tick() {

View File

@ -1,63 +0,0 @@
<html>
<head> </head>
<body>
<canvas id="mainCanvas" width="500" height="500"></canvas>
<script type="module">
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();
</script>
</body>
</html>

55
examples/balls.js Normal file
View File

@ -0,0 +1,55 @@
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();

View File

@ -1,38 +0,0 @@
<html>
<head> </head>
<body>
<canvas id="mainCanvas" width="500" height="500"></canvas>
<script type="module">
import {
World,
Color,
Pico8,
Group,
Circle,
Random,
Vector2,
degToRad,
} 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).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);
}
world.draw();
</script>
</body>
</html>

18
examples/circles.js Normal file
View File

@ -0,0 +1,18 @@
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,28 +0,0 @@
<html>
<head> </head>
<body>
<canvas id="mainCanvas" width="500" height="500"></canvas>
<script type="module">
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();
</script>
</body>
</html>

20
examples/copies.js Normal file
View File

@ -0,0 +1,20 @@
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,57 +0,0 @@
<html>
<head> </head>
<body>
<canvas id="mainCanvas" width="500" height="500"></canvas>
<script type="module">
import {
World,
Color,
Pico8,
Group,
Rect,
Random,
Vector2,
Line,
Circle,
Var,
degToRad,
} 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().pos(new Vector2(250, 250));
world.drawable(g);
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();
</script>
</body>
</html>

41
examples/dataclock.js Normal file
View File

@ -0,0 +1,41 @@
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();

113
examples/gallery.html Normal file
View File

@ -0,0 +1,113 @@
<!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>

View File

@ -1,19 +0,0 @@
<html>
<head> </head>
<body>
<canvas id="mainCanvas" width="800" height="800"></canvas>
<script type="module">
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();
</script>
</body>
</html>

11
examples/lines.js Normal file
View File

@ -0,0 +1,11 @@
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,31 +0,0 @@
<html>
<head> </head>
<body>
<canvas id="mainCanvas" width="500" height="500"></canvas>
<script type="module">
import {
artworld,
Color,
Pico8,
Group,
Rect,
Arc,
Line,
Circle,
Random,
Vector2,
degToRad,
} from "../artworld/index.js";
artworld.bindCanvas("mainCanvas");
window.artworld = artworld;
let types = [Rect, Line, Circle, Arc];
for (let i = 0; i < 100; i++) {
let Cls = Random.choice(types);
new Cls().random().strokeWeight(2);
}
artworld.draw();
</script>
</body>
</html>

10
examples/liskov.js Normal file
View File

@ -0,0 +1,10 @@
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,29 +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,
Polygon,
degToRad,
} from "../artworld/index.js";
artworld.bindCanvas("mainCanvas");
window.artworld = artworld;
for (let i = 1; i < 8; i++) {
let p = new Polygon().random(3 + i);
let p2 = new Polygon().random(i * 10);
// p.animate("point", new_point, to_modify=e)
}
artworld.draw();
</script>
</body>
</html>

9
examples/polygons.js Normal file
View File

@ -0,0 +1,9 @@
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();

11
examples/rect.js Normal file
View File

@ -0,0 +1,11 @@
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();

View File

@ -1,28 +0,0 @@
<html>
<head> </head>
<body>
<canvas id="mainCanvas" width="500" height="500"></canvas>
<script type="module">
import {
World,
Color,
Pico8,
Group,
Rect,
Random,
Vector2,
degToRad,
} 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();
</script>
</body>
</html>

View File

@ -1,59 +0,0 @@
<html>
<head> </head>
<body>
<canvas id="mainCanvas" width="800" height="800"></canvas>
<script type="module">
import {
World,
Color,
Pico8,
Group,
Line,
Random,
Vector2,
degToRad,
} from "../artworld/index.js";
const world = new World("mainCanvas");
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();
</script>
</body>
</html>

51
examples/spiral.js Normal file
View File

@ -0,0 +1,51 @@
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();