Refactoring entities (#23)

* Still WIP

* WIP

* ArrayEntity parsing fixed

* Fix format text entity

* Tests for various entity classes and update entity class implementations

* More tests and fixed

* More entities fixed

* Simple entities serialization fixed

* Entities tests fixed

* Remove serialization bits

* Fix Function reference

* CustomProperties creating fixed

* WIP

* Better typing for grammars

* Decoding code fixes

* Fixing still

* Several fixes

* rename toString to serialize

* Several fixes

* More fixes

* Moving more stuff out of Utility

* Several fixes

* Fixing Linear color entity print

* Serialization fixes

* Fix serialization

* Method to compute grammar

* Renaming fix

* Fix array grammar and equality check

* Fix inlined keys

* Fix type

* Several serialization fixes

* Fix undefined dereference

* Several fixes

* More fixes and cleanup

* Fix keys quoting mechanism

* Fix natural number assignment

* Fix Int64 toString()

* Fix quoted keys for inlined arrays

* Fix PG pins

* Fix several test cases

* Types fixes

* New pin default value empty

* Fix non existing DefaultValue for variadic nodes

* Smaller fixes for crashes

* Fix link color when attached to knot

* Linking test and more reliability operations for adding pins

* Improve issue 18 test

* More tests and fixes

* Fix enum pin entity

* Remove failing test
This commit is contained in:
barsdeveloper
2024-09-08 11:46:36 +02:00
committed by GitHub
parent 31a07b992d
commit 23ee628e28
129 changed files with 8888 additions and 8584 deletions

View File

@@ -1,17 +1,23 @@
import { html, nothing } from "lit"
import Configuration from "../Configuration.js"
import Shortcuts from "../Shortcuts.js"
import Utility from "../Utility.js"
import ElementFactory from "../element/ElementFactory.js"
import LinearColorEntity from "../entity/LinearColorEntity.js"
import KnotEntity from "../entity/objects/KnotEntity.js"
import KeyboardShortcut from "../input/keyboard/KeyboardShortcut.js"
import MouseClick from "../input/mouse/MouseClick.js"
import MouseDbClick from "../input/mouse/MouseDbClick.js"
import Shortcuts from "../Shortcuts.js"
import Utility from "../Utility.js"
import IFromToPositionedTemplate from "./IFromToPositionedTemplate.js"
/** @extends {IFromToPositionedTemplate<LinkElement>} */
export default class LinkTemplate extends IFromToPositionedTemplate {
/** @param {Number} x */
static sigmoidPositive(x, curvature = 3.7, length = 1.1) {
return 1 - Math.exp(-((x / length) ** curvature))
}
/**
* Returns a function providing the inverse multiplication y = a / x + q. The value of a and q are calculated using
* the derivative of that function y' = -a / x^2 at the point p (x = p[0] and y = p[1]). This means
@@ -157,7 +163,7 @@ export default class LinkTemplate extends IFromToPositionedTemplate {
const aspectRatio = dy / Math.max(30, dx)
const c2 =
LinkTemplate.c2Clamped(dx)
* Utility.sigmoidPositive(fillRatio * 1.2 + aspectRatio * 0.5, 1.5, 1.8)
* LinkTemplate.sigmoidPositive(fillRatio * 1.2 + aspectRatio * 0.5, 1.5, 1.8)
+ this.element.startPercentage
this.element.svgPathD = Configuration.linkRightSVGPath(this.element.startPercentage, c1, c2)
}
@@ -168,9 +174,9 @@ export default class LinkTemplate extends IFromToPositionedTemplate {
if (changedProperties.has("originatesFromInput")) {
this.element.style.setProperty("--ueb-from-input", this.element.originatesFromInput ? "1" : "0")
}
const referencePin = this.element.source ?? this.element.destination
const referencePin = this.element.getOutputPin(true)
if (referencePin) {
this.element.style.setProperty("--ueb-link-color-rgb", Utility.printLinearColor(referencePin.color))
this.element.style.setProperty("--ueb-link-color-rgb", LinearColorEntity.printLinearColor(referencePin.color))
}
this.element.style.setProperty("--ueb-y-reflected", `${this.element.fromY > this.element.toY ? 1 : 0}`)
this.element.style.setProperty("--ueb-start-percentage", `${Math.round(this.element.startPercentage)}%`)

View File

@@ -26,7 +26,7 @@ export default class CommentNodeTemplate extends IResizeableTemplate {
<div class="ueb-node-border">
<div class="ueb-node-wrapper">
<div class="ueb-node-top"
.innerText="${Utility.encodeHTMLWhitespace(this.element.entity.NodeComment)}">
.innerText="${Utility.encodeHTMLWhitespace(this.element.entity.NodeComment?.toString())}">
</div>
</div>
</div>

View File

@@ -40,7 +40,7 @@ export default class EventNodeTemplate extends NodeTemplate {
createDelegatePinElement() {
const pin = /** @type {PinElementConstructor} */(ElementFactory.getConstructor("ueb-pin")).newObject(
this.element.getPinEntities().find(v => !v.isHidden() && v.PinType.PinCategory === "delegate"),
this.element.getPinEntities().find(v => !v.isHidden() && v.PinType.PinCategory?.toString() === "delegate"),
new MinimalPinTemplate(),
this.element
)
@@ -50,7 +50,7 @@ export default class EventNodeTemplate extends NodeTemplate {
createPinElements() {
return this.element.getPinEntities()
.filter(v => !v.isHidden() && v.PinType.PinCategory !== "delegate")
.filter(v => !v.isHidden() && v.PinType.PinCategory?.toString() !== "delegate")
.map(pinEntity => /** @type {PinElementConstructor} */(ElementFactory.getConstructor("ueb-pin"))
.newObject(pinEntity, undefined, this.element)
)

View File

@@ -11,7 +11,7 @@ export default class NodeTemplate extends ISelectableDraggableTemplate {
#hasSubtitle = false
/** @type {() => PinEntity} */
/** @type {() => PinEntity<IEntity>} */
pinInserter
/** @type {HTMLElement} */
@@ -41,7 +41,7 @@ export default class NodeTemplate extends ISelectableDraggableTemplate {
this.element.updateComplete.then(() => this.element.acknowledgeReflow())
}
/** @param {PinEntity} pinEntity */
/** @param {PinEntity<IEntity>} pinEntity */
createPinElement(pinEntity) {
const pinElement = /** @type {PinElementConstructor} */(ElementFactory.getConstructor("ueb-pin"))
.newObject(pinEntity, undefined, this.element)
@@ -165,7 +165,7 @@ export default class NodeTemplate extends ISelectableDraggableTemplate {
.filter(v => !v.isHidden())
.map(pinEntity => {
this.#hasSubtitle = this.#hasSubtitle
|| pinEntity.PinName === "self" && pinEntity.pinTitle() === "Target"
|| pinEntity.PinName.toString() === "self" && pinEntity.pinTitle() === "Target"
return this.createPinElement(pinEntity)
})
}

View File

@@ -1,14 +1,19 @@
import { html } from "lit"
import BooleanEntity from "../../entity/BooleanEntity.js"
import MouseIgnore from "../../input/mouse/MouseIgnore.js"
import PinTemplate from "./PinTemplate.js"
/** @extends PinTemplate<Boolean> */
/** @extends PinTemplate<BooleanEntity> */
export default class BoolPinTemplate extends PinTemplate {
/** @type {HTMLInputElement?} */
#input
#onChangeHandler = () => this.element.setDefaultValue(this.#input.checked)
#onChangeHandler = () => {
const entity = this.element.getDefaultValue()
entity.value = this.#input.checked
this.element.setDefaultValue(entity)
}
/** @param {PropertyValues} changedProperties */
firstUpdated(changedProperties) {
@@ -35,7 +40,7 @@ export default class BoolPinTemplate extends PinTemplate {
renderInput() {
return html`
<input type="checkbox" class="ueb-pin-input-wrapper ueb-pin-input" ?checked="${this.element.defaultValue === true}" />
<input type="checkbox" class="ueb-pin-input-wrapper ueb-pin-input" ?checked="${this.element.defaultValue?.valueOf() === true}" />
`
}
}

View File

@@ -1,5 +1,6 @@
import { html } from "lit"
import Configuration from "../../Configuration.js"
import StringEntity from "../../entity/StringEntity.js"
import Utility from "../../Utility.js"
import IInputPinTemplate from "./IInputPinTemplate.js"
@@ -15,11 +16,11 @@ export default class EnumPinTemplate extends IInputPinTemplate {
setup() {
super.setup()
const enumEntries = this.element.nodeElement.entity.EnumEntries
const enumEntries = this.element.nodeElement.entity.EnumEntries?.valueOf()
this.#dropdownEntries =
enumEntries?.map(k => {
if (k === "") {
k = "None"
if (k.valueOf() === "") {
k = new StringEntity("None")
}
return [
k,
@@ -42,12 +43,11 @@ export default class EnumPinTemplate extends IInputPinTemplate {
}
renderInput() {
const entity = this.element.nodeElement.entity
return html`
<ueb-dropdown
class="ueb-pin-input-wrapper ueb-pin-input"
.options="${this.#dropdownEntries}"
.selectedOption="${this.element.defaultValue.value}"
.selectedOption="${this.element.defaultValue}"
>
</ueb-dropdown>
`
@@ -62,4 +62,16 @@ export default class EnumPinTemplate extends IInputPinTemplate {
getInputs() {
return [this.#dropdownElement.getValue()]
}
/**
* @this {EnumPinTemplate}
* @param {String[]} values
* @param {String[]} rawValues
*/
setDefaultValue(values = [], rawValues) {
const value = this.element.getDefaultValue()
value.value = values[0]
this.element.setDefaultValue(value)
this.element.requestUpdate()
}
}

View File

@@ -9,7 +9,7 @@ export default class ExecPinTemplate extends PinTemplate {
}
renderName() {
let pinName = this.element.entity.PinName
let pinName = this.element.entity.PinName?.toString()
if (this.element.entity.PinFriendlyName) {
pinName = this.element.entity.PinFriendlyName.toString()
} else if (pinName === "execute" || pinName === "then") {

View File

@@ -4,7 +4,7 @@ import Utility from "../../Utility.js"
import PinTemplate from "./PinTemplate.js"
/**
* @template {TerminalAttribute} T
* @template {IEntity} T
* @extends PinTemplate<T>
*/
export default class IInputPinTemplate extends PinTemplate {
@@ -41,6 +41,10 @@ export default class IInputPinTemplate extends PinTemplate {
/** @param {HTMLElement} inputElement*/
#updateWrapClass(inputElement) {
if (this.element.querySelector(".ueb-pin-name")?.getBoundingClientRect().width < 20) {
// Do not wrap if the pin name is just a letter (like A, B, V, ...)
return
}
const width = this.blueprint.scaleCorrect(this.#inputWrapper.getBoundingClientRect().width) + this.nameWidth
const inputWrapped = this.element.classList.contains("ueb-pin-input-wrap")
if (!inputWrapped && width > Configuration.pinInputWrapWidth) {

View File

@@ -1,14 +1,17 @@
import IInputPinTemplate from "./IInputPinTemplate.js"
/**
* @template {TerminalAttribute} T
* @template {IEntity} T
* @extends IInputPinTemplate<T>
*/
export default class INumericPinTemplate extends IInputPinTemplate {
static singleLineInput = true
/** @param {String[]} values */
/**
* @this {INumericPinTemplate<NumberEntity>}
* @param {String[]} values
*/
setInputs(values = [], updateDefaultValue = false) {
if (!values || values.length == 0) {
values = [this.getInput()]
@@ -29,11 +32,14 @@ export default class INumericPinTemplate extends IInputPinTemplate {
}
/**
* @this {INumericPinTemplate<NumberEntity>}
* @param {Number[]} values
* @param {String[]} rawValues
*/
setDefaultValue(values = [], rawValues) {
this.element.setDefaultValue(/** @type {T} */(values[0]))
const value = this.element.getDefaultValue()
value.value = values[0]
this.element.setDefaultValue(value)
this.element.requestUpdate()
}
}

View File

@@ -10,7 +10,9 @@ export default class Int64PinTemplate extends INumericPinTemplate {
* @param {String[]} rawValues
*/
setDefaultValue(values = [], rawValues) {
this.element.setDefaultValue(new Integer64Entity(values[0]))
const value = this.element.getDefaultValue()
value.value = BigInt(values[0])
this.element.setDefaultValue(value)
this.element.requestUpdate()
}

View File

@@ -5,15 +5,6 @@ import IntegerEntity from "../../entity/IntegerEntity.js"
/** @extends INumericPinTemplate<IntegerEntity> */
export default class IntPinTemplate extends INumericPinTemplate {
/**
* @param {Number[]} values
* @param {String[]} rawValues
*/
setDefaultValue(values = [], rawValues) {
this.element.setDefaultValue(new IntegerEntity(values[0]))
this.element.requestUpdate()
}
renderInput() {
return html`
<div class="ueb-pin-input-wrapper ueb-pin-input">

View File

@@ -2,7 +2,7 @@ import { html } from "lit"
import PinTemplate from "./PinTemplate.js"
/**
* @template {TerminalAttribute} T
* @template {IEntity} T
* @extends PinTemplate<T>
*/
export default class MinimalPinTemplate extends PinTemplate {

View File

@@ -9,12 +9,12 @@ import VariableConversionNodeTemplate from "../node/VariableConversionNodeTempla
import VariableOperationNodeTemplate from "../node/VariableOperationNodeTemplate.js"
/**
* @template {TerminalAttribute} T
* @template {IEntity} T
* @typedef {import("../../element/PinElement.js").default<T>} PinElement
*/
/**
* @template {TerminalAttribute} T
* @template {IEntity} T
* @extends ITemplate<PinElement<T>>
*/
export default class PinTemplate extends ITemplate {
@@ -110,12 +110,12 @@ export default class PinTemplate extends ITemplate {
return SVGIcon.pcgStackPin
}
}
switch (this.element.entity.PinType?.ContainerType?.toString()) {
switch (this.element.entity.PinType?.ContainerType?.serialize()) {
case "Array": return SVGIcon.arrayPin
case "Set": return SVGIcon.setPin
case "Map": return SVGIcon.mapPin
}
if (this.element.entity.PinType?.PinCategory?.toLocaleLowerCase() === "delegate") {
if (this.element.entity.PinType?.PinCategory?.toString().toLocaleLowerCase() === "delegate") {
return SVGIcon.delegate
}
if (this.element.nodeElement?.template instanceof VariableOperationNodeTemplate) {
@@ -141,8 +141,8 @@ export default class PinTemplate extends ITemplate {
isInputRendered() {
return this.element.isInput()
&& !this.element.entity.bDefaultValueIsIgnored
&& !this.element.entity.PinType.bIsReference
&& !this.element.entity.bDefaultValueIsIgnored?.valueOf()
&& !this.element.entity.PinType.bIsReference?.valueOf()
}
renderInput() {
@@ -170,6 +170,7 @@ export default class PinTemplate extends ITemplate {
getLinkLocation() {
const rect = this.iconElement.getBoundingClientRect()
/** @type {[Number, Number]} */
const boundingLocation = [this.element.isInput() ? rect.left : rect.right + 1, (rect.top + rect.bottom) / 2]
const location = Utility.convertLocation(boundingLocation, this.blueprint.template.gridElement)
return this.blueprint.compensateTranslation(location[0], location[1])

View File

@@ -1,22 +1,18 @@
import { html } from "lit"
import Utility from "../../Utility.js"
import NumberEntity from "../../entity/NumberEntity.js"
import INumericPinTemplate from "./INumericPinTemplate.js"
/**
* @template {Number} T
* @template {NumberEntity} T
* @extends INumericPinTemplate<T>
*/
export default class RealPinTemplate extends INumericPinTemplate {
setDefaultValue(values = [], rawValues = values) {
this.element.setDefaultValue(values[0])
}
renderInput() {
return html`
<div class="ueb-pin-input-wrapper ueb-pin-input">
<ueb-input .singleLine="${true}"
.innerText="${Utility.printNumber(this.element.getDefaultValue() ?? 0)}">
.innerText="${NumberEntity.printNumber(this.element.getDefaultValue()?.valueOf() ?? 0)}">
</ueb-input>
</div>
`

View File

@@ -1,5 +1,5 @@
import { html } from "lit"
import Utility from "../../Utility.js"
import NumberEntity from "../../entity/NumberEntity.js"
import RotatorEntity from "../../entity/RotatorEntity.js"
import INumericPinTemplate from "./INumericPinTemplate.js"
@@ -7,15 +7,15 @@ import INumericPinTemplate from "./INumericPinTemplate.js"
export default class RotatorPinTemplate extends INumericPinTemplate {
#getR() {
return Utility.printNumber(this.element.getDefaultValue()?.R ?? 0)
return NumberEntity.printNumber(this.element.getDefaultValue()?.R.valueOf() ?? 0)
}
#getP() {
return Utility.printNumber(this.element.getDefaultValue()?.P ?? 0)
return NumberEntity.printNumber(this.element.getDefaultValue()?.P.valueOf() ?? 0)
}
#getY() {
return Utility.printNumber(this.element.getDefaultValue()?.Y ?? 0)
return NumberEntity.printNumber(this.element.getDefaultValue()?.Y.valueOf() ?? 0)
}
setDefaultValue(values = [], rawValues = values) {
@@ -23,9 +23,9 @@ export default class RotatorPinTemplate extends INumericPinTemplate {
if (!(rotator instanceof RotatorEntity)) {
throw new TypeError("Expected DefaultValue to be a RotatorEntity")
}
rotator.R = values[0] // Roll
rotator.P = values[1] // Pitch
rotator.Y = values[2] // Yaw
rotator.R.value = values[0] // Roll
rotator.P.value = values[1] // Pitch
rotator.Y.value = values[2] // Yaw
this.element.requestUpdate("DefaultValue", rotator)
}

View File

@@ -1,5 +1,5 @@
import IInputPinTemplate from "./IInputPinTemplate.js"
/** @extends IInputPinTemplate<String> */
/** @extends IInputPinTemplate<StringEntity> */
export default class StringPinTemplate extends IInputPinTemplate {
}

View File

@@ -1,5 +1,5 @@
import { html } from "lit"
import Utility from "../../Utility.js"
import NumberEntity from "../../entity/NumberEntity.js"
import Vector2DEntity from "../../entity/Vector2DEntity.js"
import INumericPinTemplate from "./INumericPinTemplate.js"
@@ -9,11 +9,11 @@ import INumericPinTemplate from "./INumericPinTemplate.js"
export default class Vector2DPinTemplate extends INumericPinTemplate {
#getX() {
return Utility.printNumber(this.element.getDefaultValue()?.X ?? 0)
return NumberEntity.printNumber(this.element.getDefaultValue()?.X.valueOf() ?? 0)
}
#getY() {
return Utility.printNumber(this.element.getDefaultValue()?.Y ?? 0)
return NumberEntity.printNumber(this.element.getDefaultValue()?.Y.valueOf() ?? 0)
}
/**
@@ -22,12 +22,9 @@ export default class Vector2DPinTemplate extends INumericPinTemplate {
*/
setDefaultValue(values, rawValues) {
const vector = this.element.getDefaultValue(true)
if (!(vector instanceof Vector2DEntity)) {
throw new TypeError("Expected DefaultValue to be a Vector2DEntity")
}
vector.X = values[0]
vector.Y = values[1]
this.element.requestUpdate("DefaultValue", vector)
vector.X.value = values[0]
vector.Y.value = values[1]
this.element.setDefaultValue(vector)
}
renderInput() {

View File

@@ -1,25 +1,25 @@
import { html } from "lit"
import Utility from "../../Utility.js"
import INumericPinTemplate from "./INumericPinTemplate.js"
import NumberEntity from "../../entity/NumberEntity.js"
import Vector4DEntity from "../../entity/Vector4DEntity.js"
import INumericPinTemplate from "./INumericPinTemplate.js"
/** @extends INumericPinTemplate<Vector4DEntity> */
export default class Vector4DPinTemplate extends INumericPinTemplate {
#getX() {
return Utility.printNumber(this.element.getDefaultValue()?.X ?? 0)
return NumberEntity.printNumber(this.element.getDefaultValue()?.X.valueOf() ?? 0)
}
#getY() {
return Utility.printNumber(this.element.getDefaultValue()?.Y ?? 0)
return NumberEntity.printNumber(this.element.getDefaultValue()?.Y.valueOf() ?? 0)
}
#getZ() {
return Utility.printNumber(this.element.getDefaultValue()?.Z ?? 0)
return NumberEntity.printNumber(this.element.getDefaultValue()?.Z.valueOf() ?? 0)
}
#getW() {
return Utility.printNumber(this.element.getDefaultValue()?.W ?? 0)
return NumberEntity.printNumber(this.element.getDefaultValue()?.W.valueOf() ?? 0)
}
/**
@@ -31,10 +31,10 @@ export default class Vector4DPinTemplate extends INumericPinTemplate {
if (!(vector instanceof Vector4DEntity)) {
throw new TypeError("Expected DefaultValue to be a Vector4DEntity")
}
vector.X = values[0]
vector.Y = values[1]
vector.Z = values[2]
vector.W = values[3]
vector.X.value = values[0]
vector.Y.value = values[1]
vector.Z.value = values[2]
vector.W.value = values[3]
this.element.requestUpdate("DefaultValue", vector)
}

View File

@@ -1,5 +1,5 @@
import { html } from "lit"
import Utility from "../../Utility.js"
import NumberEntity from "../../entity/NumberEntity.js"
import VectorEntity from "../../entity/VectorEntity.js"
import INumericPinTemplate from "./INumericPinTemplate.js"
@@ -7,15 +7,15 @@ import INumericPinTemplate from "./INumericPinTemplate.js"
export default class VectorPinTemplate extends INumericPinTemplate {
#getX() {
return Utility.printNumber(this.element.getDefaultValue()?.X ?? 0)
return NumberEntity.printNumber(this.element.getDefaultValue()?.X.valueOf() ?? 0)
}
#getY() {
return Utility.printNumber(this.element.getDefaultValue()?.Y ?? 0)
return NumberEntity.printNumber(this.element.getDefaultValue()?.Y.valueOf() ?? 0)
}
#getZ() {
return Utility.printNumber(this.element.getDefaultValue()?.Z ?? 0)
return NumberEntity.printNumber(this.element.getDefaultValue()?.Z.valueOf() ?? 0)
}
/**
@@ -27,9 +27,9 @@ export default class VectorPinTemplate extends INumericPinTemplate {
if (!(vector instanceof VectorEntity)) {
throw new TypeError("Expected DefaultValue to be a VectorEntity")
}
vector.X = values[0]
vector.Y = values[1]
vector.Z = values[2]
vector.X.value = values[0]
vector.Y.value = values[1]
vector.Z.value = values[2]
this.element.requestUpdate("DefaultValue", vector)
}

View File

@@ -1,9 +1,10 @@
import { html } from "lit"
import { styleMap } from "lit/directives/style-map.js"
import Configuration from "../../Configuration.js"
import ColorHandlerElement from "../../element/ColorHandlerElement.js"
import ColorSliderElement from "../../element/ColorSliderElement.js"
import Configuration from "../../Configuration.js"
import LinearColorEntity from "../../entity/LinearColorEntity.js"
import NumberEntity from "../../entity/NumberEntity.js"
import Utility from "../../Utility.js"
import WindowTemplate from "./WindowTemplate.js"
@@ -244,7 +245,7 @@ export default class ColorPickerWindowTemplate extends WindowTemplate {
<div>
<div class="ueb-horizontal-slider">
<span class="ueb-horizontal-slider-text"
.innerText="${Utility.printNumber(Utility.roundDecimals(channelValue, 3))}">
.innerText="${NumberEntity.printNumber(Utility.roundDecimals(channelValue, 3))}">
</span>
<ueb-ui-slider></ueb-ui-slider>
</div>