Hosting code to git for the first time.
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
export { Vector2D, Vector3D, Vector4D } from "@engine/math"
|
||||
export { TreeNode } from "@engine/kernel"
|
||||
@@ -0,0 +1 @@
|
||||
export { TreeNode } from "./TreeNode";
|
||||
@@ -0,0 +1,234 @@
|
||||
|
||||
/**
|
||||
* A tree type data structure used to build scene and ...
|
||||
* @copyright MrKBear 2022
|
||||
* @typeParam T - Child type of `TreeNode`
|
||||
*/
|
||||
class TreeNode<T extends TreeNode = TreeNode<any>> implements Iterable<T> {
|
||||
|
||||
/**
|
||||
* Record all child nodes when this node is the root node.
|
||||
* @remarks
|
||||
* Only maintained when the node is the root node. \
|
||||
* The array is arranged in depth first traversal order.
|
||||
*/
|
||||
private _progenyNodes: Array<T> | undefined = [ ];
|
||||
|
||||
/**
|
||||
* Whether the structure of the current tree has changed.
|
||||
* @remarks
|
||||
* Only maintained when the node is the root node.
|
||||
*/
|
||||
private _isStructUpdated = false;
|
||||
|
||||
/**
|
||||
* @remarks
|
||||
* When a node is just created, it defaults the root node. \
|
||||
* So this `rootNode` points to itself.
|
||||
*/
|
||||
private _rootNode: T | this = this;
|
||||
|
||||
private _parentNode: T | undefined = void 0;
|
||||
|
||||
private _childNodes: Array<T> = [];
|
||||
|
||||
/**
|
||||
* Pointer to the root node.
|
||||
*/
|
||||
public get rootNode(): T | this { return this._rootNode; }
|
||||
|
||||
/**
|
||||
* Pointer to the parent node.
|
||||
*/
|
||||
public get parentNode(): T | undefined { return this._parentNode; }
|
||||
|
||||
/**
|
||||
* Points to the children of the current node.
|
||||
*/
|
||||
public get childNodes(): ReadonlyArray<T> { return this._childNodes; }
|
||||
|
||||
/**
|
||||
* Is this a root node?
|
||||
*/
|
||||
public get isRootNode(): boolean { return !this._parentNode && this._rootNode === this; }
|
||||
|
||||
/**
|
||||
* Depth first traverses the current `TreeNode` and its children `TreeNode`.
|
||||
* @remarks
|
||||
* This will use {@link https://en.wikipedia.org/wiki/Tree_traversal#Pre-order,_NLR Pre-order (NLR) algorithm}. \
|
||||
* So... Do not call this getter frequently.
|
||||
* @example
|
||||
* ``` javascript
|
||||
* // ❌ Deprecated implementation !!!
|
||||
* for (let i = 0; i < treeNode.progenyNodes.length; i++) {
|
||||
* doSomeThing(treeNode.progenyNodes[i]);
|
||||
* }
|
||||
*
|
||||
* // ✔️ Best Practices
|
||||
* treeNode.progenyNodes.forEach((progenyNode)=> {
|
||||
* doSomeThing(progenyNode);
|
||||
* });
|
||||
*
|
||||
* // ✔️ Best Practices
|
||||
* for(const progenyNode of treeNode.progenyNodes) {
|
||||
* doSomeThing(progenyNode);
|
||||
* }
|
||||
* ```
|
||||
* @return Progeny nodes list.
|
||||
*/
|
||||
public get progenyNodes(): ReadonlyArray<T> {
|
||||
|
||||
if (this.isRootNode && !this._isStructUpdated && this._progenyNodes) {
|
||||
return this._progenyNodes;
|
||||
}
|
||||
|
||||
else {
|
||||
const cacheArray: Array<T> = [];
|
||||
const stack: Array<T> = this._childNodes.slice().reverse();
|
||||
|
||||
while (stack.length > 0) {
|
||||
const currentNode: T | undefined = stack.pop();
|
||||
|
||||
if (currentNode) {
|
||||
cacheArray.push(currentNode);
|
||||
|
||||
// Push all child nodes in reverse order
|
||||
for (let i = currentNode.childNodes.length - 1; i >= 0; i --) {
|
||||
stack.push(<T>currentNode.childNodes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isRootNode) {
|
||||
this._progenyNodes = cacheArray;
|
||||
this._isStructUpdated = false;
|
||||
}
|
||||
|
||||
return cacheArray;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An iterator implementation for {@link TreeNode.progenyNodes}
|
||||
* @see {@link TreeNode.progenyNodes}
|
||||
*/
|
||||
public *[Symbol.iterator](): Iterator<T> { yield * this.progenyNodes; }
|
||||
|
||||
/**
|
||||
* Remove `nodes` from child nodes.
|
||||
* @param nodes - Nodes to be deleted.
|
||||
* @returns Success Times.
|
||||
*/
|
||||
public removeChild(...nodes: ReadonlyArray<T>): number {
|
||||
|
||||
let successCount = 0;
|
||||
for (const node of nodes) {
|
||||
|
||||
let removeNodesIndex = -1;
|
||||
for (let i = 0; i < this._childNodes.length; i++) {
|
||||
|
||||
if (this._childNodes[i] === node) {
|
||||
removeNodesIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (removeNodesIndex < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Perform the remove operation
|
||||
this._childNodes.splice(removeNodesIndex, 1);
|
||||
|
||||
node._parentNode = void 0;
|
||||
node._rootNode = node;
|
||||
node.markStructUpdated();
|
||||
|
||||
for (const progeny of node.progenyNodes) {
|
||||
progeny._rootNode = node;
|
||||
}
|
||||
|
||||
successCount ++;
|
||||
}
|
||||
|
||||
if (successCount) {
|
||||
this._rootNode.markStructUpdated();
|
||||
}
|
||||
|
||||
return successCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove this node from the parent node
|
||||
* @returns Success or not.
|
||||
*/
|
||||
public removeFromParent(): number {
|
||||
|
||||
if (this.isRootNode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
else {
|
||||
return this._parentNode!.removeChild(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append `nodes` into child nodes.
|
||||
* @param node - Nodes to be added.
|
||||
* @returns Success Times.
|
||||
*/
|
||||
public appendChild(...nodes: ReadonlyArray<T>): number {
|
||||
|
||||
let successCount = 0;
|
||||
for (const node of nodes) {
|
||||
|
||||
if (!node.isRootNode) {
|
||||
node.removeFromParent();
|
||||
}
|
||||
|
||||
for (const progeny of node.progenyNodes) {
|
||||
progeny._rootNode = this._rootNode;
|
||||
}
|
||||
|
||||
node._rootNode = this._rootNode;
|
||||
node._parentNode = this;
|
||||
node._progenyNodes = void 0;
|
||||
|
||||
this._childNodes.push(node);
|
||||
|
||||
successCount ++;
|
||||
}
|
||||
|
||||
if (successCount) {
|
||||
this._rootNode.markStructUpdated();
|
||||
}
|
||||
|
||||
return successCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark that the current tree structure has been updated
|
||||
* @returns true
|
||||
*/
|
||||
public markStructUpdated(): boolean {
|
||||
if (this.isRootNode) {
|
||||
return this._isStructUpdated = true;
|
||||
}
|
||||
else {
|
||||
return this.rootNode.markStructUpdated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class B extends TreeNode<B> { b(){}; }
|
||||
class A extends TreeNode<A> { a(){}; }
|
||||
class C extends A { c(){}; }
|
||||
const a = new A();
|
||||
const b = new B();
|
||||
const c = new C();
|
||||
a.rootNode.rootNode.appendChild(c);
|
||||
// a.rootNode.rootNode.appendChild(a);
|
||||
|
||||
console.log(a);
|
||||
|
||||
export { TreeNode };
|
||||
@@ -0,0 +1,3 @@
|
||||
export { Vector2D } from "./Vector2D";
|
||||
export { Vector3D } from "./Vector3D";
|
||||
export { Vector4D } from "./Vector4D";
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
/**
|
||||
* Type of update status can be recorded
|
||||
* An update flag is used here to implement data lazy calculation
|
||||
* @copyright MrKBear 2022
|
||||
*/
|
||||
abstract class UpdatedRecorder {
|
||||
|
||||
/**
|
||||
* The flag for recorded update status
|
||||
* @defaultValue `true`
|
||||
*/
|
||||
protected updateFlag: boolean = true;
|
||||
|
||||
/**
|
||||
* Initialize and set the default update status
|
||||
* @param updateFlag update status
|
||||
*/
|
||||
public constructor(updateFlag: boolean = true) {
|
||||
this.updateFlag = updateFlag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the update status
|
||||
* @returns update status
|
||||
*/
|
||||
public isUpdated(): boolean {
|
||||
return this.updateFlag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set update status `false`
|
||||
* @returns update status
|
||||
*/
|
||||
public unsetUpdated(): false {
|
||||
return this.updateFlag = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically detect and reset update status
|
||||
* @returns update status
|
||||
*/
|
||||
public testAndUnsetUpdate(): boolean {
|
||||
if (this.updateFlag) {
|
||||
this.updateFlag = false;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { UpdatedRecorder };
|
||||
@@ -0,0 +1,83 @@
|
||||
import { UpdatedRecorder } from "./UpdatedRecorder";
|
||||
|
||||
/**
|
||||
* A vector with two dimensions
|
||||
*/
|
||||
class Vector2D extends UpdatedRecorder implements ArrayLike<number>, Iterable<number> {
|
||||
|
||||
private value0: number = 0;
|
||||
private value1: number = 0;
|
||||
|
||||
readonly [n: number]: number;
|
||||
|
||||
/**
|
||||
* Vector length
|
||||
*/
|
||||
public get length(): number {
|
||||
return 2;
|
||||
};
|
||||
|
||||
public override toString(): string {
|
||||
return `Vector2D(${this.value0},${this.value1})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate this vector
|
||||
*/
|
||||
public *[Symbol.iterator](): Iterator<number> {
|
||||
yield this.value0;
|
||||
yield this.value1;
|
||||
}
|
||||
|
||||
/**
|
||||
* The first component of this vector
|
||||
*/
|
||||
public get [0](): number {
|
||||
return this.value0;
|
||||
}
|
||||
|
||||
public set [0](newValue: number) {
|
||||
if (!this.updateFlag && this.value0 !== newValue) {
|
||||
this.updateFlag = true;
|
||||
}
|
||||
this.value0 = newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* The second component of this vector
|
||||
*/
|
||||
public get [1](): number {
|
||||
return this.value1;
|
||||
}
|
||||
|
||||
public set [1](newValue: number) {
|
||||
if (!this.updateFlag && this.value1 !== newValue) {
|
||||
this.updateFlag = true;
|
||||
}
|
||||
this.value1 = newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* X component of the vector
|
||||
*/
|
||||
public get x(): number {
|
||||
return this.value0;
|
||||
}
|
||||
|
||||
public set x(newValue: number) {
|
||||
this[0] = newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Y component of the vector
|
||||
*/
|
||||
public get y(): number {
|
||||
return this.value1;
|
||||
}
|
||||
|
||||
public set y(newValue: number) {
|
||||
this[1] = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
export { Vector2D };
|
||||
@@ -0,0 +1,110 @@
|
||||
import { UpdatedRecorder } from "./UpdatedRecorder";
|
||||
|
||||
/**
|
||||
* A vector with three dimensions
|
||||
*/
|
||||
class Vector3D extends UpdatedRecorder implements ArrayLike<number>, Iterable<number> {
|
||||
|
||||
private value0: number = 0;
|
||||
private value1: number = 0;
|
||||
private value2: number = 0;
|
||||
|
||||
readonly [n: number]: number;
|
||||
|
||||
/**
|
||||
* Vector length
|
||||
*/
|
||||
public get length(): number {
|
||||
return 3;
|
||||
};
|
||||
|
||||
public override toString(): string {
|
||||
return `Vector3D(${this.value0},${this.value1},${this.value2})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate this vector
|
||||
*/
|
||||
public *[Symbol.iterator](): Iterator<number> {
|
||||
yield this.value0;
|
||||
yield this.value1;
|
||||
yield this.value2;
|
||||
}
|
||||
|
||||
/**
|
||||
* The first component of this vector
|
||||
*/
|
||||
public get [0](): number {
|
||||
return this.value0;
|
||||
}
|
||||
|
||||
public set [0](newValue: number) {
|
||||
if (!this.updateFlag && this.value0 !== newValue) {
|
||||
this.updateFlag = true;
|
||||
}
|
||||
this.value0 = newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* The second component of this vector
|
||||
*/
|
||||
public get [1](): number {
|
||||
return this.value1;
|
||||
}
|
||||
|
||||
public set [1](newValue: number) {
|
||||
if (!this.updateFlag && this.value1 !== newValue) {
|
||||
this.updateFlag = true;
|
||||
}
|
||||
this.value1 = newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* The third component of this vector
|
||||
*/
|
||||
public get [2](): number {
|
||||
return this.value2;
|
||||
}
|
||||
|
||||
public set [2](newValue: number) {
|
||||
if (!this.updateFlag && this.value2 !== newValue) {
|
||||
this.updateFlag = true;
|
||||
}
|
||||
this.value2 = newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* X component of the vector
|
||||
*/
|
||||
public get x(): number {
|
||||
return this.value0;
|
||||
}
|
||||
|
||||
public set x(newValue: number) {
|
||||
this[0] = newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Y component of the vector
|
||||
*/
|
||||
public get y(): number {
|
||||
return this.value1;
|
||||
}
|
||||
|
||||
public set y(newValue: number) {
|
||||
this[1] = newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Z component of the vector
|
||||
*/
|
||||
public get z(): number {
|
||||
return this.value2;
|
||||
}
|
||||
|
||||
public set z(newValue: number) {
|
||||
this[2] = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
export { Vector3D };
|
||||
@@ -0,0 +1,137 @@
|
||||
import { UpdatedRecorder } from "./UpdatedRecorder";
|
||||
|
||||
/**
|
||||
* A vector with four dimensions
|
||||
*/
|
||||
class Vector4D extends UpdatedRecorder implements ArrayLike<number>, Iterable<number> {
|
||||
|
||||
private value0: number = 0;
|
||||
private value1: number = 0;
|
||||
private value2: number = 0;
|
||||
private value3: number = 0;
|
||||
|
||||
readonly [n: number]: number;
|
||||
|
||||
/**
|
||||
* Vector length
|
||||
*/
|
||||
public get length(): number {
|
||||
return 4;
|
||||
};
|
||||
|
||||
public override toString(): string {
|
||||
return `Vector4D(${this.value0},${this.value1},${this.value2},${this.value3})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate this vector
|
||||
*/
|
||||
public *[Symbol.iterator](): Iterator<number> {
|
||||
yield this.value0;
|
||||
yield this.value1;
|
||||
yield this.value2;
|
||||
yield this.value3;
|
||||
}
|
||||
|
||||
/**
|
||||
* The first component of this vector
|
||||
*/
|
||||
public get [0](): number {
|
||||
return this.value0;
|
||||
}
|
||||
|
||||
public set [0](newValue: number) {
|
||||
if (!this.updateFlag && this.value0 !== newValue) {
|
||||
this.updateFlag = true;
|
||||
}
|
||||
this.value0 = newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* The second component of this vector
|
||||
*/
|
||||
public get [1](): number {
|
||||
return this.value1;
|
||||
}
|
||||
|
||||
public set [1](newValue: number) {
|
||||
if (!this.updateFlag && this.value1 !== newValue) {
|
||||
this.updateFlag = true;
|
||||
}
|
||||
this.value1 = newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* The third component of this vector
|
||||
*/
|
||||
public get [2](): number {
|
||||
return this.value2;
|
||||
}
|
||||
|
||||
public set [2](newValue: number) {
|
||||
if (!this.updateFlag && this.value2 !== newValue) {
|
||||
this.updateFlag = true;
|
||||
}
|
||||
this.value2 = newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* The fourth component of this vector
|
||||
*/
|
||||
public get [3](): number {
|
||||
return this.value3;
|
||||
}
|
||||
|
||||
public set [3](newValue: number) {
|
||||
if (!this.updateFlag && this.value3 !== newValue) {
|
||||
this.updateFlag = true;
|
||||
}
|
||||
this.value3 = newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* X component of the vector
|
||||
*/
|
||||
public get x(): number {
|
||||
return this.value0;
|
||||
}
|
||||
|
||||
public set x(newValue: number) {
|
||||
this[0] = newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Y component of the vector
|
||||
*/
|
||||
public get y(): number {
|
||||
return this.value1;
|
||||
}
|
||||
|
||||
public set y(newValue: number) {
|
||||
this[1] = newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Z component of the vector
|
||||
*/
|
||||
public get z(): number {
|
||||
return this.value2;
|
||||
}
|
||||
|
||||
public set z(newValue: number) {
|
||||
this[2] = newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* W component of the vector
|
||||
*/
|
||||
public get w(): number {
|
||||
return this.value3;
|
||||
}
|
||||
|
||||
public set w(newValue: number) {
|
||||
this[3] = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
export { Vector4D };
|
||||
@@ -0,0 +1,7 @@
|
||||
export * from "./command/Command";
|
||||
export * from "./command/CommandSet";
|
||||
export * from "./command/CommandType";
|
||||
export * from "./command/RenderCommand";
|
||||
export * from "./command/ClearCommand";
|
||||
export * from "./resource/Resource";
|
||||
export * from "./resource/ResourceType";
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { Command } from "./Command";
|
||||
import type { CommandType } from "./CommandType";
|
||||
|
||||
interface ClearCommand extends Command {
|
||||
|
||||
type: CommandType.Clear
|
||||
}
|
||||
|
||||
export type { ClearCommand };
|
||||
@@ -0,0 +1,16 @@
|
||||
import type { CommandTypeSet } from "./CommandType";
|
||||
|
||||
interface Command {
|
||||
|
||||
/**
|
||||
* Define the type of command.
|
||||
*/
|
||||
type: CommandTypeSet;
|
||||
|
||||
/**
|
||||
* Unique identification of the command for feedback.
|
||||
*/
|
||||
id?: string | number;
|
||||
}
|
||||
|
||||
export type { Command };
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { RenderCommand } from "./RenderCommand";
|
||||
import type { ClearCommand } from "./ClearCommand";
|
||||
|
||||
type CommandSet = RenderCommand | ClearCommand;
|
||||
|
||||
export type { CommandSet };
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
namespace CommandType {
|
||||
|
||||
export type Render = "RENDER" | 100_001;
|
||||
|
||||
export type Resource = "RESOURCE" | 100_002;
|
||||
|
||||
export type Clear = "CLEAR" | 100_003;
|
||||
|
||||
export type Execute = "EXECUTE" | 100_004;
|
||||
}
|
||||
|
||||
type CommandTypeSet = CommandType.Render | CommandType.Resource | CommandType.Clear | CommandType.Execute;
|
||||
|
||||
export type { CommandType, CommandTypeSet };
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { Command } from "./Command";
|
||||
import type { CommandType } from "./CommandType";
|
||||
|
||||
/**
|
||||
* Rendering command are used to execute gl programs.
|
||||
*/
|
||||
interface RenderCommand extends Command {
|
||||
|
||||
type: CommandType.Render
|
||||
}
|
||||
|
||||
export type { RenderCommand };
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { CommandSet } from "../command/CommandSet"
|
||||
|
||||
interface Renderer {
|
||||
|
||||
executeCommand: (commands: CommandSet[] | CommandSet) => void;
|
||||
}
|
||||
|
||||
export type { Renderer };
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { ResourceSet } from "./ResourceType";
|
||||
|
||||
interface Resource {
|
||||
|
||||
type: ResourceSet;
|
||||
|
||||
id: string | number;
|
||||
}
|
||||
|
||||
export type { Resource };
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
namespace ResourceType {
|
||||
|
||||
export type Shader = "SHADER" | 100_001;
|
||||
}
|
||||
|
||||
type ResourceSet = ResourceType.Shader;
|
||||
|
||||
export type { ResourceType, ResourceSet };
|
||||
@@ -0,0 +1,2 @@
|
||||
import type * as VRenderer from "virtual-renderer";
|
||||
|
||||
Reference in New Issue
Block a user