From 93eea321a8dffe3402e0bbb71488ba1e03c92ba3 Mon Sep 17 00:00:00 2001 From: Sunpy Date: Tue, 21 Apr 2020 00:03:26 +0200 Subject: [PATCH] First commits be like --- .gitignore | 1 + sunpy/canvas/renderer.js | 104 +++++++++++++++++++++++++++ sunpy/core/utils.js | 3 + sunpy/core/vector.js | 116 +++++++++++++++++++++++++++++++ sunpy/node/node.js | 26 +++++++ sunpy/node/nodes/basenode.js | 5 ++ sunpy/node/nodes/propertynode.js | 22 ++++++ sunpy/node/nodes/swapnode.js | 35 ++++++++++ sunpy/node/propertyreader.js | 69 ++++++++++++++++++ sunpy/orep/orep.js | 13 ++++ tests/init.html | 31 +++++++++ tests/render.html | 15 ++++ 12 files changed, 440 insertions(+) create mode 100644 .gitignore create mode 100644 sunpy/canvas/renderer.js create mode 100644 sunpy/core/utils.js create mode 100644 sunpy/core/vector.js create mode 100644 sunpy/node/node.js create mode 100644 sunpy/node/nodes/basenode.js create mode 100644 sunpy/node/nodes/propertynode.js create mode 100644 sunpy/node/nodes/swapnode.js create mode 100644 sunpy/node/propertyreader.js create mode 100644 sunpy/orep/orep.js create mode 100644 tests/init.html create mode 100644 tests/render.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dbe9c82 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.vscode/ \ No newline at end of file diff --git a/sunpy/canvas/renderer.js b/sunpy/canvas/renderer.js new file mode 100644 index 0000000..0d74f75 --- /dev/null +++ b/sunpy/canvas/renderer.js @@ -0,0 +1,104 @@ +import { register } from "../node/node.js"; +import { PropertyNode } from "../node/nodes/propertynode.js"; +import { PropertyReader, Property } from "../node/propertyreader.js"; +import { sleep } from "../core/utils.js"; +import { Vector } from "../core/vector.js"; + +register("canvas", initRenderNode); + +function initRenderNode(node) { + const renderNode = new RenderNode(node); + renderNode.start(); + window.a = renderNode; +} + +const propertyReader = new PropertyReader( + new Property("size", "800x600", (v) => new Vector(Int32Array, ...v.split("x").map(x => parseInt(x)))), + new Property("fps", "60", (v) => parseInt(v)), +); + +export class RenderNode extends PropertyNode { + constructor(node) { + super(node, document.createElement("canvas"), propertyReader); + + this.gl = this.node.getContext("webgl"); + + if (!this.gl) + throw Error("Unable to initialize webgl"); + + this.gl.clearDepth(1.0); + this.gl.enable(this.gl.DEPTH_TEST); + this.gl.depthFunc(this.gl.LEQUAL); + + this.fps = 0; + this.objects = []; + this.running = false; + } + + get width() { + return this.node.width; + } + + get height() { + return this.node.height; + } + + start() { + this.running = true; + this.loop(); + } + + stop() { + this.running = false; + } + + add(object) { + if (!(object instanceof Renderable)) + throw TypeError("Object must be instance of Renderable"); + + this.objects.push(object); + } + + async loop() { // TODO: Fix all variables + let ts = Date.now(); + let tsf = ts; + let frames = 0; + while (this.running) { + await this.render(); + + let tsn = Date.now(); + let ms = tsn - ts; // delta + let s = 1000 / this.properties.fps - ms; + ts = tsn + s; + + frames++; + if ((frames %= this.properties.fps) == 0) { + this.fps = (1000 / (tsn - tsf)) * this.properties.fps; + tsf = tsn; + //console.log(this.node, this.fps); + } + + await sleep(s); + } + } + + async render() { + + this.gl.clearColor(0.0, 0.0, 0.0, 1.0); + this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); + + for (const object of this.objects) { + await object.render(this); + } + } +} + +export class Renderable { + constructor() { + + } + + async render(renderNode) { + + } +} \ No newline at end of file diff --git a/sunpy/core/utils.js b/sunpy/core/utils.js new file mode 100644 index 0000000..37e04c0 --- /dev/null +++ b/sunpy/core/utils.js @@ -0,0 +1,3 @@ +export function sleep(ms) { + return new Promise(_ => setTimeout(_, ms)); +} \ No newline at end of file diff --git a/sunpy/core/vector.js b/sunpy/core/vector.js new file mode 100644 index 0000000..30a0638 --- /dev/null +++ b/sunpy/core/vector.js @@ -0,0 +1,116 @@ +const DIM_NAMES = ["xr", "yg", "zb", "wa"]; +const ARRAY_TYPES = [ + Int8Array, Int16Array, Int32Array, + Uint8Array, Uint16Array, Uint32Array, + Float32Array, Float64Array, + BigInt64Array, BigUint64Array // Note: These requires values to be of type BigInt +]; + +export class Vector { + constructor(...values) { + this.type = Float32Array; + + for (const value of values) { + if (ARRAY_TYPES.includes(value)) { + this.type = value; + break; + } + } + + this.values = new this.type(values.filter(v => !ARRAY_TYPES.includes(v))); + + const defineMacro = (index, key) => { + Object.defineProperty(this, key, { + get() { return this.values[index] }, + set(value) { this.values[index] = value } + }); + }; + + for (const i in this.values) { + defineMacro(i, i); + + if (i >= DIM_NAMES.length) + continue; + + for (const names of DIM_NAMES[i]) { + for (const char of names) { + defineMacro(i, char); + } + } + } + } + + clone() { + return new Vector(this.type, ...this.values); + } + + static _requireSameVector(vecA, vecB) { + if (!(vecA instanceof Vector)) + throw new TypeError("vecA has to be instance of `Vector`"); + + if (!(vecB instanceof Vector)) + throw new TypeError("vecB has to be instance of `Vector`"); + + if (vecA.values.length !== vecB.values.length) + throw new TypeError("Vectors are of different lengths"); + + if (vecA.type !== vecB.type) + console.warn("Vectors are not of same array type.. be careful!"); + } + + _modifyComponents(vec, func) { + if (vec instanceof Vector) { + Vector._requireSameVector(this, vec); + + for (const i in vec.values) { + this[i] = func(this[i], vec[i]); + } + + return this; + } + + for (const i in this.values) { + this[i] = func(this[i], vec); // vec is number here + } + + return this; + } + + // Basic operations + add = (vec) => this._modifyComponents(vec, (a, b) => a + b); + subtract = (vec) => this._modifyComponents(vec, (a, b) => a - b); + multiply = (vec) => this._modifyComponents(vec, (a, b) => a * b); + divide = (vec) => this._modifyComponents(vec, (a, b) => a / b); + + // Basic algebra + length() { + return Math.sqrt( this.values.map(v => v * v).reduce((a, b) => a + b, 0) ); + } + + static normalize(vec) { + return vec.clone().divide(vec.length()); + } + + static distance(vecA, vecB) { + Vector._requireSameVector(vecA, vecB); + return Math.sqrt( vecA.values.map((v, i) => v - vecB[i]).map(v => v * v).reduce((a, b) => a + b, 0) ); + } + + static dot(vecA, vecB) { + Vector._requireSameVector(vecA, vecB); + return vecA.values.map((v, i) => v * vecB[i]).reduce((a, b) => a + b, 0); + } + + static cross(vecA, vecB) { + Vector._requireSameVector(vecA, vecB); + + if (vecA.values.length !== 3) + throw TypeError(`Vector3 required, got Vector${vecA.values.length}`); + + return new Vector(vecA.type, + vecA.y * vecB.z - vecA.z * vecB.y, + vecA.z * vecB.x - vecA.x * vecB.z, + vecA.x * vecB.y - vecA.y * vecB.x + ); + } +} \ No newline at end of file diff --git a/sunpy/node/node.js b/sunpy/node/node.js new file mode 100644 index 0000000..c142597 --- /dev/null +++ b/sunpy/node/node.js @@ -0,0 +1,26 @@ +const handlers = {}; + +var onloadOld = onload || (() => {}); +onload = () => { + onloadOld(); + init(); +} + +function init() { + for(const k in handlers) { + for (const n of getNodes(k)) { + handlers[k](n); + } + } +} + +function getNodes(scriptType) { + return Array.from( document.querySelectorAll(`script[type="orep/${scriptType}"]`) ).filter(n => !n.dataset.handled); +} + +export function register(scriptType, handler) { + if (scriptType in handlers) + throw new Error(`'${scriptType}' is already registered`); + + handlers[scriptType] = handler; +} \ No newline at end of file diff --git a/sunpy/node/nodes/basenode.js b/sunpy/node/nodes/basenode.js new file mode 100644 index 0000000..3e50682 --- /dev/null +++ b/sunpy/node/nodes/basenode.js @@ -0,0 +1,5 @@ +export class BaseNode { + constructor(node) { + this.node = node; + } +} \ No newline at end of file diff --git a/sunpy/node/nodes/propertynode.js b/sunpy/node/nodes/propertynode.js new file mode 100644 index 0000000..051b71f --- /dev/null +++ b/sunpy/node/nodes/propertynode.js @@ -0,0 +1,22 @@ +import { SwapNode } from "./swapnode.js"; +import { PropertyReader } from "../propertyreader.js"; + +export class PropertyNode extends SwapNode { + constructor(node, swapNode, propertyReader) { + super(node, swapNode); + + if (!(propertyReader instanceof PropertyReader)) + throw TypeError("propertyReader must be an instance of PropertyReader"); + + this.propertyReader = propertyReader; + + super.replaceNodes(); + } + + /** + * @override + */ + linker(node, swapNode) { + this.properties = this.propertyReader.read(node.innerText); + } +} \ No newline at end of file diff --git a/sunpy/node/nodes/swapnode.js b/sunpy/node/nodes/swapnode.js new file mode 100644 index 0000000..d698447 --- /dev/null +++ b/sunpy/node/nodes/swapnode.js @@ -0,0 +1,35 @@ +import { BaseNode } from "./basenode.js"; + +export class SwapNode extends BaseNode { + constructor(node, swapNode) { + super(node); + + if (!(swapNode instanceof Node)) + throw TypeError("swapNode must be instance of Node"); + + this.swapNode = swapNode; + + this.hasReplaceNodes = false; + } + + replaceNodes() { + if (this.hasReplaceNodes) + return false; + + this.linker(this.node, this.swapNode); + + this.node.parentNode.replaceChild(this.swapNode, this.node); + this.node = this.swapNode; + + delete this.swapNode; + + return this.hasReplaceNodes = true; + } + + /** + * Override this to have access to the script tag that gets converted to the new node inplace + * @param {Node} node Node of the original script tag + * @param {Node} swapNode Node of the new element that replaces the script tag inline + */ + linker(node, swapNode) {} +} \ No newline at end of file diff --git a/sunpy/node/propertyreader.js b/sunpy/node/propertyreader.js new file mode 100644 index 0000000..c81c059 --- /dev/null +++ b/sunpy/node/propertyreader.js @@ -0,0 +1,69 @@ +class PropertyList { + constructor(...properties) { + for (const property of properties) { + if (!(property instanceof Property)) + throw TypeError("properties has to be instances of Property"); + } + + this.properties = []; + + for (const property of properties) { + this.add(property); + } + } + + add(property) { + Object.defineProperty(this, property.key, { + get() { return property.value }, + set(value) { property.parse(value) } + }); + + this.properties.push(property); + } +} + +export class Property { + constructor(key, defaultValue = null, parser = (v) => v) { + this.key = key; + this.defaultValue = defaultValue; + this.parser = parser; + + this.parse(this.defaultValue); + } + + parse(v) { + this.value = this.parser(v); + return this.value; + } + + clone() { + return new Property(this.key, this.defaultValue, this.parser); + } +} + +/** Regex attempts (first to final) + * ^\s*({0})\s*(=|:)\s*(.*)$ + * ^\s*({0})\s*(=|:)\s*([^#\n]*).*$ + * ^\s*({0})\s*(=|:)\s*(.*?)((#|\/\/).*)?$ + */ + +export class PropertyReader { + constructor(...properties) { + this.propertyList = new PropertyList(...properties); + this.regex = new RegExp(`^\\s*(${this.propertyList.properties.map(p => p.key).join("|")})\\s*(=|:)\\s*(.*?)((#|\\/\\/).*)?$`, "mg"); + } + + read(str) { + let match = null; + while ((match = this.regex.exec(str)) !== null) { + if (match.index === this.regex.lastIndex) // Infinite loop fix + this.regex.lastIndex++; + + const key = match[1]; + const value = match[3].trim(); + this.propertyList[key] = value; + } + + return this.propertyList; + } +} \ No newline at end of file diff --git a/sunpy/orep/orep.js b/sunpy/orep/orep.js new file mode 100644 index 0000000..866fa19 --- /dev/null +++ b/sunpy/orep/orep.js @@ -0,0 +1,13 @@ +import { RenderNode } from "../canvas/renderer.js"; + +/* +var onloadOld = onload || (() => {}); +onload = () => { + onloadOld(); + init(); +} + +function init() { + +} +*/ \ No newline at end of file diff --git a/tests/init.html b/tests/init.html new file mode 100644 index 0000000..5cd3603 --- /dev/null +++ b/tests/init.html @@ -0,0 +1,31 @@ + + + + + + Tests - init + + + + + + + + + \ No newline at end of file diff --git a/tests/render.html b/tests/render.html new file mode 100644 index 0000000..3c9fd5a --- /dev/null +++ b/tests/render.html @@ -0,0 +1,15 @@ + + + + + + Tests - render + + + + + + \ No newline at end of file