From c24bd4571866b672ccc6ee27d4f380e2e9f5cdaf Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 7 Feb 2023 11:20:07 -0700 Subject: [PATCH] basic drawing and initialization --- .vscode/settings.json | 24 +++ bundle.js | 390 ++++++++++++++++++++++++++++++++++++++++++ canvas.ts | 184 ++++++++++++++++++++ deno.jsonc | 14 ++ geometry/constants.ts | 3 + geometry/vector.ts | 275 +++++++++++++++++++++++++++++ global.d.ts | 11 ++ index.html | 12 ++ main.ts | 26 +++ mod.ts | 8 +- 10 files changed, 944 insertions(+), 3 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 bundle.js create mode 100644 canvas.ts create mode 100644 deno.jsonc create mode 100644 geometry/constants.ts create mode 100644 geometry/vector.ts create mode 100644 global.d.ts create mode 100644 index.html create mode 100644 main.ts diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1cdbfba --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,24 @@ +{ + "workbench.colorCustomizations": { + "activityBar.activeBackground": "#d816d8", + "activityBar.background": "#d816d8", + "activityBar.foreground": "#e7e7e7", + "activityBar.inactiveForeground": "#e7e7e799", + "activityBarBadge.background": "#caca15", + "activityBarBadge.foreground": "#15202b", + "commandCenter.border": "#e7e7e799", + "sash.hoverBorder": "#d816d8", + "statusBar.background": "#aa11aa", + "statusBar.foreground": "#e7e7e7", + "statusBarItem.hoverBackground": "#d816d8", + "statusBarItem.remoteBackground": "#aa11aa", + "statusBarItem.remoteForeground": "#e7e7e7", + "titleBar.activeBackground": "#aa11aa", + "titleBar.activeForeground": "#e7e7e7", + "titleBar.inactiveBackground": "#aa11aa99", + "titleBar.inactiveForeground": "#e7e7e799" + }, + "peacock.remoteColor": "aa11aa", + "deno.enable": true, + "deno.unstable": true +} \ No newline at end of file diff --git a/bundle.js b/bundle.js new file mode 100644 index 0000000..bcd612c --- /dev/null +++ b/bundle.js @@ -0,0 +1,390 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file +// This code was bundled using `deno bundle` and it's not recommended to edit it manually + +const Constants = { + TWO_PI: Math.PI * 2 +}; +const init = (opt)=>{ + window['doodler'] = new Doodler(opt); + window['doodler'].init(); +}; +class Doodler { + ctx; + _canvas; + layers = []; + bg; + framerate; + get width() { + return this.ctx.canvas.width; + } + get height() { + return this.ctx.canvas.height; + } + constructor({ width , height , canvas , bg , framerate }){ + if (!canvas) { + canvas = document.createElement('canvas'); + document.body.append(canvas); + } + this.bg = bg || 'white'; + this.framerate = framerate || 60; + canvas.width = width; + canvas.height = height; + this._canvas = canvas; + const ctx = canvas.getContext('2d'); + console.log(ctx); + if (!ctx) throw 'Unable to initialize Doodler: Canvas context not found'; + this.ctx = ctx; + } + init() { + this.startDrawLoop(); + } + timer; + startDrawLoop() { + this.timer = setInterval(()=>this.draw(), 1000 / this.framerate); + } + draw() { + this.ctx.fillStyle = this.bg; + this.ctx.fillRect(0, 0, this.width, this.height); + for (const [i, l] of (this.layers || []).entries()){ + l(this.ctx, i); + } + } + createLayer(layer) { + this.layers.push(layer); + } + deleteLayer(layer) { + this.layers = this.layers.filter((l)=>l !== layer); + } + moveLayer(layer, index) { + let temp = this.layers.filter((l)=>l !== layer); + temp = [ + ...temp.slice(0, index), + layer, + ...temp.slice(index) + ]; + this.layers = temp; + } + line(start, end, style) { + this.setStyle(style); + this.ctx.beginPath(); + this.ctx.moveTo(start.x, start.y); + this.ctx.lineTo(end.x, end.y); + this.ctx.stroke(); + } + dot(at, style) { + this.setStyle({ + ...style, + weight: 1 + }); + this.ctx.beginPath(); + this.ctx.arc(at.x, at.y, style?.weight || 1, 0, Constants.TWO_PI); + this.ctx.fill(); + } + drawCircle(at, radius, style) { + this.setStyle(style); + this.ctx.beginPath(); + this.ctx.arc(at.x, at.y, radius, 0, Constants.TWO_PI); + this.ctx.stroke(); + } + fillCircle(at, radius, style) { + this.setStyle(style); + this.ctx.beginPath(); + this.ctx.arc(at.x, at.y, radius, 0, Constants.TWO_PI); + this.ctx.fill(); + } + drawRect(at, width, height, style) { + this.setStyle(style); + this.ctx.strokeRect(at.x, at.y, width, height); + } + fillRect(at, width, height, style) { + this.setStyle(style); + this.ctx.fillRect(at.x, at.y, width, height); + } + drawSquare(at, size, style) { + this.drawRect(at, size, size, style); + } + fillSquare(at, size, style) { + this.fillRect(at, size, size, style); + } + drawCenteredRect(at, width, height, style) { + this.ctx.save(); + this.ctx.translate(-width / 2, -height / 2); + this.drawRect(at, width, height, style); + this.ctx.restore(); + } + fillCenteredRect(at, width, height, style) { + this.ctx.save(); + this.ctx.translate(-width / 2, -height / 2); + this.fillRect(at, width, height, style); + this.ctx.restore(); + } + drawCenteredSquare(at, size, style) { + this.drawCenteredRect(at, size, size, style); + } + fillCenteredSquare(at, size, style) { + this.fillCenteredRect(at, size, size, style); + } + drawBezier(a, b, c, d, style) { + this.setStyle(style); + this.ctx.beginPath(); + this.ctx.moveTo(a.x, a.y); + this.ctx.bezierCurveTo(b.x, b.y, c.x, c.y, d.x, d.y); + this.ctx.stroke(); + } + setStyle(style) { + const ctx = this.ctx; + ctx.fillStyle = style?.color || style?.fillColor || 'black'; + ctx.strokeStyle = style?.color || style?.strokeColor || 'black'; + ctx.lineWidth = style?.weight || 1; + } +} +class Vector { + x; + y; + z; + constructor(x = 0, y = 0, z = 0){ + this.x = x; + this.y = y; + this.z = z; + } + set(v, y, z) { + if (arguments.length === 1 && typeof v !== "number") { + this.set(v.x || v[0] || 0, v.y || v[1] || 0, v.z || v[2] || 0); + } else { + this.x = v; + this.y = y || 0; + this.z = z || 0; + } + } + get() { + return new Vector(this.x, this.y, this.z); + } + mag() { + const x = this.x, y = this.y, z = this.z; + return Math.sqrt(x * x + y * y + z * z); + } + magSq() { + const x = this.x, y = this.y, z = this.z; + return x * x + y * y + z * z; + } + setMag(v_or_len, len) { + if (len === undefined) { + len = v_or_len; + this.normalize(); + this.mult(len); + } else { + const v = v_or_len; + v.normalize(); + v.mult(len); + return v; + } + } + add(v, y, z) { + if (arguments.length === 1 && typeof v !== 'number') { + this.x += v.x; + this.y += v.y; + this.z += v.z; + } else if (arguments.length === 2) { + this.x += v; + this.y += y ?? 0; + } else { + this.x += v; + this.y += y ?? 0; + this.z += z ?? 0; + } + } + sub(v, y, z) { + if (arguments.length === 1 && typeof v !== 'number') { + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + } else if (arguments.length === 2) { + this.x -= v; + this.y -= y ?? 0; + } else { + this.x -= v; + this.y -= y ?? 0; + this.z -= z ?? 0; + } + } + mult(v) { + if (typeof v === 'number') { + this.x *= v; + this.y *= v; + this.z *= v; + } else { + this.x *= v.x; + this.y *= v.y; + this.z *= v.z; + } + return this; + } + div(v) { + if (typeof v === 'number') { + this.x /= v; + this.y /= v; + this.z /= v; + } else { + this.x /= v.x; + this.y /= v.y; + this.z /= v.z; + } + } + rotate(angle) { + const prev_x = this.x; + const c = Math.cos(angle); + const s = Math.sin(angle); + this.x = c * this.x - s * this.y; + this.y = s * prev_x + c * this.y; + } + dist(v) { + const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; + return Math.sqrt(dx * dx + dy * dy + dz * dz); + } + dot(v, y, z) { + if (arguments.length === 1 && typeof v !== 'number') { + return this.x * v.x + this.y * v.y + this.z * v.z; + } + return this.x * v + this.y * y + this.z * z; + } + cross(v) { + const x = this.x, y = this.y, z = this.z; + return new Vector(y * v.z - v.y * z, z * v.x - v.z * x, x * v.y - v.x * y); + } + lerp(v_or_x, amt_or_y, z, amt) { + const lerp_val = (start, stop, amt)=>{ + return start + (stop - start) * amt; + }; + let x, y; + if (arguments.length === 2 && typeof v_or_x !== 'number') { + amt = amt_or_y; + x = v_or_x.x; + y = v_or_x.y; + z = v_or_x.z; + } else { + x = v_or_x; + y = amt_or_y; + } + this.x = lerp_val(this.x, x, amt); + this.y = lerp_val(this.y, y, amt); + this.z = lerp_val(this.z, z, amt); + } + normalize() { + const m = this.mag(); + if (m > 0) { + this.div(m); + } + return this; + } + limit(high) { + if (this.mag() > high) { + this.normalize(); + this.mult(high); + } + } + heading() { + return -Math.atan2(-this.y, this.x); + } + heading2D() { + return this.heading(); + } + toString() { + return "[" + this.x + ", " + this.y + ", " + this.z + "]"; + } + array() { + return [ + this.x, + this.y, + this.z + ]; + } + copy() { + return new Vector(this.x, this.y, this.z); + } + drawDot() { + let doodler1 = window['doodler']; + if (!doodler1) return; + doodler1.dot(this, { + weight: 2, + color: 'red' + }); + } + static fromAngle(angle, v) { + if (v === undefined || v === null) { + v = new Vector(); + } + v.x = Math.cos(angle); + v.y = Math.sin(angle); + return v; + } + static random2D(v) { + return Vector.fromAngle(Math.random() * (Math.PI * 2), v); + } + static random3D(v) { + const angle = Math.random() * Constants.TWO_PI; + const vz = Math.random() * 2 - 1; + const mult = Math.sqrt(1 - vz * vz); + const vx = mult * Math.cos(angle); + const vy = mult * Math.sin(angle); + if (v === undefined || v === null) { + v = new Vector(vx, vy, vz); + } else { + v.set(vx, vy, vz); + } + return v; + } + static dist(v1, v2) { + return v1.dist(v2); + } + static dot(v1, v2) { + return v1.dot(v2); + } + static cross(v1, v2) { + return v1.cross(v2); + } + static add(v1, v2) { + return new Vector(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z); + } + static sub(v1, v2) { + return new Vector(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); + } + static angleBetween(v1, v2) { + return Math.acos(v1.dot(v2) / Math.sqrt(v1.magSq() * v2.magSq())); + } + static lerp(v1, v2, amt) { + const retval = new Vector(v1.x, v1.y, v1.z); + retval.lerp(v2, amt); + return retval; + } + static vectorProjection(v1, v2) { + v2 = v2.copy(); + v2.normalize(); + const sp = v1.dot(v2); + v2.mult(sp); + return v2; + } + static hypot2(a, b) { + return Vector.dot(Vector.sub(a, b), Vector.sub(a, b)); + } +} +init({ + width: 400, + height: 400 +}); +const movingVector = new Vector(100, 300); +doodler.createLayer(()=>{ + doodler.line(new Vector(100, 100), new Vector(200, 200)); + doodler.dot(new Vector(300, 300)); + doodler.fillCircle(movingVector, 6, { + color: 'red' + }); + doodler.drawRect(new Vector(50, 50), movingVector.x, movingVector.y); + doodler.fillRect(new Vector(200, 250), 30, 10); + doodler.drawCenteredSquare(new Vector(200, 200), 40, { + color: 'purple', + weight: 5 + }); + doodler.drawBezier(new Vector(100, 150), movingVector, new Vector(150, 300), new Vector(100, 250)); + movingVector.set((movingVector.x + 1) % 400, movingVector.y); +}); diff --git a/canvas.ts b/canvas.ts new file mode 100644 index 0000000..891d853 --- /dev/null +++ b/canvas.ts @@ -0,0 +1,184 @@ +/// + + +import { Constants } from "./geometry/constants.ts"; +import { Vector } from "./geometry/vector.ts"; + +export const init = (opt: IDoodlerOptions) => { + window['doodler'] = new Doodler(opt); + window['doodler'].init(); +} + +interface IDoodlerOptions { + width: number; + height: number; + canvas?: HTMLCanvasElement; + bg?: string; + framerate?: number; +} + +type layer = (ctx: CanvasRenderingContext2D, index: number) => void; + +export class Doodler { + private ctx: CanvasRenderingContext2D; + private _canvas: HTMLCanvasElement; + + private layers: layer[] = []; + + private bg: string; + private framerate: number; + + get width() { + return this.ctx.canvas.width; + } + get height() { + return this.ctx.canvas.height; + } + + constructor({ + width, + height, + canvas, + bg, + framerate + }: IDoodlerOptions) { + if (!canvas) { + canvas = document.createElement('canvas'); + document.body.append(canvas); + } + + this.bg = bg || 'white'; + this.framerate = framerate || 60; + + canvas.width = width; + canvas.height = height; + + this._canvas = canvas; + + const ctx = canvas.getContext('2d'); + console.log(ctx); + if (!ctx) throw 'Unable to initialize Doodler: Canvas context not found'; + this.ctx = ctx; + } + + init() { + this.startDrawLoop(); + } + + private timer?: number; + private startDrawLoop() { + this.timer = setInterval(() => this.draw(), 1000 / this.framerate); + } + + private draw() { + this.ctx.fillStyle = this.bg; + this.ctx.fillRect(0, 0, this.width, this.height); + for (const [i, l] of (this.layers || []).entries()) { + l(this.ctx, i); + } + } + + createLayer(layer: layer) { + this.layers.push(layer); + } + + deleteLayer(layer: layer) { + this.layers = this.layers.filter(l => l !== layer); + } + + moveLayer(layer: layer, index: number) { + let temp = this.layers.filter(l => l !== layer); + + temp = [...temp.slice(0, index), layer, ...temp.slice(index)]; + + this.layers = temp; + } + + line(start: Vector, end: Vector, style?: IStyle) { + this.setStyle(style); + this.ctx.beginPath(); + this.ctx.moveTo(start.x, start.y); + this.ctx.lineTo(end.x, end.y); + this.ctx.stroke(); + } + dot(at: Vector, style?: IStyle) { + this.setStyle({ ...style, weight: 1 }) + this.ctx.beginPath(); + + this.ctx.arc(at.x, at.y, style?.weight || 1, 0, Constants.TWO_PI); + this.ctx.fill(); + } + drawCircle(at: Vector, radius: number, style?: IStyle) { + this.setStyle(style); + this.ctx.beginPath(); + + this.ctx.arc(at.x, at.y, radius, 0, Constants.TWO_PI); + this.ctx.stroke(); + } + fillCircle(at: Vector, radius: number, style?: IStyle) { + this.setStyle(style); + this.ctx.beginPath(); + + this.ctx.arc(at.x, at.y, radius, 0, Constants.TWO_PI); + this.ctx.fill(); + } + drawRect(at: Vector, width: number, height: number, style?: IStyle) { + this.setStyle(style); + this.ctx.strokeRect(at.x, at.y, width, height); + } + fillRect(at: Vector, width: number, height: number, style?: IStyle) { + this.setStyle(style); + this.ctx.fillRect(at.x, at.y, width, height); + } + drawSquare(at: Vector, size: number, style?: IStyle) { + this.drawRect(at, size, size, style); + } + fillSquare(at: Vector, size: number, style?: IStyle) { + this.fillRect(at, size, size, style); + } + drawCenteredRect(at: Vector, width: number, height: number, style?: IStyle) { + this.ctx.save(); + this.ctx.translate(-width / 2, -height / 2); + this.drawRect(at, width, height, style); + this.ctx.restore(); + } + fillCenteredRect(at: Vector, width: number, height: number, style?: IStyle) { + this.ctx.save(); + this.ctx.translate(-width / 2, -height / 2); + this.fillRect(at, width, height, style); + this.ctx.restore(); + } + drawCenteredSquare(at: Vector, size: number, style?: IStyle) { + this.drawCenteredRect(at, size, size, style); + } + fillCenteredSquare(at: Vector, size: number, style?: IStyle) { + this.fillCenteredRect(at, size, size, style); + } + + drawBezier(a: Vector, b: Vector, c: Vector, d: Vector, style?: IStyle) { + this.setStyle(style); + this.ctx.beginPath(); + this.ctx.moveTo(a.x, a.y); + this.ctx.bezierCurveTo(b.x, b.y, c.x, c.y, d.x, d.y); + this.ctx.stroke(); + } + + setStyle(style?: IStyle) { + const ctx = this.ctx; + ctx.fillStyle = style?.color || style?.fillColor || 'black'; + ctx.strokeStyle = style?.color || style?.strokeColor || 'black'; + + ctx.lineWidth = style?.weight || 1; + } +} + +interface IStyle { + color?: string; + fillColor?: string; + strokeColor?: string; + weight?: number; +} + +interface IDrawable { + draw: () => void; +} diff --git a/deno.jsonc b/deno.jsonc new file mode 100644 index 0000000..c24fed6 --- /dev/null +++ b/deno.jsonc @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "lib": [ + "DOM", + "es2015" + ], + "types": [ + "./global.d.ts" + ] + }, + "tasks": { + "dev" : "deno bundle --watch main.ts bundle.js" + } +} \ No newline at end of file diff --git a/geometry/constants.ts b/geometry/constants.ts new file mode 100644 index 0000000..f619913 --- /dev/null +++ b/geometry/constants.ts @@ -0,0 +1,3 @@ +export const Constants = { + TWO_PI: Math.PI * 2 +} \ No newline at end of file diff --git a/geometry/vector.ts b/geometry/vector.ts new file mode 100644 index 0000000..3d4cf63 --- /dev/null +++ b/geometry/vector.ts @@ -0,0 +1,275 @@ +/// + +import { Constants } from "./constants.ts"; + +export class Vector { + x: number; + y: number; + z: number; + + constructor(x = 0, y = 0, z = 0) { + this.x = x; + this.y = y; + this.z = z; + } + + set(x: number, y: number, z?: number): void; + set(v: Vector): void; + set(v: [number, number, number]): void; + set(v: Vector | [number, number, number] | number, y?: number, z?: number) { + if (arguments.length === 1 && typeof v !== "number") { + this.set((v as Vector).x || (v as Array)[0] || 0, + (v as Vector).y || (v as Array)[1] || 0, + (v as Vector).z || (v as Array)[2] || 0); + } else { + this.x = v as number; + this.y = y || 0; + this.z = z || 0; + } + } + get() { + return new Vector(this.x, this.y, this.z); + } + mag() { + const x = this.x, + y = this.y, + z = this.z; + return Math.sqrt(x * x + y * y + z * z); + } + magSq() { + const x = this.x, + y = this.y, + z = this.z; + return (x * x + y * y + z * z); + } + setMag(len: number): void; + setMag(v: Vector, len: number): Vector + setMag(v_or_len: Vector | number, len?: number) { + if (len === undefined) { + len = v_or_len as number; + this.normalize(); + this.mult(len); + } else { + const v = v_or_len as Vector; + v.normalize(); + v.mult(len); + return v; + } + } + add(x: number, y: number, z: number): void; + add(x: number, y: number): void; + add(v: Vector): void; + add(v: Vector | number, y?: number, z?: number) { + if (arguments.length === 1 && typeof v !== 'number') { + this.x += v.x; + this.y += v.y; + this.z += v.z; + } else if (arguments.length === 2) { + // 2D Vector + this.x += v as number; + this.y += y ?? 0; + } else { + this.x += v as number; + this.y += y ?? 0; + this.z += z ?? 0; + } + } + sub(x: number, y: number, z: number): void; + sub(x: number, y: number): void; + sub(v: Vector): void; + sub(v: Vector | number, y?: number, z?: number) { + if (arguments.length === 1 && typeof v !== 'number') { + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + } else if (arguments.length === 2) { + // 2D Vector + this.x -= v as number; + this.y -= y ?? 0; + } else { + this.x -= v as number; + this.y -= y ?? 0; + this.z -= z ?? 0; + } + } + mult(v: number | Vector) { + if (typeof v === 'number') { + this.x *= v; + this.y *= v; + this.z *= v; + } else { + this.x *= v.x; + this.y *= v.y; + this.z *= v.z; + } + return this; + } + div(v: number | Vector) { + if (typeof v === 'number') { + this.x /= v; + this.y /= v; + this.z /= v; + } else { + this.x /= v.x; + this.y /= v.y; + this.z /= v.z; + } + } + rotate(angle: number) { + const prev_x = this.x; + const c = Math.cos(angle); + const s = Math.sin(angle); + this.x = c * this.x - s * this.y; + this.y = s * prev_x + c * this.y; + } + dist(v: Vector) { + const dx = this.x - v.x, + dy = this.y - v.y, + dz = this.z - v.z; + return Math.sqrt(dx * dx + dy * dy + dz * dz); + } + dot(x: number, y: number, z: number): number; + dot(v: Vector): number; + dot(v: Vector | number, y?: number, z?: number) { + if (arguments.length === 1 && typeof v !== 'number') { + return (this.x * v.x + this.y * v.y + this.z * v.z); + } + return (this.x * (v as number) + this.y * y! + this.z * z!); + } + cross(v: Vector) { + const x = this.x, + y = this.y, + z = this.z; + return new Vector(y * v.z - v.y * z, + z * v.x - v.z * x, + x * v.y - v.x * y); + } + lerp(x: number, y: number, z: number): void; + lerp(v: Vector, amt: number): void; + lerp(v_or_x: Vector | number, amt_or_y: number, z?: number, amt?: number) { + const lerp_val = (start: number, stop: number, amt: number) => { + return start + (stop - start) * amt; + }; + let x, y: number; + if (arguments.length === 2 && typeof v_or_x !== 'number') { + // given vector and amt + amt = amt_or_y; + x = v_or_x.x; + y = v_or_x.y; + z = v_or_x.z; + } else { + // given x, y, z and amt + x = v_or_x as number; + y = amt_or_y; + } + this.x = lerp_val(this.x, x, amt!); + this.y = lerp_val(this.y, y, amt!); + this.z = lerp_val(this.z, z!, amt!); + } + normalize() { + const m = this.mag(); + if (m > 0) { + this.div(m); + } + return this; + } + limit(high: number) { + if (this.mag() > high) { + this.normalize(); + this.mult(high); + } + } + heading() { + return (-Math.atan2(-this.y, this.x)); + } + heading2D() { + return this.heading(); + } + toString() { + return "[" + this.x + ", " + this.y + ", " + this.z + "]"; + } + array() { + return [this.x, this.y, this.z]; + } + + copy() { + return new Vector(this.x, this.y, this.z); + } + + drawDot() { + let doodler = window['doodler']; + if (!doodler) return; + + doodler.dot(this, {weight: 2, color: 'red'}); + } + + static fromAngle(angle: number, v?: Vector) { + if (v === undefined || v === null) { + v = new Vector(); + } + v.x = Math.cos(angle); + v.y = Math.sin(angle); + return v; + } + + static random2D(v?: Vector) { + return Vector.fromAngle(Math.random() * (Math.PI * 2), v); + } + + static random3D(v: Vector) { + const angle = Math.random() * Constants.TWO_PI; + const vz = Math.random() * 2 - 1; + const mult = Math.sqrt(1 - vz * vz); + const vx = mult * Math.cos(angle); + const vy = mult * Math.sin(angle); + if (v === undefined || v === null) { + v = new Vector(vx, vy, vz); + } else { + v.set(vx, vy, vz); + } + return v; + } + + static dist(v1: Vector, v2: Vector) { + return v1.dist(v2); + } + + static dot(v1: Vector, v2: Vector) { + return v1.dot(v2); + } + + static cross(v1: Vector, v2: Vector) { + return v1.cross(v2); + } + + static add(v1: Vector, v2: Vector) { + return new Vector(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z); + } + + static sub(v1: Vector, v2: Vector) { + return new Vector(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); + } + + static angleBetween(v1: Vector, v2: Vector) { + return Math.acos(v1.dot(v2) / Math.sqrt(v1.magSq() * v2.magSq())); + } + + static lerp(v1: Vector, v2: Vector, amt: number) { + // non-static lerp mutates object, but this version returns a new vector + const retval = new Vector(v1.x, v1.y, v1.z); + retval.lerp(v2, amt); + return retval; + } + + static vectorProjection(v1: Vector, v2: Vector) { + v2 = v2.copy(); + v2.normalize(); + const sp = v1.dot(v2); + v2.mult(sp); + return v2; + } + + static hypot2(a: Vector, b: Vector) { + return Vector.dot(Vector.sub(a, b), Vector.sub(a, b)) + } +} diff --git a/global.d.ts b/global.d.ts new file mode 100644 index 0000000..7034e6a --- /dev/null +++ b/global.d.ts @@ -0,0 +1,11 @@ +import { Doodler } from "./canvas.ts"; + +declare global { + interface Window { doodler: Doodler; } + // interface global { + // doodler: Doodler; + // } + let doodler: Doodler; +} + +export{} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..b9cdf36 --- /dev/null +++ b/index.html @@ -0,0 +1,12 @@ + + + + + + + Doodler + + + + + \ No newline at end of file diff --git a/main.ts b/main.ts new file mode 100644 index 0000000..73ed360 --- /dev/null +++ b/main.ts @@ -0,0 +1,26 @@ +import { Doodler } from "./canvas.ts"; +/// + +import { Vector, initializeDoodler } from './mod.ts' + +initializeDoodler({ + width: 400, + height: 400 +}) + +// let doodler = window['doodler']; + +const movingVector = new Vector(100, 300); +doodler.createLayer(() => { + + doodler.line(new Vector(100, 100), new Vector(200, 200)) + doodler.dot(new Vector(300, 300)) + doodler.fillCircle(movingVector, 6, { color: 'red' }); + doodler.drawRect(new Vector(50, 50), movingVector.x, movingVector.y); + doodler.fillRect(new Vector(200, 250), 30, 10) + + doodler.drawCenteredSquare(new Vector(200, 200), 40, { color: 'purple', weight: 5 }) + doodler.drawBezier(new Vector(100, 150), movingVector, new Vector(150, 300), new Vector(100, 250)) + + movingVector.set((movingVector.x + 1) % 400, movingVector.y); +}); diff --git a/mod.ts b/mod.ts index 8721841..dc8291d 100644 --- a/mod.ts +++ b/mod.ts @@ -1,3 +1,5 @@ -export const hello = () => { - console.log('HELLO WORLD'); -} \ No newline at end of file +/// + +export { init as initializeDoodler } from './canvas.ts'; + +export { Vector } from './geometry/vector.ts';