Various fixes

This commit is contained in:
barsdeveloper
2022-09-09 20:39:08 +02:00
parent 9d424809c9
commit 57ef15c943
13 changed files with 349 additions and 100 deletions

View File

@@ -123,7 +123,7 @@ export default class Utility {
/**
* @param {String} value
*/
static FirstCapital(value) {
static firstCapital(value) {
return value.charAt(0).toUpperCase() + value.substring(1)
}
@@ -164,7 +164,15 @@ export default class Utility {
for (let j = 0; j < b.length; ++j) {
for (let i = 0; i < a.length; ++i) {
if (a[i] == b[j]) {
result.push(...a.splice(0, i), ...b.splice(0, j), ...a.splice(0, 1))
// Found a corresponding element in the two arrays
result.push(
// Take and append all the elements skipped from a
...a.splice(0, i),
// Take and append all the elements skippend from b
...b.splice(0, j),
// Take and append the element in common
...a.splice(0, 1)
)
j = 0
i = 0
b.shift()
@@ -172,6 +180,7 @@ export default class Utility {
}
}
}
// Append remaining the elements in the arrays and make it unique
return [...(new Set(result.concat(...a, ...b)))]
}
@@ -227,4 +236,8 @@ export default class Utility {
static printLinearColor(value) {
return `${Math.round(value.R * 255)}, ${Math.round(value.G * 255)}, ${Math.round(value.B * 255)}`
}
static isFunction(value) {
return value instanceof Function && value.prototype === undefined
}
}

View File

@@ -11,6 +11,7 @@ import PinTemplate from "../template/PinTemplate"
import RealPinTemplate from "../template/RealPinTemplate"
import StringPinTemplate from "../template/StringPinTemplate"
import Utility from "../Utility"
import VectorPinTemplate from "../template/VectorPinTemplate"
/**
* @typedef {import("../entity/GuidEntity").default} GuidEntity
@@ -31,6 +32,7 @@ export default class PinElement extends IElement {
"real": RealPinTemplate,
"string": StringPinTemplate,
"/Script/CoreUObject.LinearColor": LinearColorPinTemplate,
"/Script/CoreUObject.Vector": VectorPinTemplate,
}
static properties = {
@@ -43,10 +45,10 @@ export default class PinElement extends IElement {
type: LinearColorEntity,
converter: {
fromAttribute: (value, type) => {
return ISerializer.grammar.LinearColorFromAnyColor.parse(value).value
return value ? ISerializer.grammar.LinearColorFromAnyColor.parse(value).value : null
},
toAttribute: (value, type) => {
return Utility.printLinearColor(value)
return value ? Utility.printLinearColor(value) : null
},
},
attribute: "data-color",
@@ -111,7 +113,7 @@ export default class PinElement extends IElement {
this.advancedView = entity.bAdvancedView
this.unreactiveDefaultValue = entity.getDefaultValue()
this.pinType = this.entity.getType()
this.color = this.constructor.properties.color.converter.fromAttribute(Configuration.pinColor[this.pinType].toString())
this.color = this.constructor.properties.color.converter.fromAttribute(Configuration.pinColor[this.pinType]?.toString())
this.isLinked = false
this.pinDirection = entity.isInput() ? "input" : entity.isOutput() ? "output" : "hidden"

View File

@@ -0,0 +1,22 @@
/**
* @typedef {import("./IEntity").default} IEntity
*/
export default class CalculatedType {
#f
/**
* @param {Function} f
*/
constructor(f) {
this.#f = f
}
/**
* @param {IEntity} entity
*/
calculate(entity) {
return this.f(entity)
}
}

View File

@@ -1,6 +1,7 @@
import Observable from "../Observable"
import TypeInitialization from "./TypeInitialization"
import Utility from "../Utility"
import CalculatedType from "./CalculatedType"
export default class IEntity extends Observable {
@@ -20,26 +21,33 @@ export default class IEntity extends Observable {
Object.getOwnPropertyNames(properties),
Object.getOwnPropertyNames(values ?? {})
)) {
let defaultValue = properties[property]
const defaultType = Utility.getType(defaultValue)
if (!(property in properties)) {
console.warn(`Property ${prefix}${property} is not defined in ${this.constructor.name}`)
console.warn(`Property ${prefix}${property} is not defined in ${this.constructor.name}.attributes`)
} else if (
defaultValue != null
&& !(defaultValue instanceof TypeInitialization && !defaultValue.showDefault)
&& !(property in values)
) {
console.warn(`${this.constructor.name} adds property ${prefix}${property} not defined in the serialized data`)
console.warn(
`${this.constructor.name} adds property ${prefix}${property} not defined in the serialized data`
)
}
// Not instanceof because all objects are instenceof Object, exact match needed
if (defaultType === Object) {
target[property] = {}
defineAllAttributes(target[property], properties[property], values[property], property + ".")
continue
}
/*
* The value can either be:
* - Array: can contain multiple values, its property is assigned multiple times like (X=1, X=4, X="Hello World").
* - Array: can contain multiple values, its property is assigned multiple times like (X=1, X="4").
* - CalculatedType: the exact type depends on the previous attributes assigned to this entity.
* - TypeInitialization: contains the maximum amount of information about the attribute.
* - A type: the default value will be default constructed object without arguments.
* - A proper value.
@@ -50,6 +58,10 @@ export default class IEntity extends Observable {
// We have a value, need nothing more
continue
}
if (defaultValue instanceof CalculatedType) {
defaultValue = defaultValue.calculate(this)
defaultType = Utility.getType(defaultValue)
}
if (defaultValue instanceof TypeInitialization) {
if (!defaultValue.showDefault) {
target[property] = undefined // Declare undefined to preserve the order of attributes
@@ -61,9 +73,6 @@ export default class IEntity extends Observable {
target[property] = []
continue
}
if (defaultValue instanceof Function) {
defaultValue = TypeInitialization.sanitize(new defaultValue(), defaultType)
}
target[property] = TypeInitialization.sanitize(defaultValue, defaultType)
}
}

View File

@@ -30,7 +30,12 @@ export default class PinEntity extends IEntity {
bSerializeAsSinglePrecisionFloat: false,
},
LinkedTo: new TypeInitialization([PinReferenceEntity], false),
DefaultValue: new TypeInitialization(new SerializedType(LinearColorEntity, String), false),
DefaultValue: new TypeInitialization(
new SerializedType([
LinearColorEntity,
]),
false
),
AutogeneratedDefaultValue: new TypeInitialization(String, false),
DefaultObject: new TypeInitialization(ObjectReferenceEntity, false, null),
PersistentGuid: GuidEntity,

View File

@@ -8,7 +8,19 @@ export default class SerializedType {
this.#types = v
}
constructor(...acceptedTypes) {
this.#types = acceptedTypes
#stringFallback
get stringFallback() {
return this.#stringFallback
}
set stringFallback(v) {
this.#stringFallback = v
}
constructor([...acceptedTypes], stringFallback = true) {
this.#types = [...new Set([
...acceptedTypes,
...(stringFallback ? [String] : [])
])]
this.#stringFallback = stringFallback
}
}

View File

@@ -35,7 +35,6 @@ export default class TypeInitialization {
if (targetType === undefined) {
targetType = value?.constructor
}
let wrongType = false
if (
targetType
&& targetType !== SerializedType

10
js/entity/VectorEntity.js Normal file
View File

@@ -0,0 +1,10 @@
import IEntity from "./IEntity"
export default class LinearColorEntity extends IEntity {
static attributes = {
X: Number,
Y: Number,
Z: Number,
}
}

View File

@@ -15,6 +15,8 @@ import PinReferenceEntity from "../entity/PinReferenceEntity"
import SerializedType from "../entity/SerializedType"
import TypeInitialization from "../entity/TypeInitialization"
import Utility from "../Utility"
import VectorEntity from "../entity/VectorEntity"
import CalculatedType from "../entity/CalculatedType"
/**
* @typedef {import("../entity/IEntity").default} IEntity
@@ -28,13 +30,14 @@ export default class Grammar {
static getGrammarForType(r, attributeType, defaultGrammar) {
if (attributeType instanceof TypeInitialization) {
// Unpack TypeInitialization
attributeType = attributeType.type
return Grammar.getGrammarForType(r, attributeType, defaultGrammar)
}
if (attributeType instanceof SerializedType) {
const noStringTypes = attributeType.types.filter(t => t !== String)
const nonStringTypes = attributeType.types.filter(t => t !== String)
let result = P.alt(
...noStringTypes.map(t =>
...nonStringTypes.map(t =>
Grammar.getGrammarForType(r, t).wrap(P.string('"'), P.string('"')).map(
/**
* @param {IEntity} entity
@@ -46,8 +49,13 @@ export default class Grammar {
)
)
)
if (noStringTypes.length < attributeType.types.length) {
result = result.or(r.String) // Separated because it cannot be wrapped into " and "
if (nonStringTypes.length < attributeType.types.length) {
result = result.or(r.String/*.map(v => {
if (attributeType.stringFallback) {
console.log("Unrecognized value, fallback on String")
}
return v
})*/) // Separated because it cannot be wrapped into " and "
}
return result
}
@@ -72,6 +80,8 @@ export default class Grammar {
return r.InvariantText
case PinReferenceEntity:
return r.PinReference
case VectorEntity:
return r.Vector
case LinearColorEntity:
return r.LinearColor
case FunctionReferenceEntity:
@@ -99,36 +109,28 @@ export default class Grammar {
}
}
static createAttributeGrammar = (r, entityType, valueSeparator = P.string("=").trim(P.optWhitespace)) =>
static createPropertyGrammar = (r, entityType, valueSeparator = P.string("=").trim(P.optWhitespace)) =>
r.AttributeName.skip(valueSeparator)
.chain(attributeName => {
// Once the property name is known, look into entityType.properties to get its type
const attributeKey = attributeName.split(".")
const attribute = Utility.objectGet(entityType.attributes, attributeKey)
let attributeValueGrammar = Grammar.getGrammarForType(r, attribute, r.AttributeAnyValue)
// Returns attributeSetter: a function called with an object as argument that will set the correct attribute value
// Returns a setter function for the property
return attributeValueGrammar.map(attributeValue =>
entity => Utility.objectSet(entity, attributeKey, attributeValue, true)
)
})
/**
* @template T
* @param {new (values: Object) => T} entityType
* @returns {Parsimmon.Parser<T>}
*/
static createMultiAttributeGrammar = (r, entityType) =>
/**
* Basically this creates a parser that looks for a string like 'Key (A=False,B="Something",)'
* Then it populates an object of type EntityType with the attribute values found inside the parentheses.
*/
static createEntityGrammar = (r, entityType) =>
P.seqMap(
entityType.lookbehind
? P.seq(P.string(entityType.lookbehind), P.optWhitespace, P.string("("))
: P.string("("),
Grammar.createAttributeGrammar(r, entityType)
.trim(P.optWhitespace)
.sepBy(P.string(","))
.skip(P.regex(/,?/).then(P.optWhitespace)), // Optional trailing comma
Grammar.createPropertyGrammar(r, entityType)
.trim(P.optWhitespace) // Drop spaces around a property assignment
.sepBy(P.string(",")) // Assignments are separated by comma
.skip(P.regex(/,?/).then(P.optWhitespace)), // Optional trailing comma and maybe additional space
P.string(')'),
(_, attributes, __) => {
let values = {}
@@ -183,8 +185,7 @@ export default class Grammar {
Integer = r => P.regex(/[\-\+]?[0-9]+/).map(v => new IntegerEntity(v)).desc("an integer")
Guid = r => P.regex(/[0-9a-zA-Z]{32}/).map(v => new GuidEntity({ value: v }))
.desc("32 digit hexadecimal (accepts all the letters for safety) value")
Guid = r => r.HexDigit.times(32).tie().map(v => new GuidEntity({ value: v })).desc("32 digit hexadecimal value")
Identifier = r => P.regex(/\w+/).map(v => new IdentifierEntity(v))
@@ -240,7 +241,9 @@ export default class Grammar {
r.Guid,
r.LocalizedText,
r.InvariantText,
r.Reference
r.Reference,
r.Vector,
r.LinearColor,
)
PinReference = r => P.seqMap(
@@ -249,22 +252,24 @@ export default class Grammar {
r.Guid, // Goes into pinGuid
(objectName, _, pinGuid) => new PinReferenceEntity({
objectName: objectName,
pinGuid: pinGuid
pinGuid: pinGuid,
})
)
LinearColor = r => Grammar.createMultiAttributeGrammar(r, LinearColorEntity)
Vector = r => Grammar.createEntityGrammar(r, VectorEntity)
FunctionReference = r => Grammar.createMultiAttributeGrammar(r, FunctionReferenceEntity)
LinearColor = r => Grammar.createEntityGrammar(r, LinearColorEntity)
FunctionReference = r => Grammar.createEntityGrammar(r, FunctionReferenceEntity)
KeyBinding = r => P.alt(
r.Identifier.map(identifier => new KeyBindingEntity({
Key: identifier
})),
Grammar.createMultiAttributeGrammar(r, KeyBindingEntity)
Grammar.createEntityGrammar(r, KeyBindingEntity)
)
Pin = r => Grammar.createMultiAttributeGrammar(r, PinEntity)
Pin = r => Grammar.createEntityGrammar(r, PinEntity)
CustomProperties = r =>
P.string("CustomProperties")
@@ -283,7 +288,7 @@ export default class Grammar {
P
.alt(
r.CustomProperties,
Grammar.createAttributeGrammar(r, ObjectEntity)
Grammar.createPropertyGrammar(r, ObjectEntity)
)
.sepBy1(P.whitespace),
P.seq(r.MultilineWhitespace, P.string("End"), P.whitespace, P.string("Object")),
@@ -356,6 +361,6 @@ export default class Grammar {
r.LinearColorFromRGBList,
r.LinearColorFromHex,
r.LinearColorFromRGB,
r.LinearColorFromRGBA
r.LinearColorFromRGBA,
)
}

View File

@@ -74,7 +74,7 @@ export default class ISerializer {
case Function:
return this.writeValue(value(), fullKey, insideString)
case Boolean:
return Utility.FirstCapital(value.toString())
return Utility.firstCapital(value.toString())
case Number:
return value.toString()
case String:

View File

@@ -0,0 +1,50 @@
import { html } from "lit"
import IInputPinTemplate from "./IInputPinTemplate"
/**
* @typedef {import("../element/PinElement").default} PinElement
* @typedef {import("../entity/LinearColorEntity").default} LinearColorEntity}
*/
export default class VectorPinTemplate extends IInputPinTemplate {
/** @type {HTMLInputElement} */
#input
/**
* @param {PinElement} pin
* @param {Map} changedProperties
*/
firstUpdated(pin, changedProperties) {
super.firstUpdated(pin, changedProperties)
this.#input = pin.querySelector(".ueb-pin-input")
}
/**
* @param {PinElement} pin
*/
getInputs(pin) {
return [this.#input.dataset.linearColor]
}
/**
* @param {PinElement} pin
* @param {String[]} value
*/
setInputs(pin, value = []) {
}
/**
* @param {PinElement} pin
*/
renderInput(pin) {
if (pin.isInput()) {
return html`
<div class="ueb-pin-input">
<span class="ueb-pin-input-x" role="textbox" contenteditable="true" .innerText=${pin.unreactiveDefaultValue}></span>
</div>
`
}
return super.renderInput(pin)
}
}