commit fc12f05e3a520f38443d1fcc6c6e18732558f87d Author: James Turk Date: Sat Jul 6 14:17:10 2024 -0500 moving Python POC to JS diff --git a/artworld/color.js b/artworld/color.js new file mode 100644 index 0000000..1601d4e --- /dev/null +++ b/artworld/color.js @@ -0,0 +1,51 @@ +export class Color { + static defaultColorMode = "hsl"; + + constructor(p1, p2, p3, alpha = 1, mode = null) { + this.mode = mode || Color.defaultColorMode; + this.p1 = p1; + this.p2 = p2; + this.p3 = p3; + this.alpha = alpha; + } + + toStr() { + return `${this.mode}(${this.p1} ${this.p2} ${this.p3} / ${this.alpha})`; + } + + adjustHue(delta) { + this.p1 += delta; + } + + adjustSat(delta) { + this.p2 += delta; + } + + adjustLum(delta) { + this.p3 += delta; + } + + adjustAlpha(delta) { + this.alpha += alpha; + } +} + +// Palette Source: https://pico-8.fandom.com/wiki/Palette +export const Pico8 = { + BLACK: "rgb(0, 0, 0)", + DARK_BLUE: "rgb(29, 43, 83)", + PURPLE: "rgb(126, 37, 83)", + DARK_GREEN: "rgb(0, 135, 81)", + BROWN: "rgb(171, 82, 54)", + DARK_GREY: "rgb(95, 87, 79)", + LIGHT_GREY: "rgb(194, 195, 199)", + WHITE: "rgb(255, 241, 232)", + RED: "rgb(255, 0, 77)", + ORANGE: "rgb(255, 163, 0)", + YELLOW: "rgb(255, 236, 39)", + GREEN: "rgb(0, 228, 54)", + BLUE: "rgb(41, 173, 255)", + LAVENDER: "rgb(131, 118, 156)", + PINK: "rgb(255, 119, 168)", + LIGHT_PEACH: "rgb(255, 204, 170)", +}; diff --git a/artworld/drawable.js b/artworld/drawable.js new file mode 100644 index 0000000..0eb0855 --- /dev/null +++ b/artworld/drawable.js @@ -0,0 +1,101 @@ +import { Vector2 } from "./math.js"; +import { artworld } from "./world.js"; + +export class Drawable { + constructor(parent) { + this._parent = parent; + this._updates = []; + this._fill = parent ? parent._fill : null; + this._stroke = parent ? parent._stroke : null; + this._strokeWeight = parent ? parent._strokeWeight : null; + this._z_index = parent ? parent._z_index : null; + this._posVec = new Vector2(0, 0); + this._register(); + } + + _register() { + if (this._parent) { + this._parent.push(this); + } + artworld.register(this); + } + + copy() { + let obj = Object.assign({}, this); + obj._register(); + return obj; + } + + fill(color) { + this._fill = color; + return this; + } + + stroke(color) { + this._stroke = color; + return this; + } + + strokeWeight(scalar) { + this._strokeWeight = scalar; + return this; + } + + pos(vec) { + this._posVec = vec; + return this; + } + + x(scalar) { + this._posVec.x = scalar; + return this; + } + + y(scalar) { + this._posVec.y = scalar; + return this; + } + + z(scalar) { + this._z_index = scalar; + return this; + } + + // Modifiers /////////////////////////// + + move(byVec) { + this._posVec = this._posVec.add(byVec); + } + + // TODO random() + // TODO draw() abstract + + // getters ///////////////////////////// + + get worldPos() { + return this._parent ? this._parent.worldPos.add(this._pos) : this._pos; + } +} + +export class Group extends Drawable { + constructor() { + super(); + this._children = []; + } + + copy() { + let newobj = super.copy(); + for (let child of this._children) { + let ccopy = Object.assign({}, child); + ccopy._parent = newobj; + ccopy._register(); + } + return newobj; + } + + add(drawable) { + this._children.push(drawable); + drawable._parent = this; + return this; + } +} diff --git a/artworld/index.js b/artworld/index.js new file mode 100644 index 0000000..f570896 --- /dev/null +++ b/artworld/index.js @@ -0,0 +1,4 @@ +export { artworld } from "./world.js"; +export { Color } from "./color.js"; +export { Vector2 } from "./math.js"; +export { Line } from "./shapes.js"; diff --git a/artworld/math.js b/artworld/math.js new file mode 100644 index 0000000..b9ccc21 --- /dev/null +++ b/artworld/math.js @@ -0,0 +1,37 @@ +export const TO_RADIANS = 180 / Math.PI; + +export class Vector2 { + constructor(x, y) { + this.x = x; + this.y = y; + } + + add(other) { + return Vector2(this.x + other.x, this.y + other.y); + } + + sub(other) { + return Vector2(this.x - other.x, this.y - other.y); + } + + scale(s) { + return Vector2(s * this.x, s * this.y); + } + + static random(x, y) { + let theta = random() * 2 * Math.PI; + // if neither specified, use (1, 1) + if (x === undefined) { + x = 1; + } + // if only x specified, use (x, x) + if (y === undefined) { + y = x; + } + return Vector2(x * cos(theta), y * sin(theta)); + } + + static polar(mag, angle) { + return Vector2(mag * Math.cos(angle), mag * Math.sin(angle)); + } +} diff --git a/artworld/rect.js b/artworld/rect.js new file mode 100644 index 0000000..044c9a8 --- /dev/null +++ b/artworld/rect.js @@ -0,0 +1,17 @@ +class Rectangle { + constructor(x, y, width, height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + contains(x, y) { + return ( + x > this.x && + x < this.x + this.width && + y > this.y && + y < this.y + this.height + ); + } +} diff --git a/artworld/shapes.js b/artworld/shapes.js new file mode 100644 index 0000000..aa58bf9 --- /dev/null +++ b/artworld/shapes.js @@ -0,0 +1,29 @@ +import { Vector2 } from "./math.js"; +import { Drawable } from "./drawable.js"; + +export class Line extends Drawable { + constructor(parent) { + super(parent); + this._offsetVec = new Vector2(10, 0); + } + + draw() { + artworld.setStrokeColor(this._stroke); + artworld.setStrokeWeight(this._strokeWeight); + artworld.drawLine(this._posVec, this._offsetVec); + } + + to(vec) { + this._offsetVec = vec; + return this; + } + + polar(mag, angle) { + return this.to(Vector2.polar(mag, angle)); + } + + angle(angle) { + let mag = this._offsetVec.magnitude; + return this.to(Vector2.polar(mag, angle)); + } +} diff --git a/artworld/world.js b/artworld/world.js new file mode 100644 index 0000000..42d2e66 --- /dev/null +++ b/artworld/world.js @@ -0,0 +1,52 @@ +export class World { + constructor() { + this.drawables = []; + this.ctx = null; + } + + bindCanvas(id) { + this.ctx = document.getElementById(id).getContext("2d"); + } + + register(drawable) { + this.drawables.push(drawable); + } + + draw() { + for (let d of this.drawables) { + d.draw(); + } + } + + // Canvas 2D ///////////////// + + setStrokeWeight(scalar) { + this.ctx.lineWidth = scalar; + } + + setStrokeColor(color) { + this.ctx.strokeStyle = color.toStr(); + } + + setFillColor(color) { + this.ctx.fillStyle = color.toStr(); + } + + fillRect(rect) { + this.ctx.fillRect(rect.x, rect.y, rect.width, rect.height); + } + + strokeRect(rect) { + this.ctx.fillRect(rect.x, rect.y, rect.width, rect.height); + } + + drawLine(from, to) { + this.ctx.beginPath(); + this.ctx.moveTo(from.x, from.y); + this.ctx.lineTo(to.x, to.y); + this.ctx.stroke(); + } +} + +// singleton (for now) +export const artworld = new World(); diff --git a/examples/lines.html b/examples/lines.html new file mode 100644 index 0000000..41b0fb3 --- /dev/null +++ b/examples/lines.html @@ -0,0 +1,19 @@ + + + + + + +