This commit is contained in:
barsdeveloper
2024-12-11 21:23:31 +00:00
committed by GitHub
329 changed files with 6822 additions and 5249 deletions

0
.gitignore vendored Normal file → Executable file
View File

0
.vscode/extensions.json vendored Normal file → Executable file
View File

0
.vscode/launch.json vendored Normal file → Executable file
View File

0
.vscode/settings.json vendored Normal file → Executable file
View File

0
CONTRIBUTING.md Normal file → Executable file
View File

0
LICENSE Normal file → Executable file
View File

0
README.md Normal file → Executable file
View File

0
assets/fonts/roboto-bold.woff2 Normal file → Executable file
View File

0
assets/fonts/roboto-condensed-bold.woff2 Normal file → Executable file
View File

0
assets/fonts/roboto-light.woff2 Normal file → Executable file
View File

0
assets/fonts/roboto-regular.woff2 Normal file → Executable file
View File

19
dist/css/ueb-style.css vendored Normal file → Executable file
View File

@@ -43,7 +43,7 @@ ueb-blueprint svg {
}
}
.ueb-zoom-changed .ueb-viewport-zoom {
animation: 600ms ueb-zoom-animation;
animation: 1500ms ueb-zoom-animation;
}
.ueb-viewport-zoom {
@@ -811,6 +811,23 @@ ueb-node[data-type="/Script/BlueprintGraph.K2Node_VariableSet"] ueb-pin[data-dir
outline: none;
}
.ueb-pin-input[type=checkbox] {
display: grid;
place-content: center;
appearance: none;
width: 18px;
height: 18px;
border: 1px solid #353535;
background: #0f0f0f;
}
.ueb-pin-input[type=checkbox]:checked::before {
content: "";
height: 0.7em;
width: 0.8em;
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
background: #0070e0;
}
ueb-pin[data-linked=true] .ueb-pin-input,
ueb-pin[data-linked=true] .ueb-pin-input-wrapper {
display: none;

2
dist/css/ueb-style.css.map vendored Normal file → Executable file

File diff suppressed because one or more lines are too long

2
dist/css/ueb-style.min.css vendored Normal file → Executable file

File diff suppressed because one or more lines are too long

2
dist/css/ueb-style.min.css.map vendored Normal file → Executable file

File diff suppressed because one or more lines are too long

0
dist/font/roboto-bold.woff vendored Normal file → Executable file
View File

0
dist/font/roboto-bold.woff2 vendored Normal file → Executable file
View File

0
dist/font/roboto-condensed-bold.woff2 vendored Normal file → Executable file
View File

0
dist/font/roboto-condensed-regular.woff2 vendored Normal file → Executable file
View File

0
dist/font/roboto-light.woff vendored Normal file → Executable file
View File

0
dist/font/roboto-light.woff2 vendored Normal file → Executable file
View File

0
dist/font/roboto-regular.woff vendored Normal file → Executable file
View File

0
dist/font/roboto-regular.woff2 vendored Normal file → Executable file
View File

841
dist/ueblueprint.js vendored

File diff suppressed because it is too large Load Diff

10
dist/ueblueprint.min.js vendored Normal file → Executable file

File diff suppressed because one or more lines are too long

View File

@@ -11,7 +11,6 @@
padding: 0;
--ueb-height: 100vh;
}
</style>
</head>

View File

@@ -5,6 +5,7 @@ import LinkElement from "./element/LinkElement.js"
import NodeElement from "./element/NodeElement.js"
import BlueprintEntity from "./entity/BlueprintEntity.js"
import BooleanEntity from "./entity/BooleanEntity.js"
import NiagaraClipboardContent from "./entity/objects/NiagaraClipboardContent.js"
import BlueprintTemplate from "./template/BlueprintTemplate.js"
/** @extends {IElement<BlueprintEntity, BlueprintTemplate>} */
@@ -297,29 +298,11 @@ export default class Blueprint extends IElement {
return [x, y]
}
getNodes(
selected = false,
[t, r, b, l] = [
Number.MIN_SAFE_INTEGER,
Number.MAX_SAFE_INTEGER,
Number.MAX_SAFE_INTEGER,
Number.MIN_SAFE_INTEGER,
]
) {
getNodes(selected = false) {
let result = this.nodes
if (selected) {
result = result.filter(n => n.selected)
}
if (
t > Number.MIN_SAFE_INTEGER
|| r < Number.MAX_SAFE_INTEGER
|| b < Number.MAX_SAFE_INTEGER
|| l > Number.MIN_SAFE_INTEGER
) {
result = result.filter(n => {
return n.topBoundary() >= t && n.rightBoundary() <= r && n.bottomBoundary() <= b && n.leftBoundary() >= l
})
}
return result
}
@@ -384,6 +367,22 @@ export default class Blueprint extends IElement {
this.getNodes().forEach(node => Blueprint.nodeSelectToggleFunction(node, false))
}
getSerializedText() {
const nodes = this.blueprint.getNodes(true).map(n => n.entity)
let exports = false
let result = nodes
.filter(n => {
exports ||= n.exported
return !n.exported
})
.reduce((acc, cur) => acc + cur.serialize(), "")
if (exports) {
const object = new NiagaraClipboardContent(this.blueprint.entity, nodes)
result = object.serialize() + result
}
return result
}
/** @param {...IElement} graphElements */
addGraphElement(...graphElements) {
/** @param {CustomEvent} event */
@@ -414,7 +413,7 @@ export default class Blueprint extends IElement {
this.entity = this.entity.mergeWith(element.entity)
const additionalSerialization = atob(element.entity.ExportedNodes.toString())
this.template.getPasteInputObject().pasted(additionalSerialization)
.forEach(node => node.entity._exported = true)
.forEach(node => node.entity.exported = true)
continue
}
const name = element.entity.getObjectName()

View File

@@ -178,12 +178,16 @@ export default class Configuration {
multiGate: "/Script/BlueprintGraph.K2Node_MultiGate",
niagaraBool: "/Script/Niagara.NiagaraBool",
niagaraClipboardContent: "/Script/NiagaraEditor.NiagaraClipboardContent",
niagaraDataInterfaceCollisionQuery: "/Script/Niagara.NiagaraDataInterfaceCollisionQuery",
niagaraDataInterfaceCurlNoise: "/Script/Niagara.NiagaraDataInterfaceCurlNoise",
niagaraDataInterfaceVolumeTexture: "/Script/Niagara.NiagaraDataInterfaceVolumeTexture",
niagaraFloat: "/Script/Niagara.NiagaraFloat",
niagaraMatrix: "/Script/Niagara.NiagaraMatrix",
niagaraInt32: "/Script/Niagara.NiagaraInt32",
niagaraNodeConvert: "/Script/NiagaraEditor.NiagaraNodeConvert",
niagaraNodeFunctionCall: "/Script/NiagaraEditor.NiagaraNodeFunctionCall",
niagaraNodeInput: "/Script/NiagaraEditor.NiagaraNodeInput",
niagaraNodeOp: "/Script/NiagaraEditor.NiagaraNodeOp",
niagaraNumeric: "/Script/Niagara.NiagaraNumeric",
niagaraParameterMap: "/Script/Niagara.NiagaraParameterMap",
niagaraPosition: "/Script/Niagara.NiagaraPosition",
pawn: "/Script/Engine.Pawn",
pcgEditorGraphNode: "/Script/PCGEditor.PCGEditorGraphNode",
@@ -213,6 +217,7 @@ export default class Configuration {
variableSet: "/Script/BlueprintGraph.K2Node_VariableSet",
vector: "/Script/CoreUObject.Vector",
vector2D: "/Script/CoreUObject.Vector2D",
vector2f: "/Script/CoreUObject.Vector2f",
vector3f: "/Script/CoreUObject.Vector3f",
vector4f: "/Script/CoreUObject.Vector4f",
whileLoop: "/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:WhileLoop",

7
js/SVGIcon.js Normal file → Executable file
View File

@@ -388,6 +388,13 @@ export default class SVGIcon {
</svg>
`
static staticPin = html`
<svg width="16" height="12" viewBox="1 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path class="ueb-pin-tofill" d="M1 7C1 4 3 1 7 1C10 1 14 3 17 6C18 7 18 7 17 8C14 11 10 13 7 13C3 13 1 10 1 7Z" fill="none" stroke="currentColor" stroke-width="2" />
<path class="ueb-pin-tostroke" d="M 9 4 V 3.5 H 5 V 7 H 9 V 10.5 H 5 V 10" stroke="currentColor" stroke-width="2" />
</svg>
`
static switch = html`
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3" y="2" width="6" height="2" fill="white" />

0
js/Shortcuts.js Normal file → Executable file
View File

View File

@@ -80,7 +80,7 @@ export default class Utility {
}
/**
* @param {Attribute} entity
* @param {IEntity} entity
* @param {String} key
* @returns {Boolean}
*/
@@ -147,29 +147,30 @@ export default class Utility {
/**
* @template T
* @param {Array<T>} a
* @param {Array<T>} b
* @param {Array<T>} reference
* @param {Array<T>} additional
* @param {(v: T) => void} adding - Process added element
* @param {(l: T, r: T) => Boolean} predicate
*/
static mergeArrays(a = [], b = [], predicate = (l, r) => l == r) {
static mergeArrays(reference = [], additional = [], predicate = (l, r) => l == r, adding = v => { }) {
let result = []
a = [...a]
b = [...b]
reference = [...reference]
additional = [...additional]
restart:
while (true) {
for (let j = 0; j < b.length; ++j) {
for (let i = 0; i < a.length; ++i) {
if (predicate(a[i], b[j])) {
for (let j = 0; j < additional.length; ++j) {
for (let i = 0; i < reference.length; ++i) {
if (predicate(reference[i], additional[j])) {
// Found an element in common in the two arrays
result.push(
// Take and append all the elements skipped from a
...a.splice(0, i),
...reference.splice(0, i),
// Take and append all the elements skippend from b
...b.splice(0, j),
...additional.splice(0, j).map(v => (adding(v), v)),
// Take and append the element in common
...a.splice(0, 1)
...reference.splice(0, 1)
)
b.shift() // Remove the same element from b
additional.shift() // Remove the same element from b
continue restart
}
}
@@ -177,7 +178,9 @@ export default class Utility {
break restart
}
// Append remaining the elements in the arrays and make it unique
return [...(new Set(result.concat(...a, ...b)))]
result.push(...reference)
result.push(...additional.filter(vb => !result.some(vr => predicate(vr, vb))))
return result
}
/** @param {String} value */

0
js/action/RemoveAllNodes.js Normal file → Executable file
View File

12
js/decoding/nodeColor.js Normal file → Executable file
View File

@@ -20,6 +20,15 @@ export default function nodeColor(entity) {
return Configuration.nodeColors.blue
case Configuration.paths.materialExpressionTextureSample:
return Configuration.nodeColors.darkTurquoise
case Configuration.paths.niagaraNodeInput:
switch (entity["Usage"]?.toString()) {
case "Attribute": return Configuration.nodeColors.intenseGreen
case "Parameter": return Configuration.nodeColors.red
case "RapidIterationParameter": return Configuration.nodeColors.black
case "SystemConstant": return Configuration.nodeColors.gray
case "TranslatorConstant": return Configuration.nodeColors.gray
default: return Configuration.nodeColors.red
}
}
switch (entity.getClass()) {
case Configuration.paths.callFunction:
@@ -76,5 +85,8 @@ export default function nodeColor(entity) {
if (entity.bIsPureFunc?.valueOf()) {
return Configuration.nodeColors.green
}
if (entity["Input"]?.["Name"]) {
return Configuration.nodeColors.gray
}
return Configuration.nodeColors.blue
}

0
js/decoding/nodeIcon.js Normal file → Executable file
View File

3
js/decoding/nodeSubtitle.js Normal file → Executable file
View File

@@ -10,12 +10,13 @@ export default function nodeSubtitle(entity) {
switch (entity.getType()) {
case Configuration.paths.addDelegate:
case Configuration.paths.clearDelegate:
case Configuration.paths.callDelegate:
case Configuration.paths.removeDelegate:
return null
}
const targetPin = entity
.getPinEntities()
.find(pin => pin.PinName?.toString() === "self" && pinTitle(pin) === "Target")
.find(pin => !pin.isHidden() && pin.PinName?.toString() === "self" && pinTitle(pin) === "Target")
if (targetPin) {
const target = entity.FunctionReference?.MemberParent?.getName()
?? targetPin.PinType?.PinSubCategoryObject?.getName()

110
js/decoding/nodeTemplate.js Normal file → Executable file
View File

@@ -9,22 +9,86 @@ import VariableAccessNodeTemplate from "../template/node/VariableAccessNodeTempl
import VariableConversionNodeTemplate from "../template/node/VariableConversionNodeTemplate.js"
import VariableOperationNodeTemplate from "../template/node/VariableOperationNodeTemplate.js"
const niagaraOperationNodes = [
"Boolean::LogicEq",
"Boolean::LogicNEq",
"Integer::EnumNEq",
"Integer::EnumEq",
...[
"Abs",
"Add",
"ArcCosine(Degrees)",
"ArcCosine(Radians)",
"ArcSine(Degrees)",
"ArcSine(Radians)",
"ArcTangent(Degrees)",
"ArcTangent(Radians)",
"Ceil",
"CmpEQ",
"CmpGE",
"CmpGT",
"CmpLE",
"CmpLT",
"CmpNEQ",
"Cosine(Degrees)",
"Cosine(Radians)",
"DegreesToRadians",
"Div",
"Dot",
"Exp",
"Exp2",
"Floor",
"FMod",
"Frac",
"Length",
"Lerp",
"Log",
"Log2",
"Madd",
"Max",
"Min",
"Mul",
"Negate",
"Normalize",
"OneMinus",
"PI",
"RadiansToDegrees",
"Rcp",
"RcpFast",
"Round",
"RSqrt",
"Sign",
"Sine(Degrees)",
"Sine(Radians)",
"Sqrt",
"Step",
"Subtract",
"Tangent(Degrees)",
"Tangent(Radians)",
"Trunc",
"TWO_PI",
].map(v => "Numeric::" + v),
"Vector3::Cross",
]
const paths = Configuration.paths
/**
* @param {ObjectEntity} nodeEntity
* @return {new () => NodeTemplate}
*/
export default function nodeTemplateClass(nodeEntity) {
if (
nodeEntity.getClass() === Configuration.paths.callFunction
|| nodeEntity.getClass() === Configuration.paths.commutativeAssociativeBinaryOperator
|| nodeEntity.getClass() === Configuration.paths.callArrayFunction
nodeEntity.getClass() === paths.callFunction
|| nodeEntity.getClass() === paths.commutativeAssociativeBinaryOperator
|| nodeEntity.getClass() === paths.callArrayFunction
) {
const memberParent = nodeEntity.FunctionReference?.MemberParent?.path ?? ""
const memberName = nodeEntity.FunctionReference?.MemberName?.toString()
if (
memberName && (
memberParent === Configuration.paths.kismetMathLibrary
|| memberParent === Configuration.paths.kismetArrayLibrary
memberParent === paths.kismetMathLibrary
|| memberParent === paths.kismetArrayLibrary
)) {
if (memberName.startsWith("Conv_")) {
return VariableConversionNodeTemplate
@@ -77,45 +141,37 @@ export default function nodeTemplateClass(nodeEntity) {
return VariableOperationNodeTemplate
}
}
if (memberParent === Configuration.paths.blueprintSetLibrary) {
if (memberParent === paths.blueprintSetLibrary) {
return VariableOperationNodeTemplate
}
if (memberParent === Configuration.paths.blueprintMapLibrary) {
if (memberParent === paths.blueprintMapLibrary) {
return VariableOperationNodeTemplate
}
}
switch (nodeEntity.getClass()) {
case Configuration.paths.comment:
case Configuration.paths.materialGraphNodeComment:
case paths.comment:
case paths.materialGraphNodeComment:
return CommentNodeTemplate
case Configuration.paths.createDelegate:
case paths.createDelegate:
return NodeTemplate
case Configuration.paths.metasoundEditorGraphExternalNode:
case paths.metasoundEditorGraphExternalNode:
if (nodeEntity["ClassName"]?.["Name"] == "Add") {
return MetasoundOperationTemplate
}
return MetasoundNodeTemplate
case Configuration.paths.niagaraNodeOp:
if (
[
"Boolean::LogicEq",
"Boolean::LogicNEq",
"Numeric::Abs",
"Numeric::Add",
"Numeric::Mul",
].includes(nodeEntity.OpName?.toString())
) {
case paths.niagaraNodeOp:
if (niagaraOperationNodes.includes(nodeEntity.OpName?.toString())) {
return VariableOperationNodeTemplate
}
break
case Configuration.paths.promotableOperator:
case paths.promotableOperator:
return VariableOperationNodeTemplate
case Configuration.paths.knot:
case paths.knot:
return KnotNodeTemplate
case Configuration.paths.literal:
case Configuration.paths.self:
case Configuration.paths.variableGet:
case Configuration.paths.variableSet:
case paths.literal:
case paths.self:
case paths.variableGet:
case paths.variableSet:
return VariableAccessNodeTemplate
}
if (nodeEntity.isEvent()) {

201
js/decoding/nodeTitle.js Normal file → Executable file
View File

@@ -5,6 +5,7 @@ import LinearColorEntity from "../entity/LinearColorEntity.js"
import MirroredEntity from "../entity/MirroredEntity.js"
import VectorEntity from "../entity/VectorEntity.js"
const paths = Configuration.paths
const sequencerScriptingNameRegex = /\/Script\/SequencerScripting\.MovieSceneScripting(.+)Channel/
const keyNameValue = {
"A_AccentGrave": "à",
@@ -34,6 +35,66 @@ const keyNameValue = {
"Subtract": "Num -",
"Tilde": "`",
}
const niagaraNodeNames = {
"Boolean::LogicAnd": "Logic AND",
"Boolean::LogicEq": "==",
"Boolean::LogicNEq": "!=",
"Boolean::LogicNot": "Logic NOT",
"Boolean::LogicOr": "Logic OR",
"Integer::BitAnd": "Bitwise AND",
"Integer::BitLShift": "Bitwise Left Shift",
"Integer::BitNot": "Bitwise NOT",
"Integer::BitOr": "Bitwise OR",
"Integer::BitRShift": "Bitwise Right Shift",
"Integer::BitXOr": "Bitwise XOR",
"Integer::EnumEq": "==",
"Integer::EnumNEq": "!=",
"Matrix::MatrixMultiply": "Multiply (Matrix * Matrix)",
"Matrix::MatrixVectorMultiply": "Multiply (Matrix * Vector4)",
// Numeric::
...Object.fromEntries(Object.entries({
"Add": "+",
"ArcCosine": "ArcCosine",
"ArcCosine(Degrees)": "ArcCos(D)",
"ArcCosine(Radians)": "ArcCos(R)",
"ArcSine": "ArcSine",
"ArcSine(Degrees)": "ArcSin(D)",
"ArcSine(Radians)": "ArcSin(R)",
"ArcTangent(Degrees)": "ArcTan(D)",
"ArcTangent(Radians)": "ArcTan(R)",
"CmpEQ": "==",
"CmpGE": ">=",
"CmpGT": ">",
"CmpLE": "<=",
"CmpLT": "<",
"CmpNEQ": "!=",
"Cosine(Degrees)": "Cos(D)",
"Cosine(Radians)": "Cos(R)",
"DegreesToRadians": "DegToRad",
"DistancePos": "Distance",
"Div": String.fromCharCode(0x00f7),
"FMod": "%",
"FModFast": "Modulo Fast",
"Length": "Len",
"Madd": `(A${String.fromCharCode(0x2a2f)}B)+C`,
"Mul": String.fromCharCode(0x2a2f),
"Negate": "-A",
"OneMinus": "1-A",
"PI": String.fromCharCode(0x03C0),
"RadiansToDegrees": "RadToDeg",
"Rand Float": "Random Float",
"Rand Integer": "Random Integer",
"Rand": "Random",
"Rcp": "Reciprocal",
"RSqrt": "Rcp Sqrt",
"Sine(Degrees)": "Sin(D)",
"Sine(Radians)": "Sin(R)",
"Subtract": "-",
"Tangent(Degrees)": "Tan(D)",
"Tangent(Radians)": "Tan(R)",
"TWO_PI": `2 ${String.fromCharCode(0x03C0)}`,
}).map(([k, v]) => ["Numeric::" + k, v])),
}
/** @param {String} value */
function numberFromText(value = "") {
@@ -78,58 +139,58 @@ function keyName(value) {
export default function nodeTitle(entity) {
let value
switch (entity.getType()) {
case Configuration.paths.addDelegate:
case paths.addDelegate:
value ??= "Bind Event to "
case Configuration.paths.clearDelegate:
case paths.clearDelegate:
value ??= "Unbind all Events from "
case Configuration.paths.removeDelegate:
case paths.removeDelegate:
value ??= "Unbind Event from "
return value + Utility.formatStringName(
entity.DelegateReference?.MemberName?.toString().replace(/Delegate$/, "") ?? "None"
)
case Configuration.paths.asyncAction:
case paths.asyncAction:
if (entity.ProxyFactoryFunctionName) {
return Utility.formatStringName(entity.ProxyFactoryFunctionName?.toString())
}
case Configuration.paths.actorBoundEvent:
case Configuration.paths.componentBoundEvent:
case paths.actorBoundEvent:
case paths.componentBoundEvent:
return `${Utility.formatStringName(entity.DelegatePropertyName?.toString())} (${entity.ComponentPropertyName?.toString() ?? "Unknown"})`
case Configuration.paths.callDelegate:
case paths.callDelegate:
return `Call ${entity.DelegateReference?.MemberName?.toString() ?? "None"}`
case Configuration.paths.createDelegate:
case paths.createDelegate:
return "Create Event"
case Configuration.paths.customEvent:
case paths.customEvent:
if (entity.CustomFunctionName) {
return entity.CustomFunctionName?.toString()
}
case Configuration.paths.dynamicCast:
case paths.dynamicCast:
if (!entity.TargetType) {
return "Bad cast node" // Target type not found
}
return `Cast To ${entity.TargetType?.getName()}`
case Configuration.paths.enumLiteral:
case paths.enumLiteral:
return `Literal enum ${entity.Enum?.getName()}`
case Configuration.paths.event:
case paths.event:
return `Event ${(entity.EventReference?.MemberName?.toString() ?? "").replace(/^Receive/, "")}`
case Configuration.paths.executionSequence:
case paths.executionSequence:
return "Sequence"
case Configuration.paths.forEachElementInEnum:
case paths.forEachElementInEnum:
return `For Each ${entity.Enum?.getName()}`
case Configuration.paths.forEachLoopWithBreak:
case paths.forEachLoopWithBreak:
return "For Each Loop with Break"
case Configuration.paths.functionEntry:
case paths.functionEntry:
return entity.FunctionReference?.MemberName?.toString() === "UserConstructionScript"
? "Construction Script"
: entity.FunctionReference?.MemberName?.toString()
case Configuration.paths.functionResult:
case paths.functionResult:
return "Return Node"
case Configuration.paths.ifThenElse:
case paths.ifThenElse:
return "Branch"
case Configuration.paths.makeStruct:
case paths.makeStruct:
if (entity.StructType) {
return `Make ${entity.StructType.getName()}`
}
case Configuration.paths.materialExpressionComponentMask: {
case paths.materialExpressionComponentMask: {
const materialObject = entity.getMaterialSubobject()
if (materialObject) {
return `Mask ( ${Configuration.rgba
@@ -138,15 +199,15 @@ export default function nodeTitle(entity) {
.join("")})`
}
}
case Configuration.paths.materialExpressionConstant:
case paths.materialExpressionConstant:
value ??= [entity.getCustomproperties().find(pinEntity => pinEntity.PinName.toString() == "Value")?.DefaultValue]
case Configuration.paths.materialExpressionConstant2Vector:
case paths.materialExpressionConstant2Vector:
value ??= [
entity.getCustomproperties().find(pinEntity => pinEntity.PinName?.toString() == "X")?.DefaultValue,
entity.getCustomproperties().find(pinEntity => pinEntity.PinName?.toString() == "Y")?.DefaultValue,
]
case Configuration.paths.materialExpressionConstant3Vector:
case Configuration.paths.materialExpressionConstant4Vector:
case paths.materialExpressionConstant3Vector:
case paths.materialExpressionConstant4Vector:
if (!value) {
const vector = entity.getCustomproperties()
.find(pinEntity => pinEntity.PinName?.toString() == "Constant")
@@ -160,32 +221,32 @@ export default function nodeTitle(entity) {
}
value = undefined
break
case Configuration.paths.materialExpressionFunctionInput: {
case paths.materialExpressionFunctionInput: {
const materialObject = entity.getMaterialSubobject()
const inputName = materialObject?.InputName ?? "In"
const inputType = materialObject?.InputType?.value.match(/^.+?_(\w+)$/)?.[1] ?? "Vector3"
return `Input ${inputName} (${inputType})`
}
case Configuration.paths.materialExpressionLogarithm:
case paths.materialExpressionLogarithm:
return "Ln"
case Configuration.paths.materialExpressionLogarithm10:
case paths.materialExpressionLogarithm10:
return "Log10"
case Configuration.paths.materialExpressionLogarithm2:
case paths.materialExpressionLogarithm2:
return "Log2"
case Configuration.paths.materialExpressionMaterialFunctionCall:
case paths.materialExpressionMaterialFunctionCall:
const materialFunction = entity.getMaterialSubobject()?.MaterialFunction
if (materialFunction) {
return materialFunction.getName()
}
break
case Configuration.paths.materialExpressionSquareRoot:
case paths.materialExpressionSquareRoot:
return "Sqrt"
case Configuration.paths.materialExpressionSubtract:
case paths.materialExpressionSubtract:
const materialObject = entity.getMaterialSubobject()
if (materialObject) {
return `Subtract(${materialObject.ConstA ?? "1"},${materialObject.ConstB ?? "1"})`
}
case Configuration.paths.metasoundEditorGraphExternalNode: {
case paths.metasoundEditorGraphExternalNode: {
const name = entity["ClassName"]?.["Name"]
if (name) {
switch (name) {
@@ -194,11 +255,19 @@ export default function nodeTitle(entity) {
}
}
}
case Configuration.paths.pcgEditorGraphNodeInput:
case paths.niagaraNodeConvert:
/** @type {String} */
const targetType = (entity["AutowireMakeType"]?.["ClassStructOrEnum"] ?? "")
.toString()
.match(/(?:Niagara)?(\w+)['"]*$/)
?.[1]
?? ""
return `Make ${targetType}`
case paths.pcgEditorGraphNodeInput:
return "Input"
case Configuration.paths.pcgEditorGraphNodeOutput:
case paths.pcgEditorGraphNodeOutput:
return "Output"
case Configuration.paths.spawnActorFromClass:
case paths.spawnActorFromClass:
let className = entity.getCustomproperties()
.find(pinEntity => pinEntity.PinName.toString() == "ReturnValue")
?.PinType
@@ -208,15 +277,16 @@ export default function nodeTitle(entity) {
className = null
}
return `SpawnActor ${Utility.formatStringName(className ?? "NONE")}`
case Configuration.paths.switchEnum:
case paths.switchEnum:
return `Switch on ${entity.Enum?.getName() ?? "Enum"}`
case Configuration.paths.switchInteger:
case paths.switchInteger:
return `Switch on Int`
case Configuration.paths.variableGet:
case paths.variableGet:
return ""
case Configuration.paths.variableSet:
case paths.variableSet:
return "SET"
}
const className = entity.getClass()
let switchTarget = entity.switchTarget()
if (switchTarget) {
if (switchTarget[0] !== "E") {
@@ -231,18 +301,19 @@ export default function nodeTitle(entity) {
if (keyNameSymbol) {
const name = keyNameSymbol.toString()
let title = keyName(name) ?? Utility.formatStringName(name)
if (entity.getClass() === Configuration.paths.inputDebugKey) {
if (className === paths.inputDebugKey) {
title = "Debug Key " + title
} else if (entity.getClass() === Configuration.paths.getInputAxisKeyValue) {
} else if (className === paths.getInputAxisKeyValue) {
title = "Get " + title
}
return title
}
if (entity.getClass() === Configuration.paths.macro) {
if (className === paths.macro) {
return Utility.formatStringName(entity.MacroGraphReference?.getMacroName())
}
if (entity.isMaterial() && entity.getMaterialSubobject()) {
let result = nodeTitle(entity.getMaterialSubobject())
const materialSubobject = entity.getMaterialSubobject()
if (materialSubobject) {
let result = nodeTitle(materialSubobject)
result = result.match(/Material Expression (.+)/)?.[1] ?? result
return result
}
@@ -257,7 +328,7 @@ export default function nodeTitle(entity) {
}
const settingsObject = entity.getSettingsObject()
if (settingsObject) {
if (settingsObject.ExportPath.type === Configuration.paths.pcgHiGenGridSizeSettings) {
if (settingsObject.ExportPath?.valueOf()?.type === paths.pcgHiGenGridSizeSettings) {
return `Grid Size: ${(
settingsObject.HiGenGridSize?.toString().match(/\d+/)?.[0]?.concat("00")
?? settingsObject.HiGenGridSize?.toString().match(/^\w+$/)?.[0]
@@ -299,10 +370,10 @@ export default function nodeTitle(entity) {
)
}
switch (memberParent) {
case Configuration.paths.blueprintGameplayTagLibrary:
case Configuration.paths.kismetMathLibrary:
case Configuration.paths.slateBlueprintLibrary:
case Configuration.paths.timeManagementBlueprintLibrary:
case paths.blueprintGameplayTagLibrary:
case paths.kismetMathLibrary:
case paths.slateBlueprintLibrary:
case paths.timeManagementBlueprintLibrary:
const leadingLetter = memberName.match(/[BF]([A-Z]\w+)/)
if (leadingLetter) {
// Some functions start with B or F (Like FCeil, FMax, BMin)
@@ -386,7 +457,7 @@ export default function nodeTitle(entity) {
return "^"
}
break
case Configuration.paths.blueprintSetLibrary:
case paths.blueprintSetLibrary:
{
const setOperationMatch = memberName.match(/Set_(\w+)/)
if (setOperationMatch) {
@@ -394,7 +465,7 @@ export default function nodeTitle(entity) {
}
}
break
case Configuration.paths.blueprintMapLibrary:
case paths.blueprintMapLibrary:
{
const setOperationMatch = memberName.match(/Map_(\w+)/)
if (setOperationMatch) {
@@ -402,7 +473,7 @@ export default function nodeTitle(entity) {
}
}
break
case Configuration.paths.kismetArrayLibrary:
case paths.kismetArrayLibrary:
{
const arrayOperationMath = memberName.match(/Array_(\w+)/)
if (arrayOperationMath) {
@@ -414,20 +485,8 @@ export default function nodeTitle(entity) {
return Utility.formatStringName(memberName)
}
if (entity.OpName) {
switch (entity.OpName.toString()) {
case "Boolean::LogicAnd": return "Logic AND"
case "Boolean::LogicEq": return "=="
case "Boolean::LogicNEq": return "!="
case "Boolean::LogicNot": return "Logic NOT"
case "Boolean::LogicOr": return "Logic OR"
case "Matrix::MatrixMultiply": return "Multiply (Matrix * Matrix)"
case "Matrix::MatrixVectorMultiply": return "Multiply (Matrix * Vector4)"
case "Numeric::Abs": return "Abs"
case "Numeric::Add": return "+"
case "Numeric::DistancePos": return "Distance"
case "Numeric::Mul": return String.fromCharCode(0x2a2f)
}
return Utility.formatStringName(entity.OpName.toString()).replaceAll("::", " ")
return niagaraNodeNames[entity.OpName.toString()]
?? Utility.formatStringName(entity.OpName.toString().replaceAll(/(?:^\w+(?<!^Matrix))?::/g, " "))
}
if (entity.FunctionDisplayName) {
return Utility.formatStringName(entity.FunctionDisplayName.toString())
@@ -435,5 +494,15 @@ export default function nodeTitle(entity) {
if (entity.ObjectRef) {
return entity.ObjectRef.getName()
}
let prefix
if (
className.startsWith(prefix = "/Script/NiagaraEditor.NiagaraNodeParameter")
|| className.startsWith(prefix = "/Script/NiagaraEditor.NiagaraNode")
) {
return entity["Input"]?.["Name"]?.toString() ?? Utility.formatStringName(className.substring(prefix.length))
}
if (entity.ParameterName) {
return entity.ParameterName.toString()
}
return Utility.formatStringName(entity.getNameAndCounter()[0])
}

0
js/decoding/nodeVariadic.js Normal file → Executable file
View File

48
js/decoding/pinColor.js Normal file → Executable file
View File

@@ -2,24 +2,12 @@ import { css } from "lit"
import Configuration from "../Configuration.js"
const colors = {
[Configuration.paths.niagaraBool]: css`146, 0, 0`,
[Configuration.paths.niagaraDataInterfaceVolumeTexture]: css`0, 168, 242`,
[Configuration.paths.niagaraFloat]: css`160, 250, 68`,
[Configuration.paths.niagaraMatrix]: css`0, 88, 200`,
[Configuration.paths.niagaraNumeric]: css`0, 88, 200`,
[Configuration.paths.niagaraPosition]: css`251, 146, 251`,
[Configuration.paths.quat4f]: css`0, 88, 200`,
[Configuration.paths.rotator]: css`157, 177, 251`,
[Configuration.paths.transform]: css`227, 103, 0`,
[Configuration.paths.vector]: css`251, 198, 34`,
[Configuration.paths.vector3f]: css`250, 200, 36`,
[Configuration.paths.vector4f]: css`0, 88, 200`,
"Any": css`132, 132, 132`,
"Any[]": css`132, 132, 132`,
"audio": css`252, 148, 252`,
"blue": css`0, 0, 255`,
"bool": css`146, 0, 0`,
"byte": css`0, 109, 99`,
"byte": css`0, 110, 100`,
"class": css`88, 0, 186`,
"default": css`255, 255, 255`,
"delegate": css`255, 56, 56`,
@@ -27,16 +15,16 @@ const colors = {
"exec": css`240, 240, 240`,
"float": css`160, 252, 70`,
"green": css`0, 255, 0`,
"int": css`31, 224, 172`,
"int": css`30, 224, 172`,
"int32": css`30, 224, 172`,
"int64": css`169, 223, 172`,
"int64": css`170, 224, 172`,
"interface": css`238, 252, 168`,
"name": css`201, 128, 251`,
"name": css`200, 128, 252`,
"object": css`0, 168, 242`,
"Param": css`255, 166, 39`,
"Param[]": css`255, 166, 39`,
"Point": css`63, 137, 255`,
"Point[]": css`63, 137, 255`,
"Param": css`255, 166, 40`,
"Param[]": css`255, 166, 40`,
"Point": css`64, 138, 255`,
"Point[]": css`64, 137, 255`,
"real": css`54, 208, 0`,
"red": css`255, 0, 0`,
"string": css`251, 0, 208`,
@@ -48,6 +36,21 @@ const colors = {
"Volume": css`230, 69, 188`,
"Volume[]": css`230, 69, 188`,
"wildcard": css`128, 120, 120`,
[Configuration.paths.linearColor]: css`0, 88, 200`,
[Configuration.paths.niagaraBool]: css`146, 0, 0`,
[Configuration.paths.niagaraDataInterfaceCollisionQuery]: css`0, 168, 242`,
[Configuration.paths.niagaraDataInterfaceCurlNoise]: css`0, 168, 242`,
[Configuration.paths.niagaraDataInterfaceVolumeTexture]: css`0, 168, 242`,
[Configuration.paths.niagaraFloat]: css`160, 250, 68`,
[Configuration.paths.niagaraInt32]: css`30, 224, 172`,
[Configuration.paths.niagaraPosition]: css`251, 146, 251`,
[Configuration.paths.quat4f]: css`0, 88, 200`,
[Configuration.paths.rotator]: css`157, 177, 251`,
[Configuration.paths.transform]: css`227, 103, 0`,
[Configuration.paths.vector]: css`251, 198, 34`,
[Configuration.paths.vector2f]: css`0, 88, 200`,
[Configuration.paths.vector3f]: css`250, 200, 36`,
[Configuration.paths.vector4f]: css`0, 88, 200`,
}
const pinColorMaterial = css`120, 120, 120`
@@ -62,7 +65,8 @@ export default function pinColor(entity) {
} else if (entity.PinType.PinCategory?.toString() === "optional") {
return pinColorMaterial
}
return colors[entity.getType()]
const type = entity.getType()
return colors[type]
?? colors[entity.PinType.PinCategory?.toString().toLowerCase()]
?? colors["default"]
?? (type.startsWith("/Script/Niagara.") ? colors["struct"] : colors["default"])
}

8
js/decoding/pinTemplate.js Normal file → Executable file
View File

@@ -19,6 +19,7 @@ const inputPinTemplates = {
"bool": BoolPinTemplate,
"byte": IntPinTemplate,
"enum": EnumPinTemplate,
"float": RealPinTemplate,
"int": IntPinTemplate,
"int64": Int64PinTemplate,
"MUTABLE_REFERENCE": ReferencePinTemplate,
@@ -28,10 +29,13 @@ const inputPinTemplates = {
"string": StringPinTemplate,
[Configuration.paths.linearColor]: LinearColorPinTemplate,
[Configuration.paths.niagaraBool]: BoolPinTemplate,
[Configuration.paths.niagaraFloat]: RealPinTemplate,
[Configuration.paths.niagaraInt32]: IntPinTemplate,
[Configuration.paths.niagaraPosition]: VectorPinTemplate,
[Configuration.paths.rotator]: RotatorPinTemplate,
[Configuration.paths.vector]: VectorPinTemplate,
[Configuration.paths.vector2D]: Vector2DPinTemplate,
[Configuration.paths.vector2f]: Vector2DPinTemplate,
[Configuration.paths.vector3f]: VectorPinTemplate,
[Configuration.paths.vector4f]: Vector4DPinTemplate,
}
@@ -44,9 +48,9 @@ export default function pinTemplate(entity) {
if (entity.PinType.bIsReference?.valueOf() && !entity.PinType.bIsConst?.valueOf()) {
return inputPinTemplates["MUTABLE_REFERENCE"]
}
const type = entity.getType()
if (type === "exec") {
if (entity.isExecution()) {
return ExecPinTemplate
}
const type = entity.getType()
return (entity.isInput() ? inputPinTemplates[type] : PinTemplate) ?? PinTemplate
}

1
js/decoding/pinTitle.js Normal file → Executable file
View File

@@ -11,5 +11,6 @@ export default function pinTitle(entity) {
return match[1] // In case they match, then keep the case of the PinToolTip
}
}
result = result.replace(/^Module\./, "")
return result
}

0
js/element/ColorHandlerElement.js Normal file → Executable file
View File

0
js/element/ColorSliderElement.js Normal file → Executable file
View File

0
js/element/DropdownElement.js Normal file → Executable file
View File

0
js/element/ElementFactory.js Normal file → Executable file
View File

0
js/element/IDraggableControlElement.js Normal file → Executable file
View File

0
js/element/IDraggableElement.js Normal file → Executable file
View File

0
js/element/IElement.js Normal file → Executable file
View File

0
js/element/IFromToPositionedElement.js Normal file → Executable file
View File

0
js/element/ISelectableDraggableElement.js Normal file → Executable file
View File

0
js/element/InputElement.js Normal file → Executable file
View File

0
js/element/LinkElement.js Normal file → Executable file
View File

0
js/element/NodeElement.js Normal file → Executable file
View File

0
js/element/PinElement.js Normal file → Executable file
View File

0
js/element/SelectorElement.js Normal file → Executable file
View File

0
js/element/WindowElement.js Normal file → Executable file
View File

0
js/element/defineElements.js Normal file → Executable file
View File

0
js/entity/AlternativesEntity.js Normal file → Executable file
View File

3
js/entity/ArrayEntity.js Normal file → Executable file
View File

@@ -33,8 +33,7 @@ export default class ArrayEntity extends IEntity {
if ((trailing !== undefined) !== Self.trailing) {
Self = Self.flagTrailing(trailing !== undefined)
}
const result = new Self(values)
return result
return new Self(values)
}).label(`ArrayEntity of ${this.type?.className() ?? "unknown values"}`)
}

85
js/entity/BlueprintEntity.js Normal file → Executable file
View File

@@ -6,6 +6,7 @@ export default class BlueprintEntity extends ObjectEntity {
/** @type {Map<String, Number>} */
#objectEntitiesNameCounter = new Map()
#variableNames = new Set()
/** @type {ObjectEntity[]}" */
#objectEntities = []
@@ -13,6 +14,11 @@ export default class BlueprintEntity extends ObjectEntity {
return this.#objectEntities
}
static attributes = {
...super.attributes,
ScriptVariables: super.attributes.ScriptVariables.asUniqueClass(true).withDefault(),
}
/** @param {ObjectEntity} entity */
getHomonymObjectEntity(entity) {
const name = entity.getObjectName()
@@ -54,35 +60,90 @@ export default class BlueprintEntity extends ObjectEntity {
return false
}
/**
* @param {ObjectReferenceEntity} variable
* @param {IEntity} entity
*/
renameScriptVariable(variable, entity) {
const name = variable.getName()
const newName = this.takeFreeName(name)
{
[true, false].forEach(v => {
/** @type {ObjectEntity} */
let object = this[Configuration.subObjectAttributeNameFromReference(variable, v)]
object.Name.value = newName
object.Name = object.Name
})
}
variable.path.replace(name, newName)
}
/** @param {ObjectEntity} entity */
mergeWith(entity) {
if (!entity.ScriptVariables || entity.ScriptVariables.length === 0) {
if ((entity.ScriptVariables?.length ?? 0) === 0) {
// The entity does not add new variables
return this
}
if (!this.ScriptVariables || this.ScriptVariables.length === 0) {
this.ScriptVariables = entity.ScriptVariables
}
let scriptVariables = Utility.mergeArrays(
this.ScriptVariables.valueOf(),
entity.ScriptVariables.valueOf(),
(l, r) => l.OriginalChangeId.value == r.OriginalChangeId.value
(l, r) => l.OriginalChangeId.value == r.OriginalChangeId.value,
added => {
const name = added.ScriptVariable.getName()
if (this.#variableNames.has(name)) {
this.renameScriptVariable(added.ScriptVariable, entity)
}
}
)
if (scriptVariables.length === this.ScriptVariables.length) {
// The entity does not add new variables
return this
}
scriptVariables.reverse()
const entries = scriptVariables.concat(scriptVariables).map((v, i) => {
const name = Configuration.subObjectAttributeNameFromReference(v.ScriptVariable, i >= scriptVariables.length)
return [
name,
this[name] ?? entity[name]
]
})
const name = Configuration.subObjectAttributeNameFromReference(
v.ScriptVariable,
i >= scriptVariables.length // First take all the small objects then all name only
)
const object = this[name] ?? entity[name]
return object ? [name, object] : null
}).filter(v => v)
entries.push(
...Object.entries(this).filter(([k, v]) =>
!k.startsWith(Configuration.subObjectAttributeNamePrefix)
&& k !== "ExportedNodes"
)
),
["ScriptVariables", new (BlueprintEntity.attributes.ScriptVariables)(scriptVariables.reverse())]
)
return new BlueprintEntity(Object.fromEntries(entries))
}
/** @param {ObjectEntity[]} entities */
getVariablesAttributesReferringTo(...entities) {
let pins = new Set(...entities.flatMap(entity => entity.getPinEntities()).map(pin => pin.PinName.toString()))
let attributes = this.ScriptVariables
.valueOf()
.map(v => {
const keySimple = Configuration.subObjectAttributeNameFromReference(v.ScriptVariable, false)
const keyFull = Configuration.subObjectAttributeNameFromReference(v.ScriptVariable, true)
return {
simple: [keySimple, this[keySimple]],
full: [keyFull, this[keyFull]],
variable: v,
}
})
.filter(v => pins.has(v.full?.["Variable"]?.["Name"]))
.reduce(
(acc, cur) => {
acc.simple.push([cur.simple[0], cur.simple[1]])
acc.full.push([cur.full[0], cur.full[1]])
acc.ScriptVariables.push(cur.variable)
return acc
},
({ simple: [], full: [], ScriptVariables: [] })
)
return {
}
}
}

0
js/entity/ColorChannelEntity.js Normal file → Executable file
View File

0
js/entity/ComputedTypeEntity.js Normal file → Executable file
View File

0
js/entity/FormatTextEntity.js Normal file → Executable file
View File

10
js/entity/IEntity.js Normal file → Executable file
View File

@@ -44,6 +44,7 @@ export default class IEntity {
this.#keys = [... new Set(value)]
}
// @ts-expect-error
#lookbehind = /** @type {String} */(this.constructor.lookbehind)
get lookbehind() {
return this.#lookbehind.trim()
@@ -145,9 +146,9 @@ export default class IEntity {
* @this {T}
* @returns {T}
*/
static asUniqueClass() {
static asUniqueClass(alwaysCreate = false) {
let result = this
if (this.name.length) {
if (this.name.length || alwaysCreate) {
// @ts-expect-error
result = (() => class extends this { })() // Comes from a lambda otherwise the class will have name "result"
result.grammar = result.createGrammar() // Reassign grammar to capture the correct this from subclass
@@ -429,4 +430,9 @@ export default class IEntity {
}
return true
}
/** @returns {IEntity | Boolean | Number | String | BigInt | (IEntity | Boolean | Number | String | BigInt)[]} */
valueOf() {
return this
}
}

10
js/entity/InvariantTextEntity.js Normal file → Executable file
View File

@@ -19,18 +19,22 @@ export default class InvariantTextEntity extends IEntity {
P.reg(new RegExp(`${this.lookbehind}\\s*\\(`)),
P.doubleQuotedString,
P.reg(/\s*\)/)
).map(([_0, value, _2]) => Number(value)),
P.reg(new RegExp(this.lookbehind)).map(() => 0) // InvariantTextEntity can not have arguments
).map(([_0, value, _2]) => value),
P.reg(new RegExp(this.lookbehind)).map(() => "") // InvariantTextEntity can have no arguments
)
.map(value => new this(value))
.label("InvariantTextEntity")
}
doSerialize() {
return this.lookbehind + "(" + this.value + ")"
return this.lookbehind + '("' + this.value + '")'
}
valueOf() {
return this.value
}
toString() {
return this.value
}
}

0
js/entity/KeyBindingEntity.js Normal file → Executable file
View File

0
js/entity/LinearColorEntity.js Normal file → Executable file
View File

8
js/entity/MirroredEntity.js Normal file → Executable file
View File

@@ -66,13 +66,13 @@ export default class MirroredEntity extends IEntity {
return this.getter?.().equals(other)
}
/** @returns {InstanceType<T>} */
valueOf() {
this.valueOf = this.getter().valueOf.bind(this.getter())
return this.valueOf()
// @ts-expect-error
return this.getter().valueOf()
}
toString() {
this.toString = this.getter().toString.bind(this.getter())
return this.toString()
return this.getter().toString()
}
}

0
js/entity/NullEntity.js Normal file → Executable file
View File

View File

@@ -47,7 +47,7 @@ export default class ObjectEntity extends IEntity {
Class: ObjectReferenceEntity,
Name: StringEntity,
Archetype: ObjectReferenceEntity,
ExportPath: ObjectReferenceEntity,
ExportPath: MirroredEntity.of(ObjectReferenceEntity),
ObjectRef: ObjectReferenceEntity,
BlueprintElementType: ObjectReferenceEntity,
BlueprintElementInstance: ObjectReferenceEntity,
@@ -107,8 +107,11 @@ export default class ObjectEntity extends IEntity {
SizeX: MirroredEntity.of(IntegerEntity),
SizeY: MirroredEntity.of(IntegerEntity),
Text: MirroredEntity.of(StringEntity),
ParameterName: StringEntity,
ExpressionGUID: GuidEntity,
MaterialExpressionEditorX: MirroredEntity.of(IntegerEntity),
MaterialExpressionEditorY: MirroredEntity.of(IntegerEntity),
MaterialExpressionGuid: GuidEntity,
NodeTitle: StringEntity,
NodeTitleColor: LinearColorEntity,
PositionX: MirroredEntity.of(IntegerEntity),
@@ -136,7 +139,7 @@ export default class ObjectEntity extends IEntity {
NodeGuid: GuidEntity,
ErrorType: IntegerEntity,
ErrorMsg: StringEntity,
ScriptVariables: ArrayEntity.of(ScriptVariableEntity),
ScriptVariables: ArrayEntity.flagInlined().of(ScriptVariableEntity),
Node: MirroredEntity.of(ObjectReferenceEntity),
ExportedNodes: StringEntity,
CustomProperties: ArrayEntity.of(AlternativesEntity.accepting(PinEntity, UnknownPinEntity)).withDefault().flagSilent(),
@@ -154,29 +157,28 @@ export default class ObjectEntity extends IEntity {
Grammar.symbolQuoted.map(v => [v, true]),
Grammar.symbol.map(v => [v, false]),
),
P.reg(new RegExp(String.raw`\s*\(\s*(\d+)\s*\)\s*\=\s*`), 1).map(Number)
)
.chain(
/** @param {[[keyof ObjectEntity.attributes, Boolean], Number]} param */
([[symbol, quoted], index]) =>
(this.attributes[symbol]?.grammar ?? IEntity.unknownEntityGrammar).map(currentValue =>
values => {
if (values[symbol] === undefined) {
let arrayEntity = ArrayEntity
if (quoted != arrayEntity.quoted) {
arrayEntity = arrayEntity.flagQuoted(quoted)
}
if (!arrayEntity.inlined) {
arrayEntity = arrayEntity.flagInlined()
}
values[symbol] = new arrayEntity()
P.reg(new RegExp(String.raw`\s*\(\s*(\d+)\s*\)\s*\=\s*`), 1).map(Number) // Number in parentheses then equal
).chain(
/** @param {[[keyof ObjectEntity.attributes, Boolean], Number]} param */
([[symbol, quoted], index]) =>
(this.attributes[symbol]?.grammar ?? IEntity.unknownEntityGrammar).map(currentValue =>
values => {
if (values[symbol] === undefined) {
let arrayEntity = ArrayEntity
if (quoted != arrayEntity.quoted) {
arrayEntity = arrayEntity.flagQuoted(quoted)
}
/** @type {ArrayEntity} */
const target = values[symbol]
target.values[index] = currentValue
if (!arrayEntity.inlined) {
arrayEntity = arrayEntity.flagInlined()
}
values[symbol] = new arrayEntity()
}
)
)
/** @type {ArrayEntity} */
const target = values[symbol]
target.values[index] = currentValue
}
)
)
static grammar = this.createGrammar()
static grammarMultipleObjects = P.seq(
P.whitespaceOpt,
@@ -202,20 +204,20 @@ export default class ObjectEntity extends IEntity {
super(values)
// Attributes
/** @type {ArrayEntity<typeof PinEntity | typeof UnknownPinEntity>} */ this.CustomProperties
/** @type {InstanceType<typeof ObjectEntity.attributes.AddedPins>} */ this.AddedPins
/** @type {InstanceType<typeof ObjectEntity.attributes.AdvancedPinDisplay>} */ this.AdvancedPinDisplay
/** @type {InstanceType<typeof ObjectEntity.attributes.Archetype>} */ this.Archetype
/** @type {InstanceType<typeof ObjectEntity.attributes.AxisKey>} */ this.AxisKey
/** @type {InstanceType<typeof ObjectEntity.attributes.bIsPureFunc>} */ this.bIsPureFunc
/** @type {InstanceType<typeof ObjectEntity.attributes.BlueprintElementInstance>} */ this.BlueprintElementInstance
/** @type {InstanceType<typeof ObjectEntity.attributes.ConstA>} */ this.ConstA
/** @type {InstanceType<typeof ObjectEntity.attributes.ConstB>} */ this.ConstB
/** @type {InstanceType<typeof ObjectEntity.attributes.BlueprintElementType>} */ this.BlueprintElementType
/** @type {InstanceType<typeof ObjectEntity.attributes.Class>} */ this.Class
/** @type {InstanceType<typeof ObjectEntity.attributes.CommentColor>} */ this.CommentColor
/** @type {InstanceType<typeof ObjectEntity.attributes.ComponentPropertyName>} */ this.ComponentPropertyName
/** @type {InstanceType<typeof ObjectEntity.attributes.ConstA>} */ this.ConstA
/** @type {InstanceType<typeof ObjectEntity.attributes.ConstB>} */ this.ConstB
/** @type {InstanceType<typeof ObjectEntity.attributes.CustomFunctionName>} */ this.CustomFunctionName
/** @type {ArrayEntity<typeof PinEntity | typeof UnknownPinEntity>} */ this.CustomProperties
/** @type {InstanceType<typeof ObjectEntity.attributes.DelegatePropertyName>} */ this.DelegatePropertyName
/** @type {InstanceType<typeof ObjectEntity.attributes.DelegateReference>} */ this.DelegateReference
/** @type {InstanceType<typeof ObjectEntity.attributes.EnabledState>} */ this.EnabledState
@@ -254,9 +256,10 @@ export default class ObjectEntity extends IEntity {
/** @type {InstanceType<typeof ObjectEntity.attributes.Operation>} */ this.Operation
/** @type {InstanceType<typeof ObjectEntity.attributes.OpName>} */ this.OpName
/** @type {InstanceType<typeof ObjectEntity.attributes.OutputPins>} */ this.OutputPins
/** @type {InstanceType<typeof ObjectEntity.attributes.ParameterName>} */ this.ParameterName
/** @type {InstanceType<typeof ObjectEntity.attributes.PCGNode>} */ this.PCGNode
/** @type {InstanceType<typeof ObjectEntity.attributes.PinTags>} */ this.PinTags
/** @type {InstanceType<typeof ObjectEntity.attributes.PinNames>} */ this.PinNames
/** @type {InstanceType<typeof ObjectEntity.attributes.PinTags>} */ this.PinTags
/** @type {InstanceType<typeof ObjectEntity.attributes.PositionX>} */ this.PositionX
/** @type {InstanceType<typeof ObjectEntity.attributes.PositionY>} */ this.PositionY
/** @type {InstanceType<typeof ObjectEntity.attributes.ProxyFactoryFunctionName>} */ this.ProxyFactoryFunctionName
@@ -364,6 +367,17 @@ export default class ObjectEntity extends IEntity {
? outputIndex++
: i
})
const reference = this.ExportPath?.valueOf()
if (reference?.path.endsWith(this.Name?.toString())) {
const mirroredEntity = /** @type {typeof ObjectEntity} */(this.constructor).attributes.ExportPath
const objectReferenceEntity = /** @type {typeof ObjectReferenceEntity} */(mirroredEntity.type)
const nameLength = this.Name.valueOf().length
this.ExportPath = new mirroredEntity(() => new objectReferenceEntity(
reference.type,
reference.path.substring(0, reference.path.length - nameLength) + this.Name,
reference.full,
))
}
}
/** @returns {P<ObjectEntity>} */
@@ -414,7 +428,7 @@ export default class ObjectEntity extends IEntity {
getClass() {
if (!this.#class) {
this.#class = (this.Class?.path ? this.Class.path : this.Class?.type)
?? this.ExportPath?.type
?? this.ExportPath?.valueOf()?.type
?? ""
if (this.#class && !this.#class.startsWith("/")) {
// Old path names did not start with /Script or /Engine, check tests/resources/LegacyNodes.js
@@ -428,14 +442,14 @@ export default class ObjectEntity extends IEntity {
}
getType() {
let classValue = this.getClass()
if (this.MacroGraphReference?.MacroGraph?.path) {
return this.MacroGraphReference.MacroGraph.path
const path = this.MacroGraphReference?.MacroGraph?.path
if (path) {
return path
}
if (this.MaterialExpression) {
return this.MaterialExpression.type
}
return classValue
return this.getClass()
}
getObjectName(dropCounter = false) {
@@ -555,24 +569,10 @@ export default class ObjectEntity extends IEntity {
}
isMaterial() {
return this.getClass() === Configuration.paths.materialGraphNode
// return [
// Configuration.paths.materialExpressionConstant,
// Configuration.paths.materialExpressionConstant2Vector,
// Configuration.paths.materialExpressionConstant3Vector,
// Configuration.paths.materialExpressionConstant4Vector,
// Configuration.paths.materialExpressionLogarithm,
// Configuration.paths.materialExpressionLogarithm10,
// Configuration.paths.materialExpressionLogarithm2,
// Configuration.paths.materialExpressionMaterialFunctionCall,
// Configuration.paths.materialExpressionSquareRoot,
// Configuration.paths.materialExpressionTextureCoordinate,
// Configuration.paths.materialExpressionTextureSample,
// Configuration.paths.materialGraphNode,
// Configuration.paths.materialGraphNodeComment,
// ]
// .includes(this.getClass())
const classValue = this.getClass()
return classValue.startsWith("/Script/Engine.MaterialExpression")
|| classValue.startsWith("/Script/InterchangeImport.MaterialExpression")
|| classValue.startsWith("/Script/UnrealEd.MaterialGraph")
}
/** @return {ObjectEntity} */
@@ -690,9 +690,9 @@ export default class ObjectEntity extends IEntity {
? ` Archetype${keySeparator}${this.Archetype.serialize(insideString)}`
: ""
)
+ ((this.ExportPath?.type || this.ExportPath?.path)
// && Self.attributes.ExportPath.ignored !== true
// && this.ExportPath.ignored !== true
+ ((this.ExportPath?.valueOf()?.type || this.ExportPath?.valueOf()?.path)
// && Self.attributes.ExportPath.valueOf().ignored !== true
// && this.ExportPath.valueOf().ignored !== true
? ` ExportPath${keySeparator}${this.ExportPath.serialize(insideString)}`
: ""
)

View File

@@ -5,12 +5,6 @@ import IEntity from "./IEntity.js"
export default class ObjectReferenceEntity extends IEntity {
/** @protected */
static _quotedParser = P.regArray(new RegExp(
`'"(${Grammar.Regex.InsideString.source})"'`
+ "|"
+ `'(${Grammar.Regex.InsideSingleQuotedString.source})'`
)).map(([_0, a, b]) => a ?? b)
static typeReference = P.reg(
// @ts-expect-error
new RegExp(Grammar.Regex.Path.source + "|" + Grammar.symbol.getParser().regexp.source)
@@ -31,11 +25,10 @@ export default class ObjectReferenceEntity extends IEntity {
return this.#path
}
set path(value) {
this.#name = ""
this.#path = value
}
#fullEscaped
/** @type {String} */
#full
get full() {
return this.#full
@@ -44,16 +37,19 @@ export default class ObjectReferenceEntity extends IEntity {
this.#full = value
}
#name = ""
constructor(type = "None", path = "", full = null) {
/** @param {(t: String, p: String) => String} full */
constructor(
type = "None",
path = "",
full = type.includes("/") || path
? (t, p) => `"${t + (p ? (`'${p}'`) : "")}"`
: (t, p) => t) {
super()
this.#type = type
this.#path = path
this.#full = full ?? (
this.type.includes("/") || this.path
? `"${this.type + (this.path ? (`'${this.path}'`) : "")}"`
: this.type
)
this.#full = full
}
/** @returns {P<ObjectReferenceEntity>} */
@@ -71,10 +67,21 @@ export default class ObjectReferenceEntity extends IEntity {
new RegExp(
// @ts-expect-error
"(" + this.typeReference.getParser().regexp.source + ")"
// @ts-expect-error
+ "(?:" + this._quotedParser.getParser().parser.regexp.source + ")"
+ "(?:"
+ `'"(${Grammar.Regex.InsideString.source})"'`
+ "|"
+ `'(${Grammar.Regex.InsideSingleQuotedString.source})'`
+ ")"
)
).map(([full, type, ...path]) => new this(type, path.find(v => v), full))
).map(([full, type, fullQuotedPath, simpleQuotedPath]) => {
let fullQuoted = fullQuotedPath ? true : false
let quotes = fullQuoted ? [`'"`, `"'`] : ["'", "'"]
return new this(
type,
fullQuoted ? fullQuotedPath : simpleQuotedPath,
(t, p) => t + quotes[0] + p + quotes[1]
)
})
}
/** @returns {P<ObjectReferenceEntity>} */
@@ -84,30 +91,34 @@ export default class ObjectReferenceEntity extends IEntity {
'"(' + Grammar.Regex.InsideString.source + "?)"
+ "(?:'(" + Grammar.Regex.InsideSingleQuotedString.source + `?)')?"`
)
).map(([full, type, path]) => new this(type, path, full))
).map(([_0, type, path]) => new this(type, path, (t, p) => `"${t}${p ? `'${p}'` : ""}"`))
}
/** @returns {P<ObjectReferenceEntity>} */
static createTypeReferenceGrammar() {
return this.typeReference.map(v => new this(v, "", v))
return this.typeReference.map(v => new this(v, "", (t, p) => t))
}
static createNoneInstance() {
return new ObjectReferenceEntity("None", "", "None")
return new this("None")
}
getName(dropCounter = false) {
return Utility.getNameFromPath(this.path.replace(/_C$/, ""), dropCounter)
if (!this.#name) {
if (!dropCounter) {
return this.#name = Utility.getNameFromPath(this.path.replace(/_C$/, ""), dropCounter)
}
return Utility.getNameFromPath(this.path.replace(/_C$/, ""), dropCounter)
}
return this.#name
}
doSerialize(insideString = false) {
let result = this.full(this.type, this.path)
if (insideString) {
if (this.#fullEscaped === undefined) {
this.#fullEscaped = Utility.escapeString(this.#full, false)
}
return this.#fullEscaped
result = Utility.escapeString(result, false)
}
return this.full
return result
}
/** @param {IEntity} other */
@@ -117,4 +128,8 @@ export default class ObjectReferenceEntity extends IEntity {
}
return this.type == other.type && this.path == other.path
}
toString() {
return this.full(this.type, this.path)
}
}

View File

@@ -43,12 +43,15 @@ export default class PinEntity extends IEntity {
"byte": ByteEntity,
"enum": EnumEntity,
"exec": StringEntity,
"float": NumberEntity,
"int": IntegerEntity,
"int64": Integer64Entity,
"name": StringEntity,
"real": NumberEntity,
"string": StringEntity,
[Configuration.paths.linearColor]: LinearColorEntity,
[Configuration.paths.niagaraBool]: BooleanEntity,
[Configuration.paths.niagaraFloat]: NumberEntity,
[Configuration.paths.niagaraPosition]: VectorEntity,
[Configuration.paths.rotator]: RotatorEntity,
[Configuration.paths.vector]: VectorEntity,
@@ -170,9 +173,10 @@ export default class PinEntity extends IEntity {
return new PinEntity(objectEntity)
}
/** @returns {String} */
getType() {
const category = this.PinType.PinCategory?.toString().toLocaleLowerCase()
if (category === "struct" || category === "class" || category === "object" || category === "type") {
if (["struct", "class", "object", "type", "statictype"].includes(category)) {
return this.PinType.PinSubCategoryObject?.path
}
if (this.isEnum()) {
@@ -257,6 +261,7 @@ export default class PinEntity extends IEntity {
isExecution() {
return this.PinType.PinCategory.toString() === "exec"
|| this.getType() === Configuration.paths.niagaraParameterMap
}
isHidden() {

0
js/entity/RBSerializationVector2DEntity.js Normal file → Executable file
View File

0
js/entity/RotatorEntity.js Normal file → Executable file
View File

0
js/entity/ScriptVariableEntity.js Normal file → Executable file
View File

0
js/entity/SimpleSerializationRotatorEntity.js Normal file → Executable file
View File

0
js/entity/SimpleSerializationVector2DEntity.js Normal file → Executable file
View File

0
js/entity/SimpleSerializationVector4DEntity.js Normal file → Executable file
View File

0
js/entity/SimpleSerializationVectorEntity.js Normal file → Executable file
View File

0
js/entity/SymbolEntity.js Normal file → Executable file
View File

0
js/entity/TerminalTypeEntity.js Normal file → Executable file
View File

5
js/entity/UnknownKeysEntity.js Normal file → Executable file
View File

@@ -1,9 +1,14 @@
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import GuidEntity from "./GuidEntity.js"
import IEntity from "./IEntity.js"
export default class UnknownKeysEntity extends IEntity {
static attributes = {
...super.attributes,
VariableGuid: GuidEntity,
}
static grammar = this.createGrammar()
static {

View File

@@ -16,7 +16,7 @@ export default class UnknownPinEntity extends PinEntity {
static createGrammar() {
return P.seq(
// Lookbehind
P.reg(new RegExp(`(${Grammar.Regex.Symbol.source}\\s*)\\(\\s*`), 1),
P.reg(new RegExp(`(${Grammar.Regex.Symbol.source}\\s*)?\\(\\s*`), 1),
Grammar.createAttributeGrammar(this).sepBy(Grammar.commaSeparation),
P.reg(/\s*(?:,\s*)?\)/)
).map(([lookbehind, attributes, _2]) => {

0
js/entity/Vector2DEntity.js Normal file → Executable file
View File

0
js/entity/Vector4DEntity.js Normal file → Executable file
View File

0
js/entity/VectorEntity.js Normal file → Executable file
View File

2
js/entity/objects/KnotEntity.js Normal file → Executable file
View File

@@ -25,7 +25,7 @@ export default class KnotEntity extends ObjectEntity {
inputPinEntity.copyTypeFrom(pinReferenceForType)
outputPinEntity.copyTypeFrom(pinReferenceForType)
}
values["CustomProperties"] = new (ObjectEntity.attributes.CustomProperties)([inputPinEntity, outputPinEntity])
values.CustomProperties = new (ObjectEntity.attributes.CustomProperties)([inputPinEntity, outputPinEntity])
super(values)
}
}

View File

@@ -0,0 +1,30 @@
import Configuration from "../../Configuration.js"
import ObjectEntity from "../ObjectEntity.js"
import ObjectReferenceEntity from "../ObjectReferenceEntity.js"
import StringEntity from "../StringEntity.js"
export default class NiagaraClipboardContent extends ObjectEntity {
/**
* @param {BlueprintEntity} blueprint
* @param {ObjectEntity[]} nodes
*/
constructor(blueprint, nodes) {
const typePath = Configuration.paths.niagaraClipboardContent
const name = blueprint.takeFreeName("NiagaraClipboardContent")
const exportPath = `/Engine/Transient.${name}`
let exported = ""
for (const node of nodes) {
if (node.exported) {
exported += node.serialize()
}
}
const result = nodes.filter(n => !n.exported).map(n => n.serialize())
super({
Class: new ObjectReferenceEntity(typePath),
Name: new StringEntity(name),
ExportPath: new ObjectReferenceEntity(typePath, exportPath),
ExportedNodes: new StringEntity(btoa(exported))
})
}
}

0
js/input/IInput.js Normal file → Executable file
View File

0
js/input/InputCombination.js Normal file → Executable file
View File

View File

@@ -1,3 +1,4 @@
import NiagaraClipboardContent from "../../entity/objects/NiagaraClipboardContent.js"
import IInput from "../IInput.js"
/**
@@ -28,20 +29,8 @@ export default class Copy extends IInput {
window.removeEventListener("copy", this.#copyHandler)
}
getSerializedText() {
const allNodes = this.blueprint.getNodes(true).map(n => n.entity)
const exported = allNodes.filter(n => n.exported).map(n => n.serialize())
const result = allNodes.filter(n => !n.exported).map(n => n.serialize())
if (exported.length) {
this.blueprint.entity.ExportedNodes.value = btoa(exported.join(""))
result.splice(0, 0, this.blueprint.entity.serialize(false))
delete this.blueprint.entity.ExportedNodes
}
return result.join("")
}
copied() {
const value = this.getSerializedText()
const value = this.blueprint.getSerializedText()
navigator.clipboard.writeText(value)
return value
}

View File

@@ -33,13 +33,6 @@ export default class Cut extends IInput {
window.removeEventListener("cut", this.#cutHandler)
}
getSerializedText() {
return this.blueprint
.getNodes(true)
.map(node => node.entity.serialize())
.join("")
}
cut() {
this.blueprint.template.getCopyInputObject().copied()
this.blueprint.removeGraphElement(...this.blueprint.getNodes(true))

0
js/input/keyboard/KeyboardEnableZoom.js Normal file → Executable file
View File

0
js/input/keyboard/KeyboardShortcut.js Normal file → Executable file
View File

0
js/input/mouse/IMouseClickDrag.js Normal file → Executable file
View File

0
js/input/mouse/IPointing.js Normal file → Executable file
View File

0
js/input/mouse/MouseClick.js Normal file → Executable file
View File

0
js/input/mouse/MouseDbClick.js Normal file → Executable file
View File

0
js/input/mouse/MouseIgnore.js Normal file → Executable file
View File

0
js/input/mouse/MouseWheel.js Normal file → Executable file
View File

Some files were not shown because too many files have changed in this diff Show More