diff --git a/addons/vuforia-spatial-core-addon b/addons/vuforia-spatial-core-addon index 4c02e16fe..0e795e653 160000 --- a/addons/vuforia-spatial-core-addon +++ b/addons/vuforia-spatial-core-addon @@ -1 +1 @@ -Subproject commit 4c02e16fece1e3177c50de2eb561fac0a4128fa7 +Subproject commit 0e795e6533e4cb509e85996a443c59bf9db4b3cb diff --git a/libraries/objectDefaultFiles/scene/AnchoredGroupNode.js b/libraries/objectDefaultFiles/scene/AnchoredGroupNode.js index 15c57f424..3b75b8160 100644 --- a/libraries/objectDefaultFiles/scene/AnchoredGroupNode.js +++ b/libraries/objectDefaultFiles/scene/AnchoredGroupNode.js @@ -17,17 +17,8 @@ class AnchoredGroupNode extends ObjectNode { * * @param {ObjectInterface} listener */ - constructor(listener) { - super(listener, AnchoredGroupNode.TYPE); - } - - /** - * - * @param {string} toolId - * @returns {AnchoredGroupNodeState} - */ - getStateForTool(toolId) { - return {}; + constructor() { + super(AnchoredGroupNode.TYPE); } } diff --git a/libraries/objectDefaultFiles/scene/AnchoredGroupStore.js b/libraries/objectDefaultFiles/scene/AnchoredGroupStore.js deleted file mode 100644 index 921a73c12..000000000 --- a/libraries/objectDefaultFiles/scene/AnchoredGroupStore.js +++ /dev/null @@ -1,25 +0,0 @@ -import ObjectStore from "./ObjectStore.js"; - -/** - * @typedef {import("./AnchoredGroupNode.js").default} AnchoredGroupNode - */ - -class AnchoredGroupStore extends ObjectStore { - /** - * - */ - constructor() { - super(); - } - - /** - * @override - * @param {AnchoredGroupNode} _thisNode - * @returns {NodeDict} - */ - getProperties(_thisNode) { - return {}; - } -} - -export default AnchoredGroupStore; diff --git a/libraries/objectDefaultFiles/scene/BaseEntity.js b/libraries/objectDefaultFiles/scene/BaseEntity.js index ca7b8219c..b612b4623 100644 --- a/libraries/objectDefaultFiles/scene/BaseEntity.js +++ b/libraries/objectDefaultFiles/scene/BaseEntity.js @@ -6,7 +6,7 @@ class BaseEntity { /** @type {{order: number, component: ComponentInterface}[]} */ #components; - /** @type {EntityInterface} */ + /** @type {{[key: string]: BaseEntity}} */ #children; constructor() { @@ -84,6 +84,9 @@ class BaseEntity { */ updateComponents() { for (let entry of this.#components) { + if (!entry || !entry.component || !entry.component.update) { + console.log("here"); + } entry.component.update(); } for (let child of Object.values(this.#children)) { @@ -117,15 +120,6 @@ class BaseEntity { delete this.#children[key]; } - /** - * - * @param {string} _name - * @returns {BaseEntity} - */ - createEntity(_name) { - throw Error("Can't instantiate abstract class"); - } - /** * */ diff --git a/libraries/objectDefaultFiles/scene/EntityNode.js b/libraries/objectDefaultFiles/scene/BaseEntityNode.js similarity index 70% rename from libraries/objectDefaultFiles/scene/EntityNode.js rename to libraries/objectDefaultFiles/scene/BaseEntityNode.js index 9ff0a93bc..f497cf10e 100644 --- a/libraries/objectDefaultFiles/scene/EntityNode.js +++ b/libraries/objectDefaultFiles/scene/BaseEntityNode.js @@ -1,4 +1,7 @@ +import ComponentsNode from "./ComponentsNode.js"; +import EntitiesNode from "./EntitiesNode.js"; import ObjectNode from "./ObjectNode.js"; +import TransformComponentNode from "./TransformComponentNode.js"; /** * @typedef {import("./BaseNode.js").BaseNodeState} BaseNodeState @@ -16,28 +19,26 @@ import ObjectNode from "./ObjectNode.js"; * @typedef {{getPosition: () => Vector3Value, setPosition: (position: Vector3Value) => void, getRotation: () => QuaternionValue, setRotation: (rotation: QuaternionValue) => void, getScale: () => Vector3Value, setScale: (scale: Vector3Value) => void}} EntityInterface */ -class EntityNode extends ObjectNode { +class BaseEntityNode extends ObjectNode { static TYPE = "Object.Entity"; + /** @tpye {EntityInterface} */ + #entity; + /** - * - * @param {DictionaryInterface} listener + * @param {EntityInterface} entity * @param {string} type */ - constructor(listener, type = EntityNode.TYPE) { - super(listener, type); - this.get("components").setEntityNode(this); - if (!this.get("components").has("0")) { - this.get("components").set("0", listener.createTransform(), false); - } + constructor(entity, type = BaseEntityNode.TYPE) { + super(type); + this._set("children", new EntitiesNode(this)); + this._set("components", new ComponentsNode(this)); + this.#entity = entity + this.setComponent("0", new TransformComponentNode(this.#entity), false); } - /** - * - * @returns {EntityInterface|null} - */ - getEntity() { - return this.getListener().getEntity(); + get entity() { + return this.#entity; } /** @@ -62,20 +63,12 @@ class EntityNode extends ObjectNode { this.get("children").set(name, entity, makeDirty); } - createEntity(key, state) { - return this.getListener().createEntity(key, state); - } - - createComponent(order, state) { - return this.getListener().createComponent(order, state); - } - /** * * @param {number} order * @param {ComponentNode} component */ - addComponent(order, component, makeDirty = true) { + setComponent(order, component, makeDirty = true) { this.get("components").set(order, component, makeDirty); } @@ -95,24 +88,29 @@ class EntityNode extends ObjectNode { return this.getComponentByType(type) !== undefined; } - setPosition(x, y, z) { + /** + * @param {Vector3Value} value + */ + set position(value) { const transform = this.get("components").get(0); - transform.setPosition({x, y, z}); + transform.position = value; } - setRotation(x, y, z, w) { + /** + * @param {QuaternionValue} value + */ + set rotation(value) { const transform = this.get("components").get(0); - transform.setRotation({x, y, z, w}); + transform.rotation = value; } - setScale(x, y, z) { + /** + * @param {Vector3Value} value + */ + set scale(value) { const transform = this.get("components").get(0); - transform.setScale({x, y, z}); - } - - dispose() { - this.getEntity().dispose(); + transform.scale = value; } } -export default EntityNode; +export default BaseEntityNode; diff --git a/libraries/objectDefaultFiles/scene/BaseEntityStore.js b/libraries/objectDefaultFiles/scene/BaseEntityStore.js deleted file mode 100644 index 9d6000222..000000000 --- a/libraries/objectDefaultFiles/scene/BaseEntityStore.js +++ /dev/null @@ -1,50 +0,0 @@ -import ObjectStore from "./ObjectStore.js"; -import EntitiesNode from "./EntitiesNode.js"; -import EntitiesStore from "./EntitiesStore.js"; -import ComponentsNode from "./ComponentsNode.js"; -import ComponentsStore from "./ComponentsStore.js"; -import TransformComponentNode from "./TransformComponentNode.js"; -import TransformComponentStore from "./TransformComponentStore.js"; - -class BaseEntityStore extends ObjectStore { - /** @type {EntityInterface} */ - #entity; - - /** - * - */ - constructor(entity) { - super(); - this.#entity = entity; - } - - /** - * @override - * @param {EntityNode} thisNode - * @returns {NodeDict} - */ - getProperties(thisNode) { - const ret = { - "children": new EntitiesNode(new EntitiesStore(thisNode)), - "components": new ComponentsNode(new ComponentsStore(thisNode)) - }; - return ret; - } - - /** - * @returns {EntityInterface} - */ - getEntity() { - return this.#entity; - } - - /** - * - * @returns {TransformComponentNode} - */ - createTransform() { - return new TransformComponentNode(new TransformComponentStore(this.#entity.getPosition(), this.#entity.getRotation(), this.#entity.getScale())); - } -} - -export default BaseEntityStore; diff --git a/libraries/objectDefaultFiles/scene/BaseNode.js b/libraries/objectDefaultFiles/scene/BaseNode.js index 70644f509..17af07043 100644 --- a/libraries/objectDefaultFiles/scene/BaseNode.js +++ b/libraries/objectDefaultFiles/scene/BaseNode.js @@ -28,9 +28,8 @@ class BaseNode { * @returns {string|null} */ getName() { - const parent = this.getParent(); - if (parent) { - for (const entry of parent.entries()) { + if (this.parent) { + for (const entry of this.parent.entries()) { if (entry[1] === this) { return entry[0]; } @@ -43,7 +42,7 @@ class BaseNode { * * @returns {BaseNode|null} */ - getParent() { + get parent() { const parent = this.#parent ? this.#parent.deref() : null; return parent ? parent : null; } @@ -52,7 +51,7 @@ class BaseNode { * internal use only, doesn't propogate isDirty * @param {BaseNode|null} parent */ - setParent(parent) { + set parent(parent) { this.#parent = parent ? new WeakRef(parent) : null; } @@ -139,7 +138,7 @@ class BaseNode { * @param {BaseNode} node */ static setParentDirty(node) { - const parent = node.getParent(); + const parent = node.parent; if (parent && (!parent.isInternalDirty())) { parent.setInternalDirty(); BaseNode.setParentDirty(parent); diff --git a/libraries/objectDefaultFiles/scene/ColorNode.js b/libraries/objectDefaultFiles/scene/ColorNode.js index 1a83a90f3..2a54cf510 100644 --- a/libraries/objectDefaultFiles/scene/ColorNode.js +++ b/libraries/objectDefaultFiles/scene/ColorNode.js @@ -1,4 +1,5 @@ import ObjectNode from "./ObjectNode.js"; +import ValueNode from "./ValueNode.js"; /** * @typedef {import("./BaseNode.js").BaseNodeDelta} BaseNodeDelta @@ -6,39 +7,81 @@ import ObjectNode from "./ObjectNode.js"; * @typedef {import("./ObjectNode.js").ObjectInterface} ObjectInterface * @typedef {{r: number, g: number, b: number}} ColorValue * @typedef {BaseNodeDelta & {properties?: {r?: ValueNodeDelta, g?: ValueNodeDelta, b?: ValueNodeDelta}}} ColorDelta + * @typedef {(node: ColorNode) => void} onChangedFunc */ class ColorNode extends ObjectNode { static TYPE = "Object.Color"; + /** @type {onChangedFunc|null} */ + #onChanged + /** * - * @param {ObjectInterface} listener + * @param {ObjectInterface} value */ - constructor(listener) { - super(listener, ColorNode.TYPE); + constructor(value = {r: 0, g: 0, b: 0}) { + super(ColorNode.TYPE); + this.#addValue("r", value.r); + this.#addValue("g", value.g); + this.#addValue("b", value.b); + this.#onChanged = null; } /** - * @param {ColorValue} value + * + * @param {string} key + * @param {number} value */ - setValue(value) { - this.get("r").value = value.r; - this.get("g").value = value.g; - this.get("b").value = value.b; + #addValue(key, value) { + const node = new ValueNode(value); + node.onChanged = (_node) => {this.#safeOnChanged();}; + this._set(key, node); + } + + /** + * + */ + #safeOnChanged() { + if (this.#onChanged) { + this.#onChanged(this); + } + } + + /** + * @returns {onChangedFunc} + */ + get onChanged() { + return this.#onChanged; + } + + /** + * @param {onChangedFunc} onChanged + */ + set onChanged(onChanged) { + this.#onChanged = onChanged; } /** * * @returns {ColorValue} */ - getValue() { + get value() { return { "r": this.get("r").value, "g": this.get("g").value, "b": this.get("b").value }; } + + /** + * @param {ColorValue} value + */ + set value(value) { + this.get("r").value = value.r; + this.get("g").value = value.g; + this.get("b").value = value.b; + } } export default ColorNode; diff --git a/libraries/objectDefaultFiles/scene/ColorStore.js b/libraries/objectDefaultFiles/scene/ColorStore.js deleted file mode 100644 index 62577bbb5..000000000 --- a/libraries/objectDefaultFiles/scene/ColorStore.js +++ /dev/null @@ -1,37 +0,0 @@ -import ObjectStore from "./ObjectStore.js"; -import ValueNode from "./ValueNode.js"; - -/** - * @typedef {import("./ObjectNode.js").NodeDict} NodeDict - * @typedef {import("./ColorNode.js").default} ColorNode - * * @typedef {import("./ColorNode.js").ColorValue} ColorValue - */ - -class ColorStore extends ObjectStore { - /** @type {ColorValue} */ - #initValues; - - /** - * - * @param {ColorValue} value - */ - constructor(value = {r: 0, g: 0, b: 0}) { - super(); - this.#initValues = value; - } - - /** - * @override - * @param {ColorNode} _thisNode - * @returns {{r: ValueNode, g: ValueNode, b: ValueNode}} - */ - getProperties(_thisNode) { - return { - "r": new ValueNode(this.#initValues.r), - "g": new ValueNode(this.#initValues.g), - "b": new ValueNode(this.#initValues.b) - }; - } -} - -export default ColorStore; diff --git a/libraries/objectDefaultFiles/scene/ComponentsNode.js b/libraries/objectDefaultFiles/scene/ComponentsNode.js index eeb649803..5dfec43f5 100644 --- a/libraries/objectDefaultFiles/scene/ComponentsNode.js +++ b/libraries/objectDefaultFiles/scene/ComponentsNode.js @@ -1,4 +1,9 @@ import DictionaryNode from "./DictionaryNode.js"; +import TransformComponentNode from "./TransformComponentNode.js"; +import VisibilityComponentNode from "./VisibilityComponentNode.js"; +import SimpleAnimationComponentNode from "./SimpleAnimationComponentNode.js"; +import ValueComponentNode from "./ValueComponentNode.js"; +import DictionaryComponentNode from "./DictionaryComponentNode.js"; /** * @typedef {import("./BaseNode.js").BaseNodeState} BaseNodeState @@ -13,19 +18,23 @@ import DictionaryNode from "./DictionaryNode.js"; class ComponentsNode extends DictionaryNode { static TYPE = "Object.Components"; + /** @type {EntityNodeInterface} */ + #entityNode + /** * - * @param {DictionaryInterface} listener + * @param {EntityNodeInterface} entityNode */ - constructor(listener) { - super(listener, ComponentsNode.TYPE); + constructor(entityNode) { + super(ComponentsNode.TYPE); + this.#entityNode = entityNode } set(key, value, makeDirty = true) { - value.setEntityNode(this.getParent()); - const entity = this.getParent().getEntity(); + value.setEntityNode(this.parent); + const entity = this.parent.entity; if (entity) { - entity.setComponent(key, value.getComponent()); + entity.setComponent(key, value.component); } super.set(key, value, makeDirty); } @@ -48,6 +57,44 @@ class ComponentsNode extends DictionaryNode { component.setEntityNode(node); } } + + /** + * @override + * @param {string} key + * @param {BaseNodeState} state + * @returns {ComponentNode|undefined} + */ + _create(key, state) { + if (state.hasOwnProperty("type") && (state.type.startsWith("Object.Component") || state.type.startsWith("Value.Component"))) { + let ret = this.#entityNode.createComponent(key, state); + if (!ret) { + if (state.type === TransformComponentNode.TYPE) { + ret = new TransformComponentNode(); + } else if (state.type === VisibilityComponentNode.TYPE) { + ret = new VisibilityComponentNode(); + } else if (state.type === SimpleAnimationComponentNode.TYPE) { + ret = new SimpleAnimationComponentNode(); + } else if (state.type.startsWith("Object.Component")) { + ret = new DictionaryComponentNode(state.type); + } else { + ret = new ValueComponentNode(state.type); + } + } + return ret; + } else { + throw Error("Not a component"); + } + } + + /** + * @override + * @param {string} _key + * @param {BaseNode} _oldNode + * @param {BaseNodeState} _state + */ + _cast(_key, _oldNode, _state) { + throw Error("Can't cast"); + } } export default ComponentsNode; diff --git a/libraries/objectDefaultFiles/scene/ComponentsStore.js b/libraries/objectDefaultFiles/scene/ComponentsStore.js deleted file mode 100644 index d9084874f..000000000 --- a/libraries/objectDefaultFiles/scene/ComponentsStore.js +++ /dev/null @@ -1,65 +0,0 @@ -import DictionaryStore from "./DictionaryStore.js"; -import DictionaryComponentNode from "./DictionaryComponentNode.js"; -import DictionaryComponentStore from "./DictionaryComponentStore.js"; -import TransformComponentNode from "./TransformComponentNode.js"; -import TransformComponentStore from "./TransformComponentStore.js"; -import VisibilityComponentNode from "./VisibilityComponentNode.js"; -import SimpleAnimationComponentNode from "./SimpleAnimationComponentNode.js"; -import ValueComponentNode from "./ValueComponentNode.js"; - -/** - * @typedef {import("./BaseNode.js").BaseNodeState} BaseNodeState - * @typedef {import("./BaseNode.js").default} BaseNode - */ - -class ComponentsStore extends DictionaryStore { - #entityNode; - - /** - * @param {EntityNode} entityNode - */ - constructor(entityNode) { - super(); - this.#entityNode = entityNode; - } - - /** - * @override - * @param {string} key - * @param {BaseNodeState} state - * @returns {ComponentNode|undefined} - */ - create(key, state) { - if (state.hasOwnProperty("type") && (state.type.startsWith("Object.Component") || state.type.startsWith("Value.Component"))) { - let ret = this.#entityNode.createComponent(key, state); - if (!ret) { - if (state.type === TransformComponentNode.TYPE) { - ret = new TransformComponentNode(new TransformComponentStore()); - } else if (state.type === VisibilityComponentNode.TYPE) { - ret = new VisibilityComponentNode(); - } else if (state.type === SimpleAnimationComponentNode.TYPE) { - ret = new SimpleAnimationComponentNode(); - } else if (state.type.startsWith("Object.Component")) { - ret = new DictionaryComponentNode(new DictionaryComponentStore(), state.type); - } else { - ret = new ValueComponentNode(state.type); - } - } - return ret; - } else { - throw Error("Not a component"); - } - } - - /** - * @override - * @param {string} _key - * @param {BaseNode} _oldNode - * @param {BaseNodeState} _state - */ - cast(_key, _oldNode, _state) { - throw Error("Can't cast"); - } -} - -export default ComponentsStore; diff --git a/libraries/objectDefaultFiles/scene/DictionaryComponentNode.js b/libraries/objectDefaultFiles/scene/DictionaryComponentNode.js index b0548ffbe..58b536d4f 100644 --- a/libraries/objectDefaultFiles/scene/DictionaryComponentNode.js +++ b/libraries/objectDefaultFiles/scene/DictionaryComponentNode.js @@ -10,19 +10,17 @@ class DictionaryComponentNode extends DictionaryNode { /** * - * @param {DictionaryInterface} listener * @param {string} type */ - constructor(listener, type = DictionaryComponentNode.TYPE) { - super(listener, type); + constructor(type = DictionaryComponentNode.TYPE) { + super(type); } setEntityNode(node) { - this.getListener().setEntityNode(node); } getComponent() { - return this.getListener().getComponent(); + return this; } } diff --git a/libraries/objectDefaultFiles/scene/DictionaryComponentStore.js b/libraries/objectDefaultFiles/scene/DictionaryComponentStore.js deleted file mode 100644 index c88bbea2f..000000000 --- a/libraries/objectDefaultFiles/scene/DictionaryComponentStore.js +++ /dev/null @@ -1,24 +0,0 @@ -import DictionaryStore from "./DictionaryStore.js"; - -class DictionaryComponentStore extends DictionaryStore { - /** - * - */ - constructor() { - super(); - } - - /** - * - * @param {EntityNode} _node - */ - setEntityNode(_node) { - - } - - getComponent() { - return {update: () => {}, release: () => {}}; - } -} - -export default DictionaryComponentStore; diff --git a/libraries/objectDefaultFiles/scene/DictionaryNode.js b/libraries/objectDefaultFiles/scene/DictionaryNode.js index 388f1d9e2..2fc5a2124 100644 --- a/libraries/objectDefaultFiles/scene/DictionaryNode.js +++ b/libraries/objectDefaultFiles/scene/DictionaryNode.js @@ -1,6 +1,8 @@ import BaseNode from "./BaseNode.js"; import DeleteNode from "./DeleteNode.js"; import ObjectNode from "./ObjectNode.js"; +import ValueNode from "./ValueNode.js"; +import VersionedNode from "./VersionedNode.js"; /** * @typedef {import("./ObjectNode.js").DefaultApplyChangesFunc} DefaultApplyChangesFunc @@ -20,24 +22,16 @@ class DictionaryNode extends BaseNode { /** @type {boolean} */ #isPropertiesDirty; - /** @type {DictionaryInterface} */ - #listener; - /** * * @param {string} type */ - constructor(listener, type = DictionaryNode.TYPE) { + constructor(type = DictionaryNode.TYPE) { super(type); - this.#listener = listener; this.#properties = {}; this.#isPropertiesDirty = false; } - getListener() { - return this.#listener; - } - /** * */ @@ -53,7 +47,6 @@ class DictionaryNode extends BaseNode { */ delete(key) { if (this.#properties.hasOwnProperty(key)) { - this.#listener.delete(key, this.#properties[key]); this.set(key, new DeleteNode(this)); } } @@ -129,7 +122,7 @@ class DictionaryNode extends BaseNode { */ set(key, value, makeDirty = true) { this.#properties[key] = value; - value.setParent(this); + value.parent = this; if (makeDirty) { value.setTypeDirty(); value.setDirty(); @@ -242,26 +235,17 @@ class DictionaryNode extends BaseNode { * @param {boolean} useSetState */ setChanges(delta, useSetState = false) { - this.#listener.applyChanges(delta, (modifiedDelta) => this.#applyChanges(modifiedDelta, useSetState)); - } - - /** - * - * @param {ObjectToolRenderNode} delta - * @param {boolean} useSetState - */ - #applyChanges(delta, useSetState) { if (delta.hasOwnProperty("properties")) { for (const entry of Object.entries(delta.properties)) { if (this.#properties.hasOwnProperty(entry[0])) { if (entry[1].hasOwnProperty("type")) { if (entry[1].type === DeleteNode.TYPE) { - const canDelete = this.#listener.delete(entry[0], this.#properties[entry[0]]); + const canDelete = this._canDelete(entry[0], this.#properties[entry[0]]); if (canDelete) { delete this.#properties[entry[0]]; } } else if (entry[1].type !== this.#properties[entry[0]].getType()) { - const newNode = this.#listener.cast(entry[0], this.#properties[entry[0]], entry[1]); + const newNode = this._cast(entry[0], this.#properties[entry[0]], entry[1]); if (newNode !== undefined) { this.#properties[entry[0]] = newNode; } @@ -281,16 +265,67 @@ class DictionaryNode extends BaseNode { } } else { if (!(entry[1].hasOwnProperty("type") && entry[1].type === DeleteNode.TYPE)) { - const newProp = this.#listener.create(entry[0], entry[1]); - if (newProp) { - this.set(entry[0], newProp, false); - newProp.setState(entry[1]); - } + const newProp = this._create(entry[0], entry[1]); + this.set(entry[0], newProp, false); + newProp.setState(entry[1]); } } } } } + + /** + * + * @param {string} _key + * @param {BaseNodeState} state + * @returns {BaseNode} + */ + _create(_key, state) { + if (state.hasOwnProperty("type")) { + if (state.type.startsWith("Object")) { + return new DictionaryNode(state.type); + } else if (state.type.startsWith("Value")) { + if (!state.hasOwnProperty("value")) { + throw Error("Can't create ValueNode without initial value"); + } + return new ValueNode(state.value, state.type); + } else if (state.type.startsWith("Versioned")) { + if (!state.hasOwnProperty("value")) { + throw Error("Can't create VaersionedNode without initial value"); + } + if (!state.hasOwnProperty("version")) { + throw Error("Can't create VersionedNode without initial version"); + } + return new VersionedNode(state.value, state.type, state.version); + } else { + throw Error("Can't create property with type: " + state.type); + } + } else { + throw Error("Can't create property without type information"); + } + } + + /** + * + * @param {string} _key + * @param {BaseNode} _old + * @returns {boolean} + */ + _canDelete(_key, _old) { + return true; + } + + /** + * + * @param {string} key + * @param {BaseNode} old + * @param {ObjectNodeState} state + * @returns {BaseNode|undefined} + */ + _cast(key, old, state) { + this.delete(key, old); + return this._create(key, state); + } } export default DictionaryNode; diff --git a/libraries/objectDefaultFiles/scene/DictionaryStore.js b/libraries/objectDefaultFiles/scene/DictionaryStore.js deleted file mode 100644 index 2d96915d2..000000000 --- a/libraries/objectDefaultFiles/scene/DictionaryStore.js +++ /dev/null @@ -1,86 +0,0 @@ -import DictionaryNode from "./DictionaryNode.js"; -import ValueNode from "./ValueNode.js"; -import VersionedNode from "./VersionedNode.js"; - -/** - * @typedef {import("./ObjectNode").ObjectNodeState} ObjectNodeState - * @typedef {import("./ObjectNode").ObjectNodeDelta} ObjectNodeDelta - * @typedef {import("./BaseNode.js").BaseNodeState} BaseNodeState - * @typedef {import("./DictionaryNode.js").DictionaryInterface} DictionaryInterface - */ - -/** - * @implements {DictionaryInterface} - */ -class DictionaryStore { - /** - * - */ - constructor() { - - } - - /** - * - * @param {string} _key - * @param {BaseNodeState} state - * @returns {BaseNode|undefined} - */ - create(_key, state) { - if (state.hasOwnProperty("type")) { - if (state.type.startsWith("Object")) { - return new DictionaryNode(new DictionaryStore(), state.type); - } else if (state.type.startsWith("Value")) { - if (!state.hasOwnProperty("value")) { - throw Error("Can't create ValueNode without initial value"); - } - return new ValueNode(state.value, state.type); - } else if (state.type.startsWith("Versioned")) { - if (!state.hasOwnProperty("value")) { - throw Error("Can't create VaersionedNode without initial value"); - } - if (!state.hasOwnProperty("version")) { - throw Error("Can't create VersionedNode without initial version"); - } - return new VersionedNode(state.value, state.type, state.version); - } else { - throw Error("Can't create property with type: " + state.type); - } - } else { - throw Error("Can't create property without type information"); - } - } - - /** - * - * @param {string} key - * @param {BaseNode} old - * @param {ObjectNodeState} state - * @returns {BaseNode|undefined} - */ - cast(key, old, state) { - this.delete(key, old); - return this.create(key, state); - } - - /** - * - * @param {string} _key - * @param {BaseNode} _old - * @returns {boolean} - */ - delete(_key, _old) { - return true; - } - - /** - * @override - * @param {ObjectNodeDelta} delta - * @param {(delta: ObjectNodeDelta) => void} defaultApplyChanges - */ - applyChanges(delta, defaultApplyChanges) { - defaultApplyChanges(delta); - } -} - -export default DictionaryStore; diff --git a/libraries/objectDefaultFiles/scene/EntitiesNode.js b/libraries/objectDefaultFiles/scene/EntitiesNode.js index 2f880f3f9..c1bb09938 100644 --- a/libraries/objectDefaultFiles/scene/EntitiesNode.js +++ b/libraries/objectDefaultFiles/scene/EntitiesNode.js @@ -1,10 +1,11 @@ import DictionaryNode from "./DictionaryNode.js"; +import BaseEntityNode from "./BaseEntityNode.js" /** * @typedef {import("./BaseNode.js").BaseNodeState} BaseNodeState * @typedef {import("./BaseNode.js").BaseNodeDelta} BaseNodeDelta - * @typedef {import("./EntityNode.js").EntityNodeState} EntityNodeState - * @typedef {import("./EntityNode.js").EntityNodeDelta} EntityNodeDelta + * @typedef {import("./BaseEntityNode.js").EntityNodeState} EntityNodeState + * @typedef {import("./BaseEntityNode.js").EntityNodeDelta} EntityNodeDelta * @typedef {{properties: {[key: string]: EntityNodeState}} & BaseNodeState} EntitiesNodeState * @typedef {{properties?: {[key: string]: EntityNodeDelta}} & BaseNodeDelta} EntitiesNodeDelta * @typedef {import("./DictionaryNode.js").DictionaryInterface} DictionaryInterface @@ -13,19 +14,53 @@ import DictionaryNode from "./DictionaryNode.js"; class EntitiesNode extends DictionaryNode { static TYPE = "Object.Entities"; + /** @type {EntityNodeInterface} */ + #entityNode; + /** * - * @param {DictionaryInterface} listener + * @param {EntityNodeInterface} entityNode */ - constructor(listener) { - super(listener, EntitiesNode.TYPE); + constructor(entityNode) { + super(EntitiesNode.TYPE); + this.#entityNode = entityNode; } set(key, value, makeDirty = true) { - const entity = this.getListener().getEntity(); - entity.setChild(key, value.getEntity()); + const entity = this.#entityNode.entity; + entity.setChild(key, value.entity); super.set(key, value, makeDirty); } + + /** + * @override + * @param {string} key + * @param {BaseNodeState} state + * @returns {BaseNode|undefined} + */ + _create(key, state) { + if (state.hasOwnProperty("type") && state.type.startsWith(BaseEntityNode.TYPE)) { + return this.#entityNode.createEntity(key, state); + } else { + throw Error("Not an Entity"); + } + } + + /** + * @override + * @param {string} _key + * @param {BaseNode} _oldNode + * @param {BaseNodeState} _state + * @returns {BaseNode|undefined} + */ + _cast(_key, _oldNode, _state) { + throw Error("Can't cast"); + } + + _delete(_key, _oldNode) { + _oldNode.onDelete(); + return true; + } } export default EntitiesNode; diff --git a/libraries/objectDefaultFiles/scene/EntitiesStore.js b/libraries/objectDefaultFiles/scene/EntitiesStore.js deleted file mode 100644 index 1585f4fcc..000000000 --- a/libraries/objectDefaultFiles/scene/EntitiesStore.js +++ /dev/null @@ -1,59 +0,0 @@ -import DictionaryStore from "./DictionaryStore.js"; -import EntityNode from "./EntityNode.js"; - -/** - * @typedef {import("./BaseNode.js").BaseNodeState} BaseNodeState - * @typedef {import("./BaseNode.js").default} BaseNode - */ - -class EntitiesStore extends DictionaryStore { - #entityNode; - - /** - * - */ - constructor(entityNode) { - super(); - this.#entityNode = entityNode; - } - - /** - * - * @returns {EntityInterface} - */ - getEntity() { - return this.#entityNode.getEntity(); - } - - /** - * @override - * @param {string} key - * @param {BaseNodeState} state - * @returns {BaseNode|undefined} - */ - create(key, state) { - if (state.hasOwnProperty("type") && state.type.startsWith(EntityNode.TYPE)) { - return this.#entityNode.createEntity(key, state); - } else { - throw Error("Not an Entity"); - } - } - - /** - * @override - * @param {string} _key - * @param {BaseNode} _oldNode - * @param {BaseNodeState} _state - * @returns {BaseNode|undefined} - */ - cast (_key, _oldNode, _state) { - throw Error("Can't cast"); - } - - delete(_key, _oldNode) { - _oldNode.onDelete(); - return true; - } -} - -export default EntitiesStore; diff --git a/libraries/objectDefaultFiles/scene/EntityStore.js b/libraries/objectDefaultFiles/scene/EntityStore.js deleted file mode 100644 index 3c7955a56..000000000 --- a/libraries/objectDefaultFiles/scene/EntityStore.js +++ /dev/null @@ -1,48 +0,0 @@ -import GLTFLoaderComponentNode from "./GLTFLoaderComponentNode.js"; -import GLTFLoaderComponentStore from "./GLTFLoaderComponentStore.js"; -import MaterialComponentNode from "./MaterialComponentNode.js"; -import MaterialComponentStore from "./MaterialComponentStore.js"; -import EntityNode from "./EntityNode.js"; -import DefaultEntity from "./DefaultEntity.js"; -import BaseEntityStore from "./BaseEntityStore.js"; - -/** - * @typedef {import("./ObjectNode.js").NodeDict} NodeDict - * @typedef {import("./EntityNode.js").default} EntityNode - */ - -class EntityStore extends BaseEntityStore { - /** - * - */ - constructor(entity) { - super(entity); - } - - /** - * - * @param {string} _name - * @returns {DefaultEntity} - */ - createEntity(_name, _state) { - return new EntityNode(new EntityStore(new DefaultEntity())); - } - - /** - * - * @param {ValueDict} state - * @returns {ComponentInterface} - */ - createComponent(_order, state) { - if (state.hasOwnProperty("type")) { - if (state.type === GLTFLoaderComponentNode.TYPE) { - return new GLTFLoaderComponentNode(new GLTFLoaderComponentStore()); - } else if (state.type === MaterialComponentNode.TYPE) { - return new MaterialComponentNode(new MaterialComponentStore()); - } - } - return null; - } -} - -export default EntityStore; diff --git a/libraries/objectDefaultFiles/scene/EulerAnglesNode.js b/libraries/objectDefaultFiles/scene/EulerAnglesNode.js index f9010d5b4..24497467c 100644 --- a/libraries/objectDefaultFiles/scene/EulerAnglesNode.js +++ b/libraries/objectDefaultFiles/scene/EulerAnglesNode.js @@ -1,4 +1,5 @@ import ObjectNode from "./ObjectNode.js"; +import ValueNode from "./ValueNode.js"; /** * @typedef {import("./BaseNode.js").BaseNodeDelta} BaseNodeDelta @@ -6,34 +7,67 @@ import ObjectNode from "./ObjectNode.js"; * @typedef {import("./ObjectNode.js").ObjectInterface} ObjectInterface * @typedef {{x: number, y: number, z: number, order: string}} EulerAnglesValue * @typedef {BaseNodeDelta & {properties?: {x?: ValueNodeDelta, y?: ValueNodeDelta, z?: ValueNodeDelta, order?: ValueNodeDelta}}} EulerAnglesDelta + * @typedef {(node: EulerAnglesNode) => void} onChangedFunc */ class EulerAnglesNode extends ObjectNode { static TYPE = "Object.EulerAngles"; + /** @type {onChangedFunc|null} */ + #onChanged + /** * - * @param {ObjectInterface} listener + * @param {EulerAnglesValue} value */ - constructor(listener) { - super(listener, EulerAnglesNode.TYPE); + constructor(value = {x: 0, y: 0, z: 0, order: "XYZ"}) { + super(EulerAnglesNode.TYPE); + this.#addValue("x", value.x); + this.#addValue("y", value.y); + this.#addValue("z", value.z); + this.#addValue("order", value.order); + this.#onChanged = null; } /** - * @param {EulerAnglesValue} value + * + * @param {string} key + * @param {number|string} value */ - setValue(value) { - this.get("x").value = value.x; - this.get("y").value = value.y; - this.get("z").value = value.z; - this.get("order").value = value.order; + #addValue(key, value) { + const node = new ValueNode(value); + node.onChanged = (_node) => {this.#safeOnChanged();}; + this._set(key, node); + } + + /** + * + */ + #safeOnChanged() { + if (this.#onChanged) { + this.#onChanged(this); + } + } + + /** + * @returns {onChangedFunc} + */ + get onChanged() { + return this.#onChanged; + } + + /** + * @param {onChangedFunc} onChanged + */ + set onChanged(onChanged) { + this.#onChanged = onChanged; } /** * * @returns {EulerAnglesValue} */ - getValue() { + get value() { return { "x": this.get("x").value, "y": this.get("y").value, @@ -41,6 +75,18 @@ class EulerAnglesNode extends ObjectNode { "order": this.get("order").value }; } + + /** + * @param {EulerAnglesValue} value + */ + set value(value) { + this.get("x").value = value.x; + this.get("y").value = value.y; + this.get("z").value = value.z; + this.get("order").value = value.order; + } + + } export default EulerAnglesNode; diff --git a/libraries/objectDefaultFiles/scene/EulerAnglesStore.js b/libraries/objectDefaultFiles/scene/EulerAnglesStore.js deleted file mode 100644 index 3a65d217e..000000000 --- a/libraries/objectDefaultFiles/scene/EulerAnglesStore.js +++ /dev/null @@ -1,38 +0,0 @@ -import ObjectStore from "./ObjectStore.js"; -import ValueNode from "./ValueNode.js"; - -/** - * @typedef {import("./ObjectNode.js").NodeDict} NodeDict - * @typedef {import("./EulerAnglesNode.js").default} EulerAnglesNode - * @typedef {import("./EulerAnglesNode.js").EulerAnglesValue} EulerAnglesValue - */ - -class EulerAnglesStore extends ObjectStore { - /** @type {EulerAnglesValue} */ - #initValues; - - /** - * - * @param {EulerAnglesValue} value - */ - constructor(value = {x: 0, y: 0, z: 0, order: "XYZ"}) { - super(); - this.#initValues = value; - } - - /** - * @override - * @param {EulerAnglesNode} _thisNode - * @returns {{x: ValueNode, y: ValueNode, z: ValueNode, order: ValueNode}} - */ - getProperties(_thisNode) { - return { - "x": new ValueNode(this.#initValues.x), - "y": new ValueNode(this.#initValues.y), - "z": new ValueNode(this.#initValues.z), - "order": new ValueNode(this.#initValues.order) - }; - } -} - -export default EulerAnglesStore; diff --git a/libraries/objectDefaultFiles/scene/GLTFLoaderComponentNode.js b/libraries/objectDefaultFiles/scene/GLTFLoaderComponentNode.js index fefeed07e..f702e9ea7 100644 --- a/libraries/objectDefaultFiles/scene/GLTFLoaderComponentNode.js +++ b/libraries/objectDefaultFiles/scene/GLTFLoaderComponentNode.js @@ -1,34 +1,32 @@ import BaseComponentNode from "./BaseComponentNode.js"; +import VersionedNode from "./VersionedNode.js"; class GLTFLoaderComponentNode extends BaseComponentNode { static TYPE = BaseComponentNode.TYPE + ".GLTFLoader"; /** * - * @param {ObjectInterface} listener */ - constructor(listener) { - super(listener, GLTFLoaderComponentNode.TYPE); + constructor() { + super(GLTFLoaderComponentNode.TYPE); + this._set("url", new VersionedNode("")); } setUrl(url) { this.get("url").value = url; } - setEntityNode(node) { - this.getListener().setEntityNode(node); + setEntityNode(_node) { } update() { - this.getListener().update(); } - getComponent() { + get component() { return this; } release() { - this.getListener().release(); } } diff --git a/libraries/objectDefaultFiles/scene/GLTFLoaderComponentStore.js b/libraries/objectDefaultFiles/scene/GLTFLoaderComponentStore.js deleted file mode 100644 index 4248bdc7a..000000000 --- a/libraries/objectDefaultFiles/scene/GLTFLoaderComponentStore.js +++ /dev/null @@ -1,43 +0,0 @@ -import ObjectStore from "./ObjectStore.js"; -import VersionedNode from "./VersionedNode.js"; - -/** - * @typedef {import("./ObjectNode.js").NodeDict} NodeDict - * @typedef {import("./GLTFLoaderComponentNode.js").default} GLTFLoaderComponentNode - * @typedef {import("./EntityNode.js").default} EntityNode - */ - -class GLTFLoaderComponentStore extends ObjectStore { - /** - * - */ - constructor() { - super(); - } - - /** - * @override - * @param {GLTFLoaderComponentNode} _thisNode - * @returns {NodeDict} - */ - getProperties(_thisNode) { - return { - "url": new VersionedNode("") - }; - } - - /** - * @param {EntityNode} _node - */ - setEntityNode(_node) { - - } - - update() { - } - - release() { - } -} - -export default GLTFLoaderComponentStore; diff --git a/libraries/objectDefaultFiles/scene/MaterialComponentNode.js b/libraries/objectDefaultFiles/scene/MaterialComponentNode.js index d5da6d98f..b5061fcb9 100644 --- a/libraries/objectDefaultFiles/scene/MaterialComponentNode.js +++ b/libraries/objectDefaultFiles/scene/MaterialComponentNode.js @@ -1,30 +1,30 @@ import ObjectNode from "./ObjectNode.js"; +import ValueNode from "./ValueNode.js"; +import DictionaryNode from "./DictionaryNode.js"; class MaterialComponentNode extends ObjectNode { static TYPE = "Object.Component.Material"; /** * - * @param {ObjectInterface} listener */ - constructor(listener) { - super(listener, MaterialComponentNode.TYPE); + constructor() { + super(MaterialComponentNode.TYPE); + this._set("material", new ValueNode("")); + this._set("properties", new DictionaryNode()); } setEntityNode(node) { - this.getListener().setEntityNode(node); } update() { - this.getListener().update(); } - getComponent() { + get component() { return this; } release() { - this.getListener().release(); } } diff --git a/libraries/objectDefaultFiles/scene/MaterialComponentStore.js b/libraries/objectDefaultFiles/scene/MaterialComponentStore.js deleted file mode 100644 index 06f6bb539..000000000 --- a/libraries/objectDefaultFiles/scene/MaterialComponentStore.js +++ /dev/null @@ -1,32 +0,0 @@ -import ObjectStore from "./ObjectStore.js"; -import ValueNode from "./ValueNode.js"; -import DictionaryNode from "./DictionaryNode.js"; -import DictionaryStore from "./DictionaryStore.js"; - -class MaterialComponentStore extends ObjectStore { - constructor() { - super(); - } - - getProperties() { - return { - "material": new ValueNode(""), - "properties": new DictionaryNode(new DictionaryStore()) - }; - } - - /** - * @param {EntityNode} _node - */ - setEntityNode(_node) { - - } - - update() { - } - - release() { - } -} - -export default MaterialComponentStore; diff --git a/libraries/objectDefaultFiles/scene/DefaultEntity.js b/libraries/objectDefaultFiles/scene/MemoryEntity.js similarity index 84% rename from libraries/objectDefaultFiles/scene/DefaultEntity.js rename to libraries/objectDefaultFiles/scene/MemoryEntity.js index 178bb8060..2cade772e 100644 --- a/libraries/objectDefaultFiles/scene/DefaultEntity.js +++ b/libraries/objectDefaultFiles/scene/MemoryEntity.js @@ -5,7 +5,7 @@ import BaseEntity from "./BaseEntity.js"; * @typedef {import("./QuaternionNode.js").QuaternionValue} QuaternionValue */ -class DefaultEntity extends BaseEntity { +class MemoryEntity extends BaseEntity { /** @type {Vector3Value} */ #position; @@ -30,7 +30,7 @@ class DefaultEntity extends BaseEntity { * * @returns {Vector3Value} */ - getPosition() { + get position() { return this.#position; } @@ -38,7 +38,7 @@ class DefaultEntity extends BaseEntity { * * @param {Vector3Value} position */ - setPosition(position) { + set position(position) { this.#position = position; } @@ -46,7 +46,7 @@ class DefaultEntity extends BaseEntity { * * @returns {QuaternionValue} */ - getRotation() { + get rotation() { return this.#rotation; } @@ -54,7 +54,7 @@ class DefaultEntity extends BaseEntity { * * @param {QuaternionValue} rotation */ - setRotation(rotation) { + set rotation(rotation) { this.#rotation = rotation; } @@ -62,7 +62,7 @@ class DefaultEntity extends BaseEntity { * * @returns {Vector3Value} */ - getScale() { + get scale() { return this.#scale; } @@ -70,7 +70,7 @@ class DefaultEntity extends BaseEntity { * * @param {Vector3Value} scale */ - setScale(scale) { + set scale(scale) { this.#scale = scale; } @@ -78,7 +78,7 @@ class DefaultEntity extends BaseEntity { * * @param {boolean} isVisible */ - setVisible(isVisible) { + set isVisible(isVisible) { this.#isVisible = isVisible; } @@ -86,7 +86,7 @@ class DefaultEntity extends BaseEntity { * * @returns {boolean} */ - isVisible() { + get isVisible() { return this.#isVisible; } @@ -94,4 +94,4 @@ class DefaultEntity extends BaseEntity { } } -export default DefaultEntity; +export default MemoryEntity; diff --git a/libraries/objectDefaultFiles/scene/MemoryEntityNode.js b/libraries/objectDefaultFiles/scene/MemoryEntityNode.js new file mode 100644 index 000000000..9e832a925 --- /dev/null +++ b/libraries/objectDefaultFiles/scene/MemoryEntityNode.js @@ -0,0 +1,41 @@ +import BaseEntityNode from "./BaseEntityNode.js"; +import MemoryEntity from "./MemoryEntity.js"; +import GLTFLoaderComponentNode from "./GLTFLoaderComponentNode.js"; +import MaterialComponentNode from "./MaterialComponentNode.js"; + +class MemoryEntityNode extends BaseEntityNode { + /** + * @param {MemoryEntity} entity + * @param {string} type + */ + constructor(entity, type = BaseEntityNode.TYPE) { + super(entity, type); + } + + /** + * @param {string} _key + * @param {string} _name + * @returns {MemoryEntity} + */ + createEntity(_key, _name) { + return new MemoryEntityNode(new MemoryEntity()); + } + + /** + * @param {number} _index + * @param {ValueDict} state + * @returns {ComponentInterface} + */ + createComponent(_index, state) { + if (state.hasOwnProperty("type")) { + if (state.type === GLTFLoaderComponentNode.TYPE) { + return new GLTFLoaderComponentNode(); + } else if (state.type === MaterialComponentNode.TYPE) { + return new MaterialComponentNode(); + } + } + return null; + } +} + +export default MemoryEntityNode; diff --git a/libraries/objectDefaultFiles/scene/ObjectNode.js b/libraries/objectDefaultFiles/scene/ObjectNode.js index 0adc9265b..2a221a1a7 100644 --- a/libraries/objectDefaultFiles/scene/ObjectNode.js +++ b/libraries/objectDefaultFiles/scene/ObjectNode.js @@ -19,32 +19,16 @@ class ObjectNode extends BaseNode { /** @type {boolean} */ #isPropertiesDirty; - /** @type {ObjectInterface} */ - #listener; - /** * - * @param {ObjectInterface} listener * @param {string} type */ - constructor(listener, type) { + constructor(type) { super(type); - this.#listener = listener; - this.#properties = this.#listener.getProperties(this); - for (const value of Object.values(this.#properties)) { - value.setParent(this); - } + this.#properties = {}; this.#isPropertiesDirty = false; } - /** - * - * @returns {ObjectInterface} - */ - getListener() { - return this.#listener; - } - /** * * @returns {[string, Node][]} @@ -92,6 +76,16 @@ class ObjectNode extends BaseNode { return Object.keys(this.#properties); } + /** + * + * @param {string} key + * @param {BaseNode} node + */ + _set(key, node) { + this.#properties[key] = node; + node.parent = this; + } + /** * * @returns {BaseNode[]} @@ -182,15 +176,6 @@ class ObjectNode extends BaseNode { * @param {boolean} useSetState */ setChanges(delta, useSetState = false) { - this.#listener.applyChanges(delta, (modifiedDelta) => this.applyChanges(modifiedDelta, useSetState)); - } - - /** - * - * @param {ObjectToolRenderNode} delta - * @param {boolean} useSetState - */ - applyChanges(delta, useSetState) { if (delta.hasOwnProperty("properties")) { for (const entry of Object.entries(delta.properties)) { if (this.#properties.hasOwnProperty(entry[0])) { diff --git a/libraries/objectDefaultFiles/scene/ObjectStore.js b/libraries/objectDefaultFiles/scene/ObjectStore.js deleted file mode 100644 index 63d2bcf9a..000000000 --- a/libraries/objectDefaultFiles/scene/ObjectStore.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @typedef {import("./ObjectNode.js").ObjectInterface} ObjectInterface - * @typedef {import("./ObjectNode.js").default} ObjectNode - */ - -/** - * @implements {ObjectInterface} - */ -class ObjectStore { - /** - * - */ - constructor() { - - } - - /** - * @override - * @param {ObjectNode} _node - * @returns {NodeDict} - */ - getProperties(_node) { - return {}; - } - - /** - * @override - * @param {ObjectNodeDelta} delta - * @param {(delta: ObjectNodeDelta) => void} defaultApplyChanges - */ - applyChanges(delta, defaultApplyChanges) { - defaultApplyChanges(delta); - } -} - -export default ObjectStore; diff --git a/libraries/objectDefaultFiles/scene/QuaternionNode.js b/libraries/objectDefaultFiles/scene/QuaternionNode.js index f1b685c57..217898ecd 100644 --- a/libraries/objectDefaultFiles/scene/QuaternionNode.js +++ b/libraries/objectDefaultFiles/scene/QuaternionNode.js @@ -1,4 +1,5 @@ import ObjectNode from "./ObjectNode.js"; +import ValueNode from "./ValueNode.js"; /** * @typedef {import("./BaseNode.js").BaseNodeDelta} BaseNodeDelta @@ -6,24 +7,66 @@ import ObjectNode from "./ObjectNode.js"; * @typedef {import("./ObjectNode.js").ObjectInterface} ObjectInterface * @typedef {{x: number, y: number, z: number, w: number}} QuaternionValue * @typedef {BaseNodeDelta & {properties?: {x?: ValueNodeDelta, y?: ValueNodeDelta, z?: ValueNodeDelta, w?: ValueNodeDelta}}} QuaternionDelta + * @typedef {(node: QuaternionNode) +> void} onChangedFunc */ class QuaternionNode extends ObjectNode { static TYPE = "Object.Quaternion"; + /** @type {onChangedFunc|null} */ + #onChanged + /** - * - * @param {ObjectInterface} listener + * @param {QuaternionValue} value + */ + constructor(value = {x: 0, y: 0, z: 0, w: 1}) { + super(QuaternionNode.TYPE); + this.#addValue("x", value.x); + this.#addValue("y", value.y); + this.#addValue("z", value.z); + this.#addValue("w", value.w); + this.#onChanged = null; + } + + /** + * + * @param {string} key + * @param {number} value + */ + #addValue(key, value) { + const node = new ValueNode(value); + node.onChanged = (_node) => {this.#safeOnChanged();}; + this._set(key, node); + } + + /** + * + */ + #safeOnChanged() { + if (this.#onChanged) { + this.#onChanged(this); + } + } + + /** + * @returns {onChangedFunc} + */ + get onChanged() { + return this.#onChanged; + } + + /** + * @param {onChangedFunc} onChanged */ - constructor(listener) { - super(listener, QuaternionNode.TYPE); + set onChanged(onChanged) { + this.#onChanged = onChanged; } /** * * @param {QuaternionValue} value */ - setValue(value) { + set value(value) { this.get("x").value = value.x; this.get("y").value = value.y; this.get("z").value = value.z; @@ -35,7 +78,7 @@ class QuaternionNode extends ObjectNode { * * @returns {QuaternionValue} */ - getValue() { + get value() { return { "x": this.get("x").value, "y": this.get("y").value, diff --git a/libraries/objectDefaultFiles/scene/QuaternionStore.js b/libraries/objectDefaultFiles/scene/QuaternionStore.js deleted file mode 100644 index 5871106f2..000000000 --- a/libraries/objectDefaultFiles/scene/QuaternionStore.js +++ /dev/null @@ -1,37 +0,0 @@ -import ObjectStore from "./ObjectStore.js"; -import ValueNode from "./ValueNode.js"; - -/** - * @typedef {import("./ObjectNode.js").NodeDict} NodeDict - * @typedef {import("./QuaternionNode.js").default} QuaternionNode - * @typedef {import("./QuaternionNode.js").QuaternionValue} QuaternionValue - */ - -class QuaternionStore extends ObjectStore { - /** @type {QuaternionValue} */ - #initValues; - - /** - * @param {QuaternionValue} value - */ - constructor(value = {x: 0, y: 0, z: 0, w: 1}) { - super(); - this.#initValues = value; - } - - /** - * @override - * @param {QuaternionNode} _node - * @returns {{x: ValueNode, y: ValueNode, z: ValueNode, w: ValueNode}} - */ - getProperties(_node) { - return { - "x": new ValueNode(this.#initValues.x), - "y": new ValueNode(this.#initValues.y), - "z": new ValueNode(this.#initValues.z), - "w": new ValueNode(this.#initValues.w) - }; - } -} - -export default QuaternionStore; diff --git a/libraries/objectDefaultFiles/scene/SimpleAnimationComponentNode.js b/libraries/objectDefaultFiles/scene/SimpleAnimationComponentNode.js index d0e5d2a83..c3f9fc230 100644 --- a/libraries/objectDefaultFiles/scene/SimpleAnimationComponentNode.js +++ b/libraries/objectDefaultFiles/scene/SimpleAnimationComponentNode.js @@ -1,5 +1,6 @@ import ObjectNode from "./ObjectNode.js"; -import SimpleAnimationComponentStore from "./SimpleAnimationComponentStore.js"; +import Vector3Node from "./Vector3Node.js"; +import ValueNode from "./ValueNode.js"; /** * @typedef {import("../three/addons/DateTimer.js").Timer} Timer @@ -28,7 +29,11 @@ class SimpleAnimationComponentNode extends ObjectNode { * @param {animationFunc|null} animation */ constructor(animation = null) { - super(new SimpleAnimationComponentStore(), SimpleAnimationComponentNode.TYPE); + super(SimpleAnimationComponentNode.TYPE); + this._set("oldSample", new Vector3Node()); + this._set("oldTimestamp", new ValueNode(0)); + this._set("newSample", new Vector3Node()); + this._set("newTimestamp", new ValueNode(0)); this.#animation = animation; this.#isInitialized = false; this.#timer = null; @@ -41,7 +46,7 @@ class SimpleAnimationComponentNode extends ObjectNode { * @returns {WorldNode} */ #findWorldNode(node) { - const parent = node.getParent(); + const parent = node.parent; if (parent) { return this.#findWorldNode(parent); } @@ -59,7 +64,7 @@ class SimpleAnimationComponentNode extends ObjectNode { /** * @returns {SimpleAnimationComponentNode} */ - getComponent() { + get component() { return this; } @@ -74,26 +79,26 @@ class SimpleAnimationComponentNode extends ObjectNode { update() { if (!this.#timer) { - this.#timer = this.#findWorldNode(this.#entityNode).getTimer(); + this.#timer = this.#findWorldNode(this.#entityNode).timer; } const timestamp = this.#timer.getElapsed(); let sample = {x: 0, y: 0, z: 0}; if (this.#animation) { sample = this.#animation(timestamp); if (this.#isInitialized) { - this.get("oldSample").setValue(this.get("newSample").getValue()); + this.get("oldSample").value = this.get("newSample").value; this.get("oldTimestamp").value = this.get("newTimestamp").value; } else { - this.get("oldSample").setValue(sample); + this.get("oldSample").value = sample; this.get("oldTimestamp").value = timestamp - 1; this.#isInitialized = true; } - this.get("newSample").setValue(sample); + this.get("newSample").value = sample; this.get("newTimestamp").value = timestamp; } else { - const oldSample = this.get("oldSample").getValue(); + const oldSample = this.get("oldSample").value; const oldTimestamp = this.get("oldTimestamp").value; - const newSample = this.get("newSample").getValue(); + const newSample = this.get("newSample").value; const newTimestamp = this.get("newTimestamp").value; const interp = (timestamp - oldTimestamp) / (newTimestamp - oldTimestamp); sample.x = oldSample.x + (interp * (newSample.x - oldSample.x)); @@ -101,7 +106,7 @@ class SimpleAnimationComponentNode extends ObjectNode { sample.z = oldSample.z + (interp * (newSample.z - oldSample.z)); sample = newSample; } - this.#entityNode.getEntity().setPosition(sample); + this.#entityNode.entity.position = sample; } release() { diff --git a/libraries/objectDefaultFiles/scene/SimpleAnimationComponentStore.js b/libraries/objectDefaultFiles/scene/SimpleAnimationComponentStore.js deleted file mode 100644 index fdb367ba3..000000000 --- a/libraries/objectDefaultFiles/scene/SimpleAnimationComponentStore.js +++ /dev/null @@ -1,25 +0,0 @@ -import ObjectStore from "./ObjectStore.js"; -import Vector3Node from "./Vector3Node.js"; -import Vector3Store from "./Vector3Store.js"; -import ValueNode from "./ValueNode.js"; - -class SimpleAnimationComponentStore extends ObjectStore { - constructor() { - super(); - } - - /** - * - * @returns {{oldSample: Vector3Node, oldTimestamp: ValueNode, newSample: Vector3Node, newTimestamp: ValueNode}} - */ - getProperties() { - return { - "oldSample": new Vector3Node(new Vector3Store()), - "oldTimestamp": new ValueNode(0), - "newSample": new Vector3Node(new Vector3Store()), - "newTimestamp": new ValueNode(0) - }; - } -} - -export default SimpleAnimationComponentStore; diff --git a/libraries/objectDefaultFiles/scene/TextureNode.js b/libraries/objectDefaultFiles/scene/TextureNode.js index 73233223a..9da8eb28d 100644 --- a/libraries/objectDefaultFiles/scene/TextureNode.js +++ b/libraries/objectDefaultFiles/scene/TextureNode.js @@ -1,4 +1,6 @@ import ObjectNode from "./ObjectNode.js"; +import {UVMapping, ClampToEdgeWrapping, LinearFilter} from "../../thirdPartyCode/three/three.module.js"; +import ValueNode from "./ValueNode.js"; /** * @typedef {import("./BaseNode.js").BaseNodeDelta} BaseNodeDelta @@ -6,17 +8,152 @@ import ObjectNode from "./ObjectNode.js"; * @typedef {import("./ObjectNode.js").ObjectInterface} ObjectInterface * @typedef {{id: string, mapping: number, wrapS: number, wrapT: number, magFilter: number, minFilter: number, anisotropy: number}} TextureValue * @typedef {BaseNodeDelta & {id: string}} TextureDelta + * @typedef {(node: TextureNode) => void} onChangedFunc */ class TextureNode extends ObjectNode { static TYPE = "Object.Texture"; + /** @type {onChangedFunc|null} */ + #onChanged + /** * - * @param {ObjectInterface} listener + * @param {TextureValue} value + */ + constructor(value = {id: null, mapping: UVMapping, wrapS: ClampToEdgeWrapping, wrapT: ClampToEdgeWrapping, magFilter: LinearFilter, minFilter: LinearFilter, anisotropy: 1}) { + super(TextureNode.TYPE); + this._set("id", new ValueNode(value.id)); + this.#addValue("mapping", value.mapping); + this.#addValue("wrapS", value.wrapS); + this.#addValue("wrapT", value.wrapT); + this.#addValue("magFilter", value.magFilter); + this.#addValue("minFilter", value.minFilter); + this.#addValue("anisotropy", value.anisotropy); + this.#onChanged = null; + } + + /** + * + * @param {string} key + * @param {number|string} value + */ + #addValue(key, value) { + const node = new ValueNode(value); + node.onChanged = (_node) => {this.#safeOnChanged();}; + this._set(key, node); + } + + /** + * + */ + #safeOnChanged() { + if (this.#onChanged) { + this.#onChanged(this); + } + } + + /** + * @returns {onChangedFunc} + */ + get onChanged() { + return this.#onChanged; + } + + /** + * @param {onChangedFunc} onChanged + */ + set onChanged(onChanged) { + this.#onChanged = onChanged; + } + + /** @returns {string} */ + get id() { + return this.get("id").value; + } + + /** + * @returns {number} + */ + get mapping() { + return this.get("mapping").value; + } + + /** + * @param {number} value + */ + set mapping(value) { + this.get("mapping").value = value; + } + + /** + * @returns {number} + */ + get wrapS() { + return this.get("wrapS").value; + } + + /** + * @param {number} value + */ + set wrapS(value) { + this.get("wrapS").value = value; + } + + /** + * @returns {number} + */ + get wrapT() { + return this.get("wrapT").value; + } + + /** + * @param {number} value + */ + set wrapT(value) { + this.get("wrapT").value = value; + } + + /** + * @returns {number} + */ + get magFilter() { + return this.get("magFilter").value; + } + + /** + * @param {number} value + */ + set magFilter(value) { + this.get("magFilter").value = value; + } + + /** + * @returns {number} + */ + get minFlter() { + return this.get("minFilter").value; + } + + /** + * @param {number} value + */ + set minFilter(value) { + this.get("minFilter").value = value; + } + + /** + * @returns {number} + */ + get anisotropy() { + return this.get("anisotropy").value; + } + + /** + * @param {number} value */ - constructor(listener) { - super(listener, TextureNode.TYPE); + set anisotropy(value) { + this.get("anisotropy").value = value; } } diff --git a/libraries/objectDefaultFiles/scene/TextureStore.js b/libraries/objectDefaultFiles/scene/TextureStore.js deleted file mode 100644 index e37b286be..000000000 --- a/libraries/objectDefaultFiles/scene/TextureStore.js +++ /dev/null @@ -1,43 +0,0 @@ -import ObjectStore from "./ObjectStore.js"; -import ValueNode from "./ValueNode.js"; -import {UVMapping, ClampToEdgeWrapping, LinearFilter} from "../../thirdPartyCode/three/three.module.js"; - -/** - * @typedef {import("./ObjectNode.js").NodeDict} NodeDict - * @typedef {import("./TextureNode.js").default} TextureNode - * @typedef {import("./TextureNode.js").TextureValue} TextureValue - * @typedef {string} resourceId - */ - -class TextureStore extends ObjectStore { - /** @type {TextureValue} */ - #initValues; - - /** - * - * @param {TextureValue} value - */ - constructor(value = {id: null, mapping: UVMapping, wrapS: ClampToEdgeWrapping, wrapT: ClampToEdgeWrapping, magFilter: LinearFilter, minFilter: LinearFilter, anisotropy: 1}) { - super(); - this.#initValues = value; - } - - /** - * @override - * @param {TextureNode} _thisNode - * @returns {{id: ValueNode, mapping: ValueNode, wrapS: ValueNode, wrapT: ValueNode, magFilter: ValueNode, minFilter: ValueNode, anisotropy: ValueNode}} - */ - getProperties(_thisNode) { - return { - "id": new ValueNode(this.#initValues.id), - "mapping": new ValueNode(this.#initValues.mapping), - "wrapS": new ValueNode(this.#initValues.wrapS), - "wrapT": new ValueNode(this.#initValues.wrapT), - "magFilter": new ValueNode(this.#initValues.magFilter), - "minFilter": new ValueNode(this.#initValues.minFilter), - "anisotropy": new ValueNode(this.#initValues.anisotropy) - }; - } -} - -export default TextureStore; diff --git a/libraries/objectDefaultFiles/scene/Tool3D.js b/libraries/objectDefaultFiles/scene/Tool3D.js index 4e6bfa9d0..01b1ac0de 100644 --- a/libraries/objectDefaultFiles/scene/Tool3D.js +++ b/libraries/objectDefaultFiles/scene/Tool3D.js @@ -2,7 +2,6 @@ import DateTimer from "./DateTimer.js"; import {ToolRenderSocket} from "./ToolRenderStream.js"; import {ParentMessageInterface} from "./MessageInterface.js"; import WorldNode from "./WorldNode.js"; -import WorldStore from "./WorldStore.js"; import VisibilityComponentNode from "./VisibilityComponentNode.js"; import ToolNode from "./ToolNode.js"; @@ -89,27 +88,43 @@ class Tool3D { onReceivedSet(state) { console.log(`compositon layer -> ${this.#toolId} (set): `, state); if (!this.#world) { - this.#world = new WorldNode(new WorldStore(this.#timer)); + // if there is no world then this is the first a set command is received and we might want to run additional logic + + // create world with toolsRoot + this.#world = new WorldNode(this.#timer); let toolsRoot = this.#world; for (const key of state.toolsRoot) { toolsRoot = toolsRoot.get(key); } - this.#toolNode = this.#listener.createToolNode(); - toolsRoot.set(this.#toolId, this.#toolNode, false); + // make sure the toolsroot knows about the custom tool + toolsRoot.registerType(this.#listener.getToolNodeType(), () => {return this.#listener.createToolNode();}); + + // apply all saved information this.#world.setState(state); - if (!this.#toolNode.hasComponentWithType(VisibilityComponentNode.TYPE)) { - this.#toolNode.addComponent("1", new VisibilityComponentNode()); + // verify that the tool node is there + if (!toolsRoot.has(this.#toolId)) { + this.#toolNode = this.#listener.createToolNode(); + toolsRoot.set(this.#toolId, this.#toolNode, false); + } else { + this.#toolNode = toolsRoot.get(this.#toolId); } + // add visibility to the tool root + if (!this.#toolNode.hasComponentWithType(VisibilityComponentNode.TYPE)) { + this.#toolNode.setComponent("1", new VisibilityComponentNode()); + } this.setVisible(this.#isToolVisible); + // callback to the user after the basic scene graph has been created this.#listener.onStart(); + // start websorker which will call us back every so often to run the update loop this.#updateTimer = new Worker("/objectDefaultFiles/scene/WebWorkerTimer.js"); this.#updateTimer.onmessage = () => this.onUpdate(); } else { + // if it is not the first run we can just merge all given information this.#world.setState(state); } } @@ -136,7 +151,7 @@ class Tool3D { */ onUpdate() { this.#timer.update(); - this.#toolNode.getEntity().updateComponents(); + this.#toolNode.entity.updateComponents(); this.#socket.sendUpdate(this.#world.getChanges()); } diff --git a/libraries/objectDefaultFiles/scene/ToolNode.js b/libraries/objectDefaultFiles/scene/ToolNode.js index 19957a42f..d4442b7d1 100644 --- a/libraries/objectDefaultFiles/scene/ToolNode.js +++ b/libraries/objectDefaultFiles/scene/ToolNode.js @@ -1,4 +1,4 @@ -import EntityNode from "./EntityNode.js"; +import MemoryEntityNode from "./MemoryEntityNode.js"; /** * @typedef {import("./BaseNode.js").BaseNodeState} BaseNodeState @@ -12,15 +12,15 @@ import EntityNode from "./EntityNode.js"; * @typedef {import("./ObjectNode.js").ObjectInterface} ObjectInterface */ -class ToolNode extends EntityNode { +class ToolNode extends MemoryEntityNode { static TYPE = "Object.Tool"; /** * * @param {ObjectInterface} listener */ - constructor(listener, type = ToolNode.TYPE) { - super(listener, type); + constructor(entity, type = ToolNode.TYPE) { + super(entity, type); } } diff --git a/libraries/objectDefaultFiles/scene/ToolStore.js b/libraries/objectDefaultFiles/scene/ToolStore.js deleted file mode 100644 index 77f289f26..000000000 --- a/libraries/objectDefaultFiles/scene/ToolStore.js +++ /dev/null @@ -1,17 +0,0 @@ -import EntityStore from "./EntityStore.js"; - -/** - * @typedef {import("./BaseEntity.js").EntityInterface} EntityInterface - */ - -class ToolStore extends EntityStore { - - /** - * @param {EntityInterface} entity - */ - constructor(entity) { - super(entity); - } -} - -export default ToolStore; diff --git a/libraries/objectDefaultFiles/scene/ToolsRootNode.js b/libraries/objectDefaultFiles/scene/ToolsRootNode.js index f97f5fcd0..1dd3f342f 100644 --- a/libraries/objectDefaultFiles/scene/ToolsRootNode.js +++ b/libraries/objectDefaultFiles/scene/ToolsRootNode.js @@ -8,17 +8,22 @@ import DictionaryNode from "./DictionaryNode.js"; * @typedef {{properties: {[key: string]: ToolNodeState}} & BaseNodeState} ToolsRootNodeState * @typedef {{properties?: {[key: string]: ToolNodeDelta}} & BaseNodeDelta} ToolsRootNodeDelta * @typedef {import("./DictionaryNode.js").DictionaryInterface} DictionaryInterface + * @typedef {() => BaseNode} TypeConstructionFunc + * @typedef {{[type: string]: TypeConstructionFunc}} TypeConstructionDictionary */ class ToolsRootNode extends DictionaryNode { static TYPE = "Object.ToolsRoot"; + /** @type {TypeConstructionDictionary} */ + #typeDictionary; + /** * - * @param {DictionaryInterface} listener */ - constructor(listener) { - super(listener, ToolsRootNode.TYPE); + constructor() { + super(ToolsRootNode.TYPE); + this.#typeDictionary = {}; } getStateForTool(toolId) { @@ -27,6 +32,35 @@ class ToolsRootNode extends DictionaryNode { ret.properties[toolId] = this.get(toolId).getState(); return ret; } + + registerType(type, constructorFunc) { + this.#typeDictionary[type] = constructorFunc; + } + + /** + * @override + * @param {string} _key + * @param {BaseNodeState} state + * @returns {BaseNode} + */ + _create(_key, state) { + if (state.hasOwnProperty("type") && this.#typeDictionary.hasOwnProperty(state.type)) { + const ret = this.#typeDictionary[state.type](); + ret.setState(state); + return ret; + } + return undefined; + } + + /** + * @override + * @param {string} _key + * @param {BaseNode} _oldNode + * @param {BaseNodeState} _state + */ + _cast(_key, _oldNode, _state) { + throw Error("ToolsRoot only accepts tools, can't cast"); + } } export default ToolsRootNode; diff --git a/libraries/objectDefaultFiles/scene/ToolsRootStore.js b/libraries/objectDefaultFiles/scene/ToolsRootStore.js deleted file mode 100644 index a9dcc008b..000000000 --- a/libraries/objectDefaultFiles/scene/ToolsRootStore.js +++ /dev/null @@ -1,42 +0,0 @@ -import DefaultEntity from "./DefaultEntity.js"; -import DictionaryStore from "./DictionaryStore.js"; -import ToolNode from "./ToolNode.js"; -import ToolStore from "./ToolStore.js"; - -/** - * @typedef {import("./BaseNode.js").BaseNodeState} BaseNodeState - * @typedef {import("./BaseNode.js").default} BaseNode - */ - -class ToolsRootStore extends DictionaryStore { - constructor() { - super(); - } - - /** - * @override - * @param {string} _key - * @param {BaseNodeState} state - * @returns {BaseNode} - */ - create(_key, state) { - if (state.hasOwnProperty("type") && state.type.startsWith(ToolNode.TYPE)) { - const ret = new ToolNode(new ToolStore(new DefaultEntity(), state.type)); - ret.setState(state); - return ret; - } - return undefined; - } - - /** - * @override - * @param {string} _key - * @param {BaseNode} _oldNode - * @param {BaseNodeState} _state - */ - cast(_key, _oldNode, _state) { - throw Error("ToolsRoot only accepts tools, can't cast"); - } -} - -export default ToolsRootStore; diff --git a/libraries/objectDefaultFiles/scene/TransformComponentNode.js b/libraries/objectDefaultFiles/scene/TransformComponentNode.js index cedfca3a7..890753ad2 100644 --- a/libraries/objectDefaultFiles/scene/TransformComponentNode.js +++ b/libraries/objectDefaultFiles/scene/TransformComponentNode.js @@ -1,4 +1,6 @@ import BaseComponentNode from "./BaseComponentNode.js"; +import Vector3Node from "./Vector3Node.js"; +import QuaternionNode from "./QuaternionNode.js"; /** * @typedef {import("./Vector3Node.js").Vector3Value} Vector3Value @@ -8,86 +10,104 @@ import BaseComponentNode from "./BaseComponentNode.js"; class TransformComponentNode extends BaseComponentNode { static TYPE = BaseComponentNode.TYPE + ".Transform"; + /** @type {boolean} */ + #entityNeedsUpdate; + + /** @type {EntityInterface} */ + #entity; + /** + * @param {EntityInterface} entity + */ + constructor(entity) { + super(TransformComponentNode.TYPE); + const position = new Vector3Node(entity.position); + position.onChanged = () => {this.#entityNeedsUpdate = true;}; + this._set("position", position); + const rotation = new QuaternionNode(entity.rotation); + rotation.onChanged = () => {this.#entityNeedsUpdate = true;}; + this._set("rotation", rotation); + const scale = new Vector3Node(entity.scale); + scale.onChanged = () => {this.#entityNeedsUpdate = true;}; + this._set("scale", scale); + this.#entityNeedsUpdate = false; + this.#entity = entity; + } + + /** * - * @param {ObjectInterface} listener + * @param {EntityNode} entityNode */ - constructor(listener) { - super(listener, TransformComponentNode.TYPE); + setEntityNode(entityNode) { + this.#entity = entityNode.entity; + this.#entityNeedsUpdate = true; } /** * * @returns {Vector3Value} */ - getPosition() { - return this.get("position").getValue(); + get position() { + return this.get("position").value; } /** * * @param {Vector3Value} position */ - setPosition(position) { - this.get("position").setValue(position); + set position(position) { + this.get("position").value = position; } /** * * @returns {QuaternionValue} */ - getRotation() { - return this.get("rotation").getValue(); + get rotation() { + return this.get("rotation").value; } /** * * @param {QuaternionValue} rotation */ - setRotation(rotation) { - this.get("rotation").setValue(rotation); + set rotation(rotation) { + this.get("rotation").value = rotation; } /** * * @returns {Vector3Value} */ - getScale() { - return this.get("scale").getValue(); + get scale() { + return this.get("scale").value; } /** * * @param {Vector3Value} scale */ - setScale(scale) { - this.get("scale").setValue(scale); - } - - fromMatrix(matrix) { - this.getListener().fromMatrix(matrix); - } - - /** - * - * @param {EntityNode} node - */ - setEntityNode(node) { - this.getListener().setEntityNode(node); + set scale(scale) { + this.get("scale").value = scale; } /** * */ update() { - this.getListener().update(); + if (this.#entityNeedsUpdate) { + this.#entity.position = this.get("position").value; + this.#entity.rotation = this.get("rotation").value; + this.#entity.scale = this.get("scale").value; + this.#entityNeedsUpdate = false; + } } /** * * @returns {ComponentInterface} */ - getComponent() { + get component() { return this; } diff --git a/libraries/objectDefaultFiles/scene/TransformComponentStore.js b/libraries/objectDefaultFiles/scene/TransformComponentStore.js deleted file mode 100644 index 55199042f..000000000 --- a/libraries/objectDefaultFiles/scene/TransformComponentStore.js +++ /dev/null @@ -1,72 +0,0 @@ -import ObjectStore from "./ObjectStore.js"; -import Vector3Node from "./Vector3Node.js"; -import TriggerVector3Store from "./TriggerVector3Store.js"; -import QuaternionNode from "./QuaternionNode.js"; -import TriggerQuaternionStore from "./TriggerQuaternionStore.js"; - -/** - * @typedef {import("./ObjectNode.js").NodeDict} NodeDict - * @typedef {import("./TransformComponentNode.js").default} TransformComponentNode - * @typedef {import("./EntityNode.js").default} EntityNode - */ - -class TransformComponentStore extends ObjectStore { - /** @type {Engine3DPositionNode} */ - #position; - - /** @type {Engine3DRotationNode} */ - #rotation; - - /** @type {Engine3DScaleNode} */ - #scale; - - /** @type {EntityInterface} */ - #entity; - - /** @type {boolean} */ - #entityNeedsUpdate; - - /** - * @param {Vector3Value} position - * @param {QuaternionValue} rotation - * @param {vecotr3Value} scale - */ - constructor(position, rotation, scale) { - super(); - this.#position = new Vector3Node(new TriggerVector3Store(() => {this.#entityNeedsUpdate = true;}, position)); - this.#rotation = new QuaternionNode(new TriggerQuaternionStore(() => {this.#entityNeedsUpdate = true;}, rotation)); - this.#scale = new Vector3Node(new TriggerVector3Store(() => {this.#entityNeedsUpdate = true;}, scale)); - this.#entityNeedsUpdate = true; - } - - /** - * @override - * @param {TransfromComponentNode} _thisNode - * @returns {NodeDict} - */ - getProperties(_thisNode) { - return { - "position": this.#position, - "rotation": this.#rotation, - "scale": this.#scale - }; - } - - /** - * - * @param {EntityNode} entityNode - */ - setEntityNode(entityNode) { - this.#entity = entityNode.getEntity(); - } - - update() { - if (this.#entityNeedsUpdate) { - this.#entity.setPosition(this.#position.getValue()); - this.#entity.setRotation(this.#rotation.getValue()); - this.#entity.setScale(this.#scale.getValue()); - } - } -} - -export default TransformComponentStore; diff --git a/libraries/objectDefaultFiles/scene/TriggerColorStore.js b/libraries/objectDefaultFiles/scene/TriggerColorStore.js deleted file mode 100644 index b81d43bf0..000000000 --- a/libraries/objectDefaultFiles/scene/TriggerColorStore.js +++ /dev/null @@ -1,31 +0,0 @@ -import ColorStore from "./ColorStore.js"; - -/** - * @typedef {() => void} onChangedFunc - */ - -class TriggerColorStore extends ColorStore { - #onChanged; - - /** - * - * @param {ColorValue} value - * @param {onChangedFunc} onChangedFunc - */ - constructor(onChanged, value = {r: 0, g: 0, b: 0}) { - super(value); - this.#onChanged = onChanged; - } - - /** - * @override - * @param {ObjectNodeDelta} delta - * @param {(delta: ObjectNodeDelta) => void} defaultApplyChanges - */ - applyChanges(delta, defaultApplyChanges) { - defaultApplyChanges(delta); - this.#onChanged(); - } -}; - -export default TriggerColorStore; diff --git a/libraries/objectDefaultFiles/scene/TriggerEulerAnglesStore.js b/libraries/objectDefaultFiles/scene/TriggerEulerAnglesStore.js deleted file mode 100644 index dab7c0560..000000000 --- a/libraries/objectDefaultFiles/scene/TriggerEulerAnglesStore.js +++ /dev/null @@ -1,31 +0,0 @@ -import EulerAnglesStore from "./EulerAnglesStore.js"; - -/** - * @typedef {() => void} onChangedFunc - */ - -class TriggerEulerAnglesStore extends EulerAnglesStore { - #onChanged; - - /** - * - * @param {EulerAnglesValue} value - * @param {onChangedFunc} onChangedFunc - */ - constructor(onChanged, value = {x: 0, y: 0, z: 0, order: "XYZ"}) { - super(value); - this.#onChanged = onChanged; - } - - /** - * @override - * @param {ObjectNodeDelta} delta - * @param {(delta: ObjectNodeDelta) => void} defaultApplyChanges - */ - applyChanges(delta, defaultApplyChanges) { - defaultApplyChanges(delta); - this.#onChanged(); - } -}; - -export default TriggerEulerAnglesStore; diff --git a/libraries/objectDefaultFiles/scene/TriggerQuaternionStore.js b/libraries/objectDefaultFiles/scene/TriggerQuaternionStore.js deleted file mode 100644 index 4a3844dc9..000000000 --- a/libraries/objectDefaultFiles/scene/TriggerQuaternionStore.js +++ /dev/null @@ -1,31 +0,0 @@ -import QuaternionStore from "./QuaternionStore.js"; - -/** - * @typedef {() => void} onChangedFunc - */ - -class TriggerQuaternionStore extends QuaternionStore { - #onChanged; - - /** - * - * @param {QuaternionValue} value - * @param {onChangedFunc} onChangedFunc - */ - constructor(onChanged, value = {x: 0, y: 0, z: 0, w: 0}) { - super(value); - this.#onChanged = onChanged; - } - - /** - * @override - * @param {ObjectNodeDelta} delta - * @param {(delta: ObjectNodeDelta) => void} defaultApplyChanges - */ - applyChanges(delta, defaultApplyChanges) { - defaultApplyChanges(delta); - this.#onChanged(); - } -}; - -export default TriggerQuaternionStore; diff --git a/libraries/objectDefaultFiles/scene/TriggerTextureStore.js b/libraries/objectDefaultFiles/scene/TriggerTextureStore.js deleted file mode 100644 index cc4c434b5..000000000 --- a/libraries/objectDefaultFiles/scene/TriggerTextureStore.js +++ /dev/null @@ -1,31 +0,0 @@ -import TextureStore from "./TextureStore.js"; - -/** - * @typedef {() => void} onChangedFunc - */ - -class TriggerTextureStore extends TextureStore { - #onChanged; - - /** - * - * @param {TextureValue} value - * @param {onChangedFunc} onChangedFunc - */ - constructor(onChanged, value = {id: null}) { - super(value); - this.#onChanged = onChanged; - } - - /** - * @override - * @param {ObjectNodeDelta} delta - * @param {(delta: ObjectNodeDelta) => void} defaultApplyChanges - */ - applyChanges(delta, defaultApplyChanges) { - defaultApplyChanges(delta); - this.#onChanged(); - } -}; - -export default TriggerTextureStore; diff --git a/libraries/objectDefaultFiles/scene/TriggerVector2Store.js b/libraries/objectDefaultFiles/scene/TriggerVector2Store.js deleted file mode 100644 index 91dc851af..000000000 --- a/libraries/objectDefaultFiles/scene/TriggerVector2Store.js +++ /dev/null @@ -1,31 +0,0 @@ -import Vector2Store from "./Vector2Store.js"; - -/** - * @typedef {() => void} onChangedFunc - */ - -class TriggerVector2Store extends Vector2Store { - #onChanged; - - /** - * - * @param {Vector2Value} value - * @param {onChangedFunc} onChangedFunc - */ - constructor(onChanged, value = {x: 0, y: 0}) { - super(value); - this.#onChanged = onChanged; - } - - /** - * @override - * @param {ObjectNodeDelta} delta - * @param {(delta: ObjectNodeDelta) => void} defaultApplyChanges - */ - applyChanges(delta, defaultApplyChanges) { - defaultApplyChanges(delta); - this.#onChanged(); - } -}; - -export default TriggerVector2Store; diff --git a/libraries/objectDefaultFiles/scene/TriggerVector3Store.js b/libraries/objectDefaultFiles/scene/TriggerVector3Store.js deleted file mode 100644 index f68cb21d7..000000000 --- a/libraries/objectDefaultFiles/scene/TriggerVector3Store.js +++ /dev/null @@ -1,31 +0,0 @@ -import Vector3Store from "./Vector3Store.js"; - -/** - * @typedef {() => void} onChangedFunc - */ - -class TriggerVector3Store extends Vector3Store { - #onChanged; - - /** - * - * @param {Vector3Value} value - * @param {onChangedFunc} onChangedFunc - */ - constructor(onChanged, value = {x: 0, y: 0, z: 0}) { - super(value); - this.#onChanged = onChanged; - } - - /** - * @override - * @param {ObjectNodeDelta} delta - * @param {(delta: ObjectNodeDelta) => void} defaultApplyChanges - */ - applyChanges(delta, defaultApplyChanges) { - defaultApplyChanges(delta); - this.#onChanged(); - } -}; - -export default TriggerVector3Store; diff --git a/libraries/objectDefaultFiles/scene/ValueNode.js b/libraries/objectDefaultFiles/scene/ValueNode.js index 5241ed47f..3ecd1b823 100644 --- a/libraries/objectDefaultFiles/scene/ValueNode.js +++ b/libraries/objectDefaultFiles/scene/ValueNode.js @@ -10,7 +10,7 @@ import BaseNode from "./BaseNode.js"; * @template {Value} T * @typedef {{value?: T} & BaseNodeDelta} ValueNodeDelta * @template {Value} T - * @typedef {(value: ValueNode) => void} onChangedFunc + * @typedef {(node: ValueNode) => void} onChangedFunc */ /** @@ -26,7 +26,7 @@ class ValueNode extends BaseNode { /** @type {boolean}*/ #isValueDirty; - /** @type {onChangedFunc} */ + /** @type {onChangedFunc|null} */ #onChanged; /** @@ -59,8 +59,8 @@ class ValueNode extends BaseNode { /** * @param {onChangedFunc} onChangedFunc */ - set onChanged(onChangedFunc) { - this.#onChanged = onChangedFunc; + set onChanged(onChanged) { + this.#onChanged = onChanged; } /** diff --git a/libraries/objectDefaultFiles/scene/Vector2Node.js b/libraries/objectDefaultFiles/scene/Vector2Node.js index 71f14649b..7ba7fa6d8 100644 --- a/libraries/objectDefaultFiles/scene/Vector2Node.js +++ b/libraries/objectDefaultFiles/scene/Vector2Node.js @@ -1,4 +1,5 @@ import ObjectNode from "./ObjectNode.js"; +import ValueNode from "./ValueNode.js"; /** * @typedef {import("./BaseNode.js").BaseNodeDelta} BaseNodeDelta @@ -6,23 +7,64 @@ import ObjectNode from "./ObjectNode.js"; * @typedef {import("./ObjectNode.js").ObjectInterface} ObjectInterface * @typedef {{x: number, y: number}} Vector2Value * @typedef {BaseNodeDelta & {properties?: {x?: ValueNodeDelta, y?: ValueNodeDelta}}} Vector2Delta + * @typedef {(node: Vector2Node) => void} onChangedFunc */ class Vector2Node extends ObjectNode { static TYPE = "Object.Vector2"; + /** @type {onChangedFunc} */ + #onChanged + /** * - * @param {ObjectInterface} listener + * @param {Vector2Value} value + */ + constructor(value = {x: 0, y: 0}) { + super(Vector2Node.TYPE); + this.#addValue("x", value.x); + this.#addValue("y", value.y); + this.#onChanged = null; + } + + /** + * + * @param {string} key + * @param {number} value + */ + #addValue(key, value) { + const node = new ValueNode(value); + node.onChanged = (_node) => {this.#safeOnChanged();}; + this._set(key, node); + } + + /** + * + */ + #safeOnChanged() { + if (this.#onChanged) { + this.#onChanged(this); + } + } + + /** + * @returns {onChangedFunc} + */ + get onChanged() { + return this.#onChanged; + } + + /** + * @param {onChangedFunc} onChanged */ - constructor(listener) { - super(listener, Vector2Node.TYPE); + set onChanged(onChanged) { + this.#onChanged = onChanged; } /** * @param {Vector2Value} value */ - setValue(value) { + set value(value) { this.get("x").value = value.x; this.get("y").value = value.y; } @@ -31,7 +73,7 @@ class Vector2Node extends ObjectNode { * * @returns {Vector2Value} */ - getValue() { + get value() { return { "x": this.get("x").value, "y": this.get("y").value diff --git a/libraries/objectDefaultFiles/scene/Vector2Store.js b/libraries/objectDefaultFiles/scene/Vector2Store.js deleted file mode 100644 index 035fb7a4c..000000000 --- a/libraries/objectDefaultFiles/scene/Vector2Store.js +++ /dev/null @@ -1,36 +0,0 @@ -import ObjectStore from "./ObjectStore.js"; -import ValueNode from "./ValueNode.js"; - -/** - * @typedef {import("./ObjectNode.js").NodeDict} NodeDict - * @typedef {import("./Vector2Node.js").default} Vector2Node - * @typedef {import("./Vector2Node.js").Vector2Value} Vector2Value - */ - -class Vector2Store extends ObjectStore { - /** @type {Vector2Value} */ - #initValues; - - /** - * - * @param {Vector2Value} value - */ - constructor(value = {x: 0, y: 0}) { - super(); - this.#initValues = value; - } - - /** - * @override - * @param {Vector2Node} _thisNode - * @returns {{x: ValueNode, y: ValueNode}} - */ - getProperties(_thisNode) { - return { - "x": new ValueNode(this.#initValues.x), - "y": new ValueNode(this.#initValues.y) - }; - } -} - -export default Vector2Store; diff --git a/libraries/objectDefaultFiles/scene/Vector3Node.js b/libraries/objectDefaultFiles/scene/Vector3Node.js index 46d65c5a4..aca189345 100644 --- a/libraries/objectDefaultFiles/scene/Vector3Node.js +++ b/libraries/objectDefaultFiles/scene/Vector3Node.js @@ -1,4 +1,5 @@ import ObjectNode from "./ObjectNode.js"; +import ValueNode from "./ValueNode.js"; /** * @typedef {import("./BaseNode.js").BaseNodeDelta} BaseNodeDelta @@ -6,23 +7,65 @@ import ObjectNode from "./ObjectNode.js"; * @typedef {import("./ObjectNode.js").ObjectInterface} ObjectInterface * @typedef {{x: number, y: number, z: number}} Vector3Value * @typedef {BaseNodeDelta & {properties?: {x?: ValueNodeDelta, y?: ValueNodeDelta, z?: ValueNodeDelta}}} Vector3Delta + * @typedef {(node: Vector3Node) => void} onChangedFunc */ class Vector3Node extends ObjectNode { static TYPE = "Object.Vector3"; + /** @type {onChangedFunc} */ + #onChanged + /** * - * @param {ObjectInterface} listener + * @param {Vector3Value} value + */ + constructor(value = {x: 0, y: 0, z: 0}) { + super(Vector3Node.TYPE); + this.#addValue("x", value.x); + this.#addValue("y", value.y); + this.#addValue("z", value.z); + this.#onChanged = null; + } + + /** + * + * @param {string} key + * @param {number} value + */ + #addValue(key, value) { + const node = new ValueNode(value); + node.onChanged = (_node) => {this.#safeOnChanged();}; + this._set(key, node); + } + + /** + * + */ + #safeOnChanged() { + if (this.#onChanged) { + this.#onChanged(this); + } + } + + /** + * @returns {onChangedFunc} + */ + get onChanged() { + return this.#onChanged; + } + + /** + * @param {onChangedFunc} onChanged */ - constructor(listener) { - super(listener, Vector3Node.TYPE); + set onChanged(onChanged) { + this.#onChanged = onChanged; } /** * @param {Vector3Value} value */ - setValue(value) { + set value(value) { this.get("x").value = value.x; this.get("y").value = value.y; this.get("z").value = value.z; @@ -32,7 +75,7 @@ class Vector3Node extends ObjectNode { * * @returns {Vector3Value} */ - getValue() { + get value() { return { "x": this.get("x").value, "y": this.get("y").value, diff --git a/libraries/objectDefaultFiles/scene/Vector3Store.js b/libraries/objectDefaultFiles/scene/Vector3Store.js deleted file mode 100644 index 0880188eb..000000000 --- a/libraries/objectDefaultFiles/scene/Vector3Store.js +++ /dev/null @@ -1,37 +0,0 @@ -import ObjectStore from "./ObjectStore.js"; -import ValueNode from "./ValueNode.js"; - -/** - * @typedef {import("./ObjectNode.js").NodeDict} NodeDict - * @typedef {import("./Vector3Node.js").default} Vector3Node - * @typedef {import("./Vector3Node.js").Vector3Value} Vector3Value - */ - -class Vector3Store extends ObjectStore { - /** @type {Vector3Value} */ - #initValues; - - /** - * - * @param {Vector3Value} value - */ - constructor(value = {x: 0, y: 0, z: 0}) { - super(); - this.#initValues = value; - } - - /** - * @override - * @param {Vector3Node} _thisNode - * @returns {{x: ValueNode, y: ValueNode, z: ValueNode}} - */ - getProperties(_thisNode) { - return { - "x": new ValueNode(this.#initValues.x), - "y": new ValueNode(this.#initValues.y), - "z": new ValueNode(this.#initValues.z) - }; - } -} - -export default Vector3Store; diff --git a/libraries/objectDefaultFiles/scene/VisibilityComponentNode.js b/libraries/objectDefaultFiles/scene/VisibilityComponentNode.js index ae7db7cf5..68437a293 100644 --- a/libraries/objectDefaultFiles/scene/VisibilityComponentNode.js +++ b/libraries/objectDefaultFiles/scene/VisibilityComponentNode.js @@ -11,16 +11,16 @@ class VisibilityComponentNode extends ValueNode { } setEntityNode(entityNode) { - this.#entity = entityNode.getEntity(); + this.#entity = entityNode.entity; } update() { - if (this.#entity.isVisible() !== this.value) { - this.#entity.setVisible(this.value); + if (this.#entity.isVisible !== this.value) { + this.#entity.isVisible = this.value; } } - getComponent() { + get component() { return this; } diff --git a/libraries/objectDefaultFiles/scene/WorldNode.js b/libraries/objectDefaultFiles/scene/WorldNode.js index eabc66fe0..3e1ad49b9 100644 --- a/libraries/objectDefaultFiles/scene/WorldNode.js +++ b/libraries/objectDefaultFiles/scene/WorldNode.js @@ -1,4 +1,6 @@ import ObjectNode from "./ObjectNode.js"; +import AnchoredGroupNode from "./AnchoredGroupNode.js"; +import ToolsRootNode from "./ToolsRootNode.js"; /** * @typedef {import("./BaseNode.js").BaseNodeState} BaseNodeState @@ -8,23 +10,38 @@ import ObjectNode from "./ObjectNode.js"; * @typedef {{properties: {threejsContainer: AnchoredGroupNodeState, tools: ToolsRootNodeState}} & BaseNodeState} WorldNodeState * @typedef {{properties?: {threejsContainer?: AnchoredGroupNodeDelta tools?: ToolsRootNodeDelta}} & BaseNodeDelta} WorldNodeDelta * @typedef {import("./ObjectNode.js").ObjectInterface} ObjectInterface + * @typedef {import("./DateTimer.js").DateTimer} DateTimer */ class WorldNode extends ObjectNode { static TYPE = "Object.World"; + /** @type {DateTimer} */ + #timer + /** * - * @param {ObjectInterface} listener + * @param {DateTimer} timer */ - constructor(listener) { - super(listener, WorldNode.TYPE); + constructor(timer) { + super(WorldNode.TYPE); + this._set("threejsContainer", new AnchoredGroupNode()); + this._set("tools", new ToolsRootNode()); + this.#timer = timer; } - getTimer() { - return this.getListener().getTimer(); + /** + * @returns {DateTimer} + */ + get timer() { + return this.#timer; } + /** + * + * @param {string} toolId + * @returns {WorldNodeState} + */ getStateForTool(toolId) { const ret = super.getState(); ret.properties = {}; diff --git a/libraries/objectDefaultFiles/scene/WorldStore.js b/libraries/objectDefaultFiles/scene/WorldStore.js deleted file mode 100644 index cc11b1ca9..000000000 --- a/libraries/objectDefaultFiles/scene/WorldStore.js +++ /dev/null @@ -1,41 +0,0 @@ -import ObjectStore from "./ObjectStore.js"; -import AnchoredGroupNode from "./AnchoredGroupNode.js"; -import AnchoredGroupStore from "./AnchoredGroupStore.js"; -import ToolsRootStore from "./ToolsRootStore.js"; -import ToolsRootNode from "./ToolsRootNode.js"; - -/** - * @typedef {import("./ObjectNode.js").NodeDict} NodeDict - * @typedef {import("./WorldNode.js").default} WorldNode - */ - -class WorldStore extends ObjectStore { - /** @type {Timer} */ - #timer; - - /** - * - */ - constructor(timer) { - super(); - this.#timer = timer; - } - - getTimer() { - return this.#timer; - } - - /** - * @override - * @param {WorldNode} _node - * @returns {NodeDict} - */ - getProperties(_node) { - return { - "threejsContainer": new AnchoredGroupNode(new AnchoredGroupStore()), - "tools": new ToolsRootNode(new ToolsRootStore()) - }; - } -} - -export default WorldStore; diff --git a/tests/modules/BaseNode.test.js b/tests/modules/BaseNode.test.js index cc7ed0771..3e0393682 100644 --- a/tests/modules/BaseNode.test.js +++ b/tests/modules/BaseNode.test.js @@ -50,24 +50,24 @@ describe("BaseNode", () => { test("No parent set after constructor", () => { const node = new BaseNode("test"); - expect(node.getParent()).toBeNull(); + expect(node.parent).toBeNull(); }); test("No parent set after setParent null", () => { const parent = new BaseNode("test"); const node = new BaseNode("test"); - node.setParent(parent); + node.parent = parent; - node.setParent(null); + node.parent = null; - expect(node.getParent()).toBeNull(); + expect(node.parent).toBeNull(); }); test("Parent set after setParent", () => { const parent = new BaseNode("test"); const node = new BaseNode("test"); - node.setParent(parent); + node.parent = parent; - expect(node.getParent()).toBe(parent); + expect(node.parent).toBe(parent); }); test("Type set after constructor", () => { const node = new BaseNode("test"); @@ -148,7 +148,7 @@ describe("BaseNode", () => { test("setParentDirty walker sets parent dirty", () => { const mockNode = new MockNode(); const node = new BaseNode("test"); - node.setParent(mockNode); + node.parent = mockNode; BaseNode.setParentDirty(node); @@ -158,9 +158,9 @@ describe("BaseNode", () => { const mockNode2 = new MockNode(); const mockNode = new MockNode(); const node = new BaseNode("test"); - node.setParent(mockNode); + node.parent = mockNode; mockNode.setTypeDirty(); - mockNode.setParent(mockNode2); + mockNode.parent = mockNode2; BaseNode.setParentDirty(node); @@ -174,9 +174,9 @@ describe("BaseNode", () => { test("getName with parent", () => { const parent = new MockNode(); const node = new BaseNode("test"); - node.setParent(parent); + node.parent = parent; const test2 = new BaseNode("test"); - test2.setParent(parent); + test2.parent = parent; parent.setProperties({test: node, test2: test2}); expect(node.getName()).toBe("test"); @@ -184,11 +184,11 @@ describe("BaseNode", () => { test("getName with corrupt parent", () => { const parent = new MockNode(); const node = new BaseNode("test"); - node.setParent(parent); + node.parent = parent; const test1 = new BaseNode("test"); - test1.setParent(parent); + test1.parent = parent; const test2 = new BaseNode("test"); - test2.setParent(parent); + test2.parent = parent; parent.setProperties({test: test1, test2: test2}); expect(node.getName()).toBeNull(); diff --git a/tests/modules/DictionaryNode.test.js b/tests/modules/DictionaryNode.test.js index f14a51f4f..e253e89e4 100644 --- a/tests/modules/DictionaryNode.test.js +++ b/tests/modules/DictionaryNode.test.js @@ -2,106 +2,64 @@ import {expect, jest, test, describe} from "@jest/globals"; import BaseNode from "../../libraries/objectDefaultFiles/scene/BaseNode.js"; import DictionaryNode from "../../libraries/objectDefaultFiles/scene/DictionaryNode.js"; import DeleteNode from "../../libraries/objectDefaultFiles/scene/DeleteNode.js"; +import ValueNode from "../../libraries/objectDefaultFiles/scene/ValueNode.js"; +import VersionedNode from "../../libraries/objectDefaultFiles/scene/VersionedNode.js"; -class MockDictionaryStore { - mockCreate = jest.fn((_key, _state) => {return new BaseNode("test");}); - mockCast = jest.fn((_key, _old, _state) => {return new BaseNode("test");}); - mockDelete = jest.fn((_key, _old) => {return true;}); - mockApplyChanges = jest.fn((delta, defaultApplyChanges) => {defaultApplyChanges(delta);}); - - constructor() { - } - - create(key, state) { - return this.mockCreate(key, state); - } - - cast(key, old, state) { - return this.mockCast(key, old, state); - } - - delete(key, old) { - return this.mockDelete(key, old); - } - - applyChanges(delta, defaultApplyChanges) { - this.mockApplyChanges(delta, defaultApplyChanges); - } -} - -class MockFailDictionaryStore { - mockCreate = jest.fn((_key, _state) => {return undefined;}); - mockCast = jest.fn((_key, _old, _state) => {return undefined;}); - mockDelete = jest.fn((_key, _old) => {return false;}); - mockApplyChanges = jest.fn((delta, defaultApplyChanges) => {defaultApplyChanges(delta);}); - +class NoDeleteDictionaryNode extends DictionaryNode { constructor() { + super(DictionaryNode.TYPE); } - create(key, state) { - return this.mockCreate(key, state); - } - - cast(key, old, state) { - return this.mockCast(key, old, state); - } - - delete(key, old) { - return this.mockDelete(key, old); + _canDelete(_key, _old) { + return false; } - applyChanges(delta, defaultApplyChanges) { - this.mockApplyChanges(delta, defaultApplyChanges); + _cast(_key, _old, _state) { + return undefined; } -} +}; describe("DictionaryNode", () => { const emptyState = {type: "Object.Dictionary", properties: {}}; const emptyState2Deleted = {type: "Object.Dictionary", properties: {test1: {type: "Deleted"}, test2: {type: "Deleted"}}}; - const state1 = {type: "Object.Dictionary", properties: {test1: {type: "test"}}}; - const state2 = {type: "Object.Dictionary", properties: {test1: {type: "test"}, test2: {type: "test"}}}; - const state1of2deleted = {type: "Object.Dictionary", properties: {test1: {type: "Deleted"}, test2: {type: "test"}}}; + const state1 = {type: "Object.Dictionary", properties: {test1: emptyState}}; + const state2 = {type: "Object.Dictionary", properties: {test1: emptyState, test2: emptyState}}; + const state1of2deleted = {type: "Object.Dictionary", properties: {test1: {type: "Deleted"}, test2: emptyState}}; test("constructor", () => { - expect(() => {new DictionaryNode(new MockDictionaryStore());}).not.toThrow(); + expect(() => {new DictionaryNode();}).not.toThrow(); }); test("constructor sets correct custom type", () => { - const node = new DictionaryNode(new MockDictionaryStore(), "test"); + const node = new DictionaryNode("test"); expect(node.getType()).toBe("test"); }); - test("constructor sets listener", () => { - const listener = new MockDictionaryStore({}); - const node = new DictionaryNode(listener, "test"); - - expect(node.getListener()).toBe(listener); - }); test("clear with empty properties", () => { - const node = new DictionaryNode(new MockDictionaryStore()); + const node = new DictionaryNode(); node.clear(); expect(node.getState()).toStrictEqual(emptyState); }); test("clear with empty properties does not set node dirty", () => { - const node = new DictionaryNode(new MockDictionaryStore()); + const node = new DictionaryNode(); node.clear(); expect(node.isDirty()).toBe(false); }); test("clear with properties", () => { - const node = new DictionaryNode(new MockDictionaryStore()); - node.set("test1", new BaseNode("test")); - node.set("test2", new BaseNode("test")); + const node = new DictionaryNode(); + node.set("test1", new DictionaryNode()); + node.set("test2", new DictionaryNode()); node.clear(); expect(node.getState()).toStrictEqual(emptyState2Deleted); }); test("clear with properties sets node dirty", () => { - const node = new DictionaryNode(new MockDictionaryStore()); - node.set("test1", new BaseNode("test")); - node.set("test2", new BaseNode("test")); + const node = new DictionaryNode(); + node.set("test1", new DictionaryNode()); + node.set("test2", new DictionaryNode()); node.getChanges(); node.clear(); @@ -109,41 +67,41 @@ describe("DictionaryNode", () => { expect(node.isDirty()).toBe(true); }); test("delete with empty properties", () => { - const node = new DictionaryNode(new MockDictionaryStore()); + const node = new DictionaryNode(); node.delete("test"); expect(node.getState()).toStrictEqual(emptyState); }); test("delete with empty properties does not set node dirty", () => { - const node = new DictionaryNode(new MockDictionaryStore()); + const node = new DictionaryNode(); node.delete("test"); expect(node.isDirty()).toBe(false); }); test("delete with missing property", () => { - const node = new DictionaryNode(new MockDictionaryStore()); - node.set("test1", new BaseNode("test")); - node.set("test2", new BaseNode("test")); + const node = new DictionaryNode(); + node.set("test1", new DictionaryNode()); + node.set("test2", new DictionaryNode()); node.delete("test"); expect(node.getState()).toStrictEqual(state2); }); test("delete with properties", () => { - const node = new DictionaryNode(new MockDictionaryStore()); - node.set("test1", new BaseNode("test")); - node.set("test2", new BaseNode("test")); + const node = new DictionaryNode(); + node.set("test1", new DictionaryNode()); + node.set("test2", new DictionaryNode()); node.delete("test1"); expect(node.getState()).toStrictEqual(state1of2deleted); }); test("delete with properties previously deleted", () => { - const node = new DictionaryNode(new MockDictionaryStore()); - node.set("test1", new BaseNode("test")); - node.set("test2", new BaseNode("test")); + const node = new DictionaryNode(); + node.set("test1", new DictionaryNode()); + node.set("test2", new DictionaryNode()); node.delete("test1"); node.delete("test1"); @@ -151,9 +109,9 @@ describe("DictionaryNode", () => { expect(node.getState()).toStrictEqual(state1of2deleted); }); test("delete with properties sets node dirty", () => { - const node = new DictionaryNode(new MockDictionaryStore()); - node.set("test1", new BaseNode("test")); - node.set("test2", new BaseNode("test")); + const node = new DictionaryNode(); + node.set("test1", new DictionaryNode()); + node.set("test2", new DictionaryNode()); node.getChanges(); node.delete("test1"); @@ -161,27 +119,27 @@ describe("DictionaryNode", () => { expect(node.isDirty()).toBe(true); }); test("entries with no properties", () => { - const node = new DictionaryNode(new MockDictionaryStore()); + const node = new DictionaryNode(); expect(node.entries()).toHaveLength(0); }); test("entries with properties", () => { - const child = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test", child); expect(node.entries()).toStrictEqual([["test", child]]); }); test("entries with deleted properties", () => { - const child = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test", child); node.set("deleted", new DeleteNode()); expect(node.entries()).toStrictEqual([["test", child]]); }); test("forEach with no properties", () => { - const node = new DictionaryNode(new MockDictionaryStore()); + const node = new DictionaryNode(); const mockCallback = jest.fn((_value, _key, _map) => {}); node.forEach(mockCallback); @@ -189,9 +147,9 @@ describe("DictionaryNode", () => { expect(mockCallback).not.toHaveBeenCalled(); }); test("forEach with properties", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); const mockCallback = jest.fn((_value, _key, _map) => {}); @@ -207,9 +165,9 @@ describe("DictionaryNode", () => { expect(mockCallback.mock.calls[1][2]).toBe(node); }); test("forEach with deleted properties", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); node.set("deleted", new DeleteNode()); @@ -226,9 +184,9 @@ describe("DictionaryNode", () => { expect(mockCallback.mock.calls[1][2]).toBe(node); }); test("forEach with properties with custom this", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); const obj = {func: jest.fn((_value, _key, _map) => {})}; @@ -244,101 +202,101 @@ describe("DictionaryNode", () => { expect(obj.func.mock.calls[1][2]).toBe(node); }); test("has with no properties", () => { - const node = new DictionaryNode(new MockDictionaryStore()); + const node = new DictionaryNode(); expect(node.has("test2")).toBe(false); }); test("has with properties but missing property", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); expect(node.has("test3")).toBe(false); }); test("has with properties", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); expect(node.has("test2")).toBe(true); }); test("has with property deleted", () => { - const node = new DictionaryNode(new MockDictionaryStore()); + const node = new DictionaryNode(); node.set("test1", new DeleteNode()); expect(node.has("test1")).toBe(false); }); test("keys with no properties", () => { - const node = new DictionaryNode(new MockDictionaryStore()); + const node = new DictionaryNode(); expect(node.keys()).toStrictEqual([]); }); test("keys with properties", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); expect(node.keys()).toStrictEqual(["test1", "test2"]); }); test("keys with property deleted", () => { - const child1 = new BaseNode("test"); + const child1 = new DictionaryNode(); const child2 = new DeleteNode(); - const node = new DictionaryNode(new MockDictionaryStore()); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); expect(node.keys()).toStrictEqual(["test1"]); }); test("set new property", () => { - const child1 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); expect(node.getState()).toStrictEqual(state1); }); test("set new property sets node dirty", () => { - const child1 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); expect(node.isDirty()).toBe(true); }); test("set new property sets child dirty", () => { - const child1 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); expect(child1.isDirty()).toBe(true); }); test("set new property (makeDirty false) doesn't set node dirty", () => { - const child1 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1, false); expect(node.isDirty()).toBe(false); }); test("set new property (makeDirty false) deosn't set child dirty", () => { - const child1 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1, false); expect(child1.isDirty()).toBe(false); }); test("set existing property", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child2); node.set("test1", child1); @@ -346,9 +304,9 @@ describe("DictionaryNode", () => { expect(node.getState()).toStrictEqual(state1); }); test("set existing property sets node dirty", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child2); node.getChanges(); @@ -357,9 +315,9 @@ describe("DictionaryNode", () => { expect(node.isDirty()).toBe(true); }); test("set existing property sets child dirty", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child2); node.getChanges(); @@ -368,32 +326,32 @@ describe("DictionaryNode", () => { expect(child1.isDirty()).toBe(true); }); test("values with no properties", () => { - const node = new DictionaryNode(new MockDictionaryStore()); + const node = new DictionaryNode(); expect(node.values()).toStrictEqual([]); }); test("values with properties", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); expect(node.values()).toStrictEqual([child1, child2]); }); test("values with property deleted", () => { - const child1 = new BaseNode("test"); + const child1 = new DictionaryNode(); const child2 = new DeleteNode(); - const node = new DictionaryNode(new MockDictionaryStore()); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); expect(node.values()).toStrictEqual([child1]); }); test("get state when node dirty", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); @@ -401,17 +359,17 @@ describe("DictionaryNode", () => { }); test("get state includes delete node", () => { const child1 = new DeleteNode(); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); expect(node.getState()).toStrictEqual(state1of2deleted); }); test("get state when node not dirty", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); node.getChanges(); @@ -419,9 +377,9 @@ describe("DictionaryNode", () => { expect(node.getState()).toStrictEqual(state2); }); test("set state empty, clears dictionary", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); @@ -430,9 +388,9 @@ describe("DictionaryNode", () => { expect(node.getState()).toStrictEqual(emptyState); }); test("set state with new properties", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.setState({type: DictionaryNode.TYPE, properties: {"test1": child1.getState(), "test2": child2.getState()}}); @@ -441,9 +399,9 @@ describe("DictionaryNode", () => { test("set state with property cast", () => { const child1 = new BaseNode("test1"); const child2 = new BaseNode("test1"); - const newChild1 = new BaseNode("test"); - const newChild2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const newChild1 = new DictionaryNode(); + const newChild2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); @@ -452,11 +410,11 @@ describe("DictionaryNode", () => { expect(node.getState()).toStrictEqual(state2); }); test("set state with property with same types", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const newChild1 = new BaseNode("test"); - const newChild2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const newChild1 = new DictionaryNode(); + const newChild2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); @@ -467,9 +425,9 @@ describe("DictionaryNode", () => { expect(node.get("test2")).toBe(child2); }); test("set state with property without type updates properties", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); @@ -480,9 +438,9 @@ describe("DictionaryNode", () => { expect(node.get("test2")).toBe(child2); }); test("get changes when node dirty", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); node.setTypeDirty(); @@ -491,8 +449,8 @@ describe("DictionaryNode", () => { }); test("get changes includes delete node", () => { const child1 = new DeleteNode(); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); node.setTypeDirty(); @@ -502,7 +460,7 @@ describe("DictionaryNode", () => { test("get changes when node not dirty", () => { const child1 = new BaseNode("test"); const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); node.getChanges(); @@ -512,7 +470,7 @@ describe("DictionaryNode", () => { test("get changes with one child dirty", () => { const child1 = new BaseNode("test"); const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); node.getChanges(); @@ -521,9 +479,9 @@ describe("DictionaryNode", () => { expect(node.getChanges()).toStrictEqual({properties: {"test1": {type: "test"}}}); }); test("set changes with empty changeset changes nothing", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); @@ -532,9 +490,9 @@ describe("DictionaryNode", () => { expect(node.getState()).toStrictEqual(state2); }); test("set changes with new properties", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.setChanges({type: DictionaryNode.TYPE, properties: {test1: child1.getState(), test2: child2.getState()}}); @@ -543,9 +501,9 @@ describe("DictionaryNode", () => { test("set changes with property cast", () => { const child1 = new BaseNode("test1"); const child2 = new BaseNode("test1"); - const newChild1 = new BaseNode("test"); - const newChild2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const newChild1 = new DictionaryNode(); + const newChild2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); @@ -554,11 +512,11 @@ describe("DictionaryNode", () => { expect(node.getState()).toStrictEqual(state2); }); test("set changes of existing properties with same type", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const newChild1 = new BaseNode("test"); - const newChild2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const newChild1 = new DictionaryNode(); + const newChild2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); @@ -569,9 +527,9 @@ describe("DictionaryNode", () => { expect(node.get("test2")).toBe(child2); }); test("set changes of existing properties without type info", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); @@ -582,9 +540,9 @@ describe("DictionaryNode", () => { expect(node.get("test2")).toBe(child2); }); test("set changes with deleted properties", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); @@ -593,9 +551,9 @@ describe("DictionaryNode", () => { expect(node.getState()).toStrictEqual(state1); }); test("set changes with no properties", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockDictionaryStore()); + const child1 = new DictionaryNode(); + const child2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); @@ -603,23 +561,12 @@ describe("DictionaryNode", () => { expect(node.getState()).toStrictEqual(state2); }); - test("set changes with deleted properties, fail", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockFailDictionaryStore()); - node.set("test1", child1); - node.set("test2", child2); - - node.setChanges({type: DictionaryNode.TYPE, properties: {"test1": new DeleteNode().getState()}}); - - expect(node.getState()).toStrictEqual(state2); - }); test("set changes with property cast, fail", () => { const child1 = new BaseNode("test"); const child2 = new BaseNode("test"); - const newChild1 = new BaseNode("test1"); - const newChild2 = new BaseNode("test1"); - const node = new DictionaryNode(new MockFailDictionaryStore()); + const newChild1 = new DictionaryNode(); + const newChild2 = new DictionaryNode(); + const node = new DictionaryNode(); node.set("test1", child1); node.set("test2", child2); @@ -627,20 +574,66 @@ describe("DictionaryNode", () => { expect(node.getState()).toStrictEqual(state2); }); - test("set changes with new properties, fail", () => { - const child1 = new BaseNode("test"); - const child2 = new BaseNode("test"); - const node = new DictionaryNode(new MockFailDictionaryStore()); + test("set changes with new delete properties, doesn't create new properties", () => { + const node = new DictionaryNode(); - node.setChanges({type: DictionaryNode.TYPE, properties: {test1: child1.getState(), test2: child2.getState()}}); + node.setChanges({type: DictionaryNode.TYPE, properties: {test1: {type: DeleteNode.TYPE}, test2: {type: DeleteNode.TYPE}}}); expect(node.getState()).toStrictEqual({type: DictionaryNode.TYPE, properties: {}}); }); - test("set changes with new delete properties, doesn't create new properties", () => { - const node = new DictionaryNode(new MockFailDictionaryStore()); + test("new ValueNode", () => { + const node = new DictionaryNode(); - node.setChanges({type: DictionaryNode.TYPE, properties: {test1: {type: DeleteNode.TYPE}, test2: {type: DeleteNode.TYPE}}}); + node.setChanges({properties: {"test": {"type": ValueNode.TYPE, "value": 0}}}); - expect(node.getState()).toStrictEqual({type: DictionaryNode.TYPE, properties: {}}); + expect(node.get("test")).toBeInstanceOf(ValueNode); + }); + test("new ValueNode with missing value", () => { + const node = new DictionaryNode(); + + expect(() => {node.setChanges({properties: {"test": {"type": ValueNode.TYPE}}});}).toThrow(); + }); + test("new VersionedNode", () => { + const node = new DictionaryNode(); + + node.setChanges({properties: {"test": {"type": VersionedNode.TYPE, "version": 0, "value": 0}}}); + + expect(node.get("test")).toBeInstanceOf(ValueNode); + }); + test("new VersionedNode with missing value", () => { + const node = new DictionaryNode(); + + expect(() => {node.setChanges({properties: {"test": {"type": VersionedNode.TYPE, "version": 0}}});}).toThrow(); + }); + test("new VersionedNode with missing version", () => { + const node = new DictionaryNode(); + + expect(() => {node.setChanges({properties: {"test": {"type": VersionedNode.TYPE, "value": 0}}});}).toThrow(); + }); + test("new with unknown type", () => { + const node = new DictionaryNode(); + + expect(() => {node.setChanges({properties: {"test": {"type": "UnknownType"}}});}).toThrow(); + }); + test("new with missing type", () => { + const node = new DictionaryNode(); + + expect(() => {node.setChanges({properties: {"test": {}}});}).toThrow(); + }); + test("delete not allowed and ignored", () => { + const node = new NoDeleteDictionaryNode(); + node.set("test", new DictionaryNode()); + + node.setChanges({properties: {"test": {"type": DeleteNode.TYPE}}}); + + expect(node.get("test")).toBeInstanceOf(DictionaryNode); + }); + test("cast not allowed and ignored", () => { + const node = new NoDeleteDictionaryNode(); + node.set("test", new DictionaryNode()); + + node.setChanges({properties: {"test": {"type": BaseNode.TYPE}}}); + + expect(node.get("test")).toBeInstanceOf(DictionaryNode); }); }); diff --git a/tests/modules/DictionaryStore.test.js b/tests/modules/DictionaryStore.test.js deleted file mode 100644 index 46213bc74..000000000 --- a/tests/modules/DictionaryStore.test.js +++ /dev/null @@ -1,84 +0,0 @@ -import {expect, jest, test, describe} from "@jest/globals"; -import DictionaryNode from "../../libraries/objectDefaultFiles/scene/DictionaryNode.js"; -import DictionaryStore from "../../libraries/objectDefaultFiles/scene/DictionaryStore.js"; -import ValueNode from "../../libraries/objectDefaultFiles/scene/ValueNode.js"; -import VersionedNode from "../../libraries/objectDefaultFiles/scene/VersionedNode.js"; - -describe("DictionaryStore", () => { - test("Constructor", () => { - expect(() => {new DictionaryStore();}).not.toThrow(); - }); - test("Create without type", () => { - const store = new DictionaryStore(); - expect(() => {store.create("test", {});}).toThrow(); - }); - test("Create with unknown type", () => { - const store = new DictionaryStore(); - expect(() => {store.create("test", {type: "test"});}).toThrow(); - }); - test("Create Dictionary", () => { - const store = new DictionaryStore(); - const correctNode = new DictionaryNode(new DictionaryStore(), "Object.test"); - - const node = store.create("test", {type: "Object.test", properties: {}}); - - expect(node).toStrictEqual(correctNode); - }); - // test with properties - test("Create Value", () => { - const store = new DictionaryStore(); - const correctNode = new ValueNode(0, "Value.test"); - - const node = store.create("test", {type: "Value.test", value: 0}); - - expect(node).toStrictEqual(correctNode); - }); - test("Create Value without value throws", () => { - const store = new DictionaryStore(); - - expect(() => {store.create("test", {type: "Value.test"});}).toThrow(); - }); - test("Create Versioned", () => { - const store = new DictionaryStore(); - const correctNode = new VersionedNode(0, "Versioned.test"); - correctNode.incrementVersion(); - - const node = store.create("test", {type: "Versioned.test", value: 0, version: 0}); - - expect(node).toStrictEqual(correctNode); - }); - test("Create Versioned without value throws", () => { - const store = new DictionaryStore(); - - expect(() => {store.create("test", {type: "Versioned.test", version: 0});}).toThrow(); - }); - test("Create Versioned without version throws", () => { - const store = new DictionaryStore(); - - expect(() => {store.create("test", {type: "Versioned.test", value: 0});}).toThrow(); - }); - test("Cast value to versioned node", () => { - const store = new DictionaryStore(); - const correctNode = new VersionedNode(0, "Versioned.test"); - correctNode.incrementVersion(); - - const node = store.cast("test", new ValueNode(0, "Value.test"), {type: "Versioned.test", value: 0, version: 0}); - - expect(node).toStrictEqual(correctNode); - }); - test("Delete doesn't throw", () => { - const store = new DictionaryStore(); - - expect(() => {store.delete("test", new ValueNode(0, "Value.test"));}).not.toThrow(); - }); - test("applyChanges event calls default handler", () => { - const store = new DictionaryStore(); - const mockCallback = jest.fn((_state) => {}); - const mockState = {type: "test"}; - - store.applyChanges(mockState, mockCallback); - - expect(mockCallback).toHaveBeenCalled(); - expect(mockCallback.mock.calls[0][0]).toBe(mockState); - }); -}); diff --git a/tests/modules/ObjectNode.test.js b/tests/modules/ObjectNode.test.js index a05b21818..64be3135b 100644 --- a/tests/modules/ObjectNode.test.js +++ b/tests/modules/ObjectNode.test.js @@ -2,22 +2,19 @@ import {expect, jest, test, describe} from "@jest/globals"; import BaseNode from "../../libraries/objectDefaultFiles/scene/BaseNode.js"; import ObjectNode from "../../libraries/objectDefaultFiles/scene/ObjectNode.js"; -class MockObjectStore { - mockGetProperties = jest.fn((_thisNode) => {return this.#properties;}); - mockApplyChanges = jest.fn((delta, defaultApplyChanges) => {defaultApplyChanges(delta);}); - - #properties; - - constructor(properties) { - this.#properties = properties; +class MockObjectNode extends ObjectNode { + mockSetChanges = jest.fn((_delta, _useSetState) => {}); + + constructor(properties = {}, type = "Object.Mock") { + super(type); + for (const entry of Object.entries(properties)) { + this._set(entry[0], entry[1]); + } } - getProperties(thisNode) { - return this.mockGetProperties(thisNode); - } - - applyChanges(delta, defaultApplyChanges) { - this.mockApplyChanges(delta, defaultApplyChanges); + setChanges(delta, useSetState = false) { + this.mockSetChanges(delta, useSetState); + super.setChanges(delta, useSetState); } } @@ -29,52 +26,38 @@ describe("ObjectNode", () => { const objectState1of2Delta = {properties: {test1: {type: "test"}}}; const objectState2ChangedTypeDelta = {properties: {test1: {type: "test1"}, test2: {type: "test1"}}}; test("constructor", () => { - expect(() => {new ObjectNode(new MockObjectStore({}), "test");}).not.toThrow(); - }); - test("constructor calls getproperties with correct thisNode", () => { - const listener = new MockObjectStore({}); - - const node = new ObjectNode(listener, "test"); - - expect(listener.mockGetProperties).toBeCalled(); - expect(listener.mockGetProperties.mock.calls[0][0]).toBe(node); + expect(() => {new MockObjectNode({}, "test");}).not.toThrow(); }); test("constructor sets properties", () => { const child = new BaseNode("test"); - const node = new ObjectNode(new MockObjectStore({test: child}), "test"); + const node = new MockObjectNode({test: child}, "test"); expect(node.get("test")).toBe(child); }); test("constructor sets property's parent", () => { const child = new BaseNode("test"); - const node = new ObjectNode(new MockObjectStore({test: child}), "test"); - - expect(node.get("test").getParent()).toBe(node); - }); - test("constructor sets listener", () => { - const listener = new MockObjectStore({}); - const node = new ObjectNode(listener, "test"); + const node = new MockObjectNode({test: child}, "test"); - expect(node.getListener()).toBe(listener); + expect(node.get("test").parent).toBe(node); }); test("constructor sets correct custom type", () => { - const node = new ObjectNode(new MockObjectStore({}), "test"); + const node = new ObjectNode("test"); expect(node.getType()).toBe("test"); }); test("entries with no properties", () => { - const node = new ObjectNode(new MockObjectStore({}), "test"); + const node = new MockObjectNode({}, "test"); expect(node.entries()).toHaveLength(0); }); test("entries with properties", () => { const child = new BaseNode("test"); - const node = new ObjectNode(new MockObjectStore({test: child}), "test"); + const node = new MockObjectNode({test: child}, "test"); expect(node.entries()).toStrictEqual([["test", child]]); }); test("forEach with no properties", () => { - const node = new ObjectNode(new MockObjectStore({}), "test"); + const node = new MockObjectNode({}, "test"); const mockCallback = jest.fn((_value, _key, _map) => {}); node.forEach(mockCallback); @@ -84,7 +67,7 @@ describe("ObjectNode", () => { test("forEach with properties", () => { const child1 = new BaseNode("test"); const child2 = new BaseNode("test"); - const node = new ObjectNode(new MockObjectStore({test1: child1, test2: child2}), "test"); + const node = new MockObjectNode({test1: child1, test2: child2}, "test"); const mockCallback = jest.fn((_value, _key, _map) => {}); node.forEach(mockCallback); @@ -100,7 +83,7 @@ describe("ObjectNode", () => { test("forEach with properties with custom this", () => { const child1 = new BaseNode("test"); const child2 = new BaseNode("test"); - const node = new ObjectNode(new MockObjectStore({test1: child1, test2: child2}), "test"); + const node = new MockObjectNode({test1: child1, test2: child2}, "test"); const obj = {func: jest.fn((_value, _key, _map) => {})}; node.forEach(obj.func, obj); @@ -114,57 +97,57 @@ describe("ObjectNode", () => { expect(obj.func.mock.calls[1][2]).toBe(node); }); test("has with no properties", () => { - const node = new ObjectNode(new MockObjectStore({}), "test"); + const node = new MockObjectNode({}, "test"); expect(node.has("test2")).toBe(false); }); test("has with properties but missing property", () => { const child1 = new BaseNode("test"); const child2 = new BaseNode("test"); - const node = new ObjectNode(new MockObjectStore({test1: child1, test2: child2}), "test"); + const node = new MockObjectNode({test1: child1, test2: child2}, "test"); expect(node.has("test3")).toBe(false); }); test("has with properties", () => { const child1 = new BaseNode("test"); const child2 = new BaseNode("test"); - const node = new ObjectNode(new MockObjectStore({test1: child1, test2: child2}), "test"); + const node = new MockObjectNode({test1: child1, test2: child2}, "test"); expect(node.has("test2")).toBe(true); }); test("keys with no properties", () => { - const node = new ObjectNode(new MockObjectStore({}), "test"); + const node = new MockObjectNode({}, "test"); expect(node.keys()).toStrictEqual([]); }); test("keys with properties", () => { const child1 = new BaseNode("test"); const child2 = new BaseNode("test"); - const node = new ObjectNode(new MockObjectStore({test1: child1, test2: child2}), "test"); + const node = new MockObjectNode({test1: child1, test2: child2}, "test"); expect(node.keys()).toStrictEqual(["test1", "test2"]); }); test("values with no properties", () => { - const node = new ObjectNode(new MockObjectStore({}), "test"); + const node = new MockObjectNode({}, "test"); expect(node.values()).toStrictEqual([]); }); test("values with properties", () => { const child1 = new BaseNode("test"); const child2 = new BaseNode("test"); - const node = new ObjectNode(new MockObjectStore({test1: child1, test2: child2}), "test"); + const node = new MockObjectNode({test1: child1, test2: child2}, "test"); expect(node.values()).toStrictEqual([child1, child2]); }); test("set dirty sets node dirty", () => { - const node = new ObjectNode(new MockObjectStore({}), "test"); + const node = new MockObjectNode({}, "test"); node.setDirty(); expect(node.isDirty()).toBe(true); }); test("getState with no properties", () => { - const node = new ObjectNode(new MockObjectStore({}), "test"); + const node = new MockObjectNode({}, "test"); node.setDirty(); const state = node.getState(); @@ -172,7 +155,7 @@ describe("ObjectNode", () => { expect(state).toStrictEqual(objectStateNoProp); }); test("getState with properties", () => { - const node = new ObjectNode(new MockObjectStore({test1: new BaseNode("test"), test2: new BaseNode("test")}), "test"); + const node = new MockObjectNode({test1: new BaseNode("test"), test2: new BaseNode("test")}, "test"); node.setDirty(); const state = node.getState(); @@ -180,41 +163,39 @@ describe("ObjectNode", () => { expect(state).toStrictEqual(objectState2); }); test("getState with properties while node not dirty", () => { - const node = new ObjectNode(new MockObjectStore({test1: new BaseNode("test"), test2: new BaseNode("test")}), "test"); + const node = new MockObjectNode({test1: new BaseNode("test"), test2: new BaseNode("test")}, "test"); const state = node.getState(); expect(state).toStrictEqual(objectState2); }); test("setState can't add properties", () => { - const node = new ObjectNode(new MockObjectStore({}), "test"); + const node = new MockObjectNode({}, "test"); expect(() => {node.setState(objectState2);}).toThrow(); }); test("setState can't change property type", () => { - const node = new ObjectNode(new MockObjectStore({test1: new BaseNode("test"), test2: new BaseNode("test")}), "test"); + const node = new MockObjectNode({test1: new BaseNode("test"), test2: new BaseNode("test")}, "test"); expect(() => {node.setState(objectState2ChangedTypeDelta);}).toThrow(); }); test("setState can't remove property", () => { - const node = new ObjectNode(new MockObjectStore({test1: new BaseNode("test"), test2: new BaseNode("test"), test3: new BaseNode("test")}), "test"); + const node = new MockObjectNode({test1: new BaseNode("test"), test2: new BaseNode("test"), test3: new BaseNode("test")}, "test"); expect(() => {node.setState(objectState2Delta);}).toThrow(); }); test("setState changes properties", () => { - const mock1 = new MockObjectStore({}); - const mock2 = new MockObjectStore({}); - const child1 = new ObjectNode(mock1, "test"); - const child2 = new ObjectNode(mock2, "test"); - const node = new ObjectNode(new MockObjectStore({test1: child1, test2: child2}), "test"); + const child1 = new MockObjectNode({}, "test"); + const child2 = new MockObjectNode({}, "test"); + const node = new MockObjectNode({test1: child1, test2: child2}, "test"); node.setState(objectState2); - expect(mock1.mockApplyChanges).toHaveBeenCalled(); - expect(mock2.mockApplyChanges).toHaveBeenCalled(); + expect(child1.mockSetChanges).toHaveBeenCalled(); + expect(child2.mockSetChanges).toHaveBeenCalled(); }); test("getChanges with no properties", () => { - const node = new ObjectNode(new MockObjectStore({}), "test"); + const node = new MockObjectNode({}, "test"); node.setDirty(); const delta = node.getChanges(); @@ -224,9 +205,9 @@ describe("ObjectNode", () => { test("getChanges with properties", () => { const child1 = new BaseNode("test"); const child2 = new BaseNode("test"); - const node = new ObjectNode(new MockObjectStore({test1: child1, test2: child2}), "test"); - child1.setParent(node); - child2.setParent(node); + const node = new MockObjectNode({test1: child1, test2: child2}, "test"); + child1.parent = node; + child2.parent = node; child1.setTypeDirty(); child2.setTypeDirty(); @@ -237,9 +218,9 @@ describe("ObjectNode", () => { test("getChanges only returns changes", () => { const child1 = new BaseNode("test"); const child2 = new BaseNode("test"); - const node = new ObjectNode(new MockObjectStore({test1: child1, test2: child2}), "test"); - child1.setParent(node); - child2.setParent(node); + const node = new MockObjectNode({test1: child1, test2: child2}, "test"); + child1.parent = node; + child2.parent = node; child1.setTypeDirty(); const delta = node.getChanges(); @@ -247,40 +228,36 @@ describe("ObjectNode", () => { expect(delta).toStrictEqual(objectState1of2Delta); }); test("getChanges with properties while node not dirty", () => { - const node = new ObjectNode(new MockObjectStore({test1: new BaseNode("test"), test2: new BaseNode("test")}), "test"); + const node = new MockObjectNode({test1: new BaseNode("test"), test2: new BaseNode("test")}, "test"); const delta = node.getChanges(); expect(delta).toStrictEqual({}); }); test("setChanges can't add properties", () => { - const node = new ObjectNode(new MockObjectStore({}), "test"); + const node = new ObjectNode("test"); expect(() => {node.setChanges(objectState2);}).toThrow(); }); test("setChanges can't change property type", () => { - const node = new ObjectNode(new MockObjectStore({test1: new BaseNode("test"), test2: new BaseNode("test")}), "test"); + const node = new MockObjectNode({test1: new BaseNode("test"), test2: new BaseNode("test")}, "test"); expect(() => {node.setChanges(objectState2ChangedTypeDelta);}).toThrow(); }); test("setChanges changes properties", () => { - const mock1 = new MockObjectStore({}); - const mock2 = new MockObjectStore({}); - const child1 = new ObjectNode(mock1, "test"); - const child2 = new ObjectNode(mock2, "test"); - const node = new ObjectNode(new MockObjectStore({test1: child1, test2: child2}), "test"); + const child1 = new MockObjectNode({}, "test"); + const child2 = new MockObjectNode({}, "test"); + const node = new MockObjectNode({test1: child1, test2: child2}, "test"); node.setChanges(objectState2); - expect(mock1.mockApplyChanges).toHaveBeenCalled(); - expect(mock2.mockApplyChanges).toHaveBeenCalled(); + expect(child1.mockSetChanges).toHaveBeenCalled(); + expect(child2.mockSetChanges).toHaveBeenCalled(); }); test("setChildrenDirty sets children dirty", () => { - const mock1 = new MockObjectStore({}); - const mock2 = new MockObjectStore({}); - const child1 = new ObjectNode(mock1, "test"); - const child2 = new ObjectNode(mock2, "test"); - const node = new ObjectNode(new MockObjectStore({test1: child1, test2: child2}), "test"); + const child1 = new MockObjectNode({}, "test"); + const child2 = new MockObjectNode({}, "test"); + const node = new MockObjectNode({test1: child1, test2: child2}, "test"); ObjectNode.setChildrenDirty(node); @@ -290,7 +267,7 @@ describe("ObjectNode", () => { test("setChildrenDirty sets children without forEach dirty", () => { const child1 = new BaseNode("test"); const child2 = new BaseNode("test"); - const node = new ObjectNode(new MockObjectStore({test1: child1, test2: child2}), "test"); + const node = new MockObjectNode({test1: child1, test2: child2}, "test"); ObjectNode.setChildrenDirty(node); @@ -298,11 +275,9 @@ describe("ObjectNode", () => { expect(child2.isDirty()).toBe(true); }); test("setChildrenDirty sets children's children dirty", () => { - const mock1 = new MockObjectStore({}); - const child1 = new ObjectNode(mock1, "test"); - const mock2 = new MockObjectStore({test1: child1}); - const child2 = new ObjectNode(mock2, "test"); - const node = new ObjectNode(new MockObjectStore({test2: child2}), "test"); + const child1 = new MockObjectNode({}, "test"); + const child2 = new MockObjectNode({test1: child1}, "test"); + const node = new MockObjectNode({test2: child2}, "test"); ObjectNode.setChildrenDirty(node); @@ -312,7 +287,7 @@ describe("ObjectNode", () => { test("setChildrenDirty sets children type dirty", () => { const child1 = new BaseNode("test"); const child2 = new BaseNode("test"); - const node = new ObjectNode(new MockObjectStore({test1: child1, test2: child2}), "test"); + const node = new MockObjectNode({test1: child1, test2: child2}, "test"); node.setDirty(); ObjectNode.setChildrenDirty(node); diff --git a/tests/modules/ObjectStore.test.js b/tests/modules/ObjectStore.test.js deleted file mode 100644 index 3b5eed8b4..000000000 --- a/tests/modules/ObjectStore.test.js +++ /dev/null @@ -1,23 +0,0 @@ -import {expect, jest, test, describe} from "@jest/globals"; -import ObjectStore from "../../libraries/objectDefaultFiles/scene/ObjectStore.js"; - -describe("ObjectStore", () => { - test("Constructor", () => { - expect(() => {new ObjectStore();}).not.toThrow(); - }); - test("getProperties returns empty dictionary", () => { - const store = new ObjectStore(); - - expect(store.getProperties(null)).toStrictEqual({}); - }); - test("applyChanges event calls default handler", () => { - const store = new ObjectStore(); - const mockCallback = jest.fn((_state) => {}); - const mockState = {type: "test"}; - - store.applyChanges(mockState, mockCallback); - - expect(mockCallback).toHaveBeenCalled(); - expect(mockCallback.mock.calls[0][0]).toBe(mockState); - }); -}); diff --git a/tests/modules/ValueNode.test.js b/tests/modules/ValueNode.test.js index e9bf35640..afc5dd208 100644 --- a/tests/modules/ValueNode.test.js +++ b/tests/modules/ValueNode.test.js @@ -83,7 +83,7 @@ describe("ValueNode", () => { test("setValue sets parent node dirty", () => { const parent = new MockNode(); const node = new ValueNode(0); - node.setParent(parent); + node.parent = parent; node.value = 1; diff --git a/tests/modules/VersionedNode.test.js b/tests/modules/VersionedNode.test.js index 84294a329..bfa734071 100644 --- a/tests/modules/VersionedNode.test.js +++ b/tests/modules/VersionedNode.test.js @@ -84,7 +84,7 @@ describe("VersionedNode", () => { test("setValue sets parent node dirty", () => { const parent = new MockNode(); const node = new VersionedNode(0, "test"); - node.setParent(parent); + node.parent = parent; node.value = 1;