mirror of
https://github.com/barsdeveloper/ueblueprint.git
synced 2026-02-25 09:14:41 +08:00
Compare commits
60 Commits
typescript
...
74edd2b9d8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74edd2b9d8 | ||
|
|
72057d6c4b | ||
|
|
2f357818f4 | ||
|
|
0e2ecdf93e | ||
|
|
1073691794 | ||
|
|
dd81356d68 | ||
|
|
9985071d61 | ||
|
|
c5f57dd3ef | ||
|
|
5b9b3a2592 | ||
|
|
c73bcdcdb5 | ||
|
|
6167e97406 | ||
|
|
0c6bda6fd4 | ||
|
|
58bb21b332 | ||
|
|
b4fdb99819 | ||
|
|
41988b1599 | ||
|
|
1d9a88125e | ||
|
|
dfa402707c | ||
|
|
15a1769b95 | ||
|
|
c356878e3f | ||
|
|
6403fec906 | ||
|
|
e40e6ba52b | ||
|
|
2173e2b421 | ||
|
|
3b68fb46a8 | ||
|
|
c0032fed76 | ||
|
|
e405a7245d | ||
|
|
3de0e74ef0 | ||
|
|
626b1eaafd | ||
|
|
365732a99c | ||
|
|
20e64e8511 | ||
|
|
d6f6513391 | ||
|
|
079fdb59c9 | ||
|
|
25bb1717df | ||
|
|
bbe92166f0 | ||
|
|
876b8ce47f | ||
|
|
a02877b9b9 | ||
|
|
4cdcae1493 | ||
|
|
5c26c95b5d | ||
|
|
007eec971b | ||
|
|
73176a8102 | ||
|
|
b9c2df5a3e | ||
|
|
4865f5fdab | ||
|
|
b1dc894f03 | ||
|
|
8d91a52e8c | ||
|
|
2352d866a6 | ||
|
|
a8d78483d5 | ||
|
|
404a2aed4f | ||
|
|
90d0cd7016 | ||
|
|
2b028e2800 | ||
|
|
5ea4d75a81 | ||
|
|
3f0185fca5 | ||
|
|
8b38173204 | ||
|
|
c03d0c8355 | ||
|
|
5d4f020a85 | ||
|
|
a763544b3b | ||
|
|
4885a988f9 | ||
|
|
4601c9dc48 | ||
|
|
19ef3bd10e | ||
|
|
2114abef5c | ||
|
|
c246c82bfe | ||
|
|
23ee628e28 |
0
.gitignore
vendored
Normal file → Executable file
0
.gitignore
vendored
Normal file → Executable file
0
.vscode/extensions.json
vendored
Normal file → Executable file
0
.vscode/extensions.json
vendored
Normal file → Executable file
27
.vscode/launch.json
vendored
27
.vscode/launch.json
vendored
@@ -5,31 +5,12 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch index.html",
|
||||
"name": "Launch Blueprintue",
|
||||
"type": "firefox",
|
||||
"request": "launch",
|
||||
"reAttach": true,
|
||||
"file": "${workspaceFolder}/index.html"
|
||||
"url": "http://127.0.0.1:8080/debug.html",
|
||||
"tmpDir": "~/.tmp/"
|
||||
},
|
||||
{
|
||||
"name": "Launch localhost",
|
||||
"type": "firefox",
|
||||
"request": "launch",
|
||||
"reAttach": true,
|
||||
"url": "http://localhost/index.html",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"name": "Attach",
|
||||
"type": "firefox",
|
||||
"request": "attach"
|
||||
},
|
||||
{
|
||||
"name": "Launch WebExtension",
|
||||
"type": "firefox",
|
||||
"request": "launch",
|
||||
"reAttach": true,
|
||||
"addonPath": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
0
.vscode/settings.json
vendored
Normal file → Executable file
0
.vscode/settings.json
vendored
Normal file → Executable file
11
CONTRIBUTING.md
Normal file → Executable file
11
CONTRIBUTING.md
Normal file → Executable file
@@ -1,7 +1,7 @@
|
||||
# Development Guide
|
||||
Getting started with the development of this application is very easy because it has a (arguably) well defined object-oriented architecture and separation of concerns.
|
||||
Getting started with the development of this application is very easy. It has a (arguably) well defined object-oriented architecture and separation of concerns.
|
||||
|
||||
Before starting, the gentle reader might want to make sure to be familiar with the [Lit](https://lit.dev/) library and its element [lifecycle](https://lit.dev/docs/components/lifecycle/). This library is used extensively throught the application to keep the HTML elements in sync with the data and avoid updating the DOM too often. The original author is aware that there are way more popular alternatives out there like React, Vue and Svelte, but the design of Lit fits very well into the original design of this application: vanilla JavaScript and object-oriented. This allowed the introduction of Lit with a relatively small amount of changes to the existing code, yes because the decision to use Lit was made at a later point in time. One important detail is that it does not make use of the shadow DOM (part of the Web Components), the real reason is that the development started without using Lit but it is still nice to be able to have a global CSS style (which wouldn't be possibile with a shadow root) so that restyling the library is just a matter of adding another CSS file and rewrite a few properties.
|
||||
Before starting, the gentle reader might want to make sure to be familiar with the [Lit](https://lit.dev/) library and its element [lifecycle](https://lit.dev/docs/components/lifecycle/). This library is used extensively throught the application to keep the HTML elements in sync with the data and avoid updating the DOM too often. The original author is aware that there are way more popular alternatives out there like React, Vue and Svelte, but the design of Lit fits very well into the original design of this application: vanilla JavaScript and object-oriented. This allowed the introduction of Lit with a relatively small amount of changes to the existing code because the decision to use Lit was made at a later point in time. One important detail is that it does not make use of the shadow DOM (part of the Web Components), the real reason is that the development started without using Lit but it is still nice to be able to have a global CSS style (which wouldn't be possibile with a shadow root) so that restyling the library is just a matter of adding another CSS file and rewrite a few properties.
|
||||
|
||||
The only other external library that is used here is [Parsernostrum](https://github.com/barsdeveloper/parsernostrum): a very small but capable text parsing library used to deserialize the text produced by the UE Blueprint Editor.
|
||||
|
||||
@@ -47,12 +47,7 @@ And refresh the HTML page possibly holding `Shift`.
|
||||
There are a few concepts that must be assimilated in order to understand the design. Those concepts are in general each mapped into a subfolder in `js/`.
|
||||
|
||||
### Entity
|
||||
An Entity is just a data holder object that does not really do anything by itself, it has a purely information storage related purpose. The top class at the hierarchy of entities is `IEntity`. This one is a bit more complicated in the sense that it does the initialization of the entity in its constructor according to the information contained in the object provided as an argument or from the attributes static field. This ended up being a somewhat wacky runtime type system. Each subclass can specify its attributes static member variable of type object where each entry is either a value (the default value the attribute will have), a function (called to generate such default value) or an object and in that case it will be of type `AttributeInformation` (please note that in case one wants to assign as default value a specific object, the solution is either to wrap it into a `AttributeInformation` object or to return it from a function).
|
||||
|
||||
### Grammar and Serializer
|
||||
In the `serialization/` folder the gentle reader will find all the classes responsible for transforming entities from and to text that the UE Blueprint Enditor can understand. One important class here is `Grammar` that contains similar formal grammar rules that use the [Parsernostrum library](https://github.com/barsdeveloper/parsernostrum) to create entities from Blueprint text. `Serializer` is at the top of the serializer classes hierarchy and it uses a factory design pattern to register serializers for the various entities types (check `js/serialization/initializeSerializerFactory.js`). It does both read and write of entities: to read it will use the Grammar after creating a language using a function from Parsernostrum, to write it will use methods from the class itself.
|
||||
|
||||
Grammar is usually the first place to look when pasting valid Blueprint code does fail. Most likely newer version of Unreal Engine did add some new data type that was not implemented yet (or this library never managed to handle it in the first place). In that case the approach should be trying to fix the existing grammar and entities to accept it, then implement the new entities and attributes.
|
||||
An Entity is just a data holder object that does not really do anything by itself, it has a purely information storage related purpose. The top class at the hierarchy of entities is `IEntity`. It is responsible for the initialization of the entity in its constructor according to the information contained in the object provided as an argument or from the attributes static field. This ended up being a somewhat wacky runtime type system. Each subclass can specify its attributes in the form of a static member variable of type vanilla object where each entry is a subclass of IEntity. The entities moreover are responsible for their serialization and deserialization: each class has a `serialize()` method and a `grammar` attribute to parse entities.
|
||||
|
||||
### Element
|
||||
Each element is just a custom HTML element type and its tag name is defined in the class file. The top level of the hierarchy is `IElement` and it inherits from `LitElement`. This class can be thought as an association between an entity and a template (and those are the arguments of the constructor). The top class `IElement` does propagate the lifecycle provided by `LitElement` to the template so that a template can hook into it.
|
||||
|
||||
26
README.md
Normal file → Executable file
26
README.md
Normal file → Executable file
@@ -1,22 +1,22 @@
|
||||
# UEBlueprint
|
||||
|
||||
A stand alone editor implementation of the UE's Blueprint visual language. (WIP)
|
||||
A stand alone implementation of the UE's Blueprint visual language editor
|
||||
|
||||
https://www.npmjs.com/package/ueblueprint
|
||||
|
||||
## Features:
|
||||
|
||||
- Intercommunicates with UE (can copy nodes both ways).
|
||||
- Can be used as a WEB library to visualize, interact and modify Blueprint graphs.
|
||||
- Can be used inside VS Code to operate directly on files from a UE project (in the future).
|
||||
- Can be used as a WEB library to visualize and interact with Blueprint graphs.
|
||||
- Graph shown is pixel-similar to how it appears in UE.
|
||||
- Graph behaves the same way as it does in UE (with the default settings).
|
||||
- All the information shown in he graph is just the one embedded in the serialized text (in VS Code it should be able to access assets also).
|
||||
- Modern object oriented, clean, JavaScript codebase.
|
||||
- All the information shown in he graph is just the one embedded in the serialized text.
|
||||
|
||||
## Demo:
|
||||
[Try it!](https://barsdeveloper.github.io/ueblueprint/)
|
||||
|
||||

|
||||
### [Try it!](https://barsdeveloper.github.io/ueblueprint/)
|
||||
|
||||
[](https://barsdeveloper.github.io/ueblueprint/)
|
||||
|
||||
## Getting started:
|
||||
|
||||
@@ -33,8 +33,9 @@ npx http-server
|
||||
### Use in a web page
|
||||
|
||||
You can check `index.html` for a working example, the main steps are the following:
|
||||
1. Include `dist/css/ueb-style.css` stylesheet in your page.
|
||||
2. Define eventual CSS variables.
|
||||
1. Make the `dist` directory available in your website by copying it or installing through npm `npm i ueblueprint`.
|
||||
2. Include `dist/css/ueb-style.min.css` stylesheet in your page.
|
||||
3. Define eventual CSS variables.
|
||||
```HTML
|
||||
<style>
|
||||
ueb-blueprint {
|
||||
@@ -42,13 +43,14 @@ You can check `index.html` for a working example, the main steps are the followi
|
||||
}
|
||||
</style>
|
||||
```
|
||||
3. Import the class Blueprint in JavaScript (this library uses modules).
|
||||
4. Import the class Blueprint in JavaScript (this library uses modules).
|
||||
```HTML
|
||||
<script type="module">
|
||||
import { Blueprint } from "./dist/ueblueprint.js"
|
||||
import { Blueprint } from "./dist/ueblueprint.min.js"
|
||||
</script>
|
||||
```
|
||||
4. Define your blueprint the by just writing the code inside a `ueb-blueprint`, inside a `template` element.
|
||||
5. Define your blueprint by writing the code inside a `template`, inside a `ueb-blueprint` element.
|
||||
It can have light background using the following CSS class: `<ueb-blueprint class="ueb-light-mode">`
|
||||
```HTML
|
||||
<ueb-blueprint>
|
||||
<template>
|
||||
|
||||
0
assets/fonts/roboto-bold.woff2
Normal file → Executable file
0
assets/fonts/roboto-bold.woff2
Normal file → Executable file
0
assets/fonts/roboto-condensed-bold.woff2
Normal file → Executable file
0
assets/fonts/roboto-condensed-bold.woff2
Normal file → Executable file
0
assets/fonts/roboto-light.woff2
Normal file → Executable file
0
assets/fonts/roboto-light.woff2
Normal file → Executable file
0
assets/fonts/roboto-regular.woff2
Normal file → Executable file
0
assets/fonts/roboto-regular.woff2
Normal file → Executable file
@@ -11,7 +11,6 @@
|
||||
padding: 0;
|
||||
--ueb-height: 100vh;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
135
dist/css/ueb-style.css
vendored
Normal file → Executable file
135
dist/css/ueb-style.css
vendored
Normal file → Executable file
@@ -11,6 +11,9 @@
|
||||
ueb-blueprint {
|
||||
--ueb-scale: 1;
|
||||
--ueb-grid-actual-size: var(--ueb-grid-size);
|
||||
--ueb-grid-line-color: #353535;
|
||||
--ueb-grid-set-line-color: #161616;
|
||||
--ueb-grid-axis-line-color: #000;
|
||||
display: block;
|
||||
position: relative;
|
||||
font-family: Roboto, Noto, Oxygen, Ubuntu, "Open Sans", "Helvetica Neue", sans-serif;
|
||||
@@ -20,7 +23,8 @@ ueb-blueprint {
|
||||
}
|
||||
|
||||
ueb-blueprint svg {
|
||||
overflow: visible;
|
||||
overflow: visible !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
.ueb-viewport-header {
|
||||
@@ -33,6 +37,15 @@ ueb-blueprint svg {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.ueb-viewport-about {
|
||||
margin-top: -6px;
|
||||
padding: 0 8px;
|
||||
align-self: center;
|
||||
font-size: 20px;
|
||||
font-weight: bolder;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@keyframes ueb-zoom-animation {
|
||||
0% {
|
||||
color: #7f7f7f;
|
||||
@@ -42,7 +55,7 @@ ueb-blueprint svg {
|
||||
}
|
||||
}
|
||||
.ueb-zoom-changed .ueb-viewport-zoom {
|
||||
animation: 600ms ueb-zoom-animation;
|
||||
animation: 1500ms ueb-zoom-animation;
|
||||
}
|
||||
|
||||
.ueb-viewport-zoom {
|
||||
@@ -89,7 +102,6 @@ ueb-blueprint[data-scrolling=false] .ueb-grid {
|
||||
|
||||
ueb-blueprint.ueb-zoom--4 {
|
||||
--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 2);
|
||||
--ueb-node-radius: 0 !important;
|
||||
}
|
||||
|
||||
ueb-blueprint.ueb-zoom--6 {
|
||||
@@ -144,6 +156,50 @@ ueb-selector > * {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ueb-info-dialog {
|
||||
border: 6px solid #202020;
|
||||
padding: 5px 30px;
|
||||
background: #121212;
|
||||
color: #f0f0f0;
|
||||
box-shadow: 0 2px 10px 0 #000;
|
||||
}
|
||||
.ueb-info-dialog a:link,
|
||||
.ueb-info-dialog a:visited,
|
||||
.ueb-info-dialog a:hover,
|
||||
.ueb-info-dialog a:active {
|
||||
outline: none;
|
||||
color: #f0f0f0;
|
||||
}
|
||||
|
||||
.ueb-info-dialog::backdrop {
|
||||
background-color: rgba(0, 0, 0, 0.6274509804);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
/* Light mode */
|
||||
ueb-blueprint.ueb-light-mode {
|
||||
--ueb-grid-line-color: #ddd;
|
||||
--ueb-grid-set-line-color: #c0c0c0;
|
||||
--ueb-grid-axis-line-color: #a0a0a0;
|
||||
}
|
||||
|
||||
ueb-blueprint.ueb-light-mode .ueb-grid {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
ueb-blueprint.ueb-light-mode ueb-node.ueb-node-style-glass .ueb-node-wrapper {
|
||||
background-color: rgba(0, 0, 0, 0.5647058824);
|
||||
}
|
||||
|
||||
ueb-blueprint.ueb-light-mode ueb-link .ueb-link-path {
|
||||
filter: saturate(200%) brightness(0.6);
|
||||
}
|
||||
|
||||
ueb-blueprint.ueb-light-mode .ueb-viewport-header {
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
color: #101010;
|
||||
}
|
||||
|
||||
ueb-node.ueb-node-style-minimal {
|
||||
box-shadow: none;
|
||||
}
|
||||
@@ -175,9 +231,7 @@ ueb-node.ueb-node-style-minimal[data-selected=true] .ueb-node-border {
|
||||
ueb-link {
|
||||
position: absolute;
|
||||
--ueb-link-color: rgb(var(--ueb-link-color-rgb));
|
||||
--ueb-from-input-coefficient: calc(2 * var(--ueb-from-input) - 1);
|
||||
/* when from-y > to-y */
|
||||
--ueb-y-reflected: clamp(0, var(--ueb-from-y) - var(--ueb-to-y) - 1, 1);
|
||||
display: block;
|
||||
margin-left: calc(var(--ueb-link-start) * -1px);
|
||||
min-width: calc(var(--ueb-link-min-width) * 1px);
|
||||
@@ -189,12 +243,11 @@ ueb-link {
|
||||
}
|
||||
|
||||
ueb-link > svg {
|
||||
--ueb-y-reflected-coefficient: calc(2 * var(--ueb-y-reflected) - 1);
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 1px;
|
||||
transform: scaleY(calc(var(--ueb-y-reflected-coefficient) * var(--ueb-from-input-coefficient)));
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
min-height: 1px !important;
|
||||
transform: scaleX(var(--ueb-link-scale-x)) scaleY(var(--ueb-link-scale-y));
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@@ -221,10 +274,12 @@ ueb-link[data-dragging=true] .ueb-link-message {
|
||||
}
|
||||
|
||||
.ueb-link-message {
|
||||
--ueb-link-message-top: calc(50% * (var(--ueb-link-scale-y) + 1) + 22px);
|
||||
--ueb-link-message-left: calc(100% - var(--ueb-start-percentage) + 15px);
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: calc(100% * (1 - var(--ueb-y-reflected)) + 22px);
|
||||
left: calc((1 - var(--ueb-from-input)) * 100% + (var(--ueb-from-input-coefficient)) * var(--ueb-start-percentage) + 15px);
|
||||
top: var(--ueb-link-message-top);
|
||||
left: var(--ueb-link-message-left);
|
||||
border: 1px solid #000;
|
||||
border-radius: 2px;
|
||||
background: linear-gradient(to bottom, #2a2a2a 0, #151515 50%, #2a2a2a 100%);
|
||||
@@ -233,6 +288,11 @@ ueb-link[data-dragging=true] .ueb-link-message {
|
||||
z-index: 1000000;
|
||||
}
|
||||
|
||||
ueb-link[data-from-input=true] .ueb-link-message {
|
||||
--ueb-link-message-top: calc(-50% * (var(--ueb-link-scale-y) - 1) + 22px);
|
||||
--ueb-link-message-left: calc(var(--ueb-start-percentage) + 15px);
|
||||
}
|
||||
|
||||
.ueb-link-message-icon {
|
||||
display: inline-block;
|
||||
padding: 4px;
|
||||
@@ -266,10 +326,6 @@ ueb-node.ueb-node-style-comment {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.ueb-zoom--2 ueb-node {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.ueb-node-border {
|
||||
margin: -3px;
|
||||
padding: 3px;
|
||||
@@ -309,6 +365,10 @@ ueb-node[data-selected=true] > .ueb-node-border {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ueb-zoom--2 .ueb-node-wrapper {
|
||||
background: rgb(14, 16, 10);
|
||||
}
|
||||
|
||||
ueb-node.ueb-node-style-operation .ueb-node-wrapper {
|
||||
grid-template-rows: min-content auto auto min-content min-content;
|
||||
grid-template-columns: 50% 0% 1fr;
|
||||
@@ -334,7 +394,6 @@ ueb-blueprint[data-scrolling=false][data-selecting=false] .ueb-draggable {
|
||||
}
|
||||
|
||||
.ueb-zoom--2 .ueb-node-wrapper {
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
background: #101010;
|
||||
}
|
||||
@@ -371,10 +430,6 @@ ueb-node.ueb-node-style-event .ueb-node-top {
|
||||
background: linear-gradient(rgba(255, 255, 255, 0.1882352941) 1px, transparent 1px), linear-gradient(170deg, rgb(var(--ueb-node-color)) 0%, rgb(var(--ueb-node-color)) 50%, transparent 100%);
|
||||
}
|
||||
|
||||
.ueb-zoom--2 .ueb-node-top {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.ueb-zoom--2 .ueb-node-style-default .ueb-node-top {
|
||||
background: rgb(var(--ueb-node-color));
|
||||
}
|
||||
@@ -523,6 +578,10 @@ ueb-node.ueb-node-style-glass .ueb-node-wrapper {
|
||||
background-repeat: repeat, no-repeat;
|
||||
}
|
||||
|
||||
.ueb-zoom--2 ueb-node.ueb-node-style-glass .ueb-node-wrapper {
|
||||
background: #101010;
|
||||
}
|
||||
|
||||
ueb-node.ueb-node-style-glass .ueb-node-name {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
@@ -646,7 +705,7 @@ ueb-node.ueb-node-style-conversion .ueb-node-wrapper::after {
|
||||
grid-area: center;
|
||||
align-self: center;
|
||||
justify-self: center;
|
||||
margin: 10px;
|
||||
margin: 10px 10px 10px -6px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 3px;
|
||||
@@ -740,11 +799,6 @@ ueb-pin[data-connectable=false] .ueb-pin-icon {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.ueb-node-style-event ueb-pin[data-type=delegate] .ueb-pin-icon {
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
}
|
||||
|
||||
.ueb-node-inputs .ueb-pin-icon {
|
||||
margin-right: 6px;
|
||||
}
|
||||
@@ -815,6 +869,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;
|
||||
@@ -865,7 +936,7 @@ ueb-pin[data-type="/Script/CoreUObject.LinearColor"] .ueb-pin-input {
|
||||
max-height: 16em;
|
||||
/* 97% is to get an effective font size of 12.6px from --ueb-font-size which is 13 px by default */
|
||||
font-size: 97%;
|
||||
white-space: nowrap;
|
||||
white-space: pre;
|
||||
background: none;
|
||||
color: inherit;
|
||||
overflow: auto;
|
||||
@@ -1280,4 +1351,12 @@ ueb-ui-slider {
|
||||
color: #c0c0c0;
|
||||
}
|
||||
|
||||
/* To improve a bit performance */
|
||||
.ueb-zoom--2 * {
|
||||
border: none !important;
|
||||
border-radius: 0 !important;
|
||||
box-shadow: none !important;
|
||||
text-shadow: none !important;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=ueb-style.css.map */
|
||||
|
||||
2
dist/css/ueb-style.css.map
vendored
Normal file → Executable file
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
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
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
0
dist/font/roboto-bold.woff
vendored
Normal file → Executable file
0
dist/font/roboto-bold.woff2
vendored
Normal file → Executable file
0
dist/font/roboto-bold.woff2
vendored
Normal file → Executable file
0
dist/font/roboto-condensed-bold.woff2
vendored
Normal file → Executable file
0
dist/font/roboto-condensed-bold.woff2
vendored
Normal file → Executable file
0
dist/font/roboto-condensed-regular.woff2
vendored
Normal file → Executable file
0
dist/font/roboto-condensed-regular.woff2
vendored
Normal file → Executable file
0
dist/font/roboto-light.woff
vendored
Normal file → Executable file
0
dist/font/roboto-light.woff
vendored
Normal file → Executable file
0
dist/font/roboto-light.woff2
vendored
Normal file → Executable file
0
dist/font/roboto-light.woff2
vendored
Normal file → Executable file
0
dist/font/roboto-regular.woff
vendored
Normal file → Executable file
0
dist/font/roboto-regular.woff
vendored
Normal file → Executable file
0
dist/font/roboto-regular.woff2
vendored
Normal file → Executable file
0
dist/font/roboto-regular.woff2
vendored
Normal file → Executable file
11103
dist/ueblueprint.js
vendored
11103
dist/ueblueprint.js
vendored
File diff suppressed because one or more lines are too long
12
dist/ueblueprint.min.js
vendored
Normal file → Executable file
12
dist/ueblueprint.min.js
vendored
Normal file → Executable file
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>UE Blueprint</title>
|
||||
<link rel="stylesheet" href="dist/css/ueb-style.css">
|
||||
<link rel="stylesheet" href="dist/css/ueb-style.min.css">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
|
||||
@@ -4,6 +4,8 @@ import IElement from "./element/IElement.js"
|
||||
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>} */
|
||||
@@ -14,19 +16,19 @@ export default class Blueprint extends IElement {
|
||||
type: Boolean,
|
||||
attribute: "data-selecting",
|
||||
reflect: true,
|
||||
converter: Utility.booleanConverter,
|
||||
converter: BooleanEntity.booleanConverter,
|
||||
},
|
||||
scrolling: {
|
||||
type: Boolean,
|
||||
attribute: "data-scrolling",
|
||||
reflect: true,
|
||||
converter: Utility.booleanConverter,
|
||||
converter: BooleanEntity.booleanConverter,
|
||||
},
|
||||
focused: {
|
||||
type: Boolean,
|
||||
attribute: "data-focused",
|
||||
reflect: true,
|
||||
converter: Utility.booleanConverter,
|
||||
converter: BooleanEntity.booleanConverter,
|
||||
},
|
||||
zoom: {
|
||||
type: Number,
|
||||
@@ -83,7 +85,6 @@ export default class Blueprint extends IElement {
|
||||
nodesNames = new Map()
|
||||
/** @type {Coordinates} */
|
||||
mousePosition = [0, 0]
|
||||
waitingExpandUpdate = false
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
@@ -296,29 +297,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
|
||||
}
|
||||
|
||||
@@ -353,25 +336,25 @@ export default class Blueprint extends IElement {
|
||||
getLinks(a = null, b = null) {
|
||||
if ((a == null) != (b == null)) {
|
||||
const pin = a ?? b
|
||||
return this.links.filter(link => link.source == pin || link.destination == pin)
|
||||
return this.links.filter(link => link.origin == pin || link.target == pin)
|
||||
}
|
||||
if (a != null && b != null) {
|
||||
return this.links.filter(link =>
|
||||
link.source == a && link.destination == b
|
||||
|| link.source == b && link.destination == a
|
||||
link.origin == a && link.target == b
|
||||
|| link.origin == b && link.target == a
|
||||
)
|
||||
}
|
||||
return this.links
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PinElement} sourcePin
|
||||
* @param {PinElement} destinationPin
|
||||
* @param {PinElement} originPin
|
||||
* @param {PinElement} targetPin
|
||||
*/
|
||||
getLink(sourcePin, destinationPin, strictDirection = false) {
|
||||
getLink(originPin, targetPin, strictDirection = false) {
|
||||
return this.links.find(link =>
|
||||
link.source == sourcePin && link.destination == destinationPin
|
||||
|| !strictDirection && link.source == destinationPin && link.destination == sourcePin
|
||||
link.origin == originPin && link.target == targetPin
|
||||
|| !strictDirection && link.origin == targetPin && link.target == originPin
|
||||
)
|
||||
}
|
||||
|
||||
@@ -383,6 +366,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 */
|
||||
@@ -409,17 +408,22 @@ export default class Blueprint extends IElement {
|
||||
for (const element of graphElements) {
|
||||
element.blueprint = this
|
||||
if (element instanceof NodeElement && !this.nodes.includes(element)) {
|
||||
const name = element.entity.getObjectName()
|
||||
this.entity.updateNameIndex(name)
|
||||
if (element.getType() == Configuration.paths.niagaraClipboardContent) {
|
||||
this.entity = this.entity.mergeWith(element.entity)
|
||||
const additionalSerialization = atob(element.entity.ExportedNodes)
|
||||
this.template.getPasteInputObject().pasted(additionalSerialization)
|
||||
.forEach(node => node.entity.isExported = true)
|
||||
const additionalSerialization = atob(element.entity.ExportedNodes?.toString() ?? "")
|
||||
if (additionalSerialization) {
|
||||
this.template.getPasteInputObject().pasted(additionalSerialization)
|
||||
.forEach(node => node.entity.exported = true)
|
||||
}
|
||||
continue
|
||||
}
|
||||
const name = element.entity.getObjectName()
|
||||
const homonym = this.entity.getHomonymObjectEntity(element.entity)
|
||||
if (homonym) {
|
||||
homonym.Name = this.entity.takeFreeName(name)
|
||||
const newName = this.entity.takeFreeName(name)
|
||||
// @ts-expect-error
|
||||
homonym.Name = new (homonym.Name.constructor)(newName)
|
||||
}
|
||||
this.nodes.push(element)
|
||||
this.entity.addObjectEntity(element.entity)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { css } from "lit"
|
||||
|
||||
export default class Configuration {
|
||||
static VERSION = "2.0.0"
|
||||
static nodeColors = {
|
||||
black: css`20, 20, 20`,
|
||||
blue: css`84, 122, 156`,
|
||||
@@ -23,7 +24,7 @@ export default class Configuration {
|
||||
static colorWindowName = "Color Picker"
|
||||
static defaultCommentHeight = 96
|
||||
static defaultCommentWidth = 400
|
||||
static distanceThreshold = 5 // px
|
||||
static distanceThreshold = 20 // px
|
||||
static dragEventName = "ueb-drag"
|
||||
static dragGeneralEventName = "ueb-drag-general"
|
||||
static edgeScrollThreshold = 50
|
||||
@@ -37,12 +38,9 @@ export default class Configuration {
|
||||
end: "blueprint-unfocus",
|
||||
}
|
||||
static fontSize = css`13px`
|
||||
static gridAxisLineColor = css`black`
|
||||
static gridExpandThreshold = 0.25 // remaining size factor threshold to cause an expansion event
|
||||
static gridLineColor = css`#353535`
|
||||
static gridLineWidth = 1 // px
|
||||
static gridSet = 8
|
||||
static gridSetLineColor = css`#161616`
|
||||
static gridShrinkThreshold = 4 // exceding size factor threshold to cause a shrink event
|
||||
static gridSize = 16 // px
|
||||
static hexColorRegex = /^\s*#(?<r>[0-9a-fA-F]{2})(?<g>[0-9a-fA-F]{2})(?<b>[0-9a-fA-F]{2})([0-9a-fA-F]{2})?|#(?<rs>[0-9a-fA-F])(?<gs>[0-9a-fA-F])(?<bs>[0-9a-fA-F])\s*$/
|
||||
@@ -79,9 +77,13 @@ export default class Configuration {
|
||||
* @param {Number} c1
|
||||
* @param {Number} c2
|
||||
*/
|
||||
static linkRightSVGPath = (start, c1, c2) => {
|
||||
let end = 100 - start
|
||||
return `M ${start} 0 C ${c1.toFixed(3)} 0, ${c2.toFixed(3)} 0, 50 50 S ${(end - c1 + start).toFixed(3)} 100, `
|
||||
static linkRightSVGPath = (start, c1, c2, arc = false) => {
|
||||
const end = 100 - start
|
||||
const mid = arc
|
||||
? 50 + (c2 - start)
|
||||
: 50
|
||||
const fin = arc ? end + c1 - start : end - c1 + start
|
||||
return `M ${start} 0 C ${c1.toFixed(2)} 0, ${c2.toFixed(2)} 0, ${mid.toFixed(2)} 50 S ${fin.toFixed(2)} 100, `
|
||||
+ `${end.toFixed(3)} 100`
|
||||
}
|
||||
static maxZoom = 7
|
||||
@@ -91,9 +93,9 @@ export default class Configuration {
|
||||
static mouseWheelZoomThreshold = 80
|
||||
static nodeDragEventName = "ueb-node-drag"
|
||||
static nodeDragGeneralEventName = "ueb-node-drag-general"
|
||||
static nodeTitle = (name, counter) => `${name}_${counter}`
|
||||
static nodeRadius = 8 // px
|
||||
static nodeReflowEventName = "ueb-node-reflow"
|
||||
static nodeTitle = (name, counter) => `${name}_${counter}`
|
||||
static nodeUpdateEventName = "ueb-node-update"
|
||||
static paths = {
|
||||
actorBoundEvent: "/Script/BlueprintGraph.K2Node_ActorBoundEvent",
|
||||
addDelegate: "/Script/BlueprintGraph.K2Node_AddDelegate",
|
||||
@@ -106,6 +108,7 @@ export default class Configuration {
|
||||
callArrayFunction: "/Script/BlueprintGraph.K2Node_CallArrayFunction",
|
||||
callDelegate: "/Script/BlueprintGraph.K2Node_CallDelegate",
|
||||
callFunction: "/Script/BlueprintGraph.K2Node_CallFunction",
|
||||
clearDelegate: "/Script/BlueprintGraph.K2Node_ClearDelegate",
|
||||
comment: "/Script/UnrealEd.EdGraphNode_Comment",
|
||||
commutativeAssociativeBinaryOperator: "/Script/BlueprintGraph.K2Node_CommutativeAssociativeBinaryOperator",
|
||||
componentBoundEvent: "/Script/BlueprintGraph.K2Node_ComponentBoundEvent",
|
||||
@@ -128,6 +131,7 @@ export default class Configuration {
|
||||
eTextureMipValueMode: "/Script/Engine.ETextureMipValueMode",
|
||||
eTraceTypeQuery: "/Script/Engine.ETraceTypeQuery",
|
||||
event: "/Script/BlueprintGraph.K2Node_Event",
|
||||
eWorldPositionIncludedOffsets: "/Script/Engine.EWorldPositionIncludedOffsets",
|
||||
executionSequence: "/Script/BlueprintGraph.K2Node_ExecutionSequence",
|
||||
flipflop: "/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:FlipFlop",
|
||||
forEachElementInEnum: "/Script/BlueprintGraph.K2Node_ForEachElementInEnum",
|
||||
@@ -147,6 +151,7 @@ export default class Configuration {
|
||||
isValid: "/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:IsValid",
|
||||
kismetArrayLibrary: "/Script/Engine.KismetArrayLibrary",
|
||||
kismetMathLibrary: "/Script/Engine.KismetMathLibrary",
|
||||
kismetStringLibrary: "/Script/Engine.KismetStringLibrary",
|
||||
knot: "/Script/BlueprintGraph.K2Node_Knot",
|
||||
linearColor: "/Script/CoreUObject.LinearColor",
|
||||
literal: "/Script/BlueprintGraph.K2Node_Literal",
|
||||
@@ -166,20 +171,26 @@ export default class Configuration {
|
||||
materialExpressionLogarithm2: "/Script/Engine.MaterialExpressionLogarithm2",
|
||||
materialExpressionMaterialFunctionCall: "/Script/Engine.MaterialExpressionMaterialFunctionCall",
|
||||
materialExpressionSquareRoot: "/Script/Engine.MaterialExpressionSquareRoot",
|
||||
materialExpressionSubtract: "/Script/Engine.MaterialExpressionSubtract",
|
||||
materialExpressionTextureCoordinate: "/Script/Engine.MaterialExpressionTextureCoordinate",
|
||||
materialExpressionTextureSample: "/Script/Engine.MaterialExpressionTextureSample",
|
||||
materialExpressionWorldPosition: "/Script/Engine.MaterialExpressionWorldPosition",
|
||||
materialGraphNode: "/Script/UnrealEd.MaterialGraphNode",
|
||||
materialGraphNodeComment: "/Script/UnrealEd.MaterialGraphNode_Comment",
|
||||
metasoundEditorGraphExternalNode: "/Script/MetasoundEditor.MetasoundEditorGraphExternalNode",
|
||||
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",
|
||||
@@ -189,6 +200,7 @@ export default class Configuration {
|
||||
pcgSubgraphSettings: "/Script/PCG.PCGSubgraphSettings",
|
||||
promotableOperator: "/Script/BlueprintGraph.K2Node_PromotableOperator",
|
||||
quat4f: "/Script/CoreUObject.Quat4f",
|
||||
removeDelegate: "/Script/BlueprintGraph.K2Node_RemoveDelegate",
|
||||
reverseForEachLoop: "/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:ReverseForEachLoop",
|
||||
rotator: "/Script/CoreUObject.Rotator",
|
||||
select: "/Script/BlueprintGraph.K2Node_Select",
|
||||
@@ -203,16 +215,19 @@ export default class Configuration {
|
||||
timeline: "/Script/BlueprintGraph.K2Node_Timeline",
|
||||
timeManagementBlueprintLibrary: "/Script/TimeManagement.TimeManagementBlueprintLibrary",
|
||||
transform: "/Script/CoreUObject.Transform",
|
||||
typedElementHandleLibrary: "/Script/TypedElementFramework.TypedElementHandleLibrary",
|
||||
userDefinedEnum: "/Script/Engine.UserDefinedEnum",
|
||||
variableGet: "/Script/BlueprintGraph.K2Node_VariableGet",
|
||||
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",
|
||||
}
|
||||
static pinInputWrapWidth = 143 // px
|
||||
static pinInputWrapWidth = 145 // px
|
||||
static pinUpdateEventName = "ueb-pin-update"
|
||||
static removeEventName = "ueb-element-delete"
|
||||
static scale = {
|
||||
[-12]: 0.133333,
|
||||
@@ -237,7 +252,7 @@ export default class Configuration {
|
||||
7: 2,
|
||||
}
|
||||
static smoothScrollTime = 1000 // ms
|
||||
static stringEscapedCharacters = /["\\]/g
|
||||
static stringEscapedCharacters = /["\\]/g // Try to remove
|
||||
static subObjectAttributeNamePrefix = "#SubObject"
|
||||
/** @param {ObjectEntity} objectEntity */
|
||||
static subObjectAttributeNameFromEntity = (objectEntity, nameOnly = false) =>
|
||||
@@ -253,7 +268,7 @@ export default class Configuration {
|
||||
begin: "ueb-tracking-mouse-begin",
|
||||
end: "ueb-tracking-mouse-end",
|
||||
}
|
||||
static unescapedBackslash = /(?<=(?:[^\\]|^)(?:\\\\)*)\\(?!\\)/
|
||||
static unescapedBackslash = /(?<=(?:[^\\]|^)(?:\\\\)*)\\(?!\\)/ // Try to remove
|
||||
static windowApplyEventName = "ueb-window-apply"
|
||||
static windowApplyButtonText = "OK"
|
||||
static windowCancelEventName = "ueb-window-cancel"
|
||||
@@ -265,6 +280,7 @@ export default class Configuration {
|
||||
"KeepWorld",
|
||||
"SnapToTarget",
|
||||
],
|
||||
[this.paths.eDrawDebugTrace]: ["None", "ForOneFrame", "ForDuration", "Persistent"],
|
||||
[this.paths.eMaterialSamplerType]: [
|
||||
"Color",
|
||||
"Grayscale",
|
||||
@@ -291,6 +307,14 @@ export default class Configuration {
|
||||
["NewEnumerator3", "A"],
|
||||
],
|
||||
[this.paths.eSamplerSourceMode]: ["From texture asset", "Shared: Wrap", "Shared: Clamp", "Hidden"],
|
||||
[this.paths.eSearchCase]: ["CaseSensitive", "IgnoreCase"],
|
||||
[this.paths.eWorldPositionIncludedOffsets]: [
|
||||
"Absolute World Position (Including Material Shader Offsets)",
|
||||
"Absolute World Position (Excluding Material Shader Offsets)",
|
||||
"Camera Relative World Position (Including Material Shader Offsets)",
|
||||
"Camera Relative World Position (Excluding Material Shader Offsets)",
|
||||
],
|
||||
[this.paths.eSearchDir]: ["FromStart", "FromEnd"],
|
||||
[this.paths.eSpawnActorCollisionHandlingMethod]: [
|
||||
["Undefined", "Default"],
|
||||
["AlwaysSpawn", "Always Spawn, Ignore Collisions"],
|
||||
@@ -298,9 +322,6 @@ export default class Configuration {
|
||||
["AdjustIfPossibleButDontSpawnIfColliding", "Try To Adjust Location, Don't Spawn If Still Colliding"],
|
||||
["DontSpawnIfColliding", "Do Not Spawn"],
|
||||
],
|
||||
[this.paths.eSearchCase]: ["CaseSensitive", "IgnoreCase"],
|
||||
[this.paths.eSearchDir]: ["FromStart", "FromEnd"],
|
||||
[this.paths.eDrawDebugTrace]: ["None", "ForOneFrame", "ForDuration", "Persistent"],
|
||||
[this.paths.eTextureMipValueMode]: [
|
||||
"None (use computed mip level)",
|
||||
"MipLevel (absolute, 0 is full resolution)",
|
||||
|
||||
9
js/SVGIcon.js
Normal file → Executable file
9
js/SVGIcon.js
Normal file → Executable file
@@ -67,7 +67,7 @@ export default class SVGIcon {
|
||||
`
|
||||
|
||||
static delegate = html`
|
||||
<svg viewBox="-2 -2 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="11" height="11" viewBox="-2 -2 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect class="ueb-pin-tofill" fill="black" width="28" height="28" rx="4" stroke="currentColor" stroke-width="5" />
|
||||
</svg>
|
||||
`
|
||||
@@ -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
0
js/Shortcuts.js
Normal file → Executable file
200
js/Utility.js
200
js/Utility.js
@@ -1,35 +1,7 @@
|
||||
import ComputedType from "./entity/ComputedType.js"
|
||||
import Configuration from "./Configuration.js"
|
||||
import MirroredEntity from "./entity/MirroredEntity.js"
|
||||
import Union from "./entity/Union.js"
|
||||
|
||||
export default class Utility {
|
||||
|
||||
static booleanConverter = {
|
||||
fromAttribute: (value, type) => {
|
||||
value ? "true" : "false"
|
||||
},
|
||||
toAttribute: (value, type) => {
|
||||
if (value === true) {
|
||||
return "true"
|
||||
}
|
||||
if (value === false) {
|
||||
return "false"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {Number} x */
|
||||
static sigmoid(x, curvature = 1.7) {
|
||||
return 1 / (1 + (x / (1 - x) ** -curvature))
|
||||
}
|
||||
|
||||
/** @param {Number} x */
|
||||
static sigmoidPositive(x, curvature = 3.7, length = 1.1) {
|
||||
return 1 - Math.exp(-((x / length) ** curvature))
|
||||
}
|
||||
|
||||
/** @param {Number} value */
|
||||
static clamp(value, min = -Infinity, max = Infinity) {
|
||||
return Math.min(Math.max(value, min), max)
|
||||
@@ -55,23 +27,6 @@ export default class Utility {
|
||||
return num.toFixed(decimals)
|
||||
}
|
||||
|
||||
/** @param {String} value */
|
||||
static numberFromText(value = "") {
|
||||
value = value.toLowerCase()
|
||||
switch (value) {
|
||||
case "zero": return 0
|
||||
case "one": return 1
|
||||
case "two": return 2
|
||||
case "three": return 3
|
||||
case "four": return 4
|
||||
case "five": return 5
|
||||
case "six": return 6
|
||||
case "seven": return 7
|
||||
case "eight": return 8
|
||||
case "nine": return 9
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Number} num
|
||||
* @param {Number} decimals
|
||||
@@ -81,16 +36,6 @@ export default class Utility {
|
||||
return Math.round(num * power) / power
|
||||
}
|
||||
|
||||
/** @param {Number} num */
|
||||
static printNumber(num) {
|
||||
if (num == Number.POSITIVE_INFINITY) {
|
||||
return "inf"
|
||||
} else if (num == Number.NEGATIVE_INFINITY) {
|
||||
return "-inf"
|
||||
}
|
||||
return Utility.minDecimals(num)
|
||||
}
|
||||
|
||||
/** @param {Number} num */
|
||||
static printExponential(num) {
|
||||
if (num == Number.POSITIVE_INFINITY) {
|
||||
@@ -135,7 +80,7 @@ export default class Utility {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Attribute} entity
|
||||
* @param {IEntity} entity
|
||||
* @param {String} key
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
@@ -184,97 +129,6 @@ export default class Utility {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Attribute} a
|
||||
* @param {Attribute} b
|
||||
*/
|
||||
static equals(a, b) {
|
||||
while (a instanceof MirroredEntity) {
|
||||
a = a.get()
|
||||
}
|
||||
while (b instanceof MirroredEntity) {
|
||||
b = b.get()
|
||||
}
|
||||
// Here we cannot check both instanceof IEntity because this would introduce a circular include dependency
|
||||
if (/** @type {IEntity?} */(a)?.equals && /** @type {IEntity?} */(b)?.equals) {
|
||||
return /** @type {IEntity} */(a).equals(/** @type {IEntity} */(b))
|
||||
}
|
||||
a = Utility.sanitize(a)
|
||||
b = Utility.sanitize(b)
|
||||
if (a?.constructor === BigInt && b?.constructor === Number) {
|
||||
b = BigInt(b)
|
||||
} else if (a?.constructor === Number && b?.constructor === BigInt) {
|
||||
a = BigInt(a)
|
||||
}
|
||||
if (a === b) {
|
||||
return true
|
||||
}
|
||||
if (a instanceof Array && b instanceof Array) {
|
||||
return a.length === b.length && a.every((value, i) => Utility.equals(value, b[i]))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {Attribute | AttributeTypeDescription} T
|
||||
* @param {T} value
|
||||
* @returns {AttributeConstructor<T>}
|
||||
*/
|
||||
static getType(value) {
|
||||
if (value === null) {
|
||||
return null
|
||||
}
|
||||
if (value?.constructor === Object && /** @type {AttributeInformation} */(value)?.type instanceof Function) {
|
||||
return /** @type {AttributeInformation} */(value).type
|
||||
}
|
||||
return /** @type {AttributeConstructor<any>} */(value?.constructor)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {Attribute} V
|
||||
* @template {AttributeConstructor<V>} C
|
||||
* @param {C} type
|
||||
* @returns {value is InstanceType<C>}
|
||||
*/
|
||||
static isValueOfType(value, type, acceptNull = false) {
|
||||
if (type instanceof MirroredEntity) {
|
||||
type = type.getTargetType()
|
||||
}
|
||||
return (acceptNull && value === null) || value instanceof type || value?.constructor === type
|
||||
}
|
||||
|
||||
/** @param {Attribute} value */
|
||||
static sanitize(value, targetType = /** @type {AttributeTypeDescription } */(value?.constructor)) {
|
||||
if (targetType instanceof Array) {
|
||||
targetType = targetType[0]
|
||||
}
|
||||
if (targetType instanceof ComputedType) {
|
||||
return value // The type is computed, can't say anything about it
|
||||
}
|
||||
if (targetType instanceof Union) {
|
||||
let type = targetType.values.find(t => Utility.isValueOfType(value, t, false))
|
||||
if (!type) {
|
||||
type = targetType.values[0]
|
||||
}
|
||||
targetType = type
|
||||
}
|
||||
if (targetType instanceof MirroredEntity) {
|
||||
if (value instanceof MirroredEntity) {
|
||||
return value
|
||||
}
|
||||
return Utility.sanitize(value, targetType.getTargetType())
|
||||
}
|
||||
if (targetType && !Utility.isValueOfType(value, targetType, true)) {
|
||||
value = targetType === BigInt
|
||||
? BigInt(/** @type {Number} */(value))
|
||||
: new /** @type {EntityConstructor} */(targetType)(value)
|
||||
}
|
||||
if (value instanceof Boolean || value instanceof Number || value instanceof String) {
|
||||
value = /** @type {TerminalAttribute} */(value.valueOf()) // Get the relative primitive value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
@@ -293,29 +147,31 @@ export default class Utility {
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {Array<T>} a
|
||||
* @param {Array<T>} b
|
||||
* @param {T[]} reference
|
||||
* @param {T[]} additional
|
||||
* @param {(v: T) => void} adding - Process added element
|
||||
* @param {(l: T, r: T) => Boolean} predicate
|
||||
* @returns {T[]}
|
||||
*/
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -323,7 +179,13 @@ 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)))
|
||||
.map((v, k) => (adding(v), v))
|
||||
)
|
||||
return result
|
||||
}
|
||||
|
||||
/** @param {String} value */
|
||||
@@ -334,11 +196,14 @@ export default class Utility {
|
||||
}
|
||||
|
||||
/** @param {String} value */
|
||||
static escapeString(value) {
|
||||
return value
|
||||
.replaceAll(new RegExp(`(${Configuration.stringEscapedCharacters.source})`, "g"), '\\$1')
|
||||
.replaceAll("\n", "\\n") // Replace newline with \n
|
||||
.replaceAll("\t", "\\t") // Replace tab with \t
|
||||
static escapeString(value, inline = true) {
|
||||
let result = value.replaceAll(new RegExp(`(${Configuration.stringEscapedCharacters.source})`, "g"), '\\$1')
|
||||
if (inline) {
|
||||
result = result
|
||||
.replaceAll("\n", "\\n") // Replace newline with \n
|
||||
.replaceAll("\t", "\\t") // Replace tab with \t
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/** @param {String} value */
|
||||
@@ -398,11 +263,6 @@ export default class Utility {
|
||||
return pathValue.match(regex)?.[1] ?? ""
|
||||
}
|
||||
|
||||
/** @param {LinearColorEntity} value */
|
||||
static printLinearColor(value) {
|
||||
return `${Math.round(value.R.valueOf() * 255)}, ${Math.round(value.G.valueOf() * 255)}, ${Math.round(value.B.valueOf() * 255)}`
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
|
||||
0
js/action/RemoveAllNodes.js
Normal file → Executable file
0
js/action/RemoveAllNodes.js
Normal file → Executable file
79
js/decoding/nodeColor.js
Normal file → Executable file
79
js/decoding/nodeColor.js
Normal file → Executable file
@@ -1,55 +1,61 @@
|
||||
import Configuration from "../Configuration.js"
|
||||
import LinearColorEntity from "../entity/LinearColorEntity.js"
|
||||
|
||||
const p = Configuration.paths
|
||||
|
||||
/** @param {ObjectEntity} entity */
|
||||
export default function nodeColor(entity) {
|
||||
switch (entity.getType()) {
|
||||
case Configuration.paths.materialExpressionConstant2Vector:
|
||||
case Configuration.paths.materialExpressionConstant3Vector:
|
||||
case Configuration.paths.materialExpressionConstant4Vector:
|
||||
case p.materialExpressionConstant2Vector:
|
||||
case p.materialExpressionConstant3Vector:
|
||||
case p.materialExpressionConstant4Vector:
|
||||
return Configuration.nodeColors.yellow
|
||||
case Configuration.paths.makeStruct:
|
||||
case p.materialExpressionFunctionInput:
|
||||
case p.materialExpressionTextureCoordinate:
|
||||
case p.materialExpressionWorldPosition:
|
||||
case p.pcgEditorGraphNodeInput:
|
||||
case p.pcgEditorGraphNodeOutput:
|
||||
return Configuration.nodeColors.red
|
||||
case p.makeStruct:
|
||||
return Configuration.nodeColors.darkBlue
|
||||
case Configuration.paths.materialExpressionMaterialFunctionCall:
|
||||
case p.materialExpressionMaterialFunctionCall:
|
||||
return Configuration.nodeColors.blue
|
||||
case Configuration.paths.materialExpressionFunctionInput:
|
||||
return Configuration.nodeColors.red
|
||||
case Configuration.paths.materialExpressionTextureSample:
|
||||
case p.materialExpressionTextureSample:
|
||||
return Configuration.nodeColors.darkTurquoise
|
||||
case Configuration.paths.materialExpressionTextureCoordinate:
|
||||
return Configuration.nodeColors.red
|
||||
case Configuration.paths.pcgEditorGraphNodeInput:
|
||||
case Configuration.paths.pcgEditorGraphNodeOutput:
|
||||
return Configuration.nodeColors.red
|
||||
case p.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:
|
||||
return entity.bIsPureFunc
|
||||
? Configuration.nodeColors.green
|
||||
: Configuration.nodeColors.blue
|
||||
case Configuration.paths.niagaraNodeFunctionCall:
|
||||
case p.niagaraNodeFunctionCall:
|
||||
return Configuration.nodeColors.darkerBlue
|
||||
case Configuration.paths.dynamicCast:
|
||||
case p.dynamicCast:
|
||||
return Configuration.nodeColors.turquoise
|
||||
case Configuration.paths.inputDebugKey:
|
||||
case Configuration.paths.inputKey:
|
||||
case p.inputDebugKey:
|
||||
case p.inputKey:
|
||||
return Configuration.nodeColors.red
|
||||
case Configuration.paths.createDelegate:
|
||||
case Configuration.paths.enumLiteral:
|
||||
case Configuration.paths.makeArray:
|
||||
case Configuration.paths.makeMap:
|
||||
case Configuration.paths.materialGraphNode:
|
||||
case Configuration.paths.select:
|
||||
case p.createDelegate:
|
||||
case p.enumLiteral:
|
||||
case p.makeArray:
|
||||
case p.makeMap:
|
||||
case p.materialGraphNode:
|
||||
case p.select:
|
||||
return Configuration.nodeColors.green
|
||||
case Configuration.paths.executionSequence:
|
||||
case Configuration.paths.ifThenElse:
|
||||
case Configuration.paths.macro:
|
||||
case Configuration.paths.multiGate:
|
||||
case p.executionSequence:
|
||||
case p.ifThenElse:
|
||||
case p.macro:
|
||||
case p.multiGate:
|
||||
return Configuration.nodeColors.gray
|
||||
case Configuration.paths.functionEntry:
|
||||
case Configuration.paths.functionResult:
|
||||
case p.functionEntry:
|
||||
case p.functionResult:
|
||||
return Configuration.nodeColors.violet
|
||||
case Configuration.paths.timeline:
|
||||
case p.timeline:
|
||||
return Configuration.nodeColors.yellow
|
||||
}
|
||||
if (entity.switchTarget()) {
|
||||
@@ -74,8 +80,11 @@ export default function nodeColor(entity) {
|
||||
return Configuration.nodeColors.intenseGreen
|
||||
}
|
||||
}
|
||||
if (entity.bIsPureFunc) {
|
||||
if (entity.bIsPureFunc?.valueOf() || entity.bDefaultsToPureFunc?.valueOf()) {
|
||||
return Configuration.nodeColors.green
|
||||
}
|
||||
if (entity["Input"]?.["Name"]) {
|
||||
return Configuration.nodeColors.gray
|
||||
}
|
||||
return Configuration.nodeColors.blue
|
||||
}
|
||||
|
||||
70
js/decoding/nodeIcon.js
Normal file → Executable file
70
js/decoding/nodeIcon.js
Normal file → Executable file
@@ -2,48 +2,52 @@ import Configuration from "../Configuration.js"
|
||||
import SVGIcon from "../SVGIcon.js"
|
||||
import nodeTitle from "./nodeTitle.js"
|
||||
|
||||
const p = Configuration.paths
|
||||
|
||||
/** @param {ObjectEntity} entity */
|
||||
export default function nodeIcon(entity) {
|
||||
if (entity.isMaterial() || entity.isPcg() || entity.isNiagara()) {
|
||||
return null
|
||||
}
|
||||
switch (entity.getType()) {
|
||||
case Configuration.paths.addDelegate:
|
||||
case Configuration.paths.asyncAction:
|
||||
case Configuration.paths.callDelegate:
|
||||
case Configuration.paths.createDelegate:
|
||||
case Configuration.paths.functionEntry:
|
||||
case Configuration.paths.functionResult:
|
||||
case p.addDelegate:
|
||||
case p.asyncAction:
|
||||
case p.callDelegate:
|
||||
case p.clearDelegate:
|
||||
case p.createDelegate:
|
||||
case p.functionEntry:
|
||||
case p.functionResult:
|
||||
case p.removeDelegate:
|
||||
return SVGIcon.node
|
||||
case Configuration.paths.customEvent: return SVGIcon.event
|
||||
case Configuration.paths.doN: return SVGIcon.doN
|
||||
case Configuration.paths.doOnce: return SVGIcon.doOnce
|
||||
case Configuration.paths.dynamicCast: return SVGIcon.cast
|
||||
case Configuration.paths.enumLiteral: return SVGIcon.enum
|
||||
case Configuration.paths.event: return SVGIcon.event
|
||||
case Configuration.paths.executionSequence:
|
||||
case Configuration.paths.multiGate:
|
||||
case p.customEvent: return SVGIcon.event
|
||||
case p.doN: return SVGIcon.doN
|
||||
case p.doOnce: return SVGIcon.doOnce
|
||||
case p.dynamicCast: return SVGIcon.cast
|
||||
case p.enumLiteral: return SVGIcon.enum
|
||||
case p.event: return SVGIcon.event
|
||||
case p.executionSequence:
|
||||
case p.multiGate:
|
||||
return SVGIcon.sequence
|
||||
case Configuration.paths.flipflop:
|
||||
case p.flipflop:
|
||||
return SVGIcon.flipflop
|
||||
case Configuration.paths.forEachElementInEnum:
|
||||
case Configuration.paths.forLoop:
|
||||
case Configuration.paths.forLoopWithBreak:
|
||||
case Configuration.paths.whileLoop:
|
||||
case p.forEachElementInEnum:
|
||||
case p.forLoop:
|
||||
case p.forLoopWithBreak:
|
||||
case p.whileLoop:
|
||||
return SVGIcon.loop
|
||||
case Configuration.paths.forEachLoop:
|
||||
case Configuration.paths.forEachLoopWithBreak:
|
||||
case p.forEachLoop:
|
||||
case p.forEachLoopWithBreak:
|
||||
return SVGIcon.forEachLoop
|
||||
case Configuration.paths.ifThenElse: return SVGIcon.branchNode
|
||||
case Configuration.paths.isValid: return SVGIcon.questionMark
|
||||
case Configuration.paths.makeArray: return SVGIcon.makeArray
|
||||
case Configuration.paths.makeMap: return SVGIcon.makeMap
|
||||
case Configuration.paths.makeSet: return SVGIcon.makeSet
|
||||
case Configuration.paths.makeStruct: return SVGIcon.makeStruct
|
||||
case Configuration.paths.metasoundEditorGraphExternalNode: return SVGIcon.metasoundFunction
|
||||
case Configuration.paths.select: return SVGIcon.select
|
||||
case Configuration.paths.spawnActorFromClass: return SVGIcon.spawnActor
|
||||
case Configuration.paths.timeline: return SVGIcon.timer
|
||||
case p.ifThenElse: return SVGIcon.branchNode
|
||||
case p.isValid: return SVGIcon.questionMark
|
||||
case p.makeArray: return SVGIcon.makeArray
|
||||
case p.makeMap: return SVGIcon.makeMap
|
||||
case p.makeSet: return SVGIcon.makeSet
|
||||
case p.makeStruct: return SVGIcon.makeStruct
|
||||
case p.metasoundEditorGraphExternalNode: return SVGIcon.metasoundFunction
|
||||
case p.select: return SVGIcon.select
|
||||
case p.spawnActorFromClass: return SVGIcon.spawnActor
|
||||
case p.timeline: return SVGIcon.timer
|
||||
}
|
||||
if (entity.switchTarget()) {
|
||||
return SVGIcon.switch
|
||||
@@ -51,7 +55,7 @@ export default function nodeIcon(entity) {
|
||||
if (nodeTitle(entity).startsWith("Break")) {
|
||||
return SVGIcon.breakStruct
|
||||
}
|
||||
if (entity.getClass() === Configuration.paths.macro) {
|
||||
if (entity.getClass() === p.macro) {
|
||||
return SVGIcon.macro
|
||||
}
|
||||
const hidValue = entity.getHIDAttribute()?.toString()
|
||||
@@ -71,7 +75,7 @@ export default function nodeIcon(entity) {
|
||||
if (entity.getDelegatePin()) {
|
||||
return SVGIcon.event
|
||||
}
|
||||
if (entity.ObjectRef?.type === Configuration.paths.ambientSound) {
|
||||
if (entity.ObjectRef?.type === p.ambientSound) {
|
||||
return SVGIcon.sound
|
||||
}
|
||||
return SVGIcon.functionSymbol
|
||||
|
||||
29
js/decoding/nodeSubtitle.js
Executable file
29
js/decoding/nodeSubtitle.js
Executable file
@@ -0,0 +1,29 @@
|
||||
import Configuration from "../Configuration.js"
|
||||
import Utility from "../Utility.js"
|
||||
import pinTitle from "./pinTitle.js"
|
||||
|
||||
const p = Configuration.paths
|
||||
|
||||
/**
|
||||
* @param {ObjectEntity} entity
|
||||
* @returns {String?}
|
||||
*/
|
||||
export default function nodeSubtitle(entity) {
|
||||
switch (entity.getType()) {
|
||||
case p.addDelegate:
|
||||
case p.clearDelegate:
|
||||
case p.callDelegate:
|
||||
case p.removeDelegate:
|
||||
return null
|
||||
}
|
||||
const targetPin = entity
|
||||
.getPinEntities()
|
||||
.find(pin => !pin.isHidden() && pin.PinName?.toString() === "self" && pinTitle(pin) === "Target")
|
||||
if (targetPin) {
|
||||
const target = entity.FunctionReference?.MemberParent?.getName()
|
||||
?? targetPin.PinType?.PinSubCategoryObject?.getName()
|
||||
?? "Untitled"
|
||||
return target.length > 0 ? `Target is ${Utility.formatStringName(target)}` : null
|
||||
}
|
||||
return null
|
||||
}
|
||||
116
js/decoding/nodeTemplate.js
Normal file → Executable file
116
js/decoding/nodeTemplate.js
Normal file → Executable file
@@ -9,22 +9,89 @@ 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 p = Configuration.paths
|
||||
|
||||
/**
|
||||
* @param {ObjectEntity} nodeEntity
|
||||
* @return {new () => NodeTemplate}
|
||||
*/
|
||||
export default function nodeTemplateClass(nodeEntity) {
|
||||
const className = nodeEntity.getClass()
|
||||
if (
|
||||
nodeEntity.getClass() === Configuration.paths.callFunction
|
||||
|| nodeEntity.getClass() === Configuration.paths.commutativeAssociativeBinaryOperator
|
||||
|| nodeEntity.getClass() === Configuration.paths.callArrayFunction
|
||||
className === p.callFunction
|
||||
|| className === p.commutativeAssociativeBinaryOperator
|
||||
|| className === p.callArrayFunction
|
||||
) {
|
||||
const memberParent = nodeEntity.FunctionReference?.MemberParent?.path ?? ""
|
||||
const memberName = nodeEntity.FunctionReference?.MemberName
|
||||
const memberName = nodeEntity.FunctionReference?.MemberName?.toString()
|
||||
if (
|
||||
memberName && (
|
||||
memberParent === Configuration.paths.kismetMathLibrary
|
||||
|| memberParent === Configuration.paths.kismetArrayLibrary
|
||||
memberParent === p.kismetArrayLibrary
|
||||
|| memberParent === p.kismetMathLibrary
|
||||
|| memberParent === p.kismetStringLibrary
|
||||
|| memberParent === p.typedElementHandleLibrary
|
||||
)) {
|
||||
if (memberName.startsWith("Conv_")) {
|
||||
return VariableConversionNodeTemplate
|
||||
@@ -51,6 +118,7 @@ export default function nodeTemplateClass(nodeEntity) {
|
||||
case "BMin":
|
||||
case "CrossProduct2D":
|
||||
case "DotProduct2D":
|
||||
case "Equal":
|
||||
case "Exp":
|
||||
case "FMax":
|
||||
case "FMin":
|
||||
@@ -77,43 +145,37 @@ export default function nodeTemplateClass(nodeEntity) {
|
||||
return VariableOperationNodeTemplate
|
||||
}
|
||||
}
|
||||
if (memberParent === Configuration.paths.blueprintSetLibrary) {
|
||||
if (memberParent === p.blueprintSetLibrary) {
|
||||
return VariableOperationNodeTemplate
|
||||
}
|
||||
if (memberParent === Configuration.paths.blueprintMapLibrary) {
|
||||
if (memberParent === p.blueprintMapLibrary) {
|
||||
return VariableOperationNodeTemplate
|
||||
}
|
||||
}
|
||||
switch (nodeEntity.getClass()) {
|
||||
case Configuration.paths.comment:
|
||||
case Configuration.paths.materialGraphNodeComment:
|
||||
switch (className) {
|
||||
case p.comment:
|
||||
case p.materialGraphNodeComment:
|
||||
return CommentNodeTemplate
|
||||
case Configuration.paths.createDelegate:
|
||||
case p.createDelegate:
|
||||
return NodeTemplate
|
||||
case Configuration.paths.metasoundEditorGraphExternalNode:
|
||||
case p.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)) {
|
||||
case p.niagaraNodeOp:
|
||||
if (niagaraOperationNodes.includes(nodeEntity.OpName?.toString())) {
|
||||
return VariableOperationNodeTemplate
|
||||
}
|
||||
break
|
||||
case Configuration.paths.promotableOperator:
|
||||
case p.promotableOperator:
|
||||
return VariableOperationNodeTemplate
|
||||
case Configuration.paths.knot:
|
||||
case p.knot:
|
||||
return KnotNodeTemplate
|
||||
case Configuration.paths.literal:
|
||||
case Configuration.paths.self:
|
||||
case Configuration.paths.variableGet:
|
||||
case Configuration.paths.variableSet:
|
||||
case p.literal:
|
||||
case p.self:
|
||||
case p.variableGet:
|
||||
case p.variableSet:
|
||||
return VariableAccessNodeTemplate
|
||||
}
|
||||
if (nodeEntity.isEvent()) {
|
||||
|
||||
332
js/decoding/nodeTitle.js
Normal file → Executable file
332
js/decoding/nodeTitle.js
Normal file → Executable file
@@ -1,5 +1,9 @@
|
||||
import Configuration from "../Configuration.js"
|
||||
import Utility from "../Utility.js"
|
||||
import BooleanEntity from "../entity/BooleanEntity.js"
|
||||
import LinearColorEntity from "../entity/LinearColorEntity.js"
|
||||
import MirroredEntity from "../entity/MirroredEntity.js"
|
||||
import VectorEntity from "../entity/VectorEntity.js"
|
||||
|
||||
const sequencerScriptingNameRegex = /\/Script\/SequencerScripting\.MovieSceneScripting(.+)Channel/
|
||||
const keyNameValue = {
|
||||
@@ -30,6 +34,85 @@ 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])),
|
||||
}
|
||||
const p = Configuration.paths
|
||||
const format = Utility.formatStringName
|
||||
|
||||
/** @param {String} value */
|
||||
function numberFromText(value = "") {
|
||||
value = value.toLowerCase()
|
||||
switch (value) {
|
||||
case "zero": return 0
|
||||
case "one": return 1
|
||||
case "two": return 2
|
||||
case "three": return 3
|
||||
case "four": return 4
|
||||
case "five": return 5
|
||||
case "six": return 6
|
||||
case "seven": return 7
|
||||
case "eight": return 8
|
||||
case "nine": return 9
|
||||
}
|
||||
}
|
||||
|
||||
function keyName(value) {
|
||||
/** @type {String} */
|
||||
@@ -37,120 +120,134 @@ function keyName(value) {
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
result = Utility.numberFromText(value)?.toString()
|
||||
result = numberFromText(value)?.toString()
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
const match = value.match(/NumPad([a-zA-Z]+)/)
|
||||
if (match) {
|
||||
result = Utility.numberFromText(match[1]).toString()
|
||||
result = numberFromText(match[1]).toString()
|
||||
if (result) {
|
||||
return "Num " + result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {ObjectEntity} entity */
|
||||
/**
|
||||
* @param {ObjectEntity} entity
|
||||
* @returns {String}
|
||||
*/
|
||||
export default function nodeTitle(entity) {
|
||||
let input
|
||||
let value
|
||||
switch (entity.getType()) {
|
||||
case Configuration.paths.asyncAction:
|
||||
case p.addDelegate:
|
||||
value ??= "Bind Event to "
|
||||
case p.clearDelegate:
|
||||
value ??= "Unbind all Events from "
|
||||
case p.removeDelegate:
|
||||
value ??= "Unbind Event from "
|
||||
return value + format(
|
||||
entity.DelegateReference?.MemberName?.toString().replace(/Delegate$/, "") ?? "None"
|
||||
)
|
||||
case p.asyncAction:
|
||||
if (entity.ProxyFactoryFunctionName) {
|
||||
return Utility.formatStringName(entity.ProxyFactoryFunctionName)
|
||||
return format(entity.ProxyFactoryFunctionName?.toString())
|
||||
}
|
||||
case Configuration.paths.actorBoundEvent:
|
||||
case Configuration.paths.componentBoundEvent:
|
||||
return `${Utility.formatStringName(entity.DelegatePropertyName)} (${entity.ComponentPropertyName ?? "Unknown"})`
|
||||
case Configuration.paths.callDelegate:
|
||||
return `Call ${entity.DelegateReference?.MemberName ?? "None"}`
|
||||
case Configuration.paths.createDelegate:
|
||||
case p.actorBoundEvent:
|
||||
case p.componentBoundEvent:
|
||||
return `${format(entity.DelegatePropertyName?.toString())} (${entity.ComponentPropertyName?.toString() ?? "Unknown"})`
|
||||
case p.callDelegate:
|
||||
return `Call ${entity.DelegateReference?.MemberName?.toString() ?? "None"}`
|
||||
case p.createDelegate:
|
||||
return "Create Event"
|
||||
case Configuration.paths.customEvent:
|
||||
case p.customEvent:
|
||||
if (entity.CustomFunctionName) {
|
||||
return entity.CustomFunctionName
|
||||
return entity.CustomFunctionName?.toString()
|
||||
}
|
||||
case Configuration.paths.dynamicCast:
|
||||
case p.dynamicCast:
|
||||
if (!entity.TargetType) {
|
||||
return "Bad cast node" // Target type not found
|
||||
}
|
||||
return `Cast To ${entity.TargetType?.getName()}`
|
||||
case Configuration.paths.enumLiteral:
|
||||
case p.enumLiteral:
|
||||
return `Literal enum ${entity.Enum?.getName()}`
|
||||
case Configuration.paths.event:
|
||||
return `Event ${(entity.EventReference?.MemberName ?? "").replace(/^Receive/, "")}`
|
||||
case Configuration.paths.executionSequence:
|
||||
case p.event:
|
||||
return `Event ${(entity.EventReference?.MemberName?.toString() ?? "").replace(/^Receive/, "")}`
|
||||
case p.executionSequence:
|
||||
return "Sequence"
|
||||
case Configuration.paths.forEachElementInEnum:
|
||||
case p.forEachElementInEnum:
|
||||
return `For Each ${entity.Enum?.getName()}`
|
||||
case Configuration.paths.forEachLoopWithBreak:
|
||||
case p.forEachLoopWithBreak:
|
||||
return "For Each Loop with Break"
|
||||
case Configuration.paths.functionEntry:
|
||||
return entity.FunctionReference?.MemberName === "UserConstructionScript"
|
||||
case p.functionEntry:
|
||||
return entity.FunctionReference?.MemberName?.toString() === "UserConstructionScript"
|
||||
? "Construction Script"
|
||||
: entity.FunctionReference?.MemberName
|
||||
case Configuration.paths.functionResult:
|
||||
: entity.FunctionReference?.MemberName?.toString()
|
||||
case p.functionResult:
|
||||
return "Return Node"
|
||||
case Configuration.paths.ifThenElse:
|
||||
case p.ifThenElse:
|
||||
return "Branch"
|
||||
case Configuration.paths.makeStruct:
|
||||
case p.makeStruct:
|
||||
if (entity.StructType) {
|
||||
return `Make ${entity.StructType.getName()}`
|
||||
}
|
||||
case Configuration.paths.materialExpressionComponentMask: {
|
||||
case p.materialExpressionComponentMask: {
|
||||
const materialObject = entity.getMaterialSubobject()
|
||||
return `Mask ( ${Configuration.rgba
|
||||
.filter(k => /** @type {MirroredEntity<any>} */(materialObject[k]).get() === true)
|
||||
.map(v => v + " ")
|
||||
.join("")})`
|
||||
if (materialObject) {
|
||||
return `Mask ( ${Configuration.rgba
|
||||
.filter(k => /** @type {MirroredEntity<typeof BooleanEntity>} */(materialObject[k]).getter().value === true)
|
||||
.map(v => v + " ")
|
||||
.join("")})`
|
||||
}
|
||||
}
|
||||
case Configuration.paths.materialExpressionConstant:
|
||||
input ??= [entity.getCustomproperties().find(pinEntity => pinEntity.PinName == "Value")?.DefaultValue]
|
||||
case Configuration.paths.materialExpressionConstant2Vector:
|
||||
input ??= [
|
||||
entity.getCustomproperties().find(pinEntity => pinEntity.PinName == "X")?.DefaultValue,
|
||||
entity.getCustomproperties().find(pinEntity => pinEntity.PinName == "Y")?.DefaultValue,
|
||||
case p.materialExpressionConstant:
|
||||
value ??= [entity.getCustomproperties().find(pinEntity => pinEntity.PinName.toString() == "Value")?.DefaultValue]
|
||||
case p.materialExpressionConstant2Vector:
|
||||
value ??= [
|
||||
entity.getCustomproperties().find(pinEntity => pinEntity.PinName?.toString() == "X")?.DefaultValue,
|
||||
entity.getCustomproperties().find(pinEntity => pinEntity.PinName?.toString() == "Y")?.DefaultValue,
|
||||
]
|
||||
case Configuration.paths.materialExpressionConstant3Vector:
|
||||
if (!input) {
|
||||
/** @type {VectorEntity} */
|
||||
case p.materialExpressionConstant3Vector:
|
||||
case p.materialExpressionConstant4Vector:
|
||||
if (!value) {
|
||||
const vector = entity.getCustomproperties()
|
||||
.find(pinEntity => pinEntity.PinName == "Constant")
|
||||
.find(pinEntity => pinEntity.PinName?.toString() == "Constant")
|
||||
?.DefaultValue
|
||||
input = [vector.X, vector.Y, vector.Z]
|
||||
value = vector instanceof VectorEntity ? [vector.X, vector.Y, vector.Z].map(v => v.valueOf())
|
||||
: vector instanceof LinearColorEntity ? [vector.R, vector.G, vector.B, vector.A].map(v => v.valueOf())
|
||||
: /** @type {Number[]} */([])
|
||||
}
|
||||
case Configuration.paths.materialExpressionConstant4Vector:
|
||||
if (!input) {
|
||||
/** @type {LinearColorEntity} */
|
||||
const vector = entity.getCustomproperties()
|
||||
.find(pinEntity => pinEntity.PinName == "Constant")
|
||||
?.DefaultValue
|
||||
input = [vector.R, vector.G, vector.B, vector.A].map(v => v.valueOf())
|
||||
}
|
||||
if (input.length > 0) {
|
||||
return input.map(v => Utility.printExponential(v)).reduce((acc, cur) => acc + "," + cur)
|
||||
if (value?.length > 0) {
|
||||
return value.map(v => Utility.printExponential(v)).join(",")
|
||||
}
|
||||
value = undefined
|
||||
break
|
||||
case Configuration.paths.materialExpressionFunctionInput: {
|
||||
case p.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 p.materialExpressionLogarithm:
|
||||
return "Ln"
|
||||
case Configuration.paths.materialExpressionLogarithm10:
|
||||
case p.materialExpressionLogarithm10:
|
||||
return "Log10"
|
||||
case Configuration.paths.materialExpressionLogarithm2:
|
||||
case p.materialExpressionLogarithm2:
|
||||
return "Log2"
|
||||
case Configuration.paths.materialExpressionMaterialFunctionCall:
|
||||
case p.materialExpressionMaterialFunctionCall:
|
||||
const materialFunction = entity.getMaterialSubobject()?.MaterialFunction
|
||||
if (materialFunction) {
|
||||
return materialFunction.getName()
|
||||
}
|
||||
break
|
||||
case Configuration.paths.materialExpressionSquareRoot:
|
||||
case p.materialExpressionSquareRoot:
|
||||
return "Sqrt"
|
||||
case Configuration.paths.metasoundEditorGraphExternalNode: {
|
||||
case p.materialExpressionSubtract:
|
||||
const materialObject = entity.getMaterialSubobject()
|
||||
if (materialObject) {
|
||||
return `Subtract(${materialObject.ConstA ?? "1"},${materialObject.ConstB ?? "1"})`
|
||||
}
|
||||
case p.metasoundEditorGraphExternalNode: {
|
||||
const name = entity["ClassName"]?.["Name"]
|
||||
if (name) {
|
||||
switch (name) {
|
||||
@@ -159,61 +256,71 @@ export default function nodeTitle(entity) {
|
||||
}
|
||||
}
|
||||
}
|
||||
case Configuration.paths.pcgEditorGraphNodeInput:
|
||||
case p.niagaraNodeConvert:
|
||||
/** @type {String} */
|
||||
const targetType = (entity["AutowireMakeType"]?.["ClassStructOrEnum"] ?? "")
|
||||
.toString()
|
||||
.match(/(?:Niagara)?(\w+)['"]*$/)
|
||||
?.[1]
|
||||
?? ""
|
||||
return `Make ${targetType}`
|
||||
case p.pcgEditorGraphNodeInput:
|
||||
return "Input"
|
||||
case Configuration.paths.pcgEditorGraphNodeOutput:
|
||||
case p.pcgEditorGraphNodeOutput:
|
||||
return "Output"
|
||||
case Configuration.paths.spawnActorFromClass:
|
||||
case p.spawnActorFromClass:
|
||||
let className = entity.getCustomproperties()
|
||||
.find(pinEntity => pinEntity.PinName == "ReturnValue")
|
||||
.find(pinEntity => pinEntity.PinName.toString() == "ReturnValue")
|
||||
?.PinType
|
||||
?.PinSubCategoryObject
|
||||
?.getName()
|
||||
if (className === "Actor") {
|
||||
className = null
|
||||
}
|
||||
return `SpawnActor ${Utility.formatStringName(className ?? "NONE")}`
|
||||
case Configuration.paths.switchEnum:
|
||||
return `SpawnActor ${format(className ?? "NONE")}`
|
||||
case p.switchEnum:
|
||||
return `Switch on ${entity.Enum?.getName() ?? "Enum"}`
|
||||
case Configuration.paths.switchInteger:
|
||||
case p.switchInteger:
|
||||
return `Switch on Int`
|
||||
case Configuration.paths.variableGet:
|
||||
case p.variableGet:
|
||||
return ""
|
||||
case Configuration.paths.variableSet:
|
||||
case p.variableSet:
|
||||
return "SET"
|
||||
}
|
||||
const className = entity.getClass()
|
||||
let switchTarget = entity.switchTarget()
|
||||
if (switchTarget) {
|
||||
if (switchTarget[0] !== "E") {
|
||||
switchTarget = Utility.formatStringName(switchTarget)
|
||||
switchTarget = format(switchTarget)
|
||||
}
|
||||
return `Switch on ${switchTarget}`
|
||||
}
|
||||
if (entity.isComment()) {
|
||||
return entity.NodeComment
|
||||
return entity.NodeComment.toString()
|
||||
}
|
||||
const keyNameSymbol = entity.getHIDAttribute()
|
||||
if (keyNameSymbol) {
|
||||
const name = keyNameSymbol.toString()
|
||||
let title = keyName(name) ?? Utility.formatStringName(name)
|
||||
if (entity.getClass() === Configuration.paths.inputDebugKey) {
|
||||
let title = keyName(name) ?? format(name)
|
||||
if (className === p.inputDebugKey) {
|
||||
title = "Debug Key " + title
|
||||
} else if (entity.getClass() === Configuration.paths.getInputAxisKeyValue) {
|
||||
} else if (className === p.getInputAxisKeyValue) {
|
||||
title = "Get " + title
|
||||
}
|
||||
return title
|
||||
}
|
||||
if (entity.getClass() === Configuration.paths.macro) {
|
||||
return Utility.formatStringName(entity.MacroGraphReference?.getMacroName())
|
||||
if (className === p.macro) {
|
||||
return format(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
|
||||
}
|
||||
if (entity.isPcg() && entity.getPcgSubobject()) {
|
||||
let pcgSubobject = entity.getPcgSubobject()
|
||||
let result = pcgSubobject.NodeTitle ? pcgSubobject.NodeTitle : nodeTitle(pcgSubobject)
|
||||
let result = pcgSubobject.NodeTitle ? pcgSubobject.NodeTitle.toString() : nodeTitle(pcgSubobject)
|
||||
return result
|
||||
}
|
||||
const subgraphObject = entity.getSubgraphObject()
|
||||
@@ -222,19 +329,19 @@ export default function nodeTitle(entity) {
|
||||
}
|
||||
const settingsObject = entity.getSettingsObject()
|
||||
if (settingsObject) {
|
||||
if (settingsObject.ExportPath.type === Configuration.paths.pcgHiGenGridSizeSettings) {
|
||||
if (settingsObject.ExportPath?.valueOf()?.type === p.pcgHiGenGridSizeSettings) {
|
||||
return `Grid Size: ${(
|
||||
settingsObject.HiGenGridSize?.toString().match(/\d+/)?.[0]?.concat("00")
|
||||
?? settingsObject.HiGenGridSize?.toString().match(/^\w+$/)?.[0]
|
||||
) ?? "256"}`
|
||||
}
|
||||
if (settingsObject.BlueprintElementInstance) {
|
||||
return Utility.formatStringName(settingsObject.BlueprintElementType.getName())
|
||||
return format(settingsObject.BlueprintElementType.getName())
|
||||
}
|
||||
if (settingsObject.Operation) {
|
||||
const match = settingsObject.Name.match(/PCGMetadata(\w+)Settings_\d+/)
|
||||
const match = settingsObject.Name?.toString().match(/PCGMetadata(\w+)Settings_\d+/)
|
||||
if (match) {
|
||||
return Utility.formatStringName(match[1] + ": " + settingsObject.Operation)
|
||||
return format(match[1] + ": " + settingsObject.Operation)
|
||||
}
|
||||
}
|
||||
const settingsSubgraphObject = settingsObject.getSubgraphObject()
|
||||
@@ -242,14 +349,14 @@ export default function nodeTitle(entity) {
|
||||
return settingsSubgraphObject.Graph.getName()
|
||||
}
|
||||
}
|
||||
let memberName = entity.FunctionReference?.MemberName
|
||||
let memberName = entity.FunctionReference?.MemberName?.toString()
|
||||
if (memberName) {
|
||||
const memberParent = entity.FunctionReference.MemberParent?.path ?? ""
|
||||
switch (memberName) {
|
||||
case "AddKey":
|
||||
let result = memberParent.match(sequencerScriptingNameRegex)
|
||||
if (result) {
|
||||
return `Add Key (${Utility.formatStringName(result[1])})`
|
||||
return `Add Key (${format(result[1])})`
|
||||
}
|
||||
case "Concat_StrStr":
|
||||
return "Append"
|
||||
@@ -260,14 +367,16 @@ export default function nodeTitle(entity) {
|
||||
+ (memberNameTraceLineMatch[1] === "Multi" ? " Multi " : " ")
|
||||
+ (memberNameTraceLineMatch[2] === ""
|
||||
? "By Channel"
|
||||
: Utility.formatStringName(memberNameTraceLineMatch[2])
|
||||
: format(memberNameTraceLineMatch[2])
|
||||
)
|
||||
}
|
||||
switch (memberParent) {
|
||||
case Configuration.paths.blueprintGameplayTagLibrary:
|
||||
case Configuration.paths.kismetMathLibrary:
|
||||
case Configuration.paths.slateBlueprintLibrary:
|
||||
case Configuration.paths.timeManagementBlueprintLibrary:
|
||||
case p.blueprintGameplayTagLibrary:
|
||||
case p.kismetMathLibrary:
|
||||
case p.kismetStringLibrary:
|
||||
case p.slateBlueprintLibrary:
|
||||
case p.timeManagementBlueprintLibrary:
|
||||
case p.typedElementHandleLibrary:
|
||||
const leadingLetter = memberName.match(/[BF]([A-Z]\w+)/)
|
||||
if (leadingLetter) {
|
||||
// Some functions start with B or F (Like FCeil, FMax, BMin)
|
||||
@@ -278,6 +387,7 @@ export default function nodeTitle(entity) {
|
||||
case "BooleanAND": return "AND"
|
||||
case "BooleanNAND": return "NAND"
|
||||
case "BooleanOR": return "OR"
|
||||
case "Equal": return "=="
|
||||
case "Exp": return "e"
|
||||
case "LineTraceSingle": return "Line Trace By Channel"
|
||||
case "Max": return "MAX"
|
||||
@@ -351,23 +461,23 @@ export default function nodeTitle(entity) {
|
||||
return "^"
|
||||
}
|
||||
break
|
||||
case Configuration.paths.blueprintSetLibrary:
|
||||
case p.blueprintSetLibrary:
|
||||
{
|
||||
const setOperationMatch = memberName.match(/Set_(\w+)/)
|
||||
if (setOperationMatch) {
|
||||
return Utility.formatStringName(setOperationMatch[1]).toUpperCase()
|
||||
return format(setOperationMatch[1]).toUpperCase()
|
||||
}
|
||||
}
|
||||
break
|
||||
case Configuration.paths.blueprintMapLibrary:
|
||||
case p.blueprintMapLibrary:
|
||||
{
|
||||
const setOperationMatch = memberName.match(/Map_(\w+)/)
|
||||
if (setOperationMatch) {
|
||||
return Utility.formatStringName(setOperationMatch[1]).toUpperCase()
|
||||
return format(setOperationMatch[1]).toUpperCase()
|
||||
}
|
||||
}
|
||||
break
|
||||
case Configuration.paths.kismetArrayLibrary:
|
||||
case p.kismetArrayLibrary:
|
||||
{
|
||||
const arrayOperationMath = memberName.match(/Array_(\w+)/)
|
||||
if (arrayOperationMath) {
|
||||
@@ -376,29 +486,27 @@ export default function nodeTitle(entity) {
|
||||
}
|
||||
break
|
||||
}
|
||||
return Utility.formatStringName(memberName)
|
||||
return format(memberName)
|
||||
}
|
||||
if (entity.OpName) {
|
||||
switch (entity.OpName) {
|
||||
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).replaceAll("::", " ")
|
||||
return niagaraNodeNames[entity.OpName.toString()]
|
||||
?? format(entity.OpName.toString().replaceAll(/(?:^\w+(?<!^Matrix))?::/g, " "))
|
||||
}
|
||||
if (entity.FunctionDisplayName) {
|
||||
return Utility.formatStringName(entity.FunctionDisplayName)
|
||||
return format(entity.FunctionDisplayName.toString())
|
||||
}
|
||||
if (entity.ObjectRef) {
|
||||
return entity.ObjectRef.getName()
|
||||
}
|
||||
return Utility.formatStringName(entity.getNameAndCounter()[0])
|
||||
let prefix
|
||||
if (
|
||||
className.startsWith(prefix = "/Script/NiagaraEditor.NiagaraNodeParameter")
|
||||
|| className.startsWith(prefix = "/Script/NiagaraEditor.NiagaraNode")
|
||||
) {
|
||||
return entity["Input"]?.["Name"]?.toString() ?? format(className.substring(prefix.length))
|
||||
}
|
||||
if (entity.ParameterName) {
|
||||
return entity.ParameterName.toString()
|
||||
}
|
||||
return format(entity.getNameAndCounter()[0])
|
||||
}
|
||||
|
||||
60
js/decoding/nodeVariadic.js
Normal file → Executable file
60
js/decoding/nodeVariadic.js
Normal file → Executable file
@@ -1,10 +1,14 @@
|
||||
import Configuration from "../Configuration.js"
|
||||
import ArrayEntity from "../entity/ArrayEntity.js"
|
||||
import GuidEntity from "../entity/GuidEntity.js"
|
||||
import NaturalNumberEntity from "../entity/NaturalNumberEntity.js"
|
||||
import PinEntity from "../entity/PinEntity.js"
|
||||
import StringEntity from "../entity/StringEntity.js"
|
||||
|
||||
/** @param {PinEntity} pinEntity */
|
||||
const indexFromUpperCaseLetterName = pinEntity =>
|
||||
pinEntity.PinName.match(/^\s*([A-Z])\s*$/)?.[1]?.charCodeAt(0) - "A".charCodeAt(0)
|
||||
pinEntity.PinName?.toString().match(/^\s*([A-Z])\s*$/)?.[1]?.charCodeAt(0) - "A".charCodeAt(0)
|
||||
const p = Configuration.paths
|
||||
|
||||
/** @param {ObjectEntity} entity */
|
||||
export default function nodeVariadic(entity) {
|
||||
@@ -15,11 +19,12 @@ export default function nodeVariadic(entity) {
|
||||
/** @type {(newPinIndex: Number, minIndex: Number, maxIndex: Number, newPin: PinEntity) => String} */
|
||||
let pinNameFromIndex
|
||||
const type = entity.getType()
|
||||
let prefix
|
||||
let name
|
||||
switch (type) {
|
||||
case Configuration.paths.commutativeAssociativeBinaryOperator:
|
||||
case Configuration.paths.promotableOperator:
|
||||
name = entity.FunctionReference?.MemberName
|
||||
case p.commutativeAssociativeBinaryOperator:
|
||||
case p.promotableOperator:
|
||||
name = entity.FunctionReference?.MemberName?.toString()
|
||||
switch (name) {
|
||||
default:
|
||||
if (
|
||||
@@ -50,19 +55,24 @@ export default function nodeVariadic(entity) {
|
||||
pinIndexFromEntity ??= indexFromUpperCaseLetterName
|
||||
pinNameFromIndex ??= (index, min = -1, max = -1) => {
|
||||
const result = String.fromCharCode(index >= 0 ? index : max + "A".charCodeAt(0) + 1)
|
||||
entity.NumAdditionalInputs = pinEntities().length - 1
|
||||
entity.NumAdditionalInputs = new NaturalNumberEntity(pinEntities().length - 1)
|
||||
return result
|
||||
}
|
||||
break
|
||||
}
|
||||
break
|
||||
case Configuration.paths.multiGate:
|
||||
case p.executionSequence:
|
||||
prefix ??= "Then"
|
||||
case p.multiGate:
|
||||
prefix ??= "Out"
|
||||
pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isOutput())
|
||||
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*Out[_\s]+(\d+)\s*$/i)?.[1])
|
||||
pinIndexFromEntity ??= pinEntity => Number(
|
||||
pinEntity.PinName?.toString().match(new RegExp(String.raw`^\s*${prefix}[_\s]+(\d+)\s*$`, "i"))?.[1]
|
||||
)
|
||||
pinNameFromIndex ??= (index, min = -1, max = -1, newPin) =>
|
||||
`Out ${index >= 0 ? index : min > 0 ? "Out 0" : max + 1}`
|
||||
`${prefix} ${index >= 0 ? index : min > 0 ? `${prefix} 0` : max + 1}`
|
||||
break
|
||||
// case Configuration.paths.niagaraNodeOp:
|
||||
// case p.niagaraNodeOp:
|
||||
// pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isInput())
|
||||
// pinIndexFromEntity ??= indexFromUpperCaseLetterName
|
||||
// pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => {
|
||||
@@ -72,28 +82,28 @@ export default function nodeVariadic(entity) {
|
||||
// return result
|
||||
// }
|
||||
// break
|
||||
case Configuration.paths.switchInteger:
|
||||
case p.switchInteger:
|
||||
pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isOutput())
|
||||
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*(\d+)\s*$/)?.[1])
|
||||
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName?.toString().match(/^\s*(\d+)\s*$/)?.[1])
|
||||
pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => (index < 0 ? max + 1 : index).toString()
|
||||
break
|
||||
case Configuration.paths.switchGameplayTag:
|
||||
case p.switchGameplayTag:
|
||||
pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => {
|
||||
const result = `Case_${index >= 0 ? index : min > 0 ? "0" : max + 1}`
|
||||
entity.PinNames ??= []
|
||||
entity.PinNames.push(result)
|
||||
delete entity.PinTags[entity.PinTags.length - 1]
|
||||
entity.PinTags[entity.PinTags.length] = null
|
||||
entity.PinNames ??= new ArrayEntity()
|
||||
entity.PinNames.valueOf().push(new StringEntity(result))
|
||||
delete entity.PinTags.valueOf()[entity.PinTags.length - 1]
|
||||
entity.PinTags.valueOf()[entity.PinTags.length] = null
|
||||
return result
|
||||
}
|
||||
case Configuration.paths.switchName:
|
||||
case Configuration.paths.switchString:
|
||||
case p.switchName:
|
||||
case p.switchString:
|
||||
pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isOutput())
|
||||
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*Case[_\s]+(\d+)\s*$/i)?.[1])
|
||||
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.toString().match(/^\s*Case[_\s]+(\d+)\s*$/i)?.[1])
|
||||
pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => {
|
||||
const result = `Case_${index >= 0 ? index : min > 0 ? "0" : max + 1}`
|
||||
entity.PinNames ??= []
|
||||
entity.PinNames.push(result)
|
||||
entity.PinNames ??= new ArrayEntity()
|
||||
entity.PinNames.valueOf().push(new StringEntity(result))
|
||||
return result
|
||||
}
|
||||
break
|
||||
@@ -138,9 +148,13 @@ export default function nodeVariadic(entity) {
|
||||
}
|
||||
)
|
||||
const newPin = new PinEntity(modelPin)
|
||||
newPin.PinId = GuidEntity.generateGuid()
|
||||
newPin.PinName = pinNameFromIndex(index, min, max, newPin)
|
||||
newPin.PinId = new GuidEntity()
|
||||
newPin.PinName = new StringEntity(pinNameFromIndex(index, min, max, newPin))
|
||||
newPin.PinToolTip = undefined
|
||||
if (newPin.DefaultValue) {
|
||||
// @ts-expect-error
|
||||
newPin.DefaultValue = new (newPin.DefaultValue.constructor)()
|
||||
}
|
||||
entity.getCustomproperties(true).push(newPin)
|
||||
return newPin
|
||||
}
|
||||
|
||||
59
js/decoding/pinColor.js
Normal file → Executable file
59
js/decoding/pinColor.js
Normal file → Executable file
@@ -1,25 +1,14 @@
|
||||
import { css } from "lit"
|
||||
import Configuration from "../Configuration.js"
|
||||
|
||||
const p = Configuration.paths
|
||||
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 +16,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,21 +37,37 @@ const colors = {
|
||||
"Volume": css`230, 69, 188`,
|
||||
"Volume[]": css`230, 69, 188`,
|
||||
"wildcard": css`128, 120, 120`,
|
||||
[p.linearColor]: css`0, 88, 200`,
|
||||
[p.niagaraBool]: css`146, 0, 0`,
|
||||
[p.niagaraDataInterfaceCollisionQuery]: css`0, 168, 242`,
|
||||
[p.niagaraDataInterfaceCurlNoise]: css`0, 168, 242`,
|
||||
[p.niagaraDataInterfaceVolumeTexture]: css`0, 168, 242`,
|
||||
[p.niagaraFloat]: css`160, 250, 68`,
|
||||
[p.niagaraInt32]: css`30, 224, 172`,
|
||||
[p.niagaraPosition]: css`251, 146, 251`,
|
||||
[p.quat4f]: css`0, 88, 200`,
|
||||
[p.rotator]: css`157, 177, 251`,
|
||||
[p.transform]: css`227, 103, 0`,
|
||||
[p.vector]: css`251, 198, 34`,
|
||||
[p.vector2f]: css`0, 88, 200`,
|
||||
[p.vector3f]: css`250, 200, 36`,
|
||||
[p.vector4f]: css`0, 88, 200`,
|
||||
}
|
||||
|
||||
const pinColorMaterial = css`120, 120, 120`
|
||||
|
||||
/** @param {PinEntity} entity */
|
||||
/** @param {PinEntity<IEntity>} entity */
|
||||
export default function pinColor(entity) {
|
||||
if (entity.PinType.PinCategory == "mask") {
|
||||
const result = colors[entity.PinType.PinSubCategory]
|
||||
if (entity.PinType.PinCategory?.toString() === "mask") {
|
||||
const result = colors[entity.PinType.PinSubCategory?.toString()]
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
} else if (entity.PinType.PinCategory == "optional") {
|
||||
} else if (entity.PinType.PinCategory?.toString() === "optional") {
|
||||
return pinColorMaterial
|
||||
}
|
||||
return colors[entity.getType()]
|
||||
?? colors[entity.PinType.PinCategory.toLowerCase()]
|
||||
?? colors["default"]
|
||||
const type = entity.getType()
|
||||
return colors[type]
|
||||
?? colors[entity.PinType.PinCategory?.toString().toLowerCase()]
|
||||
?? (type.startsWith("/Script/Niagara.") ? colors["struct"] : colors["default"])
|
||||
}
|
||||
|
||||
37
js/decoding/pinTemplate.js
Normal file → Executable file
37
js/decoding/pinTemplate.js
Normal file → Executable file
@@ -7,6 +7,7 @@ import IntPinTemplate from "../template/pin/IntPinTemplate.js"
|
||||
import LinearColorPinTemplate from "../template/pin/LinearColorPinTemplate.js"
|
||||
import NamePinTemplate from "../template/pin/NamePinTemplate.js"
|
||||
import PinTemplate from "../template/pin/PinTemplate.js"
|
||||
import ReadonlyNamePinTemplate from "../template/pin/ReadonlyInputPinTemplate.js"
|
||||
import RealPinTemplate from "../template/pin/RealPinTemplate.js"
|
||||
import ReferencePinTemplate from "../template/pin/ReferencePinTemplate.js"
|
||||
import RotatorPinTemplate from "../template/pin/RotatorPinTemplate.js"
|
||||
@@ -14,38 +15,48 @@ import StringPinTemplate from "../template/pin/StringPinTemplate.js"
|
||||
import Vector2DPinTemplate from "../template/pin/Vector2DPinTemplate.js"
|
||||
import Vector4DPinTemplate from "../template/pin/Vector4DPinTemplate.js"
|
||||
import VectorPinTemplate from "../template/pin/VectorPinTemplate.js"
|
||||
import pinTitle from "./pinTitle.js"
|
||||
|
||||
const p = Configuration.paths
|
||||
const inputPinTemplates = {
|
||||
[Configuration.paths.linearColor]: LinearColorPinTemplate,
|
||||
[Configuration.paths.niagaraBool]: BoolPinTemplate,
|
||||
[Configuration.paths.niagaraPosition]: VectorPinTemplate,
|
||||
[Configuration.paths.rotator]: RotatorPinTemplate,
|
||||
[Configuration.paths.vector]: VectorPinTemplate,
|
||||
[Configuration.paths.vector2D]: Vector2DPinTemplate,
|
||||
[Configuration.paths.vector3f]: VectorPinTemplate,
|
||||
[Configuration.paths.vector4f]: Vector4DPinTemplate,
|
||||
"bool": BoolPinTemplate,
|
||||
"byte": IntPinTemplate,
|
||||
"enum": EnumPinTemplate,
|
||||
"float": RealPinTemplate,
|
||||
"int": IntPinTemplate,
|
||||
"int64": Int64PinTemplate,
|
||||
"MUTABLE_REFERENCE": ReferencePinTemplate,
|
||||
"name": NamePinTemplate,
|
||||
"rg": Vector2DPinTemplate,
|
||||
"real": RealPinTemplate,
|
||||
"rg": Vector2DPinTemplate,
|
||||
"string": StringPinTemplate,
|
||||
[p.linearColor]: LinearColorPinTemplate,
|
||||
[p.niagaraBool]: BoolPinTemplate,
|
||||
[p.niagaraFloat]: RealPinTemplate,
|
||||
[p.niagaraInt32]: IntPinTemplate,
|
||||
[p.niagaraPosition]: VectorPinTemplate,
|
||||
[p.rotator]: RotatorPinTemplate,
|
||||
[p.vector]: VectorPinTemplate,
|
||||
[p.vector2D]: Vector2DPinTemplate,
|
||||
[p.vector2f]: Vector2DPinTemplate,
|
||||
[p.vector3f]: VectorPinTemplate,
|
||||
[p.vector4f]: Vector4DPinTemplate,
|
||||
}
|
||||
|
||||
/** @param {PinEntity} entity */
|
||||
/** @param {PinEntity<IEntity>} entity */
|
||||
export default function pinTemplate(entity) {
|
||||
if (entity.PinType.ContainerType?.toString() === "Array") {
|
||||
return PinTemplate
|
||||
}
|
||||
if (entity.PinType.bIsReference && !entity.PinType.bIsConst) {
|
||||
if (entity.PinType.bIsReference?.valueOf() && !entity.PinType.bIsConst?.valueOf()) {
|
||||
return inputPinTemplates["MUTABLE_REFERENCE"]
|
||||
}
|
||||
if (entity.getType() === "exec") {
|
||||
if (entity.isExecution()) {
|
||||
return ExecPinTemplate
|
||||
}
|
||||
return (entity.isInput() ? inputPinTemplates[entity.getType()] : PinTemplate) ?? PinTemplate
|
||||
if (entity.PinName?.toString() === "self" && pinTitle(entity) === "Target") {
|
||||
return ReadonlyNamePinTemplate
|
||||
}
|
||||
const type = entity.getType()
|
||||
return (entity.isInput() ? inputPinTemplates[type] : PinTemplate) ?? PinTemplate
|
||||
}
|
||||
|
||||
11
js/decoding/pinTitle.js
Normal file → Executable file
11
js/decoding/pinTitle.js
Normal file → Executable file
@@ -1,19 +1,16 @@
|
||||
import Utility from "../Utility.js"
|
||||
|
||||
/** @param {PinEntity} entity */
|
||||
/** @param {PinEntity<IEntity>} entity */
|
||||
export default function pinTitle(entity) {
|
||||
let result = entity.PinFriendlyName
|
||||
? entity.PinFriendlyName.toString()
|
||||
: Utility.formatStringName(entity.PinName ?? "")
|
||||
: Utility.formatStringName(entity.PinName?.toString() ?? "")
|
||||
let match
|
||||
if (
|
||||
entity.PinToolTip
|
||||
// Match up until the first \n excluded or last character
|
||||
&& (match = entity.PinToolTip.match(/\s*(.+?(?=\n)|.+\S)\s*/))
|
||||
) {
|
||||
if (match = entity.PinToolTip?.toString().match(/\s*(.+?(?=\n)|.+\S)\s*/)) {
|
||||
if (match[1].toLowerCase() === result.toLowerCase()) {
|
||||
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
0
js/element/ColorHandlerElement.js
Normal file → Executable file
0
js/element/ColorSliderElement.js
Normal file → Executable file
0
js/element/ColorSliderElement.js
Normal file → Executable file
0
js/element/DropdownElement.js
Normal file → Executable file
0
js/element/DropdownElement.js
Normal file → Executable file
4
js/element/ElementFactory.js
Normal file → Executable file
4
js/element/ElementFactory.js
Normal file → Executable file
@@ -1,11 +1,11 @@
|
||||
export default class ElementFactory {
|
||||
|
||||
/** @type {Map<String, AnyConstructor<IElement>>} */
|
||||
/** @type {Map<String, IElementConstructor>} */
|
||||
static #elementConstructors = new Map()
|
||||
|
||||
/**
|
||||
* @param {String} tagName
|
||||
* @param {AnyConstructor<IElement>} entityConstructor
|
||||
* @param {IElementConstructor} entityConstructor
|
||||
*/
|
||||
static registerElement(tagName, entityConstructor) {
|
||||
ElementFactory.#elementConstructors.set(tagName, entityConstructor)
|
||||
|
||||
0
js/element/IDraggableControlElement.js
Normal file → Executable file
0
js/element/IDraggableControlElement.js
Normal file → Executable file
0
js/element/IDraggableElement.js
Normal file → Executable file
0
js/element/IDraggableElement.js
Normal file → Executable file
0
js/element/IElement.js
Normal file → Executable file
0
js/element/IElement.js
Normal file → Executable file
36
js/element/IFromToPositionedElement.js
Normal file → Executable file
36
js/element/IFromToPositionedElement.js
Normal file → Executable file
@@ -9,19 +9,19 @@ export default class IFromToPositionedElement extends IElement {
|
||||
|
||||
static properties = {
|
||||
...super.properties,
|
||||
fromX: {
|
||||
originX: {
|
||||
type: Number,
|
||||
attribute: false,
|
||||
},
|
||||
fromY: {
|
||||
originY: {
|
||||
type: Number,
|
||||
attribute: false,
|
||||
},
|
||||
toX: {
|
||||
targetX: {
|
||||
type: Number,
|
||||
attribute: false,
|
||||
},
|
||||
toY: {
|
||||
targetY: {
|
||||
type: Number,
|
||||
attribute: false,
|
||||
},
|
||||
@@ -29,35 +29,35 @@ export default class IFromToPositionedElement extends IElement {
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.fromX = 0
|
||||
this.fromY = 0
|
||||
this.toX = 0
|
||||
this.toY = 0
|
||||
this.originX = 0
|
||||
this.originY = 0
|
||||
this.targetX = 0
|
||||
this.targetY = 0
|
||||
}
|
||||
|
||||
/** @param {Coordinates} param0 */
|
||||
setBothLocations([x, y]) {
|
||||
this.fromX = x
|
||||
this.fromY = y
|
||||
this.toX = x
|
||||
this.toY = y
|
||||
this.originX = x
|
||||
this.originY = y
|
||||
this.targetX = x
|
||||
this.targetY = y
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
*/
|
||||
addSourceLocation(x, y) {
|
||||
this.fromX += x
|
||||
this.fromY += y
|
||||
addOriginLocation(x, y) {
|
||||
this.originX += x
|
||||
this.originY += y
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
*/
|
||||
addDestinationLocation(x, y) {
|
||||
this.toX += x
|
||||
this.toY += y
|
||||
addTargetLocation(x, y) {
|
||||
this.targetX += x
|
||||
this.targetY += y
|
||||
}
|
||||
}
|
||||
|
||||
4
js/element/ISelectableDraggableElement.js
Normal file → Executable file
4
js/element/ISelectableDraggableElement.js
Normal file → Executable file
@@ -1,5 +1,5 @@
|
||||
import Configuration from "../Configuration.js"
|
||||
import Utility from "../Utility.js"
|
||||
import BooleanEntity from "../entity/BooleanEntity.js"
|
||||
import IDraggableElement from "./IDraggableElement.js"
|
||||
|
||||
/**
|
||||
@@ -15,7 +15,7 @@ export default class ISelectableDraggableElement extends IDraggableElement {
|
||||
type: Boolean,
|
||||
attribute: "data-selected",
|
||||
reflect: true,
|
||||
converter: Utility.booleanConverter,
|
||||
converter: BooleanEntity.booleanConverter,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
10
js/element/InputElement.js
Normal file → Executable file
10
js/element/InputElement.js
Normal file → Executable file
@@ -1,6 +1,6 @@
|
||||
import IElement from "./IElement.js"
|
||||
import BooleanEntity from "../entity/BooleanEntity.js"
|
||||
import InputTemplate from "../template/pin/InputTemplate.js"
|
||||
import Utility from "../Utility.js"
|
||||
import IElement from "./IElement.js"
|
||||
|
||||
/** @extends {IElement<Object, InputTemplate>} */
|
||||
export default class InputElement extends IElement {
|
||||
@@ -10,19 +10,19 @@ export default class InputElement extends IElement {
|
||||
singleLine: {
|
||||
type: Boolean,
|
||||
attribute: "data-single-line",
|
||||
converter: Utility.booleanConverter,
|
||||
converter: BooleanEntity.booleanConverter,
|
||||
reflect: true,
|
||||
},
|
||||
selectOnFocus: {
|
||||
type: Boolean,
|
||||
attribute: "data-select-focus",
|
||||
converter: Utility.booleanConverter,
|
||||
converter: BooleanEntity.booleanConverter,
|
||||
reflect: true,
|
||||
},
|
||||
blurOnEnter: {
|
||||
type: Boolean,
|
||||
attribute: "data-blur-enter",
|
||||
converter: Utility.booleanConverter,
|
||||
converter: BooleanEntity.booleanConverter,
|
||||
reflect: true,
|
||||
},
|
||||
}
|
||||
|
||||
262
js/element/LinkElement.js
Normal file → Executable file
262
js/element/LinkElement.js
Normal file → Executable file
@@ -1,9 +1,11 @@
|
||||
import { html, nothing } from "lit"
|
||||
import Configuration from "../Configuration.js"
|
||||
import IFromToPositionedElement from "./IFromToPositionedElement.js"
|
||||
import LinkTemplate from "../template/LinkTemplate.js"
|
||||
import SVGIcon from "../SVGIcon.js"
|
||||
import Utility from "../Utility.js"
|
||||
import BooleanEntity from "../entity/BooleanEntity.js"
|
||||
import LinkTemplate from "../template/LinkTemplate.js"
|
||||
import IFromToPositionedElement from "./IFromToPositionedElement.js"
|
||||
import LinearColorEntity from "../entity/LinearColorEntity.js"
|
||||
|
||||
/** @extends {IFromToPositionedElement<Object, LinkTemplate>} */
|
||||
export default class LinkElement extends IFromToPositionedElement {
|
||||
@@ -13,12 +15,37 @@ export default class LinkElement extends IFromToPositionedElement {
|
||||
dragging: {
|
||||
type: Boolean,
|
||||
attribute: "data-dragging",
|
||||
converter: Utility.booleanConverter,
|
||||
converter: BooleanEntity.booleanConverter,
|
||||
reflect: true,
|
||||
},
|
||||
originNode: {
|
||||
type: String,
|
||||
attribute: "data-origin-node",
|
||||
reflect: true,
|
||||
},
|
||||
originPin: {
|
||||
type: String,
|
||||
attribute: "data-origin-pin",
|
||||
reflect: true,
|
||||
},
|
||||
targetNode: {
|
||||
type: String,
|
||||
attribute: "data-target-node",
|
||||
reflect: true,
|
||||
},
|
||||
targetPin: {
|
||||
type: String,
|
||||
attribute: "data-target-pin",
|
||||
reflect: true,
|
||||
},
|
||||
originatesFromInput: {
|
||||
type: Boolean,
|
||||
attribute: false,
|
||||
attribute: "data-from-input",
|
||||
converter: BooleanEntity.booleanConverter,
|
||||
reflect: true,
|
||||
},
|
||||
color: {
|
||||
type: LinearColorEntity,
|
||||
},
|
||||
svgPathD: {
|
||||
type: String,
|
||||
@@ -35,30 +62,58 @@ export default class LinkElement extends IFromToPositionedElement {
|
||||
}
|
||||
|
||||
/** @type {PinElement} */
|
||||
#source
|
||||
get source() {
|
||||
return this.#source
|
||||
#origin
|
||||
get origin() {
|
||||
return this.#origin
|
||||
}
|
||||
set source(pin) {
|
||||
set origin(pin) {
|
||||
this.#setPin(pin, false)
|
||||
}
|
||||
|
||||
/** @type {PinElement} */
|
||||
#destination
|
||||
get destination() {
|
||||
return this.#destination
|
||||
#target
|
||||
get target() {
|
||||
return this.#target
|
||||
}
|
||||
set destination(pin) {
|
||||
set target(pin) {
|
||||
this.#setPin(pin, true)
|
||||
}
|
||||
|
||||
/** @param {UEBNodeUpdateEvent} e */
|
||||
#nodeUpdateHandler = e => {
|
||||
if (this.#origin.nodeElement === e.target) {
|
||||
if (this.originNode != this.#origin.nodeElement.nodeTitle) {
|
||||
this.originNode = this.#origin.nodeElement.nodeTitle
|
||||
}
|
||||
this.setOriginLocation()
|
||||
} else if (this.#target.nodeElement === e.target) {
|
||||
if (this.targetNode != this.#target.nodeElement.nodeTitle) {
|
||||
this.targetNode = this.#target.nodeElement.nodeTitle
|
||||
}
|
||||
this.setTargetLocation()
|
||||
} else {
|
||||
throw new Error("Unexpected node update")
|
||||
}
|
||||
}
|
||||
/** @param {UEBNodeUpdateEvent} e */
|
||||
#pinUpdateHandler = e => {
|
||||
const colorReferencePin = this.getOutputPin(true)
|
||||
if (!this.color?.equals(colorReferencePin.color)) {
|
||||
this.color = colorReferencePin.color
|
||||
}
|
||||
}
|
||||
#nodeDeleteHandler = () => this.remove()
|
||||
/** @param {UEBDragEvent} e */
|
||||
#nodeDragSourceHandler = e => this.addSourceLocation(...e.detail.value)
|
||||
#nodeDragOriginHandler = e => this.addOriginLocation(...e.detail.value)
|
||||
/** @param {UEBDragEvent} e */
|
||||
#nodeDragDestinatonHandler = e => this.addDestinationLocation(...e.detail.value)
|
||||
#nodeReflowSourceHandler = e => this.setSourceLocation()
|
||||
#nodeReflowDestinatonHandler = e => this.setDestinationLocation()
|
||||
#nodeDragTargetHandler = e => this.addTargetLocation(...e.detail.value)
|
||||
#nodeReflowOriginHandler = e => {
|
||||
if (this.origin.isKnot()) {
|
||||
this.originatesFromInput = this.origin.isInputVisually()
|
||||
}
|
||||
this.setOriginLocation()
|
||||
}
|
||||
#nodeReflowTargetHandler = e => this.setTargetLocation()
|
||||
|
||||
/** @type {TemplateResult | nothing} */
|
||||
linkMessageIcon = nothing
|
||||
@@ -71,168 +126,197 @@ export default class LinkElement extends IFromToPositionedElement {
|
||||
constructor() {
|
||||
super()
|
||||
this.dragging = false
|
||||
this.originNode = ""
|
||||
this.originPin = ""
|
||||
this.targetNode = ""
|
||||
this.targetPin = ""
|
||||
this.originatesFromInput = false
|
||||
this.color = new LinearColorEntity()
|
||||
this.startPercentage = 0
|
||||
this.svgPathD = ""
|
||||
this.startPixels = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PinElement} source
|
||||
* @param {PinElement?} destination
|
||||
* @param {PinElement} origin
|
||||
* @param {PinElement?} target
|
||||
*/
|
||||
static newObject(source, destination) {
|
||||
static newObject(origin, target) {
|
||||
const result = new LinkElement()
|
||||
result.initialize(source, destination)
|
||||
result.initialize(origin, target)
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PinElement} source
|
||||
* @param {PinElement?} destination
|
||||
* @param {PinElement} origin
|
||||
* @param {PinElement?} target
|
||||
*/
|
||||
// @ts-expect-error
|
||||
initialize(source, destination) {
|
||||
initialize(origin, target) {
|
||||
super.initialize({}, new LinkTemplate())
|
||||
if (source) {
|
||||
this.source = source
|
||||
if (!destination) {
|
||||
this.toX = this.fromX
|
||||
this.toY = this.fromY
|
||||
if (origin) {
|
||||
this.origin = origin
|
||||
if (!target) {
|
||||
this.targetX = this.originX
|
||||
this.targetY = this.originY
|
||||
}
|
||||
}
|
||||
if (destination) {
|
||||
this.destination = destination
|
||||
if (!source) {
|
||||
this.fromX = this.toX
|
||||
this.fromY = this.toY
|
||||
if (target) {
|
||||
this.target = target
|
||||
if (!origin) {
|
||||
this.originX = this.targetX
|
||||
this.originY = this.targetY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PinElement} pin
|
||||
* @param {Boolean} isDestinationPin
|
||||
* @param {Boolean} isTargetPin
|
||||
*/
|
||||
#setPin(pin, isDestinationPin) {
|
||||
const getCurrentPin = () => isDestinationPin ? this.destination : this.source
|
||||
#setPin(pin, isTargetPin) {
|
||||
const getCurrentPin = () => isTargetPin ? this.target : this.origin
|
||||
if (getCurrentPin() == pin) {
|
||||
return
|
||||
}
|
||||
if (getCurrentPin()) {
|
||||
const nodeElement = getCurrentPin().getNodeElement()
|
||||
nodeElement.removeEventListener(Configuration.nodeUpdateEventName, this.#nodeUpdateHandler)
|
||||
nodeElement.removeEventListener(Configuration.removeEventName, this.#nodeDeleteHandler)
|
||||
nodeElement.removeEventListener(
|
||||
Configuration.nodeDragEventName,
|
||||
isDestinationPin ? this.#nodeDragDestinatonHandler : this.#nodeDragSourceHandler
|
||||
)
|
||||
nodeElement.removeEventListener(
|
||||
Configuration.nodeReflowEventName,
|
||||
isDestinationPin ? this.#nodeReflowDestinatonHandler : this.#nodeReflowSourceHandler
|
||||
isTargetPin ? this.#nodeDragTargetHandler : this.#nodeDragOriginHandler
|
||||
)
|
||||
getCurrentPin().removeEventListener(Configuration.pinUpdateEventName, this.#pinUpdateHandler)
|
||||
this.#unlinkPins()
|
||||
}
|
||||
isDestinationPin
|
||||
? this.#destination = pin
|
||||
: this.#source = pin
|
||||
if (isTargetPin) {
|
||||
this.#target = pin
|
||||
this.targetNode = pin?.nodeElement.nodeTitle
|
||||
this.targetPin = pin?.pinId.toString()
|
||||
} else {
|
||||
this.#origin = pin
|
||||
this.originNode = pin?.nodeElement.nodeTitle
|
||||
this.originPin = pin?.pinId.toString()
|
||||
}
|
||||
if (getCurrentPin()) {
|
||||
const nodeElement = getCurrentPin().getNodeElement()
|
||||
nodeElement.addEventListener(Configuration.nodeUpdateEventName, this.#nodeUpdateHandler)
|
||||
nodeElement.addEventListener(Configuration.pinUpdateEventName, this.#pinUpdateHandler)
|
||||
nodeElement.addEventListener(Configuration.removeEventName, this.#nodeDeleteHandler)
|
||||
nodeElement.addEventListener(
|
||||
Configuration.nodeDragEventName,
|
||||
isDestinationPin ? this.#nodeDragDestinatonHandler : this.#nodeDragSourceHandler
|
||||
isTargetPin ? this.#nodeDragTargetHandler : this.#nodeDragOriginHandler
|
||||
)
|
||||
nodeElement.addEventListener(
|
||||
Configuration.nodeReflowEventName,
|
||||
isDestinationPin ? this.#nodeReflowDestinatonHandler : this.#nodeReflowSourceHandler
|
||||
)
|
||||
isDestinationPin
|
||||
? this.setDestinationLocation()
|
||||
: (this.setSourceLocation(), this.originatesFromInput = this.source.isInput())
|
||||
getCurrentPin().addEventListener(Configuration.pinUpdateEventName, this.#pinUpdateHandler)
|
||||
isTargetPin
|
||||
? this.setTargetLocation()
|
||||
: (this.setOriginLocation(), this.originatesFromInput = this.origin.isInputVisually())
|
||||
this.#linkPins()
|
||||
}
|
||||
this.color = this.getOutputPin(true)?.color
|
||||
}
|
||||
|
||||
#linkPins() {
|
||||
if (this.source && this.destination) {
|
||||
this.source.linkTo(this.destination)
|
||||
this.destination.linkTo(this.source)
|
||||
if (this.origin && this.target) {
|
||||
this.origin.linkTo(this.target)
|
||||
this.target.linkTo(this.origin)
|
||||
}
|
||||
}
|
||||
|
||||
#unlinkPins() {
|
||||
if (this.source && this.destination) {
|
||||
this.source.unlinkFrom(this.destination, false)
|
||||
this.destination.unlinkFrom(this.source, false)
|
||||
if (this.origin && this.target) {
|
||||
this.origin.unlinkFrom(this.target, false)
|
||||
this.target.unlinkFrom(this.origin, false)
|
||||
}
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
super.cleanup()
|
||||
this.#unlinkPins()
|
||||
this.source = null
|
||||
this.destination = null
|
||||
this.origin = null
|
||||
this.target = null
|
||||
}
|
||||
|
||||
/** @param {Coordinates} location */
|
||||
setSourceLocation(location = null, canPostpone = true) {
|
||||
setOriginLocation(location = null, canPostpone = true) {
|
||||
if (location == null) {
|
||||
const self = this
|
||||
if (canPostpone && (!this.hasUpdated || !this.source.hasUpdated)) {
|
||||
Promise.all([this.updateComplete, this.source.updateComplete])
|
||||
.then(() => self.setSourceLocation(null, false))
|
||||
if (canPostpone && (!this.hasUpdated || !this.origin.hasUpdated)) {
|
||||
Promise.all([this.updateComplete, this.origin.updateComplete])
|
||||
.then(() => self.setOriginLocation(null, false))
|
||||
return
|
||||
}
|
||||
location = this.source.template.getLinkLocation()
|
||||
location = this.origin.template.getLinkLocation()
|
||||
}
|
||||
const [x, y] = location
|
||||
this.fromX = x
|
||||
this.fromY = y
|
||||
this.originX = x
|
||||
this.originY = y
|
||||
}
|
||||
|
||||
/** @param {Coordinates} location */
|
||||
setDestinationLocation(location = null, canPostpone = true) {
|
||||
setTargetLocation(location = null, canPostpone = true) {
|
||||
if (location == null) {
|
||||
const self = this
|
||||
if (canPostpone && (!this.hasUpdated || !this.destination.hasUpdated)) {
|
||||
Promise.all([this.updateComplete, this.destination.updateComplete])
|
||||
.then(() => self.setDestinationLocation(null, false))
|
||||
if (canPostpone && (!this.hasUpdated || !this.target.hasUpdated)) {
|
||||
Promise.all([this.updateComplete, this.target.updateComplete])
|
||||
.then(() => self.setTargetLocation(null, false))
|
||||
return
|
||||
}
|
||||
location = this.destination.template.getLinkLocation()
|
||||
location = this.target.template.getLinkLocation()
|
||||
}
|
||||
this.toX = location[0]
|
||||
this.toY = location[1]
|
||||
this.targetX = location[0]
|
||||
this.targetY = location[1]
|
||||
}
|
||||
|
||||
getInputPin() {
|
||||
if (this.source?.isInput()) {
|
||||
return this.source
|
||||
getInputPin(getSomething = false) {
|
||||
if (this.origin?.isInput()) {
|
||||
return this.origin
|
||||
}
|
||||
if (this.target?.isInput()) {
|
||||
return this.target
|
||||
}
|
||||
if (getSomething) {
|
||||
return this.origin ?? this.target
|
||||
}
|
||||
return this.destination
|
||||
}
|
||||
|
||||
/** @param {PinElement} pin */
|
||||
setInputPin(pin) {
|
||||
if (this.source?.isInput()) {
|
||||
this.source = pin
|
||||
if (this.origin?.isInput()) {
|
||||
this.origin = pin
|
||||
}
|
||||
this.destination = pin
|
||||
this.target = pin
|
||||
}
|
||||
|
||||
getOutputPin() {
|
||||
if (this.destination?.isOutput()) {
|
||||
return this.destination
|
||||
getOutputPin(getSomething = false) {
|
||||
if (this.origin?.isOutput()) {
|
||||
return this.origin
|
||||
}
|
||||
if (this.target?.isOutput()) {
|
||||
return this.target
|
||||
}
|
||||
if (getSomething) {
|
||||
return this.origin ?? this.target
|
||||
}
|
||||
return this.source
|
||||
}
|
||||
|
||||
/** @param {PinElement} pin */
|
||||
setOutputPin(pin) {
|
||||
if (this.destination?.isOutput()) {
|
||||
this.destination = pin
|
||||
if (this.target?.isOutput()) {
|
||||
this.target = pin
|
||||
}
|
||||
this.origin = pin
|
||||
}
|
||||
|
||||
/** @param {NodeElement} node */
|
||||
getOtherPin(node) {
|
||||
if (this.origin?.nodeElement === node) {
|
||||
return this.target
|
||||
}
|
||||
if (this.target?.nodeElement === node) {
|
||||
return this.origin
|
||||
}
|
||||
this.source = pin
|
||||
}
|
||||
|
||||
startDragging() {
|
||||
@@ -250,7 +334,7 @@ export default class LinkElement extends IFromToPositionedElement {
|
||||
|
||||
setMessageConvertType() {
|
||||
this.linkMessageIcon = SVGIcon.convert
|
||||
this.linkMessageText = html`Convert ${this.source.pinType} to ${this.destination.pinType}.`
|
||||
this.linkMessageText = html`Convert ${this.origin.pinType} to ${this.target.pinType}.`
|
||||
}
|
||||
|
||||
setMessageCorrect() {
|
||||
|
||||
72
js/element/NodeElement.js
Normal file → Executable file
72
js/element/NodeElement.js
Normal file → Executable file
@@ -1,12 +1,11 @@
|
||||
import Configuration from "../Configuration.js"
|
||||
import Utility from "../Utility.js"
|
||||
import nodeTemplateClass from "../decoding/nodeTemplate.js"
|
||||
import nodeTitle from "../decoding/nodeTitle.js"
|
||||
import IdentifierEntity from "../entity/IdentifierEntity.js"
|
||||
import BooleanEntity from "../entity/BooleanEntity.js"
|
||||
import ObjectEntity from "../entity/ObjectEntity.js"
|
||||
import PinEntity from "../entity/PinEntity.js"
|
||||
import PinReferenceEntity from "../entity/PinReferenceEntity.js"
|
||||
import SerializerFactory from "../serialization/SerializerFactory.js"
|
||||
import SymbolEntity from "../entity/SymbolEntity.js"
|
||||
import NodeTemplate from "../template/node/NodeTemplate.js"
|
||||
import ISelectableDraggableElement from "./ISelectableDraggableElement.js"
|
||||
|
||||
@@ -28,7 +27,7 @@ export default class NodeElement extends ISelectableDraggableElement {
|
||||
advancedPinDisplay: {
|
||||
type: String,
|
||||
attribute: "data-advanced-display",
|
||||
converter: IdentifierEntity.attributeConverter,
|
||||
converter: SymbolEntity.attributeConverter,
|
||||
reflect: true,
|
||||
},
|
||||
enabledState: {
|
||||
@@ -42,7 +41,7 @@ export default class NodeElement extends ISelectableDraggableElement {
|
||||
},
|
||||
pureFunction: {
|
||||
type: Boolean,
|
||||
converter: Utility.booleanConverter,
|
||||
converter: BooleanEntity.booleanConverter,
|
||||
attribute: "data-pure-function",
|
||||
reflect: true,
|
||||
},
|
||||
@@ -86,7 +85,7 @@ export default class NodeElement extends ISelectableDraggableElement {
|
||||
/** @param {String} str */
|
||||
static fromSerializedObject(str) {
|
||||
str = str.trim()
|
||||
let entity = SerializerFactory.getSerializer(ObjectEntity).read(str)
|
||||
let entity = ObjectEntity.grammar.parse(str)
|
||||
return NodeElement.newObject(/** @type {ObjectEntity} */(entity))
|
||||
}
|
||||
|
||||
@@ -100,13 +99,17 @@ export default class NodeElement extends ISelectableDraggableElement {
|
||||
return result
|
||||
}
|
||||
|
||||
#redirectLinksAfterRename(name) {
|
||||
for (let sourcePinElement of this.getPinElements()) {
|
||||
for (let targetPinReference of sourcePinElement.getLinks()) {
|
||||
this.blueprint.getPin(targetPinReference).redirectLink(sourcePinElement, new PinReferenceEntity({
|
||||
objectName: name,
|
||||
pinGuid: sourcePinElement.entity.PinId,
|
||||
}))
|
||||
/** @param {String} name */
|
||||
#redirectLinksBeforeRename(name) {
|
||||
for (let originPinElement of this.getPinElements()) {
|
||||
for (let targetPinReference of originPinElement.getLinks()) {
|
||||
this.blueprint.getPin(targetPinReference).redirectLink(
|
||||
originPinElement,
|
||||
new PinReferenceEntity(
|
||||
new SymbolEntity(name),
|
||||
originPinElement.entity.PinId,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,7 +120,7 @@ export default class NodeElement extends ISelectableDraggableElement {
|
||||
this.advancedPinDisplay = entity.AdvancedPinDisplay?.toString()
|
||||
this.enabledState = entity.EnabledState
|
||||
this.nodeDisplayName = nodeTitle(entity)
|
||||
this.pureFunction = entity.bIsPureFunc
|
||||
this.pureFunction = entity.bIsPureFunc?.valueOf()
|
||||
this.dragLinkObjects = []
|
||||
super.initialize(entity, template)
|
||||
this.#pins = this.template.createPinElements()
|
||||
@@ -128,19 +131,16 @@ export default class NodeElement extends ISelectableDraggableElement {
|
||||
} else {
|
||||
this.updateComplete.then(() => this.computeSizes())
|
||||
}
|
||||
entity.listenAttribute("Name", name => {
|
||||
this.nodeTitle = entity.Name
|
||||
this.nodeDisplayName = nodeTitle(entity)
|
||||
this.#redirectLinksAfterRename(name)
|
||||
})
|
||||
}
|
||||
|
||||
async getUpdateComplete() {
|
||||
let result = await super.getUpdateComplete()
|
||||
for (const pin of this.getPinElements()) {
|
||||
result &&= await pin.updateComplete
|
||||
}
|
||||
return result
|
||||
entity.listenAttribute(
|
||||
"Name",
|
||||
/** @param {InstanceType<typeof ObjectEntity.attributes.Name>} newName */
|
||||
newName => {
|
||||
this.#redirectLinksBeforeRename(newName?.toString())
|
||||
this.nodeTitle = newName?.toString()
|
||||
this.nodeDisplayName = nodeTitle(entity)
|
||||
this.acknowledgeUpdate()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/** @param {NodeElement} commentNode */
|
||||
@@ -185,14 +185,14 @@ export default class NodeElement extends ISelectableDraggableElement {
|
||||
setNodeWidth(value) {
|
||||
this.entity.setNodeWidth(value)
|
||||
this.sizeX = value
|
||||
this.acknowledgeReflow()
|
||||
this.acknowledgeUpdate(true)
|
||||
}
|
||||
|
||||
/** @param {Number} value */
|
||||
setNodeHeight(value) {
|
||||
this.entity.setNodeHeight(value)
|
||||
this.sizeY = value
|
||||
this.acknowledgeReflow()
|
||||
this.acknowledgeUpdate(true)
|
||||
}
|
||||
|
||||
/** @param {IElement[]} nodesWhitelist */
|
||||
@@ -215,15 +215,17 @@ export default class NodeElement extends ISelectableDraggableElement {
|
||||
super.setLocation(x, y, acknowledge)
|
||||
}
|
||||
|
||||
acknowledgeReflow() {
|
||||
this.requestUpdate()
|
||||
this.updateComplete.then(() => this.computeSizes())
|
||||
let reflowEvent = new CustomEvent(Configuration.nodeReflowEventName)
|
||||
this.dispatchEvent(reflowEvent)
|
||||
acknowledgeUpdate(resize = false) {
|
||||
const event = new CustomEvent(Configuration.nodeUpdateEventName)
|
||||
if (resize) {
|
||||
this.requestUpdate()
|
||||
this.updateComplete.then(() => this.computeSizes())
|
||||
}
|
||||
this.dispatchEvent(event)
|
||||
}
|
||||
|
||||
setShowAdvancedPinDisplay(value) {
|
||||
this.entity.AdvancedPinDisplay = new IdentifierEntity(value ? "Shown" : "Hidden")
|
||||
this.entity.AdvancedPinDisplay = new SymbolEntity(value ? "Shown" : "Hidden")
|
||||
this.advancedPinDisplay = this.entity.AdvancedPinDisplay
|
||||
}
|
||||
|
||||
|
||||
168
js/element/PinElement.js
Normal file → Executable file
168
js/element/PinElement.js
Normal file → Executable file
@@ -1,15 +1,17 @@
|
||||
import Utility from "../Utility.js"
|
||||
import Configuration from "../Configuration.js"
|
||||
import pinTemplate from "../decoding/pinTemplate.js"
|
||||
import BooleanEntity from "../entity/BooleanEntity.js"
|
||||
import GuidEntity from "../entity/GuidEntity.js"
|
||||
import LinearColorEntity from "../entity/LinearColorEntity.js"
|
||||
import PinEntity from "../entity/PinEntity.js"
|
||||
import PinReferenceEntity from "../entity/PinReferenceEntity.js"
|
||||
import SymbolEntity from "../entity/SymbolEntity.js"
|
||||
import PinTemplate from "../template/pin/PinTemplate.js"
|
||||
import ElementFactory from "./ElementFactory.js"
|
||||
import IElement from "./IElement.js"
|
||||
|
||||
/**
|
||||
* @template {TerminalAttribute} T
|
||||
* @template {IEntity} T
|
||||
* @extends {IElement<PinEntity<T>, PinTemplate>}
|
||||
*/
|
||||
export default class PinElement extends IElement {
|
||||
@@ -21,7 +23,7 @@ export default class PinElement extends IElement {
|
||||
fromAttribute: (value, type) => value
|
||||
? GuidEntity.grammar.parse(value)
|
||||
: null,
|
||||
toAttribute: (value, type) => /** @type {String} */(value?.toString()),
|
||||
toAttribute: (value, type) => value?.toString(),
|
||||
},
|
||||
attribute: "data-id",
|
||||
reflect: true,
|
||||
@@ -42,7 +44,8 @@ export default class PinElement extends IElement {
|
||||
fromAttribute: (value, type) => value
|
||||
? LinearColorEntity.getLinearColorFromAnyFormat().parse(value)
|
||||
: null,
|
||||
toAttribute: (value, type) => value ? Utility.printLinearColor(value) : null,
|
||||
/** @param {LinearColorEntity} value */
|
||||
toAttribute: (value, type) => value?.toString() ?? "",
|
||||
},
|
||||
attribute: "data-color",
|
||||
reflect: true,
|
||||
@@ -53,7 +56,7 @@ export default class PinElement extends IElement {
|
||||
},
|
||||
isLinked: {
|
||||
type: Boolean,
|
||||
converter: Utility.booleanConverter,
|
||||
converter: BooleanEntity.booleanConverter,
|
||||
attribute: "data-linked",
|
||||
reflect: true,
|
||||
},
|
||||
@@ -64,7 +67,7 @@ export default class PinElement extends IElement {
|
||||
},
|
||||
connectable: {
|
||||
type: Boolean,
|
||||
converter: Utility.booleanConverter,
|
||||
converter: BooleanEntity.booleanConverter,
|
||||
attribute: "data-connectable",
|
||||
reflect: true,
|
||||
}
|
||||
@@ -89,14 +92,16 @@ export default class PinElement extends IElement {
|
||||
nodeElement = undefined
|
||||
) {
|
||||
this.nodeElement = nodeElement
|
||||
this.advancedView = entity.bAdvancedView
|
||||
this.advancedView = entity.bAdvancedView?.valueOf()
|
||||
this.isLinked = false
|
||||
this.connectable = !entity.bNotConnectable
|
||||
this.connectable = !entity.bNotConnectable?.valueOf()
|
||||
super.initialize(entity, template)
|
||||
this.pinType = this.entity.getType()
|
||||
this.pinId = this.entity.PinId
|
||||
this.updateType()
|
||||
this.defaultValue = this.entity.getDefaultValue()
|
||||
this.color = PinElement.properties.color.converter.fromAttribute(this.getColor().toString())
|
||||
this.pinDirection = entity.isInput() ? "input" : entity.isOutput() ? "output" : "hidden"
|
||||
/** @type {LinearColorEntity} */
|
||||
this.color = PinElement.properties.color.converter.fromAttribute(this.entity.pinColor().toString())
|
||||
}
|
||||
|
||||
setup() {
|
||||
@@ -104,42 +109,93 @@ export default class PinElement extends IElement {
|
||||
this.nodeElement = this.closest("ueb-node")
|
||||
}
|
||||
|
||||
createPinReference() {
|
||||
return new PinReferenceEntity({
|
||||
objectName: this.nodeElement.getNodeName(),
|
||||
pinGuid: this.getPinId(),
|
||||
})
|
||||
updateType() {
|
||||
this.pinType = this.entity.getType()
|
||||
const newColor = PinElement.properties.color.converter.fromAttribute(this.entity.pinColor().toString())
|
||||
if (!this.color?.equals(newColor)) {
|
||||
this.color = newColor
|
||||
this.acknowledgeUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
createPinReference() {
|
||||
return new PinReferenceEntity(new SymbolEntity(this.nodeElement.getNodeName()), this.getPinId())
|
||||
}
|
||||
|
||||
/** @return {GuidEntity} */
|
||||
getPinId() {
|
||||
return this.entity.PinId
|
||||
}
|
||||
|
||||
/** @returns {String} */
|
||||
getPinName() {
|
||||
return this.entity.PinName
|
||||
return this.entity.PinName?.toString() ?? ""
|
||||
}
|
||||
|
||||
getPinDisplayName() {
|
||||
return this.entity.pinTitle()
|
||||
}
|
||||
|
||||
/** @return {CSSResult} */
|
||||
getColor() {
|
||||
return this.entity.pinColor()
|
||||
/** @param {PinElement} pin */
|
||||
#traverseKnots(pin) {
|
||||
while (pin?.isKnot()) {
|
||||
const pins = pin.nodeElement.getPinElements()
|
||||
pin = pin === pins[0] ? pins[1] : pins[0]
|
||||
pin = pin.isLinked ? this.blueprint.getPin(pin.getLinks()[0]) : null
|
||||
}
|
||||
return pin?.isKnot() ? undefined : pin
|
||||
}
|
||||
|
||||
isInput() {
|
||||
return this.entity.isInput()
|
||||
isInput(ignoreKnots = false) {
|
||||
/** @type {PinElement} */
|
||||
let result = this
|
||||
if (ignoreKnots) {
|
||||
return this.#traverseKnots(result)?.isInput()
|
||||
}
|
||||
return result.entity.isInput()
|
||||
}
|
||||
|
||||
isOutput() {
|
||||
return this.entity.isOutput()
|
||||
/** @returns {boolean} True when the pin is the input part of a knot that can switch direction */
|
||||
isInputLoosely() {
|
||||
return this.isInput(false) && this.isInput(true) === undefined
|
||||
}
|
||||
|
||||
getLinkLocation() {
|
||||
return this.template.getLinkLocation()
|
||||
/** @returns {boolean} True when the pin is input and if it is a knot it appears input */
|
||||
isInputVisually() {
|
||||
const template = /** @type {KnotNodeTemplate} */(this.nodeElement.template)
|
||||
const isKnot = this.isKnot()
|
||||
return isKnot && this.isInput() != template.switchDirectionsVisually
|
||||
|| !isKnot && this.isInput()
|
||||
}
|
||||
|
||||
isOutput(ignoreKnots = false) {
|
||||
/** @type {PinElement} */
|
||||
let result = this
|
||||
if (ignoreKnots) {
|
||||
return this.#traverseKnots(result)?.isOutput()
|
||||
}
|
||||
return result.entity.isOutput()
|
||||
}
|
||||
|
||||
/** @returns {boolean} True when the pin is the output part of a knot that can switch direction */
|
||||
isOutputLoosely() {
|
||||
return this.isOutput(false) && this.isOutput(true) === undefined
|
||||
}
|
||||
|
||||
/** @returns {boolean} True when the pin is output and if it is a knot it appears output */
|
||||
isOutputVisually() {
|
||||
const template = /** @type {KnotNodeTemplate} */(this.nodeElement.template)
|
||||
const isKnot = this.isKnot()
|
||||
return isKnot && this.isOutput() != template.switchDirectionsVisually
|
||||
|| !isKnot && this.isOutput()
|
||||
}
|
||||
|
||||
|
||||
/** @returns {value is InstanceType<PinElement<>>} */
|
||||
isKnot() {
|
||||
return this.nodeElement?.getType() == Configuration.paths.knot
|
||||
}
|
||||
|
||||
getLinkLocation(oppositeDirection = false) {
|
||||
return this.template.getLinkLocation(oppositeDirection)
|
||||
}
|
||||
|
||||
getNodeElement() {
|
||||
@@ -147,7 +203,7 @@ export default class PinElement extends IElement {
|
||||
}
|
||||
|
||||
getLinks() {
|
||||
return this.entity.LinkedTo ?? []
|
||||
return this.entity.LinkedTo?.valueOf() ?? []
|
||||
}
|
||||
|
||||
getDefaultValue(maybeCreate = false) {
|
||||
@@ -165,21 +221,23 @@ export default class PinElement extends IElement {
|
||||
|
||||
/** @param {IElement[]} nodesWhitelist */
|
||||
sanitizeLinks(nodesWhitelist = []) {
|
||||
this.entity.LinkedTo = this.entity.LinkedTo?.filter(pinReference => {
|
||||
let pin = this.blueprint.getPin(pinReference)
|
||||
if (pin) {
|
||||
if (nodesWhitelist.length && !nodesWhitelist.includes(pin.nodeElement)) {
|
||||
return false
|
||||
this.entity.LinkedTo = new (PinEntity.attributes.LinkedTo)(
|
||||
this.entity.LinkedTo?.valueOf().filter(pinReference => {
|
||||
let pin = this.blueprint.getPin(pinReference)
|
||||
if (pin) {
|
||||
if (nodesWhitelist.length && !nodesWhitelist.includes(pin.nodeElement)) {
|
||||
return false
|
||||
}
|
||||
let link = this.blueprint.getLink(this, pin)
|
||||
if (!link) {
|
||||
link = /** @type {LinkElementConstructor} */(ElementFactory.getConstructor("ueb-link"))
|
||||
.newObject(this, pin)
|
||||
this.blueprint.addGraphElement(link)
|
||||
}
|
||||
}
|
||||
let link = this.blueprint.getLink(this, pin)
|
||||
if (!link) {
|
||||
link = /** @type {LinkElementConstructor} */(ElementFactory.getConstructor("ueb-link"))
|
||||
.newObject(this, pin)
|
||||
this.blueprint.addGraphElement(link)
|
||||
}
|
||||
}
|
||||
return pin
|
||||
})
|
||||
return pin
|
||||
})
|
||||
)
|
||||
this.isLinked = this.entity.isLinked()
|
||||
}
|
||||
|
||||
@@ -188,14 +246,17 @@ export default class PinElement extends IElement {
|
||||
const pinReference = this.createPinReference()
|
||||
if (
|
||||
this.isLinked
|
||||
&& this.isOutput()
|
||||
&& (this.pinType === "exec" || targetPinElement.pinType === "exec")
|
||||
&& !this.getLinks().some(ref => pinReference.equals(ref))) {
|
||||
&& this.entity.isExecution()
|
||||
&& this.isOutput(true)
|
||||
&& this.getLinks().some(ref => !pinReference.equals(ref))
|
||||
) {
|
||||
if (this.isKnot()) {
|
||||
|
||||
}
|
||||
this.unlinkFromAll()
|
||||
}
|
||||
if (this.entity.linkTo(targetPinElement.getNodeElement().getNodeName(), targetPinElement.entity)) {
|
||||
this.isLinked = this.entity.isLinked()
|
||||
this.nodeElement?.template.linksChanged()
|
||||
if (this.entity.recomputesNodeTitleOnChange) {
|
||||
this.nodeElement?.computeNodeDisplayName()
|
||||
}
|
||||
@@ -206,7 +267,6 @@ export default class PinElement extends IElement {
|
||||
unlinkFrom(targetPinElement, removeLink = true) {
|
||||
if (this.entity.unlinkFrom(targetPinElement.getNodeElement().getNodeName(), targetPinElement.entity)) {
|
||||
this.isLinked = this.entity.isLinked()
|
||||
this.nodeElement?.template.linksChanged()
|
||||
if (removeLink) {
|
||||
this.blueprint.getLink(this, targetPinElement)?.remove() // Might be called after the link is removed
|
||||
}
|
||||
@@ -217,11 +277,8 @@ export default class PinElement extends IElement {
|
||||
}
|
||||
|
||||
unlinkFromAll() {
|
||||
const isLinked = this.getLinks().length
|
||||
this.getLinks().map(ref => this.blueprint.getPin(ref)).forEach(pin => this.unlinkFrom(pin))
|
||||
if (isLinked) {
|
||||
this.nodeElement?.template.linksChanged()
|
||||
}
|
||||
const isLinked = false
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -231,12 +288,17 @@ export default class PinElement extends IElement {
|
||||
redirectLink(originalPinElement, newReference) {
|
||||
const index = this.getLinks().findIndex(pinReference =>
|
||||
pinReference.objectName.toString() == originalPinElement.getNodeElement().getNodeName()
|
||||
&& pinReference.pinGuid.valueOf() == originalPinElement.entity.PinId.valueOf()
|
||||
&& pinReference.pinGuid.toString() == originalPinElement.entity.PinId.toString()
|
||||
)
|
||||
if (index >= 0) {
|
||||
this.entity.LinkedTo[index] = newReference
|
||||
this.entity.LinkedTo.valueOf()[index] = newReference
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
acknowledgeUpdate() {
|
||||
let event = new CustomEvent(Configuration.pinUpdateEventName)
|
||||
this.dispatchEvent(event)
|
||||
}
|
||||
}
|
||||
|
||||
12
js/element/SelectorElement.js
Normal file → Executable file
12
js/element/SelectorElement.js
Normal file → Executable file
@@ -37,16 +37,16 @@ export default class SelectorElement extends IFromToPositionedElement {
|
||||
/** @param {Coordinates} finalPosition */
|
||||
selectTo(finalPosition) {
|
||||
this.selectionModel.selectTo(finalPosition)
|
||||
this.toX = finalPosition[0]
|
||||
this.toY = finalPosition[1]
|
||||
this.targetX = finalPosition[0]
|
||||
this.targetY = finalPosition[1]
|
||||
}
|
||||
|
||||
endSelect() {
|
||||
this.blueprint.selecting = false
|
||||
this.selectionModel = null
|
||||
this.fromX = 0
|
||||
this.fromY = 0
|
||||
this.toX = 0
|
||||
this.toY = 0
|
||||
this.originX = 0
|
||||
this.originY = 0
|
||||
this.targetX = 0
|
||||
this.targetY = 0
|
||||
}
|
||||
}
|
||||
|
||||
0
js/element/WindowElement.js
Normal file → Executable file
0
js/element/WindowElement.js
Normal file → Executable file
0
js/element/defineElements.js
Normal file → Executable file
0
js/element/defineElements.js
Normal file → Executable file
38
js/entity/AlternativesEntity.js
Executable file
38
js/entity/AlternativesEntity.js
Executable file
@@ -0,0 +1,38 @@
|
||||
import P from "parsernostrum"
|
||||
import IEntity from "./IEntity.js"
|
||||
|
||||
/** @template {(typeof IEntity)[]} T */
|
||||
export default class AlternativesEntity extends IEntity {
|
||||
|
||||
/** @type {(typeof IEntity)[]} */
|
||||
static alternatives = []
|
||||
|
||||
static className() {
|
||||
let result = super.className()
|
||||
if (this.alternatives.length) {
|
||||
result += ".accepting(" + this.alternatives.map(v => v.className()).join(", ") + ")"
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
static createGrammar() {
|
||||
const grammars = this.alternatives.map(entity => entity.grammar)
|
||||
if (this.alternatives.length == 0 || grammars.includes(this.unknownEntityGrammar)) {
|
||||
return this.unknownEntityGrammar
|
||||
}
|
||||
return P.alt(...grammars)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {(typeof IEntity)[]} Types
|
||||
* @param {Types} types
|
||||
*/
|
||||
static accepting(...types) {
|
||||
const result = /** @type {typeof AlternativesEntity<Types> & { alternatives: Types }} */(
|
||||
this.asUniqueClass()
|
||||
)
|
||||
result.alternatives = types
|
||||
result.grammar = result.createGrammar()
|
||||
return result
|
||||
}
|
||||
}
|
||||
108
js/entity/ArrayEntity.js
Executable file
108
js/entity/ArrayEntity.js
Executable file
@@ -0,0 +1,108 @@
|
||||
import P from "parsernostrum"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
|
||||
/** @template {typeof IEntity} T */
|
||||
export default class ArrayEntity extends IEntity {
|
||||
|
||||
/** @type {typeof IEntity} */
|
||||
static type
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
get length() {
|
||||
return this.values.length
|
||||
}
|
||||
|
||||
/** @param {(ExtractType<T>)[]} values */
|
||||
constructor(values = []) {
|
||||
super()
|
||||
this.values = values
|
||||
}
|
||||
|
||||
/** @returns {P<ArrayEntity<typeof IEntity>>} */
|
||||
static createGrammar(elementGrammar = this.type?.grammar ?? P.lazy(() => this.unknownEntityGrammar)) {
|
||||
return this.inlined
|
||||
? elementGrammar
|
||||
: P.seq(
|
||||
P.reg(/\(\s*/),
|
||||
elementGrammar.sepBy(Grammar.commaSeparation).opt(),
|
||||
P.reg(/\s*(,\s*)?\)/, 1),
|
||||
).map(([_0, values, trailing]) => {
|
||||
values = values instanceof Array ? values : []
|
||||
let Self = this
|
||||
if ((trailing !== undefined) !== Self.trailing) {
|
||||
Self = Self.flagTrailing(trailing !== undefined)
|
||||
}
|
||||
return new Self(values)
|
||||
}).label(`ArrayEntity of ${this.type?.className() ?? "unknown values"}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {typeof IEntity} T
|
||||
* @this {T}
|
||||
*/
|
||||
static flagInlined(value = true) {
|
||||
const result = this.asUniqueClass()
|
||||
result.inlined = value
|
||||
result.grammar = /** @type {P<ArrayEntity>} */(result.createGrammar())
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {typeof IEntity} T
|
||||
* @param {T} type
|
||||
*/
|
||||
static of(type) {
|
||||
const result = /** @type {{type: T, grammar: P<ArrayEntity<T>> } & typeof ArrayEntity<T>} */(
|
||||
this.asUniqueClass()
|
||||
)
|
||||
result.type = type
|
||||
result.grammar = /** @type {P<ArrayEntity>} */(result.createGrammar())
|
||||
return result
|
||||
}
|
||||
|
||||
doSerialize(
|
||||
insideString = false,
|
||||
indentation = "",
|
||||
Self = /** @type {typeof ArrayEntity<T>} */(this.constructor),
|
||||
printKey = Self.printKey,
|
||||
keySeparator = Self.keySeparator,
|
||||
attributeSeparator = Self.attributeSeparator,
|
||||
wrap = Self.wrap,
|
||||
) {
|
||||
if (Self.inlined) {
|
||||
return super.serialize.bind(
|
||||
this.values,
|
||||
insideString,
|
||||
indentation,
|
||||
Self,
|
||||
printKey,
|
||||
keySeparator,
|
||||
attributeSeparator,
|
||||
wrap
|
||||
)()
|
||||
}
|
||||
let result = this.values.map(v => v?.serialize(insideString)).join(Self.attributeSeparator)
|
||||
if (this.trailing) {
|
||||
result += Self.attributeSeparator
|
||||
}
|
||||
return `(${result})`
|
||||
}
|
||||
|
||||
valueOf() {
|
||||
return this.values
|
||||
}
|
||||
|
||||
/** @param {IEntity} other */
|
||||
equals(other) {
|
||||
if (!(other instanceof ArrayEntity) || this.values.length !== other.values.length) {
|
||||
return false
|
||||
}
|
||||
for (let i = 0; i < this.values.length; ++i) {
|
||||
if (!this.values[i].equals(other.values[i])) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {{
|
||||
* type?: AttributeTypeDescription,
|
||||
* default?: T,
|
||||
* nullable?: Boolean,
|
||||
* ignored?: Boolean,
|
||||
* serialized?: Boolean,
|
||||
* expected?: Boolean,
|
||||
* inlined?: Boolean,
|
||||
* quoted?: Boolean,
|
||||
* silent?: Boolean,
|
||||
* uninitialized?: Boolean,
|
||||
* predicate?: (value: T) => Boolean,
|
||||
* }} AttributeInfoSource
|
||||
*/
|
||||
|
||||
/** @template T */
|
||||
export default class AttributeInfo {
|
||||
|
||||
/** @typedef {keyof AttributeInfo<number>} AttributeKey */
|
||||
|
||||
static #default = {
|
||||
nullable: false,
|
||||
ignored: false, // Never serialize or deserialize
|
||||
serialized: false, // Value is written and read as string
|
||||
expected: false, // Must be there
|
||||
inlined: false, // The key is a subobject or array and printed as inlined (A.B=123, A(0)=123)
|
||||
quoted: false, // Key is serialized with quotes
|
||||
silent: false, // Do not serialize if default
|
||||
uninitialized: false, // Do not initialize with default
|
||||
}
|
||||
|
||||
/** @param {AttributeInfoSource<T>} source */
|
||||
constructor(source) {
|
||||
this.type = source.type ?? source.default?.constructor
|
||||
this.default = source.default
|
||||
this.nullable = source.nullable ?? source.default === null
|
||||
this.ignored = source.ignored
|
||||
this.serialized = source.serialized
|
||||
this.expected = source.expected
|
||||
this.inlined = source.inlined
|
||||
this.quoted = source.quoted
|
||||
this.silent = source.silent
|
||||
this.uninitialized = source.uninitialized
|
||||
this.predicate = source.predicate
|
||||
if (this.type === Array && this.default instanceof Array && this.default.length > 0) {
|
||||
this.type = this.default
|
||||
.map(v => v.constructor)
|
||||
.reduce((acc, cur) => acc.includes(cur) ? acc : (acc.push(cur), acc), [])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {AttributeTypeDescription} D
|
||||
* @param {D} type
|
||||
* @returns {AttributeInfo<DescribedType<type>>}
|
||||
*/
|
||||
static createType(type) {
|
||||
return new AttributeInfo({ type })
|
||||
}
|
||||
|
||||
/**
|
||||
* @template V
|
||||
* @param {V} value
|
||||
*/
|
||||
static createValue(value) {
|
||||
return new AttributeInfo({ default: value })
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {IEntity | Object} source
|
||||
* @param {String} attribute
|
||||
* @param {AttributeKey} key
|
||||
*/
|
||||
static hasAttribute(source, attribute, key, type = /** @type {EntityConstructor} */(source.constructor)) {
|
||||
const entity = /** @type {IEntity} */(source)
|
||||
const result = entity.attributes[attribute]?.[key]
|
||||
return /** @type {result} */(
|
||||
result
|
||||
?? type?.attributes?.[attribute]?.[key]
|
||||
?? AttributeInfo.#default[key]
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {IEntity | Object} S
|
||||
* @template {EntityConstructor} C
|
||||
* @template {keyof C["attributes"]} A
|
||||
* @template {keyof C["attributes"][attribute]} K
|
||||
* @param {S} source
|
||||
* @param {A} attribute
|
||||
* @param {K} key
|
||||
* @param {C} type
|
||||
* @returns {C["attributes"][attribute][key]}
|
||||
*/
|
||||
static getAttribute(source, attribute, key, type = /** @type {C} */(source.constructor)) {
|
||||
let result = source["attributes"]?.[attribute]?.[key]
|
||||
// Remember null is a valid asignment value for some attributes
|
||||
if (result !== undefined) {
|
||||
return result
|
||||
}
|
||||
result = /** @type {C["attributes"]} */(type?.attributes)?.[attribute]?.[key]
|
||||
if (result !== undefined) {
|
||||
return result
|
||||
}
|
||||
result = /** @type {C["attributes"][attribute]} */(AttributeInfo.#default)[key]
|
||||
if (result !== undefined) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {AttributeKey} key */
|
||||
get(key) {
|
||||
return this[key] ?? AttributeInfo.#default[key]
|
||||
}
|
||||
}
|
||||
139
js/entity/BlueprintEntity.js
Normal file → Executable file
139
js/entity/BlueprintEntity.js
Normal file → Executable file
@@ -1,6 +1,7 @@
|
||||
import Configuration from "../Configuration.js"
|
||||
import Utility from "../Utility.js"
|
||||
import ObjectEntity from "./ObjectEntity.js"
|
||||
import ScriptVariableEntity from "./ScriptVariableEntity.js"
|
||||
|
||||
export default class BlueprintEntity extends ObjectEntity {
|
||||
|
||||
@@ -13,9 +14,21 @@ export default class BlueprintEntity extends ObjectEntity {
|
||||
return this.#objectEntities
|
||||
}
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
ScriptVariables: super.attributes.ScriptVariables.asUniqueClass(true).withDefault(),
|
||||
}
|
||||
|
||||
constructor(...args) {
|
||||
super(...args)
|
||||
if (!this.Name) {
|
||||
this.Name = new (/** @type {typeof ObjectEntity} */(this.constructor).attributes.Name)("Blueprint")
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {ObjectEntity} entity */
|
||||
getHomonymObjectEntity(entity) {
|
||||
const name = entity.getObjectName(false)
|
||||
const name = entity.getObjectName()
|
||||
return this.#objectEntities.find(entity => entity.getObjectName() == name)
|
||||
}
|
||||
|
||||
@@ -27,6 +40,16 @@ export default class BlueprintEntity extends ObjectEntity {
|
||||
return Configuration.nodeTitle(name, counter)
|
||||
}
|
||||
|
||||
/** @param {String} name */
|
||||
updateNameIndex(name) {
|
||||
const match = name.match(/(.+)_(\d+)$/)
|
||||
if (match) {
|
||||
name = match[1]
|
||||
const index = Number(match[2])
|
||||
this.#objectEntitiesNameCounter.set(name, Math.max(index, this.#objectEntitiesNameCounter.get(name) ?? 0))
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {ObjectEntity} entity */
|
||||
addObjectEntity(entity) {
|
||||
if (!this.#objectEntities.includes(entity)) {
|
||||
@@ -54,35 +77,121 @@ 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)
|
||||
return newName
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ScriptVariableEntity} scriptVariableEntity
|
||||
* @returns {String}
|
||||
*/
|
||||
variableName(scriptVariableEntity) {
|
||||
return this[Configuration.subObjectAttributeNameFromReference(scriptVariableEntity.ScriptVariable, true)]
|
||||
?.["Variable"]
|
||||
?.["Name"]
|
||||
?.toString()
|
||||
}
|
||||
|
||||
/** @param {String} variableName */
|
||||
variableIndex(variableName) {
|
||||
let i = 0
|
||||
for (const v of this.ScriptVariables?.valueOf()) {
|
||||
if (variableName == this.variableName(v)) {
|
||||
return i
|
||||
}
|
||||
++i
|
||||
}
|
||||
}
|
||||
|
||||
/** @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
|
||||
}
|
||||
const variableObjectNames = this.ScriptVariables.valueOf().map(v => v.ScriptVariable.getName())
|
||||
let scriptVariables = Utility.mergeArrays(
|
||||
this.ScriptVariables,
|
||||
entity.ScriptVariables,
|
||||
(l, r) => l.OriginalChangeId.value == r.OriginalChangeId.value
|
||||
this.ScriptVariables.valueOf(),
|
||||
entity.ScriptVariables.valueOf(),
|
||||
(l, r) => this.variableName(l) == this.variableName(r),
|
||||
added => {
|
||||
let name = added.ScriptVariable.getName()
|
||||
if (variableObjectNames.includes(name)) {
|
||||
name = this.renameScriptVariable(added.ScriptVariable, entity)
|
||||
}
|
||||
this.updateNameIndex(name)
|
||||
}
|
||||
)
|
||||
if (scriptVariables.length === this.ScriptVariables.length) {
|
||||
// The entity does not add new variables
|
||||
return this
|
||||
}
|
||||
scriptVariables.reverse()
|
||||
const blueprintEntity = /** @type {typeof BlueprintEntity} */(this.constructor)
|
||||
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))
|
||||
const result = new BlueprintEntity(Object.fromEntries(entries))
|
||||
result.mirrorNameInExportPaths(entity.Name?.toString())
|
||||
result.#objectEntitiesNameCounter = this.#objectEntitiesNameCounter
|
||||
result.#objectEntities = this.#objectEntities
|
||||
return result
|
||||
}
|
||||
|
||||
/** @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 {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
63
js/entity/BooleanEntity.js
Executable file
63
js/entity/BooleanEntity.js
Executable file
@@ -0,0 +1,63 @@
|
||||
import P from "parsernostrum"
|
||||
import IEntity from "./IEntity.js"
|
||||
|
||||
export default class BooleanEntity extends IEntity {
|
||||
|
||||
static grammar = this.createGrammar()
|
||||
static booleanConverter = {
|
||||
fromAttribute: (value, type) => {
|
||||
value ? "true" : "false"
|
||||
},
|
||||
toAttribute: (value, type) => {
|
||||
if (value === true) {
|
||||
return "true"
|
||||
}
|
||||
if (value === false) {
|
||||
return "false"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
#uppercase = true
|
||||
get uppercase() {
|
||||
return this.#uppercase
|
||||
}
|
||||
set uppercase(value) {
|
||||
this.#uppercase = value
|
||||
}
|
||||
|
||||
/** @returns {P<BooleanEntity>} */
|
||||
static createGrammar() {
|
||||
return P.regArray(/(true)|(True)|(false)|(False)/)
|
||||
.map(v => {
|
||||
const result = (v[1] ?? v[2]) ? new this(true) : new this(false)
|
||||
result.uppercase = (v[2] ?? v[4]) !== undefined
|
||||
return result
|
||||
})
|
||||
.label("BooleanEntity")
|
||||
}
|
||||
|
||||
constructor(value = false) {
|
||||
super()
|
||||
this.value = value
|
||||
}
|
||||
|
||||
serialize(
|
||||
insideString = false,
|
||||
indentation = "",
|
||||
Self = /** @type {typeof IEntity} */(this.constructor),
|
||||
) {
|
||||
let result = this.value
|
||||
? this.#uppercase ? "True" : "true"
|
||||
: this.#uppercase ? "False" : "false"
|
||||
if (Self.serialized) {
|
||||
result = `"${result}"`
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
valueOf() {
|
||||
return this.value
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,23 @@
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import P from "parsernostrum"
|
||||
import IntegerEntity from "./IntegerEntity.js"
|
||||
|
||||
export default class ByteEntity extends IntegerEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
value: new AttributeInfo({
|
||||
...super.attributes.value,
|
||||
predicate: v => v % 1 == 0 && v >= 0 && v < 1 << 8,
|
||||
}),
|
||||
}
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
static createGrammar() {
|
||||
return Parsernostrum.numberByte.map(v => new this(v))
|
||||
get value() {
|
||||
return super.value
|
||||
}
|
||||
set value(value) {
|
||||
value = Math.trunc(value)
|
||||
if (value >= 0 && value < 1 << 8) {
|
||||
super.value = value
|
||||
}
|
||||
}
|
||||
|
||||
constructor(values = 0) {
|
||||
super(values)
|
||||
/** @returns {P<ByteEntity>} */
|
||||
createGrammar() {
|
||||
// @ts-expect-error
|
||||
return P.numberByte.map(v => new this(v))
|
||||
}
|
||||
}
|
||||
|
||||
36
js/entity/ColorChannelEntity.js
Normal file → Executable file
36
js/entity/ColorChannelEntity.js
Normal file → Executable file
@@ -1,28 +1,30 @@
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import P from "parsernostrum"
|
||||
import IEntity from "./IEntity.js"
|
||||
|
||||
export default class ColorChannelEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
value: AttributeInfo.createValue(0),
|
||||
}
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
static createGrammar() {
|
||||
return Parsernostrum.number.map(value => new this(value))
|
||||
constructor(value = 0) {
|
||||
super()
|
||||
this.value = value
|
||||
}
|
||||
|
||||
constructor(values = 0) {
|
||||
if (values.constructor !== Object) {
|
||||
// @ts-expect-error
|
||||
values = {
|
||||
value: values,
|
||||
}
|
||||
/** @returns {P<ColorChannelEntity>} */
|
||||
static createGrammar() {
|
||||
return P.number.map(v => new this(v))
|
||||
}
|
||||
|
||||
serialize(
|
||||
insideString = false,
|
||||
indentation = "",
|
||||
Self = /** @type {typeof IEntity} */(this.constructor),
|
||||
) {
|
||||
let result = this.value.toFixed(6)
|
||||
if (Self.serialized) {
|
||||
result = `"${result}"`
|
||||
}
|
||||
super(values)
|
||||
/** @type {Number} */ this.value
|
||||
return result
|
||||
}
|
||||
|
||||
valueOf() {
|
||||
@@ -30,6 +32,6 @@ export default class ColorChannelEntity extends IEntity {
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.value.toFixed(6)
|
||||
return this.value.toString()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
export default class ComputedType {
|
||||
|
||||
#f
|
||||
|
||||
/** @param {Function} f */
|
||||
constructor(f) {
|
||||
this.#f = f
|
||||
}
|
||||
|
||||
/** @param {IEntity} entity */
|
||||
compute(entity) {
|
||||
return this.#f(entity)
|
||||
}
|
||||
}
|
||||
28
js/entity/ComputedTypeEntity.js
Executable file
28
js/entity/ComputedTypeEntity.js
Executable file
@@ -0,0 +1,28 @@
|
||||
import IEntity from "./IEntity.js"
|
||||
import StringEntity from "./StringEntity.js"
|
||||
|
||||
export default class ComputedTypeEntity extends IEntity {
|
||||
|
||||
static grammar = this.createGrammar()
|
||||
/** @type {(entity: IEntity) => typeof IEntity} */
|
||||
static f
|
||||
|
||||
static createGrammar() {
|
||||
return StringEntity.grammar
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {typeof ComputedTypeEntity.f} T
|
||||
* @param {T} producer
|
||||
*/
|
||||
static from(producer) {
|
||||
const result = /** @type {(typeof ComputedTypeEntity) & { f: T }} */(this.asUniqueClass())
|
||||
result.f = producer
|
||||
return result
|
||||
}
|
||||
|
||||
/** @param {IEntity} entity */
|
||||
static compute(entity) {
|
||||
return this.f(entity)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import P from "parsernostrum"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import EnumEntity from "./EnumEntity.js"
|
||||
|
||||
@@ -6,7 +6,8 @@ export default class EnumDisplayValueEntity extends EnumEntity {
|
||||
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
/** @returns {P<EnumDisplayValueEntity>} */
|
||||
static createGrammar() {
|
||||
return Parsernostrum.reg(Grammar.Regex.InsideString).map(v => new this(v))
|
||||
return P.reg(Grammar.Regex.InsideString).map(v => new this(v))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import P from "parsernostrum"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import SymbolEntity from "./SymbolEntity.js"
|
||||
|
||||
@@ -5,6 +6,7 @@ export default class EnumEntity extends SymbolEntity {
|
||||
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
/** @returns {P<EnumEntity>} */
|
||||
static createGrammar() {
|
||||
return Grammar.symbol.map(v => new this(v))
|
||||
}
|
||||
|
||||
83
js/entity/FormatTextEntity.js
Normal file → Executable file
83
js/entity/FormatTextEntity.js
Normal file → Executable file
@@ -1,56 +1,62 @@
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
import P from "parsernostrum"
|
||||
import InvariantTextEntity from "./InvariantTextEntity.js"
|
||||
import LocalizedTextEntity from "./LocalizedTextEntity.js"
|
||||
import Union from "./Union.js"
|
||||
import StringEntity from "./StringEntity.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
|
||||
export default class FormatTextEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
value: new AttributeInfo({
|
||||
type: [new Union(String, LocalizedTextEntity, InvariantTextEntity, FormatTextEntity)],
|
||||
default: [],
|
||||
}),
|
||||
lookbehind: /** @type {AttributeInfo<Union<String[]>>} */(new AttributeInfo({
|
||||
...super.attributes.lookbehind,
|
||||
default: new Union("LOCGEN_FORMAT_NAMED", "LOCGEN_FORMAT_ORDERED"),
|
||||
})),
|
||||
}
|
||||
static attributeSeparator = ", "
|
||||
static lookbehind = ["LOCGEN_FORMAT_NAMED", "LOCGEN_FORMAT_ORDERED"]
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
static createGrammar() {
|
||||
return Parsernostrum.seq(
|
||||
Parsernostrum.reg(
|
||||
// Resulting regex: /(LOCGEN_FORMAT_NAMED|LOCGEN_FORMAT_ORDERED)\s*/
|
||||
new RegExp(`(${this.attributes.lookbehind.default.values.reduce((acc, cur) => acc + "|" + cur)})\\s*`),
|
||||
1
|
||||
),
|
||||
Grammar.grammarFor(this.attributes.value)
|
||||
)
|
||||
.map(([lookbehind, values]) => {
|
||||
const result = new this({
|
||||
value: values,
|
||||
lookbehind,
|
||||
})
|
||||
return result
|
||||
})
|
||||
/** @param {(StringEntity | LocalizedTextEntity | InvariantTextEntity | FormatTextEntity)[]} values */
|
||||
constructor(values) {
|
||||
super()
|
||||
this.values = values
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
super(values)
|
||||
/** @type {(String | LocalizedTextEntity | InvariantTextEntity | FormatTextEntity)[]} */ this.value
|
||||
/** @returns {P<FormatTextEntity>} */
|
||||
static createGrammar() {
|
||||
return P.lazy(() => P.seq(
|
||||
// Resulting regex: /(LOCGEN_FORMAT_NAMED|LOCGEN_FORMAT_ORDERED)\s*/
|
||||
P.reg(new RegExp(String.raw`(${this.lookbehind.join("|")})\s*\(\s*`), 1),
|
||||
P.alt(
|
||||
...[StringEntity, LocalizedTextEntity, InvariantTextEntity, FormatTextEntity].map(type => type.grammar)
|
||||
).sepBy(P.reg(/\s*\,\s*/)),
|
||||
P.reg(/\s*\)/)
|
||||
)
|
||||
.map(([lookbehind, values]) => {
|
||||
const result = new this(values)
|
||||
result.lookbehind = lookbehind
|
||||
return result
|
||||
}))
|
||||
.label("FormatTextEntity")
|
||||
}
|
||||
|
||||
doSerialize(
|
||||
insideString = false,
|
||||
indentation = "",
|
||||
Self = /** @type {typeof FormatTextEntity} */(this.constructor),
|
||||
printKey = Self.printKey,
|
||||
keySeparator = Self.keySeparator,
|
||||
attributeSeparator = Self.attributeSeparator,
|
||||
wrap = Self.wrap,
|
||||
) {
|
||||
const separator = Self.attributeSeparator
|
||||
return this.lookbehind + "("
|
||||
+ this.values.map(v => v.serialize(insideString)).join(separator)
|
||||
+ (Self.trailing ? separator : "")
|
||||
+ ")"
|
||||
}
|
||||
|
||||
toString() {
|
||||
const pattern = this.value?.[0]?.toString() // The pattern is always the first element of the array
|
||||
const pattern = this.values?.[0]?.toString() // The pattern is always the first element of the array
|
||||
if (!pattern) {
|
||||
return ""
|
||||
}
|
||||
const values = this.value.slice(1).map(v => v.toString())
|
||||
return this.lookbehind == "LOCGEN_FORMAT_NAMED"
|
||||
const values = this.values.slice(1).map(v => v?.valueOf())
|
||||
let result = this.lookbehind == "LOCGEN_FORMAT_NAMED"
|
||||
? pattern.replaceAll(/\{([a-zA-Z]\w*)\}/g, (substring, arg) => {
|
||||
const argLocation = values.indexOf(arg) + 1
|
||||
return argLocation > 0 && argLocation < values.length
|
||||
@@ -65,5 +71,6 @@ export default class FormatTextEntity extends IEntity {
|
||||
: substring
|
||||
})
|
||||
: ""
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,29 @@
|
||||
import P from "parsernostrum"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import GuidEntity from "./GuidEntity.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
import ObjectReferenceEntity from "./ObjectReferenceEntity.js"
|
||||
import StringEntity from "./StringEntity.js"
|
||||
|
||||
export default class FunctionReferenceEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
MemberParent: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
MemberName: AttributeInfo.createType(String),
|
||||
MemberGuid: AttributeInfo.createType(GuidEntity),
|
||||
MemberParent: ObjectReferenceEntity,
|
||||
MemberName: StringEntity,
|
||||
MemberGuid: GuidEntity,
|
||||
}
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
static createGrammar() {
|
||||
return Grammar.createEntityGrammar(this)
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
super(values)
|
||||
/** @type {ObjectReferenceEntity} */ this.MemberParent
|
||||
/** @type {String} */ this.MemberName
|
||||
/** @type {GuidEntity} */ this.MemberGuid
|
||||
/** @type {InstanceType<typeof FunctionReferenceEntity.attributes.MemberParent>} */ this.MemberParent
|
||||
/** @type {InstanceType<typeof FunctionReferenceEntity.attributes.MemberName>} */ this.MemberName
|
||||
/** @type {InstanceType<typeof FunctionReferenceEntity.attributes.MemberGuid>} */ this.MemberGuid
|
||||
}
|
||||
|
||||
/** @returns {P<FunctionReferenceEntity>} */
|
||||
static createGrammar() {
|
||||
return Grammar.createEntityGrammar(this, Grammar.commaSeparation, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
import P from "parsernostrum"
|
||||
|
||||
var crypto
|
||||
if (typeof window === "undefined") {
|
||||
// When used in nodejs, mainly for test purpose
|
||||
import("crypto").then(mod => crypto = mod.default).catch()
|
||||
} else {
|
||||
crypto = window.crypto
|
||||
@@ -11,43 +11,38 @@ if (typeof window === "undefined") {
|
||||
|
||||
export default class GuidEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
value: AttributeInfo.createValue(""),
|
||||
}
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
static createGrammar() {
|
||||
return Grammar.guid.map(v => new this(v))
|
||||
}
|
||||
|
||||
static generateGuid(random = true) {
|
||||
static generateGuid() {
|
||||
let values = new Uint32Array(4)
|
||||
if (random === true) {
|
||||
crypto.getRandomValues(values)
|
||||
}
|
||||
crypto.getRandomValues(values)
|
||||
let guid = ""
|
||||
values.forEach(n => {
|
||||
guid += ("0".repeat(8) + n.toString(16).toUpperCase()).slice(-8)
|
||||
})
|
||||
return new GuidEntity({ value: guid })
|
||||
return guid
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
if (!values) {
|
||||
values = GuidEntity.generateGuid().value
|
||||
}
|
||||
if (values.constructor !== Object) {
|
||||
values = {
|
||||
value: values,
|
||||
}
|
||||
}
|
||||
super(values)
|
||||
/** @type {String} */ this.value
|
||||
constructor(value = GuidEntity.generateGuid()) {
|
||||
super()
|
||||
this.value = value
|
||||
}
|
||||
|
||||
valueOf() {
|
||||
return this.value
|
||||
/** @returns {P<GuidEntity>} */
|
||||
static createGrammar() {
|
||||
return P.reg(/[0-9A-F]{32}/i).map(v => new this(v)).label("GuidEntity")
|
||||
}
|
||||
|
||||
serialize(
|
||||
insideString = false,
|
||||
indentation = "",
|
||||
Self = /** @type {typeof IEntity} */(this.constructor),
|
||||
) {
|
||||
let result = this.value
|
||||
if (Self.serialized) {
|
||||
result = `"${result}"`
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
toString() {
|
||||
|
||||
545
js/entity/IEntity.js
Normal file → Executable file
545
js/entity/IEntity.js
Normal file → Executable file
@@ -1,233 +1,438 @@
|
||||
import Configuration from "../Configuration.js"
|
||||
import P from "parsernostrum"
|
||||
import Utility from "../Utility.js"
|
||||
import Serializable from "../serialization/Serializable.js"
|
||||
import SerializerFactory from "../serialization/SerializerFactory.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import ComputedType from "./ComputedType.js"
|
||||
import MirroredEntity from "./MirroredEntity.js"
|
||||
import Union from "./Union.js"
|
||||
|
||||
/** @abstract */
|
||||
export default class IEntity extends Serializable {
|
||||
export default class IEntity {
|
||||
|
||||
/** @type {{ [attribute: String]: AttributeInfo }} */
|
||||
static attributes = {
|
||||
attributes: new AttributeInfo({
|
||||
ignored: true,
|
||||
}),
|
||||
lookbehind: new AttributeInfo({
|
||||
default: /** @type {String | Union<String[]>} */(""),
|
||||
ignored: true,
|
||||
uninitialized: true,
|
||||
}),
|
||||
}
|
||||
/** @type {(v: String) => String} */
|
||||
static same = v => v
|
||||
/** @type {(entity: IEntity, serialized: String) => String} */
|
||||
static notWrapped = (entity, serialized) => serialized
|
||||
/** @type {(entity: IEntity, serialized: String) => String} */
|
||||
static defaultWrapped = (entity, serialized) => `${entity.#lookbehind}(${serialized})`
|
||||
static wrap = this.defaultWrapped
|
||||
static attributeSeparator = ","
|
||||
static keySeparator = "="
|
||||
/** @type {(k: String) => String} */
|
||||
static printKey = k => k
|
||||
static grammar = P.lazy(() => this.createGrammar())
|
||||
/** @type {P<IEntity>} */
|
||||
static unknownEntityGrammar
|
||||
static unknownEntity
|
||||
/** @type {{ [key: String]: typeof IEntity }} */
|
||||
static attributes = {}
|
||||
/** @type {String | String[]} */
|
||||
static lookbehind = ""
|
||||
/** @type {(type: typeof IEntity) => InstanceType<typeof IEntity>} */
|
||||
static default
|
||||
static nullable = false
|
||||
static ignored = false // Never serialize or deserialize
|
||||
static serialized = false // Value is written and read as string
|
||||
static expected = false // Must be there
|
||||
static inlined = false // The key is a subobject or array and printed as inlined (A.B=123, A(0)=123)
|
||||
/** @type {Boolean} */
|
||||
static quoted // Key is serialized with quotes
|
||||
static silent = false // Do not serialize if default
|
||||
static trailing = false // Add attribute separator after the last attribute when serializing
|
||||
|
||||
/** @type {String[]} */
|
||||
#_keys
|
||||
get _keys() {
|
||||
return this.#_keys
|
||||
#keys
|
||||
get keys() {
|
||||
return this.#keys ?? Object.keys(this)
|
||||
}
|
||||
set _keys(keys) {
|
||||
this.#_keys = keys
|
||||
set keys(value) {
|
||||
this.#keys = [... new Set(value)]
|
||||
}
|
||||
|
||||
constructor(values = {}, suppressWarns = false) {
|
||||
super()
|
||||
const Self = /** @type {typeof IEntity} */(this.constructor)
|
||||
/** @type {AttributeDeclarations?} */ this.attributes
|
||||
/** @type {String} */ this.lookbehind
|
||||
const valuesKeys = Object.keys(values)
|
||||
const attributesKeys = values.attributes
|
||||
? Utility.mergeArrays(Object.keys(values.attributes), Object.keys(Self.attributes))
|
||||
: Object.keys(Self.attributes)
|
||||
const allAttributesKeys = Utility.mergeArrays(valuesKeys, attributesKeys)
|
||||
for (const key of allAttributesKeys) {
|
||||
let value = values[key]
|
||||
if (!suppressWarns && !(key in values)) {
|
||||
if (!(key in Self.attributes) && !key.startsWith(Configuration.subObjectAttributeNamePrefix)) {
|
||||
const typeName = value instanceof Array ? `[${value[0]?.constructor.name}]` : value.constructor.name
|
||||
console.warn(
|
||||
`UEBlueprint: Attribute ${key} (of type ${typeName}) in the serialized data is not defined in ${Self.name}.attributes`
|
||||
)
|
||||
}
|
||||
}
|
||||
if (!(key in Self.attributes)) {
|
||||
// Remember attributeName can come from the values and be not defined in the attributes.
|
||||
// In that case just assign it and skip the rest.
|
||||
this[key] = value
|
||||
continue
|
||||
}
|
||||
Self.attributes.lookbehind
|
||||
const predicate = AttributeInfo.getAttribute(values, key, "predicate", Self)
|
||||
const assignAttribute = !predicate
|
||||
? v => this[key] = v
|
||||
: v => {
|
||||
Object.defineProperties(this, {
|
||||
["#" + key]: {
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
},
|
||||
[key]: {
|
||||
enumerable: true,
|
||||
get() {
|
||||
return this["#" + key]
|
||||
},
|
||||
set(v) {
|
||||
if (!predicate(v)) {
|
||||
console.warn(
|
||||
`UEBlueprint: Tried to assign attribute ${key} to ${Self.name} not satisfying the predicate`
|
||||
)
|
||||
return
|
||||
}
|
||||
this["#" + key] = v
|
||||
}
|
||||
},
|
||||
})
|
||||
this[key] = v
|
||||
}
|
||||
// @ts-expect-error
|
||||
#lookbehind = /** @type {String} */(this.constructor.lookbehind)
|
||||
get lookbehind() {
|
||||
return this.#lookbehind.trim()
|
||||
}
|
||||
set lookbehind(value) {
|
||||
this.#lookbehind = value
|
||||
}
|
||||
|
||||
let defaultValue = AttributeInfo.getAttribute(values, key, "default", Self)
|
||||
if (defaultValue instanceof Function) {
|
||||
defaultValue = defaultValue(this)
|
||||
}
|
||||
let defaultType = AttributeInfo.getAttribute(values, key, "type", Self)
|
||||
if (defaultType instanceof ComputedType) {
|
||||
defaultType = defaultType.compute(this)
|
||||
}
|
||||
if (defaultType instanceof Array) {
|
||||
defaultType = Array
|
||||
}
|
||||
if (defaultType === undefined) {
|
||||
defaultType = Utility.getType(defaultValue)
|
||||
}
|
||||
#ignored = /** @type {typeof IEntity} */(this.constructor).ignored
|
||||
get ignored() {
|
||||
return this.#ignored
|
||||
}
|
||||
set ignored(value) {
|
||||
this.#ignored = value
|
||||
}
|
||||
|
||||
if (value !== undefined) {
|
||||
// Remember value can still be null
|
||||
if (
|
||||
value?.constructor === String
|
||||
&& AttributeInfo.getAttribute(values, key, "serialized", Self)
|
||||
&& defaultType !== String
|
||||
) {
|
||||
try {
|
||||
value = SerializerFactory
|
||||
.getSerializer(defaultType)
|
||||
.read(/** @type {String} */(value))
|
||||
} catch (e) {
|
||||
assignAttribute(value)
|
||||
continue
|
||||
#inlined = /** @type {typeof IEntity} */(this.constructor).inlined
|
||||
get inlined() {
|
||||
return this.#inlined
|
||||
}
|
||||
set inlined(value) {
|
||||
this.#inlined = value
|
||||
}
|
||||
|
||||
#quoted
|
||||
get quoted() {
|
||||
return this.#quoted ?? /** @type {typeof IEntity} */(this.constructor).quoted ?? false
|
||||
}
|
||||
set quoted(value) {
|
||||
this.#quoted = value
|
||||
}
|
||||
|
||||
/** @type {Boolean} */
|
||||
#trailing
|
||||
get trailing() {
|
||||
return this.#trailing ?? /** @type {typeof IEntity} */(this.constructor).trailing ?? false
|
||||
}
|
||||
set trailing(value) {
|
||||
this.#trailing = value
|
||||
}
|
||||
|
||||
constructor(values = {}) {
|
||||
const attributes = /** @type {typeof IEntity} */(this.constructor).attributes
|
||||
const keys = Utility.mergeArrays(
|
||||
Object.keys(values),
|
||||
Object.entries(attributes).filter(([k, v]) => v.default !== undefined).map(([k, v]) => k)
|
||||
)
|
||||
for (const key of keys) {
|
||||
if (values[key] !== undefined) {
|
||||
if (values[key].constructor === Object) {
|
||||
// It is part of a nested key (words separated by ".")
|
||||
values[key] = new (
|
||||
attributes[key] !== undefined ? attributes[key] : IEntity.unknownEntity
|
||||
)(values[key])
|
||||
}
|
||||
const computedEntity = /** @type {ComputedTypeEntityConstructor} */(attributes[key])
|
||||
this[key] = values[key]
|
||||
if (computedEntity?.compute) {
|
||||
/** @type {typeof IEntity} */
|
||||
const actualEntity = computedEntity.compute(this)
|
||||
const parsed = actualEntity.grammar.run(values[key].toString())
|
||||
if (parsed.status) {
|
||||
this[key] = parsed.value
|
||||
}
|
||||
}
|
||||
assignAttribute(Utility.sanitize(value, /** @type {AttributeConstructor<Attribute>} */(defaultType)))
|
||||
continue // We have a value, need nothing more
|
||||
continue
|
||||
}
|
||||
if (defaultValue !== undefined && !AttributeInfo.getAttribute(values, key, "uninitialized", Self)) {
|
||||
assignAttribute(defaultValue)
|
||||
const attribute = attributes[key]
|
||||
if (attribute.default !== undefined) {
|
||||
this[key] = attribute.default(attribute)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {AttributeTypeDescription} attributeType */
|
||||
static defaultValueProviderFromType(attributeType) {
|
||||
if (attributeType === Boolean) {
|
||||
return false
|
||||
} else if (attributeType === Number) {
|
||||
return 0
|
||||
} else if (attributeType === BigInt) {
|
||||
return 0n
|
||||
} else if (attributeType === String) {
|
||||
return ""
|
||||
} else if (attributeType === Array || attributeType instanceof Array) {
|
||||
return () => []
|
||||
} else if (attributeType instanceof Union) {
|
||||
return this.defaultValueProviderFromType(attributeType.values[0])
|
||||
} else if (attributeType instanceof MirroredEntity) {
|
||||
return () => new MirroredEntity(attributeType.type, attributeType.getter)
|
||||
} else if (attributeType instanceof ComputedType) {
|
||||
return undefined
|
||||
} else {
|
||||
return () => new /** @type {AnyConstructor<Attribute>} */(attributeType)()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {new (...args: any) => any} C
|
||||
* @param {C} type
|
||||
* @returns {value is InstanceType<C>}
|
||||
* @protected
|
||||
* @returns {P<IEntity>}
|
||||
*/
|
||||
static isValueOfType(value, type) {
|
||||
return value != null && (value instanceof type || value.constructor === type)
|
||||
static createGrammar() {
|
||||
return this.unknownEntityGrammar
|
||||
}
|
||||
|
||||
static defineAttributes(object, attributes) {
|
||||
Object.defineProperty(object, "attributes", {
|
||||
writable: true,
|
||||
configurable: false,
|
||||
})
|
||||
object.attributes = attributes
|
||||
static actualClass() {
|
||||
let self = this
|
||||
while (!self.name) {
|
||||
self = Object.getPrototypeOf(self)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
static className() {
|
||||
return this.actualClass().name
|
||||
}
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @template {typeof IEntity} T
|
||||
* @this {T}
|
||||
* @returns {T}
|
||||
*/
|
||||
static asUniqueClass(alwaysCreate = false) {
|
||||
let result = this
|
||||
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
|
||||
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {typeof IEntity} T
|
||||
* @this {T}
|
||||
* @param {String} value
|
||||
*/
|
||||
static withLookbehind(value) {
|
||||
const result = this.asUniqueClass()
|
||||
result.lookbehind = value
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {typeof IEntity} T
|
||||
* @this {T}
|
||||
* @param {(type: T) => (InstanceType<T> | NullEntity)} value
|
||||
* @returns {T}
|
||||
*/
|
||||
static withDefault(value = type => new type()) {
|
||||
const result = this.asUniqueClass()
|
||||
result.default = value
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {typeof IEntity} T
|
||||
* @this {T}
|
||||
*/
|
||||
static flagNullable(value = true) {
|
||||
const result = this.asUniqueClass()
|
||||
result.nullable = value
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {typeof IEntity} T
|
||||
* @this {T}
|
||||
*/
|
||||
static flagIgnored(value = true) {
|
||||
const result = this.asUniqueClass()
|
||||
result.ignored = value
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {typeof IEntity} T
|
||||
* @this {T}
|
||||
*/
|
||||
static flagSerialized(value = true) {
|
||||
const result = this.asUniqueClass()
|
||||
result.serialized = value
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {typeof IEntity} T
|
||||
* @this {T}
|
||||
*/
|
||||
static flagInlined(value = true) {
|
||||
const result = this.asUniqueClass()
|
||||
result.inlined = value
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {typeof IEntity} T
|
||||
* @this {T}
|
||||
*/
|
||||
static flagQuoted(value = true) {
|
||||
const result = this.asUniqueClass()
|
||||
result.quoted = value
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {typeof IEntity} T
|
||||
* @this {T}
|
||||
*/
|
||||
static flagSilent(value = true) {
|
||||
const result = this.asUniqueClass()
|
||||
result.silent = value
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {typeof IEntity} T
|
||||
* @this {T}
|
||||
*/
|
||||
static flagTrailing(value = true) {
|
||||
const result = this.asUniqueClass()
|
||||
result.trailing = value
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @param {String} string
|
||||
*/
|
||||
static asSerializedString(string) {
|
||||
return `"${string.replaceAll(/(?<=(?:[^\\]|^)(?:\\\\)*?)"/g, '\\"')}"`
|
||||
}
|
||||
|
||||
/** @param {String} key */
|
||||
showProperty(key) {
|
||||
/** @type {IEntity} */
|
||||
let value = this[key]
|
||||
const valueType = /** @type {typeof IEntity} */(value.constructor)
|
||||
if (valueType.silent && valueType.default !== undefined) {
|
||||
if (valueType["#default"] === undefined) {
|
||||
valueType["#default"] = valueType.default(valueType)
|
||||
}
|
||||
const defaultValue = valueType["#default"]
|
||||
return !value.equals(defaultValue)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} attribute
|
||||
* @param {String} attributeName
|
||||
* @param {(v: any) => void} callback
|
||||
*/
|
||||
listenAttribute(attribute, callback) {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(this, attribute)
|
||||
listenAttribute(attributeName, callback) {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(this, attributeName)
|
||||
const setter = descriptor.set
|
||||
if (setter) {
|
||||
descriptor.set = v => {
|
||||
setter(v)
|
||||
callback(v)
|
||||
}
|
||||
Object.defineProperties(this, { [attribute]: descriptor })
|
||||
Object.defineProperties(this, { [attributeName]: descriptor })
|
||||
} else if (descriptor.value) {
|
||||
|
||||
Object.defineProperties(this, {
|
||||
["#" + attribute]: {
|
||||
["#" + attributeName]: {
|
||||
value: descriptor.value,
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
},
|
||||
[attribute]: {
|
||||
[attributeName]: {
|
||||
enumerable: true,
|
||||
get() {
|
||||
return this["#" + attribute]
|
||||
return this["#" + attributeName]
|
||||
},
|
||||
set(v) {
|
||||
if (v == this["#" + attribute]) {
|
||||
return
|
||||
}
|
||||
callback(v)
|
||||
this["#" + attribute] = v
|
||||
}
|
||||
this["#" + attributeName] = v
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
getLookbehind() {
|
||||
let lookbehind = this.lookbehind ?? AttributeInfo.getAttribute(this, "lookbehind", "default")
|
||||
lookbehind = lookbehind instanceof Union ? lookbehind.values[0] : lookbehind
|
||||
return lookbehind
|
||||
/** @this {IEntity | Array} */
|
||||
doSerialize(
|
||||
insideString = false,
|
||||
indentation = "",
|
||||
Self = /** @type {typeof IEntity} */(this.constructor),
|
||||
printKey = Self.printKey,
|
||||
keySeparator = Self.keySeparator,
|
||||
attributeSeparator = Self.attributeSeparator,
|
||||
wrap = Self.wrap,
|
||||
) {
|
||||
const isSelfOverriden = Self !== this.constructor
|
||||
let result = ""
|
||||
let first = true
|
||||
const keys = this instanceof IEntity ? this.keys : Object.keys(this)
|
||||
for (const key of keys) {
|
||||
/** @type {IEntity} */
|
||||
const value = this[key]
|
||||
const valueType = /** @type {typeof IEntity} */(value?.constructor)
|
||||
if (value === undefined || this instanceof IEntity && !this.showProperty(key)) {
|
||||
continue
|
||||
}
|
||||
if (first) {
|
||||
first = false
|
||||
} else {
|
||||
result += attributeSeparator
|
||||
}
|
||||
let keyValue = this instanceof Array ? `(${key})` : key
|
||||
if (keyValue.length && (Self.attributes[key]?.quoted || value.quoted)) {
|
||||
keyValue = `"${keyValue}"`
|
||||
}
|
||||
if (value.inlined) {
|
||||
const inlinedPrintKey = valueType.className() === "ArrayEntity"
|
||||
? k => printKey(`${keyValue}${k}`)
|
||||
: k => printKey(`${keyValue}.${k}`)
|
||||
result += value.serialize(
|
||||
insideString,
|
||||
indentation,
|
||||
undefined,
|
||||
inlinedPrintKey,
|
||||
keySeparator,
|
||||
attributeSeparator,
|
||||
Self.notWrapped
|
||||
)
|
||||
continue
|
||||
}
|
||||
keyValue = printKey(keyValue)
|
||||
if (keyValue.length) {
|
||||
result += (attributeSeparator.includes("\n") ? indentation : "") + keyValue + keySeparator
|
||||
}
|
||||
let serialization = value?.serialize(insideString, indentation)
|
||||
result += serialization
|
||||
}
|
||||
if (this instanceof IEntity && (isSelfOverriden && Self.trailing || this.trailing) && result.length) {
|
||||
result += attributeSeparator
|
||||
}
|
||||
return wrap(/** @type {IEntity} */(this), result)
|
||||
}
|
||||
|
||||
unexpectedKeys() {
|
||||
return Object.keys(this).length - Object.keys(/** @type {typeof IEntity} */(this.constructor).attributes).length
|
||||
/** @this {IEntity | Array} */
|
||||
serialize(
|
||||
insideString = false,
|
||||
indentation = "",
|
||||
Self = /** @type {typeof IEntity} */(this.constructor),
|
||||
printKey = Self.printKey,
|
||||
keySeparator = Self.keySeparator,
|
||||
attributeSeparator = Self.attributeSeparator,
|
||||
wrap = Self.wrap,
|
||||
) {
|
||||
const isSelfOverriden = Self !== this.constructor
|
||||
let result = this instanceof Array
|
||||
? IEntity.prototype.doSerialize.bind(this)(insideString, indentation, Self, printKey, keySeparator, attributeSeparator, wrap)
|
||||
: this.doSerialize(insideString, indentation, Self, printKey, keySeparator, attributeSeparator, wrap)
|
||||
if (Self.serialized) {
|
||||
result = IEntity.asSerializedString(result)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/** @param {IEntity} other */
|
||||
equals(other) {
|
||||
const thisKeys = Object.keys(this)
|
||||
const otherKeys = Object.keys(other)
|
||||
if (thisKeys.length != otherKeys.length) {
|
||||
if (!(other instanceof IEntity)) {
|
||||
return false
|
||||
}
|
||||
for (const key of thisKeys) {
|
||||
if (this[key] instanceof IEntity && !this[key].equals(other[key])) {
|
||||
return false
|
||||
} else if (!Utility.equals(this[key], other[key])) {
|
||||
const thisKeys = Object.keys(this)
|
||||
const otherKeys = Object.keys(other)
|
||||
const thisType = /** @type {typeof IEntity} */(this.constructor).actualClass()
|
||||
const otherType = /** @type {typeof IEntity} */(other.constructor).actualClass()
|
||||
if (
|
||||
thisKeys.length !== otherKeys.length
|
||||
|| this.lookbehind != other.lookbehind
|
||||
|| !(other instanceof thisType) && !(this instanceof otherType)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
for (let i = 0; i < thisKeys.length; ++i) {
|
||||
const k = thisKeys[i]
|
||||
if (!otherKeys.includes(k)) {
|
||||
return false
|
||||
}
|
||||
const a = this[k]
|
||||
const b = other[k]
|
||||
if (a instanceof IEntity) {
|
||||
if (!a.equals(b)) {
|
||||
return false
|
||||
}
|
||||
} else if (a instanceof Array && b instanceof Array) {
|
||||
if (a.length !== b.length) {
|
||||
return false
|
||||
}
|
||||
for (let j = 0; j < a.length; ++j) {
|
||||
if (!(a[j] instanceof IEntity && a[j].equals(b[j])) && a[j] !== b[j]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (a !== b) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/** @returns {IEntity | Boolean | Number | String | BigInt | (IEntity | Boolean | Number | String | BigInt)[]} */
|
||||
valueOf() {
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
|
||||
export default class IdentifierEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
value: AttributeInfo.createValue(""),
|
||||
}
|
||||
static attributeConverter = {
|
||||
fromAttribute: (value, type) => new IdentifierEntity(value),
|
||||
toAttribute: (value, type) => value.toString()
|
||||
}
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
static createGrammar() {
|
||||
return Grammar.symbol.map(v => new this(v))
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
if (values.constructor !== Object) {
|
||||
values = {
|
||||
value: values,
|
||||
}
|
||||
}
|
||||
super(values)
|
||||
/** @type {String} */ this.value
|
||||
}
|
||||
|
||||
valueOf() {
|
||||
return this.value
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.value
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,45 @@
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import P from "parsernostrum"
|
||||
import IEntity from "./IEntity.js"
|
||||
|
||||
export default class Integer64Entity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
value: new AttributeInfo({
|
||||
default: 0n,
|
||||
predicate: v => v >= -(1n << 63n) && v < 1n << 63n,
|
||||
}),
|
||||
}
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
static createGrammar() {
|
||||
return Parsernostrum.numberBigInteger.map(v => new this(v))
|
||||
/**
|
||||
* @protected
|
||||
* @type {bigint}
|
||||
*/
|
||||
_value
|
||||
get value() {
|
||||
return this._value
|
||||
}
|
||||
set value(value) {
|
||||
if (value >= -(1n << 63n) && value < 1n << 63n) {
|
||||
this._value = value
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {BigInt | Number | Object} values */
|
||||
constructor(values = 0) {
|
||||
if (values.constructor !== Object) {
|
||||
values = {
|
||||
value: values,
|
||||
}
|
||||
/** @param {bigint | Number} value */
|
||||
constructor(value = 0n) {
|
||||
super()
|
||||
this.value = BigInt(value)
|
||||
}
|
||||
|
||||
/** @returns {P<Integer64Entity>} */
|
||||
static createGrammar() {
|
||||
return P.numberBigInteger.map(v => new this(v))
|
||||
}
|
||||
|
||||
serialize(
|
||||
insideString = false,
|
||||
indentation = "",
|
||||
Self = /** @type {typeof IEntity} */(this.constructor),
|
||||
) {
|
||||
let result = this.value.toString()
|
||||
if (Self.serialized) {
|
||||
result = `"${result}"`
|
||||
}
|
||||
if (values.value === -0) {
|
||||
values.value = 0n
|
||||
}
|
||||
super(values)
|
||||
/** @type {BigInt} */ this.value
|
||||
return result
|
||||
}
|
||||
|
||||
valueOf() {
|
||||
|
||||
@@ -1,42 +1,23 @@
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
import P from "parsernostrum"
|
||||
import NumberEntity from "./NumberEntity.js"
|
||||
|
||||
export default class IntegerEntity extends IEntity {
|
||||
export default class IntegerEntity extends NumberEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
value: new AttributeInfo({
|
||||
default: 0,
|
||||
predicate: v => v % 1 == 0 && v > 1 << 31 && v < -(1 << 31),
|
||||
}),
|
||||
}
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
get value() {
|
||||
return super.value
|
||||
}
|
||||
set value(value) {
|
||||
value = Math.trunc(value)
|
||||
if (value >= 1 << 31 && value < -(1 << 31)) {
|
||||
value = Math.floor(value)
|
||||
super.value = value
|
||||
}
|
||||
}
|
||||
|
||||
/** @returns {P<IntegerEntity>} */
|
||||
static createGrammar() {
|
||||
return Parsernostrum.numberInteger.map(v => new this(v))
|
||||
}
|
||||
|
||||
/** @param {Number | Object} values */
|
||||
constructor(values = 0) {
|
||||
if (values.constructor !== Object) {
|
||||
values = {
|
||||
value: values,
|
||||
}
|
||||
}
|
||||
values.value = Math.floor(values.value)
|
||||
if (values.value === -0) {
|
||||
values.value = 0
|
||||
}
|
||||
super(values)
|
||||
/** @type {Number} */ this.value
|
||||
}
|
||||
|
||||
valueOf() {
|
||||
return this.value
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.value.toString()
|
||||
return P.numberInteger.map(v => new this(v))
|
||||
}
|
||||
}
|
||||
|
||||
56
js/entity/InvariantTextEntity.js
Normal file → Executable file
56
js/entity/InvariantTextEntity.js
Normal file → Executable file
@@ -1,41 +1,37 @@
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import P from "parsernostrum"
|
||||
import IEntity from "./IEntity.js"
|
||||
|
||||
export default class InvariantTextEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
value: AttributeInfo.createValue(""),
|
||||
lookbehind: new AttributeInfo({
|
||||
...super.attributes.lookbehind,
|
||||
default: "INVTEXT",
|
||||
}),
|
||||
}
|
||||
static lookbehind = "INVTEXT"
|
||||
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
static createGrammar() {
|
||||
return Parsernostrum.alt(
|
||||
Parsernostrum.seq(
|
||||
Parsernostrum.reg(new RegExp(`${this.attributes.lookbehind.default}\\s*\\(`)),
|
||||
Grammar.grammarFor(this.attributes.value),
|
||||
Parsernostrum.reg(/\s*\)/)
|
||||
)
|
||||
.map(([_0, value, _2]) => value),
|
||||
Parsernostrum.reg(new RegExp(this.attributes.lookbehind.default)) // InvariantTextEntity can not have arguments
|
||||
.map(() => "")
|
||||
).map(value => new this(value))
|
||||
constructor(value = "") {
|
||||
super()
|
||||
this.value = value
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
if (values.constructor !== Object) {
|
||||
values = {
|
||||
value: values,
|
||||
}
|
||||
}
|
||||
super(values)
|
||||
/** @type {String} */ this.value
|
||||
/** @returns {P<InvariantTextEntity>} */
|
||||
static createGrammar() {
|
||||
return P.alt(
|
||||
P.seq(
|
||||
P.reg(new RegExp(`${this.lookbehind}\\s*\\(`)),
|
||||
P.doubleQuotedString,
|
||||
P.reg(/\s*\)/)
|
||||
).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 + '")'
|
||||
}
|
||||
|
||||
valueOf() {
|
||||
return this.value
|
||||
}
|
||||
|
||||
toString() {
|
||||
|
||||
48
js/entity/KeyBindingEntity.js
Normal file → Executable file
48
js/entity/KeyBindingEntity.js
Normal file → Executable file
@@ -1,38 +1,38 @@
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import P from "parsernostrum"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import BooleanEntity from "./BooleanEntity.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
import IdentifierEntity from "./IdentifierEntity.js"
|
||||
import StringEntity from "./StringEntity.js"
|
||||
import SymbolEntity from "./SymbolEntity.js"
|
||||
|
||||
export default class KeyBindingEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
ActionName: AttributeInfo.createValue(""),
|
||||
bShift: AttributeInfo.createValue(false),
|
||||
bCtrl: AttributeInfo.createValue(false),
|
||||
bAlt: AttributeInfo.createValue(false),
|
||||
bCmd: AttributeInfo.createValue(false),
|
||||
Key: AttributeInfo.createType(IdentifierEntity),
|
||||
ActionName: StringEntity,
|
||||
bShift: BooleanEntity,
|
||||
bCtrl: BooleanEntity,
|
||||
bAlt: BooleanEntity,
|
||||
bCmd: BooleanEntity,
|
||||
Key: SymbolEntity,
|
||||
}
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
static createGrammar() {
|
||||
return Parsernostrum.alt(
|
||||
IdentifierEntity.grammar.map(identifier => new this({
|
||||
Key: identifier
|
||||
})),
|
||||
Grammar.createEntityGrammar(this)
|
||||
)
|
||||
constructor(values) {
|
||||
super(values)
|
||||
/** @type {InstanceType<typeof KeyBindingEntity.attributes.ActionName>} */ this.ActionName
|
||||
/** @type {InstanceType<typeof KeyBindingEntity.attributes.bShift>} */ this.bShift
|
||||
/** @type {InstanceType<typeof KeyBindingEntity.attributes.bCtrl>} */ this.bCtrl
|
||||
/** @type {InstanceType<typeof KeyBindingEntity.attributes.bAlt>} */ this.bAlt
|
||||
/** @type {InstanceType<typeof KeyBindingEntity.attributes.bCmd>} */ this.bCmd
|
||||
/** @type {InstanceType<typeof KeyBindingEntity.attributes.Key>} */ this.Key
|
||||
}
|
||||
|
||||
constructor(values = {}) {
|
||||
super(values, true)
|
||||
/** @type {String} */ this.ActionName
|
||||
/** @type {Boolean} */ this.bShift
|
||||
/** @type {Boolean} */ this.bCtrl
|
||||
/** @type {Boolean} */ this.bAlt
|
||||
/** @type {Boolean} */ this.bCmd
|
||||
/** @type {IdentifierEntity} */ this.Key
|
||||
/** @returs {P<KeyBindingEntity>} */
|
||||
static createGrammar() {
|
||||
return P.alt(
|
||||
SymbolEntity.grammar.map(identifier => new this({ Key: identifier })),
|
||||
Grammar.createEntityGrammar(this),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
167
js/entity/LinearColorEntity.js
Normal file → Executable file
167
js/entity/LinearColorEntity.js
Normal file → Executable file
@@ -1,8 +1,7 @@
|
||||
import { css } from "lit"
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import P from "parsernostrum"
|
||||
import Utility from "../Utility.js"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import ColorChannelEntity from "./ColorChannelEntity.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
|
||||
@@ -10,43 +9,64 @@ export default class LinearColorEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
R: new AttributeInfo({
|
||||
type: ColorChannelEntity,
|
||||
default: () => new ColorChannelEntity(),
|
||||
expected: true,
|
||||
}),
|
||||
G: new AttributeInfo({
|
||||
type: ColorChannelEntity,
|
||||
default: () => new ColorChannelEntity(),
|
||||
expected: true,
|
||||
}),
|
||||
B: new AttributeInfo({
|
||||
type: ColorChannelEntity,
|
||||
default: () => new ColorChannelEntity(),
|
||||
expected: true,
|
||||
}),
|
||||
A: new AttributeInfo({
|
||||
type: ColorChannelEntity,
|
||||
default: () => new ColorChannelEntity(1),
|
||||
}),
|
||||
H: new AttributeInfo({
|
||||
type: ColorChannelEntity,
|
||||
default: () => new ColorChannelEntity(),
|
||||
ignored: true,
|
||||
}),
|
||||
S: new AttributeInfo({
|
||||
type: ColorChannelEntity,
|
||||
default: () => new ColorChannelEntity(),
|
||||
ignored: true,
|
||||
}),
|
||||
V: new AttributeInfo({
|
||||
type: ColorChannelEntity,
|
||||
default: () => new ColorChannelEntity(),
|
||||
ignored: true,
|
||||
}),
|
||||
R: ColorChannelEntity.withDefault(),
|
||||
G: ColorChannelEntity.withDefault(),
|
||||
B: ColorChannelEntity.withDefault(),
|
||||
A: ColorChannelEntity.withDefault(type => new type(1)),
|
||||
}
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
#H = new ColorChannelEntity()
|
||||
get H() {
|
||||
return this.#H
|
||||
}
|
||||
set H(value) {
|
||||
this.#H = value
|
||||
}
|
||||
|
||||
#S = new ColorChannelEntity()
|
||||
get S() {
|
||||
return this.#S
|
||||
}
|
||||
set S(value) {
|
||||
this.#S = value
|
||||
}
|
||||
|
||||
#V = new ColorChannelEntity()
|
||||
get V() {
|
||||
return this.#V
|
||||
}
|
||||
set V(value) {
|
||||
this.#V = value
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
super(values)
|
||||
if (values instanceof Array) {
|
||||
values = {
|
||||
R: values[0] ?? 0,
|
||||
G: values[1] ?? 0,
|
||||
B: values[2] ?? 0,
|
||||
A: values[3] ?? 1,
|
||||
}
|
||||
}
|
||||
/** @type {InstanceType<typeof LinearColorEntity.attributes.R>} */ this.R
|
||||
/** @type {InstanceType<typeof LinearColorEntity.attributes.G>} */ this.G
|
||||
/** @type {InstanceType<typeof LinearColorEntity.attributes.B>} */ this.B
|
||||
/** @type {InstanceType<typeof LinearColorEntity.attributes.A>} */ this.A
|
||||
this.#updateHSV()
|
||||
}
|
||||
|
||||
/** @returns {P<LinearColorEntity>} */
|
||||
static createGrammar() {
|
||||
return Grammar.createEntityGrammar(this, Grammar.commaSeparation, 0.5).label("LinearColorEntity")
|
||||
}
|
||||
|
||||
/** @param {LinearColorEntity} value */
|
||||
static printLinearColor(value) {
|
||||
return `${Math.round(value.R.valueOf() * 255)}, ${Math.round(value.G.valueOf() * 255)}, ${Math.round(value.B.valueOf() * 255)}`
|
||||
}
|
||||
|
||||
/** @param {Number} x */
|
||||
static linearToSRGB(x) {
|
||||
if (x <= 0) {
|
||||
@@ -75,22 +95,19 @@ export default class LinearColorEntity extends IEntity {
|
||||
|
||||
static getWhite() {
|
||||
return new LinearColorEntity({
|
||||
R: 1,
|
||||
G: 1,
|
||||
B: 1,
|
||||
R: new ColorChannelEntity(1),
|
||||
G: new ColorChannelEntity(1),
|
||||
B: new ColorChannelEntity(1),
|
||||
})
|
||||
}
|
||||
|
||||
static createGrammar() {
|
||||
return Grammar.createEntityGrammar(this, false)
|
||||
}
|
||||
|
||||
static getLinearColorFromHexGrammar() {
|
||||
return Parsernostrum.regArray(new RegExp(
|
||||
"#(" + Grammar.Regex.HexDigit.source + "{2})"
|
||||
+ "(" + Grammar.Regex.HexDigit.source + "{2})"
|
||||
+ "(" + Grammar.Regex.HexDigit.source + "{2})"
|
||||
+ "(" + Grammar.Regex.HexDigit.source + "{2})?"
|
||||
const hexDigit = /[0-9a-fA-F]/
|
||||
return P.regArray(new RegExp(
|
||||
"#(" + hexDigit.source + "{2})"
|
||||
+ "(" + hexDigit.source + "{2})"
|
||||
+ "(" + hexDigit.source + "{2})"
|
||||
+ "(" + hexDigit.source + "{2})?"
|
||||
)).map(([m, R, G, B, A]) => new this({
|
||||
R: parseInt(R, 16) / 255,
|
||||
G: parseInt(G, 16) / 255,
|
||||
@@ -100,12 +117,12 @@ export default class LinearColorEntity extends IEntity {
|
||||
}
|
||||
|
||||
static getLinearColorRGBListGrammar() {
|
||||
return Parsernostrum.seq(
|
||||
Parsernostrum.numberByte,
|
||||
return P.seq(
|
||||
P.numberByte,
|
||||
Grammar.commaSeparation,
|
||||
Parsernostrum.numberByte,
|
||||
P.numberByte,
|
||||
Grammar.commaSeparation,
|
||||
Parsernostrum.numberByte,
|
||||
P.numberByte,
|
||||
).map(([R, _1, G, _3, B]) => new this({
|
||||
R: R / 255,
|
||||
G: G / 255,
|
||||
@@ -115,23 +132,23 @@ export default class LinearColorEntity extends IEntity {
|
||||
}
|
||||
|
||||
static getLinearColorRGBGrammar() {
|
||||
return Parsernostrum.seq(
|
||||
Parsernostrum.reg(/rgb\s*\(\s*/),
|
||||
return P.seq(
|
||||
P.reg(/rgb\s*\(\s*/),
|
||||
this.getLinearColorRGBListGrammar(),
|
||||
Parsernostrum.reg(/\s*\)/)
|
||||
P.reg(/\s*\)/)
|
||||
).map(([_0, linearColor, _2]) => linearColor)
|
||||
}
|
||||
|
||||
static getLinearColorRGBAGrammar() {
|
||||
return Parsernostrum.seq(
|
||||
Parsernostrum.reg(/rgba\s*\(\s*/),
|
||||
return P.seq(
|
||||
P.reg(/rgba\s*\(\s*/),
|
||||
this.getLinearColorRGBListGrammar(),
|
||||
Parsernostrum.reg(/\s*\)/)
|
||||
P.reg(/\s*\)/)
|
||||
).map(([_0, linearColor, _2]) => linearColor)
|
||||
}
|
||||
|
||||
static getLinearColorFromAnyFormat() {
|
||||
return Parsernostrum.alt(
|
||||
return P.alt(
|
||||
this.getLinearColorFromHexGrammar(),
|
||||
this.getLinearColorRGBAGrammar(),
|
||||
this.getLinearColorRGBGrammar(),
|
||||
@@ -139,26 +156,6 @@ export default class LinearColorEntity extends IEntity {
|
||||
)
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
if (values instanceof Array) {
|
||||
values = {
|
||||
R: values[0] ?? 0,
|
||||
G: values[1] ?? 0,
|
||||
B: values[2] ?? 0,
|
||||
A: values[3] ?? 1,
|
||||
}
|
||||
}
|
||||
super(values)
|
||||
/** @type {ColorChannelEntity} */ this.R
|
||||
/** @type {ColorChannelEntity} */ this.G
|
||||
/** @type {ColorChannelEntity} */ this.B
|
||||
/** @type {ColorChannelEntity} */ this.A
|
||||
/** @type {ColorChannelEntity} */ this.H
|
||||
/** @type {ColorChannelEntity} */ this.S
|
||||
/** @type {ColorChannelEntity} */ this.V
|
||||
this.#updateHSV()
|
||||
}
|
||||
|
||||
#updateHSV() {
|
||||
const r = this.R.value
|
||||
const g = this.G.value
|
||||
@@ -302,6 +299,11 @@ export default class LinearColorEntity extends IEntity {
|
||||
+ Math.round(this.A.value * 0xff)
|
||||
}
|
||||
|
||||
/** @returns {[Number, Number, Number, Number]} */
|
||||
toArray() {
|
||||
return [this.R.value, this.G.value, this.B.value, this.A.value]
|
||||
}
|
||||
|
||||
/** @param {Number} number */
|
||||
setFromRGBANumber(number) {
|
||||
this.A.value = (number & 0xff) / 0xff
|
||||
@@ -320,12 +322,7 @@ export default class LinearColorEntity extends IEntity {
|
||||
this.#updateHSV()
|
||||
}
|
||||
|
||||
/** @returns {[Number, Number, Number, Number]} */
|
||||
toArray() {
|
||||
return [this.R.value, this.G.value, this.B.value, this.A.value]
|
||||
}
|
||||
|
||||
toString() {
|
||||
return Utility.printLinearColor(this)
|
||||
return LinearColorEntity.printLinearColor(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,50 @@
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import P from "parsernostrum"
|
||||
import Utility from "../Utility.js"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
import StringEntity from "./StringEntity.js"
|
||||
|
||||
export default class LocalizedTextEntity extends IEntity {
|
||||
|
||||
static attributeSeparator = ", "
|
||||
static printKey = k => ""
|
||||
static lookbehind = "NSLOCTEXT"
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
namespace: AttributeInfo.createValue(""),
|
||||
key: AttributeInfo.createValue(""),
|
||||
value: AttributeInfo.createValue(""),
|
||||
lookbehind: new AttributeInfo({
|
||||
...super.attributes.lookbehind,
|
||||
default: "NSLOCTEXT",
|
||||
}),
|
||||
namespace: StringEntity.withDefault(),
|
||||
key: StringEntity.withDefault(),
|
||||
value: StringEntity.withDefault(),
|
||||
}
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
static createGrammar() {
|
||||
return Parsernostrum.regArray(new RegExp(
|
||||
String.raw`${this.attributes.lookbehind.default}\s*\(`
|
||||
+ String.raw`\s*"(${Grammar.Regex.InsideString.source})"\s*,`
|
||||
+ String.raw`\s*"(${Grammar.Regex.InsideString.source})"\s*,`
|
||||
+ String.raw`\s*"(${Grammar.Regex.InsideString.source})"\s*`
|
||||
+ String.raw`(?:,\s+)?`
|
||||
+ String.raw`\)`,
|
||||
"m"
|
||||
)).map(matchResult => new this({
|
||||
namespace: Utility.unescapeString(matchResult[1]),
|
||||
key: Utility.unescapeString(matchResult[2]),
|
||||
value: Utility.unescapeString(matchResult[3]),
|
||||
}))
|
||||
constructor(values = {}) {
|
||||
super(values)
|
||||
/** @type {InstanceType<typeof LocalizedTextEntity.attributes.namespace>} */ this.namespace
|
||||
/** @type {InstanceType<typeof LocalizedTextEntity.attributes.key>} */ this.key
|
||||
/** @type {InstanceType<typeof LocalizedTextEntity.attributes.value>} */ this.value
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
super(values)
|
||||
/** @type {String} */ this.namespace
|
||||
/** @type {String} */ this.key
|
||||
/** @type {String} */ this.value
|
||||
/** @returns {P<LocalizedTextEntity>} */
|
||||
static createGrammar() {
|
||||
return P.regArray(new RegExp(
|
||||
String.raw`${LocalizedTextEntity.lookbehind}\s*\(`
|
||||
+ String.raw`\s*"(?<namespace>${Grammar.Regex.InsideString.source})"\s*,`
|
||||
+ String.raw`\s*"(?<key>${Grammar.Regex.InsideString.source})"\s*,`
|
||||
+ String.raw`\s*"(?<value>${Grammar.Regex.InsideString.source})"\s*`
|
||||
+ String.raw`(?<trailing>,\s+)?`
|
||||
+ String.raw`\)`,
|
||||
"m"
|
||||
)).map(({ groups: { namespace, key, value, trailing } }) => {
|
||||
return new this({
|
||||
namespace: new (this.attributes.namespace)(Utility.unescapeString(namespace)),
|
||||
key: new (this.attributes.namespace)(Utility.unescapeString(key)),
|
||||
value: new (this.attributes.namespace)(Utility.unescapeString(value)),
|
||||
trailing: trailing !== undefined,
|
||||
})
|
||||
}).label("LocalizedTextEntity")
|
||||
}
|
||||
|
||||
toString() {
|
||||
return Utility.capitalFirstLetter(this.value)
|
||||
return Utility.capitalFirstLetter(this.value.valueOf())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import P from "parsernostrum"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import GuidEntity from "./GuidEntity.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
import ObjectReferenceEntity from "./ObjectReferenceEntity.js"
|
||||
@@ -8,30 +8,22 @@ export default class MacroGraphReferenceEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
MacroGraph: new AttributeInfo({
|
||||
type: ObjectReferenceEntity,
|
||||
default: () => new ObjectReferenceEntity(),
|
||||
}),
|
||||
GraphBlueprint: new AttributeInfo({
|
||||
type: ObjectReferenceEntity,
|
||||
default: () => new ObjectReferenceEntity(),
|
||||
}),
|
||||
GraphGuid: new AttributeInfo({
|
||||
type: GuidEntity,
|
||||
default: () => new GuidEntity(),
|
||||
}),
|
||||
MacroGraph: ObjectReferenceEntity,
|
||||
GraphBlueprint: ObjectReferenceEntity,
|
||||
GraphGuid: GuidEntity,
|
||||
}
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
static createGrammar() {
|
||||
return Grammar.createEntityGrammar(this)
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
super(values)
|
||||
/** @type {ObjectReferenceEntity} */ this.MacroGraph
|
||||
/** @type {ObjectReferenceEntity} */ this.GraphBlueprint
|
||||
/** @type {GuidEntity} */ this.GuidEntity
|
||||
/** @type {InstanceType<typeof MacroGraphReferenceEntity.attributes.MacroGraph>} */ this.MacroGraph
|
||||
/** @type {InstanceType<typeof MacroGraphReferenceEntity.attributes.GraphBlueprint>} */ this.GraphBlueprint
|
||||
/** @type {InstanceType<typeof MacroGraphReferenceEntity.attributes.GraphGuid>} */ this.GraphGuid
|
||||
}
|
||||
|
||||
/** @returns {P<MacroGraphReferenceEntity>} */
|
||||
static createGrammar() {
|
||||
return Grammar.createEntityGrammar(this)
|
||||
}
|
||||
|
||||
getMacroName() {
|
||||
|
||||
92
js/entity/MirroredEntity.js
Normal file → Executable file
92
js/entity/MirroredEntity.js
Normal file → Executable file
@@ -1,36 +1,78 @@
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import P from "parsernostrum"
|
||||
import IEntity from "./IEntity.js"
|
||||
|
||||
/** @template {Attribute} T */
|
||||
export default class MirroredEntity {
|
||||
/** @template {typeof IEntity} T */
|
||||
export default class MirroredEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
type: new AttributeInfo({
|
||||
ignored: true,
|
||||
}),
|
||||
getter: new AttributeInfo({
|
||||
ignored: true,
|
||||
}),
|
||||
}
|
||||
/** @type {typeof IEntity} */
|
||||
static type
|
||||
|
||||
/**
|
||||
* @param {ConstructorType<T>} type
|
||||
* @param {() => T} getter
|
||||
*/
|
||||
constructor(type, getter = null) {
|
||||
this.type = type
|
||||
/** @param {() => InstanceType<T>} getter */
|
||||
constructor(getter = null) {
|
||||
super()
|
||||
const self = /** @type {typeof MirroredEntity<T>} */(this.constructor)
|
||||
getter ??= self.default !== undefined ? /** @type {MirroredEntity} */(self.default(self)).getter : getter
|
||||
this.getter = getter
|
||||
}
|
||||
|
||||
get() {
|
||||
return this.getter()
|
||||
static createGrammar(elementGrammar = this.type?.grammar ?? P.lazy(() => this.unknownEntityGrammar)) {
|
||||
return this.type?.grammar.map(v => new this(() => v))
|
||||
}
|
||||
|
||||
/** @returns {AttributeConstructor<Attribute>} */
|
||||
getTargetType() {
|
||||
const result = this.type
|
||||
if (result instanceof MirroredEntity) {
|
||||
return result.getTargetType()
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {typeof IEntity} T
|
||||
* @this {T}
|
||||
* @param {(type: T) => (InstanceType<T> | NullEntity)} value
|
||||
* @returns {T}
|
||||
*/
|
||||
// @ts-expect-error
|
||||
static withDefault(value = type => new type(() => new (type.type)())) {
|
||||
// @ts-expect-error
|
||||
return super.withDefault(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {typeof IEntity} T
|
||||
* @param {T} type
|
||||
*/
|
||||
static of(type) {
|
||||
const result = /** @type {{type: T, grammar: P<MirroredEntity<T>> } & typeof MirroredEntity<T>} */(
|
||||
this.asUniqueClass()
|
||||
)
|
||||
result.type = type
|
||||
result.grammar = result.createGrammar()
|
||||
return result
|
||||
}
|
||||
|
||||
doSerialize(
|
||||
insideString = false,
|
||||
indentation = "",
|
||||
Self = /** @type {typeof MirroredEntity<T>} */(this.constructor),
|
||||
printKey = Self.printKey,
|
||||
keySeparator = Self.keySeparator,
|
||||
attributeSeparator = Self.attributeSeparator,
|
||||
wrap = Self.wrap,
|
||||
) {
|
||||
const value = this.getter()
|
||||
return value.serialize(insideString, indentation, Self.type, printKey, keySeparator, attributeSeparator, wrap)
|
||||
}
|
||||
|
||||
/** @param {IEntity} other */
|
||||
equals(other) {
|
||||
if (other instanceof MirroredEntity) {
|
||||
other = other.getter?.()
|
||||
}
|
||||
return this.getter?.().equals(other)
|
||||
}
|
||||
|
||||
/** @returns {InstanceType<T>} */
|
||||
valueOf(arg) {
|
||||
// @ts-expect-error
|
||||
return this.getter(arg).valueOf()
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.getter().toString()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import IntegerEntity from "./IntegerEntity.js"
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import P from "parsernostrum"
|
||||
import Utility from "../Utility.js"
|
||||
import IntegerEntity from "./IntegerEntity.js"
|
||||
|
||||
export default class NaturalNumberEntity extends IntegerEntity {
|
||||
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
static createGrammar() {
|
||||
return Parsernostrum.numberNatural.map(v => new this(v))
|
||||
get value() {
|
||||
return super.value
|
||||
}
|
||||
set value(value) {
|
||||
value = Math.round(Utility.clamp(value, 0))
|
||||
super.value = value
|
||||
}
|
||||
|
||||
constructor(values = 0) {
|
||||
super(values)
|
||||
this.value = Math.round(Utility.clamp(this.value, 0))
|
||||
/** @returns {P<NaturalNumberEntity>} */
|
||||
static createGrammar() {
|
||||
return P.numberNatural.map(v => new this(v))
|
||||
}
|
||||
}
|
||||
|
||||
27
js/entity/NullEntity.js
Executable file
27
js/entity/NullEntity.js
Executable file
@@ -0,0 +1,27 @@
|
||||
import P from "parsernostrum"
|
||||
import IEntity from "./IEntity.js"
|
||||
|
||||
export default class NullEntity extends IEntity {
|
||||
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
/** @returns {P<NullEntity>} */
|
||||
static createGrammar() {
|
||||
// @ts-expect-error
|
||||
return P.reg(new RegExp(String.raw`\(${P.whitespaceInlineOpt.getParser().regexp.source}\)`))
|
||||
.map(v => new this())
|
||||
.label("NullEntity")
|
||||
}
|
||||
|
||||
serialize(
|
||||
insideString = false,
|
||||
indentation = "",
|
||||
Self = /** @type {typeof IEntity} */(this.constructor)
|
||||
) {
|
||||
let result = "()"
|
||||
if (Self.serialized) {
|
||||
result = `"${result}"`
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
102
js/entity/NumberEntity.js
Executable file
102
js/entity/NumberEntity.js
Executable file
@@ -0,0 +1,102 @@
|
||||
import P from "parsernostrum"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
import Utility from "../Utility.js"
|
||||
|
||||
export default class NumberEntity extends IEntity {
|
||||
|
||||
static numberRegexSource = String.raw`${Grammar.numberRegexSource}(?<=(?:\.(\d*0+))?)`
|
||||
static grammar = this.createGrammar()
|
||||
/** @type {Number} */
|
||||
static precision // Can override this.precision
|
||||
|
||||
#precision
|
||||
get precision() {
|
||||
return /** @type {typeof NumberEntity} */(this.constructor).precision ?? this.#precision
|
||||
}
|
||||
set precision(value) {
|
||||
this.#precision = value
|
||||
}
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @type {Number}
|
||||
*/
|
||||
_value
|
||||
get value() {
|
||||
return this._value
|
||||
}
|
||||
set value(value) {
|
||||
if (value === -0) {
|
||||
value = 0
|
||||
}
|
||||
this._value = value
|
||||
}
|
||||
|
||||
constructor(value = 0, precision = null) {
|
||||
super()
|
||||
this.value = Number(value)
|
||||
if (precision !== null) {
|
||||
this.#precision = Number(precision)
|
||||
}
|
||||
}
|
||||
|
||||
/** @returns {P<NumberEntity>} */
|
||||
static createGrammar() {
|
||||
return P.regArray(
|
||||
new RegExp(`(?<n>${this.numberRegexSource})|(?<posInf>\\+?inf)|(?<negInf>-inf)`)
|
||||
).map(({ 2: precision, groups: { n, posInf, negInf } }) => new this(
|
||||
n ? Number(n) : posInf ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY,
|
||||
precision?.length
|
||||
)
|
||||
).label("NumberEntity")
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {typeof NumberEntity} T
|
||||
* @this {T}
|
||||
* @returns {T}
|
||||
*/
|
||||
static withPrecision(value = 0) {
|
||||
const result = this.asUniqueClass()
|
||||
result.precision = value
|
||||
return result
|
||||
}
|
||||
|
||||
/** @param {Number} num */
|
||||
static printNumber(num) {
|
||||
if (num == Number.POSITIVE_INFINITY) {
|
||||
return "inf"
|
||||
} else if (num == Number.NEGATIVE_INFINITY) {
|
||||
return "-inf"
|
||||
}
|
||||
return Utility.minDecimals(num)
|
||||
}
|
||||
|
||||
serialize(
|
||||
insideString = false,
|
||||
indentation = "",
|
||||
Self = /** @type {typeof NumberEntity} */(this.constructor),
|
||||
) {
|
||||
if (this.value === Number.POSITIVE_INFINITY) {
|
||||
return "+inf"
|
||||
}
|
||||
if (this.value === Number.NEGATIVE_INFINITY) {
|
||||
return "-inf"
|
||||
}
|
||||
const precision = Self.precision ?? this.precision
|
||||
let result = precision !== undefined ? this.value.toFixed(precision) : this.value.toString()
|
||||
if (Self.serialized) {
|
||||
result = `"${result}"`
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
valueOf() {
|
||||
return this.value
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.value.toString()
|
||||
}
|
||||
}
|
||||
@@ -1,345 +1,295 @@
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import P from "parsernostrum"
|
||||
import Configuration from "../Configuration.js"
|
||||
import Utility from "../Utility.js"
|
||||
import nodeColor from "../decoding/nodeColor.js"
|
||||
import nodeIcon from "../decoding/nodeIcon.js"
|
||||
import nodeVariadic from "../decoding/nodeVariadic.js"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import Utility from "../Utility.js"
|
||||
import AlternativesEntity from "./AlternativesEntity.js"
|
||||
import ArrayEntity from "./ArrayEntity.js"
|
||||
import BooleanEntity from "./BooleanEntity.js"
|
||||
import FunctionReferenceEntity from "./FunctionReferenceEntity.js"
|
||||
import GuidEntity from "./GuidEntity.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
import IdentifierEntity from "./IdentifierEntity.js"
|
||||
import IntegerEntity from "./IntegerEntity.js"
|
||||
import LinearColorEntity from "./LinearColorEntity.js"
|
||||
import MacroGraphReferenceEntity from "./MacroGraphReferenceEntity.js"
|
||||
import MirroredEntity from "./MirroredEntity.js"
|
||||
import NaturalNumberEntity from "./NaturalNumberEntity.js"
|
||||
import NullEntity from "./NullEntity.js"
|
||||
import NumberEntity from "./NumberEntity.js"
|
||||
import ObjectReferenceEntity from "./ObjectReferenceEntity.js"
|
||||
import PinEntity from "./PinEntity.js"
|
||||
import ScriptVariableEntity from "./ScriptVariableEntity.js"
|
||||
import StringEntity from "./StringEntity.js"
|
||||
import SymbolEntity from "./SymbolEntity.js"
|
||||
import Union from "./Union.js"
|
||||
import UnknownPinEntity from "./UnknownPinEntity.js"
|
||||
import VariableReferenceEntity from "./VariableReferenceEntity.js"
|
||||
import nodeVariadic from "../decoding/nodeVariadic.js"
|
||||
|
||||
export default class ObjectEntity extends IEntity {
|
||||
|
||||
#exported = false
|
||||
get exported() {
|
||||
return this.#exported
|
||||
}
|
||||
set exported(value) {
|
||||
this.#exported = value
|
||||
}
|
||||
|
||||
static #nameRegex = /^(\w+?)(?:_(\d+))?$/
|
||||
/** @type {(k: String) => String} */
|
||||
static printKey = k => !k.startsWith(Configuration.subObjectAttributeNamePrefix) ? k : ""
|
||||
static attributeSeparator = "\n"
|
||||
static wrap = this.notWrapped
|
||||
static trailing = true
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
isExported: new AttributeInfo({
|
||||
type: Boolean,
|
||||
ignored: true,
|
||||
}),
|
||||
Class: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
Name: AttributeInfo.createType(String),
|
||||
Archetype: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
ExportPath: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
ObjectRef: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
BlueprintElementType: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
BlueprintElementInstance: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
PinTags: new AttributeInfo({
|
||||
type: [null],
|
||||
inlined: true,
|
||||
}),
|
||||
PinNames: new AttributeInfo({
|
||||
type: [String],
|
||||
inlined: true,
|
||||
}),
|
||||
AxisKey: AttributeInfo.createType(SymbolEntity),
|
||||
InputAxisKey: AttributeInfo.createType(SymbolEntity),
|
||||
InputName: AttributeInfo.createType(String),
|
||||
InputType: AttributeInfo.createType(SymbolEntity),
|
||||
NumAdditionalInputs: AttributeInfo.createType(Number),
|
||||
bIsPureFunc: AttributeInfo.createType(Boolean),
|
||||
bIsConstFunc: AttributeInfo.createType(Boolean),
|
||||
bIsCaseSensitive: AttributeInfo.createType(Boolean),
|
||||
VariableReference: AttributeInfo.createType(VariableReferenceEntity),
|
||||
SelfContextInfo: AttributeInfo.createType(SymbolEntity),
|
||||
DelegatePropertyName: AttributeInfo.createType(String),
|
||||
DelegateOwnerClass: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
ComponentPropertyName: AttributeInfo.createType(String),
|
||||
EventReference: AttributeInfo.createType(FunctionReferenceEntity),
|
||||
FunctionReference: AttributeInfo.createType(FunctionReferenceEntity),
|
||||
FunctionScript: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
CustomFunctionName: AttributeInfo.createType(String),
|
||||
TargetType: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
MacroGraphReference: AttributeInfo.createType(MacroGraphReferenceEntity),
|
||||
Enum: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
EnumEntries: new AttributeInfo({
|
||||
type: [String],
|
||||
inlined: true,
|
||||
}),
|
||||
InputKey: AttributeInfo.createType(SymbolEntity),
|
||||
OpName: AttributeInfo.createType(String),
|
||||
CachedChangeId: AttributeInfo.createType(GuidEntity),
|
||||
FunctionDisplayName: AttributeInfo.createType(String),
|
||||
AddedPins: new AttributeInfo({
|
||||
type: [UnknownPinEntity],
|
||||
default: () => [],
|
||||
inlined: true,
|
||||
silent: true,
|
||||
}),
|
||||
ChangeId: AttributeInfo.createType(GuidEntity),
|
||||
MaterialFunction: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
bOverrideFunction: AttributeInfo.createType(Boolean),
|
||||
bInternalEvent: AttributeInfo.createType(Boolean),
|
||||
bConsumeInput: AttributeInfo.createType(Boolean),
|
||||
bExecuteWhenPaused: AttributeInfo.createType(Boolean),
|
||||
bOverrideParentBinding: AttributeInfo.createType(Boolean),
|
||||
bControl: AttributeInfo.createType(Boolean),
|
||||
bAlt: AttributeInfo.createType(Boolean),
|
||||
bShift: AttributeInfo.createType(Boolean),
|
||||
bCommand: AttributeInfo.createType(Boolean),
|
||||
CommentColor: AttributeInfo.createType(LinearColorEntity),
|
||||
bCommentBubbleVisible_InDetailsPanel: AttributeInfo.createType(Boolean),
|
||||
bColorCommentBubble: AttributeInfo.createType(Boolean),
|
||||
ProxyFactoryFunctionName: AttributeInfo.createType(String),
|
||||
ProxyFactoryClass: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
ProxyClass: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
StructType: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
MaterialExpression: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
MaterialExpressionComment: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
MoveMode: AttributeInfo.createType(SymbolEntity),
|
||||
TimelineName: AttributeInfo.createType(String),
|
||||
TimelineGuid: AttributeInfo.createType(GuidEntity),
|
||||
SizeX: AttributeInfo.createType(new MirroredEntity(IntegerEntity)),
|
||||
SizeY: AttributeInfo.createType(new MirroredEntity(IntegerEntity)),
|
||||
Text: AttributeInfo.createType(new MirroredEntity(String)),
|
||||
MaterialExpressionEditorX: AttributeInfo.createType(new MirroredEntity(IntegerEntity)),
|
||||
MaterialExpressionEditorY: AttributeInfo.createType(new MirroredEntity(IntegerEntity)),
|
||||
NodeTitle: AttributeInfo.createType(String),
|
||||
NodeTitleColor: AttributeInfo.createType(LinearColorEntity),
|
||||
PositionX: AttributeInfo.createType(new MirroredEntity(IntegerEntity)),
|
||||
PositionY: AttributeInfo.createType(new MirroredEntity(IntegerEntity)),
|
||||
SettingsInterface: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
PCGNode: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
HiGenGridSize: AttributeInfo.createType(SymbolEntity),
|
||||
Operation: AttributeInfo.createType(SymbolEntity),
|
||||
NodePosX: AttributeInfo.createType(IntegerEntity),
|
||||
NodePosY: AttributeInfo.createType(IntegerEntity),
|
||||
NodeHeight: AttributeInfo.createType(IntegerEntity),
|
||||
NodeWidth: AttributeInfo.createType(IntegerEntity),
|
||||
Graph: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
SubgraphInstance: AttributeInfo.createType(String),
|
||||
InputPins: new AttributeInfo({
|
||||
type: [ObjectReferenceEntity],
|
||||
inlined: true,
|
||||
}),
|
||||
OutputPins: new AttributeInfo({
|
||||
type: [ObjectReferenceEntity],
|
||||
inlined: true,
|
||||
}),
|
||||
bExposeToLibrary: AttributeInfo.createType(Boolean),
|
||||
bCanRenameNode: AttributeInfo.createType(Boolean),
|
||||
bCommentBubblePinned: AttributeInfo.createType(Boolean),
|
||||
bCommentBubbleVisible: AttributeInfo.createType(Boolean),
|
||||
NodeComment: AttributeInfo.createType(String),
|
||||
AdvancedPinDisplay: AttributeInfo.createType(IdentifierEntity),
|
||||
DelegateReference: AttributeInfo.createType(VariableReferenceEntity),
|
||||
EnabledState: AttributeInfo.createType(IdentifierEntity),
|
||||
NodeGuid: AttributeInfo.createType(GuidEntity),
|
||||
ErrorType: AttributeInfo.createType(IntegerEntity),
|
||||
ErrorMsg: AttributeInfo.createType(String),
|
||||
ScriptVariables: new AttributeInfo({
|
||||
type: [ScriptVariableEntity],
|
||||
inlined: true,
|
||||
}),
|
||||
Node: AttributeInfo.createType(new MirroredEntity(ObjectReferenceEntity)),
|
||||
ExportedNodes: AttributeInfo.createType(String),
|
||||
CustomProperties: AttributeInfo.createType([new Union(PinEntity, UnknownPinEntity)]),
|
||||
Class: ObjectReferenceEntity,
|
||||
Name: StringEntity,
|
||||
Archetype: ObjectReferenceEntity,
|
||||
ExportPath: MirroredEntity.of(ObjectReferenceEntity),
|
||||
ObjectRef: ObjectReferenceEntity,
|
||||
BlueprintElementType: ObjectReferenceEntity,
|
||||
BlueprintElementInstance: ObjectReferenceEntity,
|
||||
ConstA: MirroredEntity.of(NumberEntity),
|
||||
ConstB: MirroredEntity.of(NumberEntity),
|
||||
PinTags: ArrayEntity.of(NullEntity).flagInlined(),
|
||||
PinNames: ArrayEntity.of(StringEntity).flagInlined(),
|
||||
AxisKey: SymbolEntity,
|
||||
InputAxisKey: SymbolEntity,
|
||||
InputName: StringEntity,
|
||||
InputType: SymbolEntity,
|
||||
NumAdditionalInputs: NaturalNumberEntity,
|
||||
bIsPureFunc: BooleanEntity,
|
||||
bIsConstFunc: BooleanEntity,
|
||||
bIsCaseSensitive: BooleanEntity,
|
||||
bDefaultsToPureFunc: BooleanEntity,
|
||||
VariableReference: VariableReferenceEntity,
|
||||
SelfContextInfo: SymbolEntity,
|
||||
DelegatePropertyName: StringEntity,
|
||||
DelegateOwnerClass: ObjectReferenceEntity,
|
||||
ComponentPropertyName: StringEntity,
|
||||
EventReference: FunctionReferenceEntity,
|
||||
FunctionReference: FunctionReferenceEntity,
|
||||
FunctionScript: ObjectReferenceEntity,
|
||||
CustomFunctionName: StringEntity,
|
||||
TargetType: ObjectReferenceEntity,
|
||||
MacroGraphReference: MacroGraphReferenceEntity,
|
||||
Enum: ObjectReferenceEntity,
|
||||
EnumEntries: ArrayEntity.of(StringEntity).flagInlined(),
|
||||
InputKey: SymbolEntity,
|
||||
OpName: StringEntity,
|
||||
CachedChangeId: GuidEntity,
|
||||
FunctionDisplayName: StringEntity,
|
||||
AddedPins: ArrayEntity.of(UnknownPinEntity).withDefault().flagInlined().flagSilent(),
|
||||
ChangeId: GuidEntity,
|
||||
MaterialFunction: ObjectReferenceEntity,
|
||||
bOverrideFunction: BooleanEntity,
|
||||
bInternalEvent: BooleanEntity,
|
||||
bConsumeInput: BooleanEntity,
|
||||
bExecuteWhenPaused: BooleanEntity,
|
||||
bOverrideParentBinding: BooleanEntity,
|
||||
bControl: BooleanEntity,
|
||||
bAlt: BooleanEntity,
|
||||
bShift: BooleanEntity,
|
||||
bCommand: BooleanEntity,
|
||||
CommentColor: LinearColorEntity,
|
||||
bCommentBubbleVisible_InDetailsPanel: BooleanEntity,
|
||||
bColorCommentBubble: BooleanEntity,
|
||||
ProxyFactoryFunctionName: StringEntity,
|
||||
ProxyFactoryClass: ObjectReferenceEntity,
|
||||
ProxyClass: ObjectReferenceEntity,
|
||||
StructType: ObjectReferenceEntity,
|
||||
MaterialExpression: ObjectReferenceEntity,
|
||||
MaterialExpressionComment: ObjectReferenceEntity,
|
||||
MoveMode: SymbolEntity,
|
||||
TimelineName: StringEntity,
|
||||
TimelineGuid: GuidEntity,
|
||||
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),
|
||||
PositionY: MirroredEntity.of(IntegerEntity),
|
||||
SettingsInterface: ObjectReferenceEntity,
|
||||
PCGNode: ObjectReferenceEntity,
|
||||
HiGenGridSize: SymbolEntity,
|
||||
Operation: SymbolEntity,
|
||||
NodePosX: IntegerEntity,
|
||||
NodePosY: IntegerEntity,
|
||||
NodeHeight: IntegerEntity,
|
||||
NodeWidth: IntegerEntity,
|
||||
Graph: ObjectReferenceEntity,
|
||||
SubgraphInstance: StringEntity,
|
||||
InputPins: ArrayEntity.of(ObjectReferenceEntity).flagInlined(),
|
||||
OutputPins: ArrayEntity.of(ObjectReferenceEntity).flagInlined(),
|
||||
bExposeToLibrary: BooleanEntity,
|
||||
bCanRenameNode: BooleanEntity,
|
||||
bCommentBubblePinned: BooleanEntity,
|
||||
bCommentBubbleVisible: BooleanEntity,
|
||||
NodeComment: StringEntity,
|
||||
AdvancedPinDisplay: SymbolEntity,
|
||||
DelegateReference: VariableReferenceEntity,
|
||||
EnabledState: SymbolEntity,
|
||||
NodeGuid: GuidEntity,
|
||||
ErrorType: IntegerEntity,
|
||||
ErrorMsg: StringEntity,
|
||||
ScriptVariables: ArrayEntity.flagInlined().of(ScriptVariableEntity),
|
||||
Node: MirroredEntity.of(ObjectReferenceEntity),
|
||||
ExportedNodes: StringEntity,
|
||||
CustomProperties: ArrayEntity
|
||||
.of(AlternativesEntity.accepting(PinEntity, UnknownPinEntity))
|
||||
.withDefault()
|
||||
.flagSilent(),
|
||||
}
|
||||
static nameRegex = /^(\w+?)(?:_(\d+))?$/
|
||||
static customPropertyGrammar = Parsernostrum.seq(
|
||||
Parsernostrum.reg(/CustomProperties\s+/),
|
||||
Grammar.grammarFor(
|
||||
undefined,
|
||||
this.attributes.CustomProperties.type[0]
|
||||
),
|
||||
static customPropertyGrammar = P.seq(
|
||||
P.reg(/CustomProperties\s+/),
|
||||
this.attributes.CustomProperties.type.grammar,
|
||||
).map(([_0, pin]) => values => {
|
||||
if (!values.CustomProperties) {
|
||||
values.CustomProperties = []
|
||||
}
|
||||
values.CustomProperties.push(pin)
|
||||
/** @type {InstanceType<typeof this.attributes.CustomProperties>} */(
|
||||
values.CustomProperties ??= new (this.attributes.CustomProperties)()
|
||||
).values.push(pin)
|
||||
})
|
||||
static inlinedArrayEntryGrammar = Parsernostrum.seq(
|
||||
Parsernostrum.alt(
|
||||
static inlinedArrayEntryGrammar = P.seq(
|
||||
P.alt(
|
||||
Grammar.symbolQuoted.map(v => [v, true]),
|
||||
Grammar.symbol.map(v => [v, false]),
|
||||
),
|
||||
Parsernostrum.reg(
|
||||
new RegExp(`\\s*\\(\\s*(\\d+)\\s*\\)\\s*\\=\\s*`),
|
||||
1
|
||||
).map(Number)
|
||||
)
|
||||
.chain(
|
||||
/** @param {[[String, Boolean], Number]} param */
|
||||
([[symbol, quoted], index]) =>
|
||||
Grammar.grammarFor(this.attributes[symbol])
|
||||
.map(currentValue =>
|
||||
values => {
|
||||
(values[symbol] ??= [])[index] = currentValue
|
||||
Utility.objectSet(values, ["attributes", symbol, "quoted"], quoted)
|
||||
if (!this.attributes[symbol]?.inlined) {
|
||||
if (!values.attributes) {
|
||||
IEntity.defineAttributes(values, {})
|
||||
}
|
||||
Utility.objectSet(values, ["attributes", symbol, "type"], [currentValue.constructor])
|
||||
Utility.objectSet(values, ["attributes", symbol, "inlined"], true)
|
||||
}
|
||||
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)
|
||||
}
|
||||
)
|
||||
)
|
||||
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 createSubObjectGrammar() {
|
||||
return Parsernostrum.lazy(() => this.grammar)
|
||||
.map(object =>
|
||||
values => values[Configuration.subObjectAttributeNameFromEntity(object)] = object
|
||||
)
|
||||
}
|
||||
|
||||
static createGrammar() {
|
||||
return Parsernostrum.seq(
|
||||
Parsernostrum.reg(/Begin +Object/),
|
||||
Parsernostrum.seq(
|
||||
Parsernostrum.whitespace,
|
||||
Parsernostrum.alt(
|
||||
this.createSubObjectGrammar(),
|
||||
this.customPropertyGrammar,
|
||||
Grammar.createAttributeGrammar(this, Parsernostrum.reg(Grammar.Regex.MultipleWordsSymbols)),
|
||||
Grammar.createAttributeGrammar(this, Grammar.attributeNameQuoted, undefined, (obj, k, v) =>
|
||||
Utility.objectSet(obj, ["attributes", ...k, "quoted"], true)
|
||||
),
|
||||
this.inlinedArrayEntryGrammar,
|
||||
)
|
||||
)
|
||||
.map(([_0, entry]) => entry)
|
||||
.many(),
|
||||
Parsernostrum.reg(/\s+End +Object/),
|
||||
)
|
||||
.map(([_0, attributes, _2]) => {
|
||||
const values = {}
|
||||
attributes.forEach(attributeSetter => attributeSetter(values))
|
||||
return new this(values)
|
||||
})
|
||||
}
|
||||
|
||||
static getMultipleObjectsGrammar() {
|
||||
return Parsernostrum.seq(
|
||||
Parsernostrum.whitespaceOpt,
|
||||
static grammarMultipleObjects = P.seq(
|
||||
P.whitespaceOpt,
|
||||
this.grammar,
|
||||
P.seq(
|
||||
P.whitespace,
|
||||
this.grammar,
|
||||
Parsernostrum.seq(
|
||||
Parsernostrum.whitespace,
|
||||
this.grammar,
|
||||
)
|
||||
.map(([_0, object]) => object)
|
||||
.many(),
|
||||
Parsernostrum.whitespaceOpt
|
||||
)
|
||||
.map(([_0, first, remaining, _4]) => [first, ...remaining])
|
||||
}
|
||||
.map(([_0, object]) => object)
|
||||
.many(),
|
||||
P.whitespaceOpt
|
||||
).map(([_0, first, remaining, _4]) => [first, ...remaining])
|
||||
|
||||
/** @type {String} */
|
||||
#class
|
||||
|
||||
constructor(values = {}, suppressWarns = false) {
|
||||
if ("NodePosX" in values !== "NodePosY" in values) {
|
||||
constructor(values = {}) {
|
||||
if (("NodePosX" in values) !== ("NodePosY" in values)) {
|
||||
const entries = Object.entries(values)
|
||||
const [key, position] = "NodePosX" in values
|
||||
? ["NodePosY", Object.keys(values).indexOf("NodePosX") + 1]
|
||||
: ["NodePosX", Object.keys(values).indexOf("NodePosY")]
|
||||
const entry = [key, new (AttributeInfo.getAttribute(values, key, "type", ObjectEntity))()]
|
||||
entries.splice(position, 0, entry)
|
||||
entries.splice(position, 0, [key, new IntegerEntity(0)])
|
||||
values = Object.fromEntries(entries)
|
||||
}
|
||||
super(values, suppressWarns)
|
||||
|
||||
// Attributes not assigned a strong type in attributes because the names are too generic
|
||||
/** @type {Number | MirroredEntity<Boolean>} */ this.R
|
||||
/** @type {Number | MirroredEntity<Boolean>} */ this.G
|
||||
/** @type {Number | MirroredEntity<Boolean>} */ this.B
|
||||
/** @type {Number | MirroredEntity<Boolean>} */ this.A
|
||||
super(values)
|
||||
|
||||
// Attributes
|
||||
/** @type {(PinEntity | UnknownPinEntity)[]} */ this.CustomProperties
|
||||
/** @type {Boolean} */ this.bIsPureFunc
|
||||
/** @type {Boolean} */ this.isExported
|
||||
/** @type {FunctionReferenceEntity} */ this.ComponentPropertyName
|
||||
/** @type {FunctionReferenceEntity} */ this.EventReference
|
||||
/** @type {FunctionReferenceEntity} */ this.FunctionReference
|
||||
/** @type {IdentifierEntity} */ this.AdvancedPinDisplay
|
||||
/** @type {IdentifierEntity} */ this.EnabledState
|
||||
/** @type {IntegerEntity} */ this.NodeHeight
|
||||
/** @type {IntegerEntity} */ this.NodePosX
|
||||
/** @type {IntegerEntity} */ this.NodePosY
|
||||
/** @type {IntegerEntity} */ this.NodeWidth
|
||||
/** @type {LinearColorEntity} */ this.CommentColor
|
||||
/** @type {LinearColorEntity} */ this.NodeTitleColor
|
||||
/** @type {MacroGraphReferenceEntity} */ this.MacroGraphReference
|
||||
/** @type {MirroredEntity} */ this.MaterialExpressionEditorX
|
||||
/** @type {MirroredEntity} */ this.MaterialExpressionEditorY
|
||||
/** @type {MirroredEntity} */ this.SizeX
|
||||
/** @type {MirroredEntity} */ this.SizeY
|
||||
/** @type {MirroredEntity} */ this.Text
|
||||
/** @type {MirroredEntity<IntegerEntity>} */ this.PositionX
|
||||
/** @type {MirroredEntity<IntegerEntity>} */ this.PositionY
|
||||
/** @type {MirroredEntity<ObjectReferenceEntity>} */ this.Node
|
||||
/** @type {null[]} */ this.PinTags
|
||||
/** @type {Number} */ this.NumAdditionalInputs
|
||||
/** @type {ObjectReferenceEntity[]} */ this.InputPins
|
||||
/** @type {ObjectReferenceEntity[]} */ this.OutputPins
|
||||
/** @type {ObjectReferenceEntity} */ this.Archetype
|
||||
/** @type {ObjectReferenceEntity} */ this.BlueprintElementInstance
|
||||
/** @type {ObjectReferenceEntity} */ this.BlueprintElementType
|
||||
/** @type {ObjectReferenceEntity} */ this.Class
|
||||
/** @type {ObjectReferenceEntity} */ this.Enum
|
||||
/** @type {ObjectReferenceEntity} */ this.ExportPath
|
||||
/** @type {ObjectReferenceEntity} */ this.FunctionScript
|
||||
/** @type {ObjectReferenceEntity} */ this.Graph
|
||||
/** @type {ObjectReferenceEntity} */ this.MaterialExpression
|
||||
/** @type {ObjectReferenceEntity} */ this.MaterialExpressionComment
|
||||
/** @type {ObjectReferenceEntity} */ this.MaterialFunction
|
||||
/** @type {ObjectReferenceEntity} */ this.ObjectRef
|
||||
/** @type {ObjectReferenceEntity} */ this.PCGNode
|
||||
/** @type {ObjectReferenceEntity} */ this.SettingsInterface
|
||||
/** @type {ObjectReferenceEntity} */ this.StructType
|
||||
/** @type {ObjectReferenceEntity} */ this.TargetType
|
||||
/** @type {ScriptVariableEntity[]} */ this.ScriptVariables
|
||||
/** @type {String[]} */ this.EnumEntries
|
||||
/** @type {String[]} */ this.PinNames
|
||||
/** @type {String} */ this.CustomFunctionName
|
||||
/** @type {String} */ this.DelegatePropertyName
|
||||
/** @type {String} */ this.ExportedNodes
|
||||
/** @type {String} */ this.FunctionDisplayName
|
||||
/** @type {String} */ this.InputName
|
||||
/** @type {String} */ this.Name
|
||||
/** @type {String} */ this.NodeComment
|
||||
/** @type {String} */ this.NodeTitle
|
||||
/** @type {String} */ this.Operation
|
||||
/** @type {String} */ this.OpName
|
||||
/** @type {String} */ this.ProxyFactoryFunctionName
|
||||
/** @type {String} */ this.SubgraphInstance
|
||||
/** @type {String} */ this.Text
|
||||
/** @type {SymbolEntity} */ this.AxisKey
|
||||
/** @type {SymbolEntity} */ this.HiGenGridSize
|
||||
/** @type {SymbolEntity} */ this.InputAxisKey
|
||||
/** @type {SymbolEntity} */ this.InputKey
|
||||
/** @type {SymbolEntity} */ this.InputType
|
||||
/** @type {UnknownPinEntity[]} */ this.AddedPins
|
||||
/** @type {VariableReferenceEntity} */ this.DelegateReference
|
||||
/** @type {VariableReferenceEntity} */ this.VariableReference
|
||||
/** @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.bDefaultsToPureFunc>} */ this.bDefaultsToPureFunc
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.BlueprintElementInstance>} */ this.BlueprintElementInstance
|
||||
/** @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 {InstanceType<typeof ObjectEntity.attributes.DelegatePropertyName>} */ this.DelegatePropertyName
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.DelegateReference>} */ this.DelegateReference
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.EnabledState>} */ this.EnabledState
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.Enum>} */ this.Enum
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.EnumEntries>} */ this.EnumEntries
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.EventReference>} */ this.EventReference
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.ExportedNodes>} */ this.ExportedNodes
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.ExportPath>} */ this.ExportPath
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.FunctionDisplayName>} */ this.FunctionDisplayName
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.FunctionReference>} */ this.FunctionReference
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.FunctionScript>} */ this.FunctionScript
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.Graph>} */ this.Graph
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.HiGenGridSize>} */ this.HiGenGridSize
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.InputAxisKey>} */ this.InputAxisKey
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.InputKey>} */ this.InputKey
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.InputName>} */ this.InputName
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.InputPins>} */ this.InputPins
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.InputType>} */ this.InputType
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.MacroGraphReference>} */ this.MacroGraphReference
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.MaterialExpression>} */ this.MaterialExpression
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.MaterialExpressionComment>} */ this.MaterialExpressionComment
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.MaterialExpressionEditorX>} */ this.MaterialExpressionEditorX
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.MaterialExpressionEditorY>} */ this.MaterialExpressionEditorY
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.MaterialFunction>} */ this.MaterialFunction
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.Name>} */ this.Name
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.Node>} */ this.Node
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.NodeComment>} */ this.NodeComment
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.NodeHeight>} */ this.NodeHeight
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.NodePosX>} */ this.NodePosX
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.NodePosY>} */ this.NodePosY
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.NodeTitle>} */ this.NodeTitle
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.NodeTitleColor>} */ this.NodeTitleColor
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.NodeWidth>} */ this.NodeWidth
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.NumAdditionalInputs>} */ this.NumAdditionalInputs
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.ObjectRef>} */ this.ObjectRef
|
||||
/** @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.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
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.ScriptVariables>} */ this.ScriptVariables
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.SettingsInterface>} */ this.SettingsInterface
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.SizeX>} */ this.SizeX
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.SizeY>} */ this.SizeY
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.StructType>} */ this.StructType
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.SubgraphInstance>} */ this.SubgraphInstance
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.TargetType>} */ this.TargetType
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.Text>} */ this.Text
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.Text>} */ this.Text
|
||||
/** @type {InstanceType<typeof ObjectEntity.attributes.VariableReference>} */ this.VariableReference
|
||||
|
||||
// Legacy nodes pins
|
||||
if (this["Pins"] instanceof Array) {
|
||||
this["Pins"].forEach(
|
||||
if (this["Pins"] instanceof ArrayEntity) {
|
||||
this["Pins"].valueOf().forEach(
|
||||
/** @param {ObjectReferenceEntity} objectReference */
|
||||
objectReference => {
|
||||
const pinObject = this[Configuration.subObjectAttributeNameFromReference(objectReference, true)]
|
||||
if (pinObject) {
|
||||
const pinEntity = PinEntity.fromLegacyObject(pinObject)
|
||||
pinEntity.LinkedTo = []
|
||||
pinEntity.LinkedTo = new (PinEntity.attributes.LinkedTo)()
|
||||
this.getCustomproperties(true).push(pinEntity)
|
||||
Utility.objectSet(this, ["attributes", "CustomProperties", "ignored"], true)
|
||||
this.CustomProperties.ignored = true
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -354,24 +304,37 @@ export default class ObjectEntity extends IEntity {
|
||||
obj.MaterialExpressionEditorX && (obj.MaterialExpressionEditorX.getter = () => this.NodePosX)
|
||||
obj.MaterialExpressionEditorY && (obj.MaterialExpressionEditorY.getter = () => this.NodePosY)
|
||||
if (this.getType() === Configuration.paths.materialExpressionComponentMask) {
|
||||
// The following attributes are too generic therefore not assigned a MirroredEntity
|
||||
const rgbaPins = Configuration.rgba.map(pinName =>
|
||||
this.getPinEntities().find(pin => pin.PinName === pinName && (pin.recomputesNodeTitleOnChange = true))
|
||||
)
|
||||
const attribute = {}
|
||||
obj.R = new MirroredEntity(Boolean, () => rgbaPins[0].DefaultValue)
|
||||
obj.G = new MirroredEntity(Boolean, () => rgbaPins[1].DefaultValue)
|
||||
obj.B = new MirroredEntity(Boolean, () => rgbaPins[2].DefaultValue)
|
||||
obj.A = new MirroredEntity(Boolean, () => rgbaPins[3].DefaultValue)
|
||||
Utility.objectSet(obj, ["attributes", "R", "default"], false)
|
||||
Utility.objectSet(obj, ["attributes", "R", "silent"], true)
|
||||
Utility.objectSet(obj, ["attributes", "G", "default"], false)
|
||||
Utility.objectSet(obj, ["attributes", "G", "silent"], true)
|
||||
Utility.objectSet(obj, ["attributes", "B", "default"], false)
|
||||
Utility.objectSet(obj, ["attributes", "B", "silent"], true)
|
||||
Utility.objectSet(obj, ["attributes", "A", "default"], false)
|
||||
Utility.objectSet(obj, ["attributes", "A", "silent"], true)
|
||||
obj._keys = [...Configuration.rgba, ...Object.keys(obj).filter(k => !Configuration.rgba.includes(k))]
|
||||
const rgbaPins = Configuration.rgba.map(pinName => {
|
||||
const result = this.getPinEntities().find(pin => pin.PinName.toString() === pinName)
|
||||
result.recomputesNodeTitleOnChange = true
|
||||
return result
|
||||
})
|
||||
// Reorder keys so that the added ones stay first
|
||||
obj.keys = [...Configuration.rgba, ...obj.keys]
|
||||
const silentBool = MirroredEntity.of(BooleanEntity).withDefault().flagSilent()
|
||||
obj["R"] = new silentBool(() => rgbaPins[0].DefaultValue)
|
||||
obj["G"] = new silentBool(() => rgbaPins[1].DefaultValue)
|
||||
obj["B"] = new silentBool(() => rgbaPins[2].DefaultValue)
|
||||
obj["A"] = new silentBool(() => rgbaPins[3].DefaultValue)
|
||||
} else if (this.getType() === Configuration.paths.materialExpressionSubtract) {
|
||||
const silentNumber = MirroredEntity
|
||||
.of(NumberEntity.withPrecision(6))
|
||||
.withDefault(() => new MirroredEntity(() => new NumberEntity(1)))
|
||||
.flagSilent()
|
||||
const pinA = this.getCustomproperties().find(pin => pin.PinName?.toString() === "A")
|
||||
const pinB = this.getCustomproperties().find(pin => pin.PinName?.toString() === "B")
|
||||
if (pinA || pinB) {
|
||||
// Reorder keys so that the added ones stay first
|
||||
obj.keys = ["ConstA", "ConstB", ...obj.keys]
|
||||
if (pinA) {
|
||||
pinA.recomputesNodeTitleOnChange = true
|
||||
obj.ConstA = new silentNumber(() => pinA.DefaultValue)
|
||||
}
|
||||
if (pinB) {
|
||||
pinB.recomputesNodeTitleOnChange = true
|
||||
obj.ConstB = new silentNumber(() => pinB.DefaultValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/** @type {ObjectEntity} */
|
||||
@@ -383,15 +346,16 @@ export default class ObjectEntity extends IEntity {
|
||||
/** @param {ObjectEntity} obj */
|
||||
obj => {
|
||||
if (obj.Node !== undefined) {
|
||||
const nodeRef = obj.Node.get()
|
||||
const nodeRef = obj.Node.getter()
|
||||
if (
|
||||
nodeRef.type === this.PCGNode.type
|
||||
&& nodeRef.path === `${this.Name}.${this.PCGNode.path}`
|
||||
) {
|
||||
obj.Node.getter = () => new ObjectReferenceEntity({
|
||||
type: this.PCGNode.type,
|
||||
path: `${this.Name}.${this.PCGNode.path}`,
|
||||
})
|
||||
obj.Node.getter = () => new ObjectReferenceEntity(
|
||||
this.PCGNode.type,
|
||||
`${this.Name}.${this.PCGNode.path}`,
|
||||
nodeRef.full,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -400,7 +364,7 @@ export default class ObjectEntity extends IEntity {
|
||||
}
|
||||
let inputIndex = 0
|
||||
let outputIndex = 0
|
||||
this.CustomProperties?.forEach((pinEntity, i) => {
|
||||
this.getCustomproperties().forEach((pinEntity, i) => {
|
||||
pinEntity.objectEntity = this
|
||||
pinEntity.pinIndex = pinEntity.isInput()
|
||||
? inputIndex++
|
||||
@@ -408,12 +372,92 @@ export default class ObjectEntity extends IEntity {
|
||||
? outputIndex++
|
||||
: i
|
||||
})
|
||||
this.mirrorNameInExportPaths()
|
||||
}
|
||||
|
||||
/** @returns {P<ObjectEntity>} */
|
||||
static createGrammar() {
|
||||
return P.seq(
|
||||
P.reg(/Begin +Object/),
|
||||
P.seq(
|
||||
P.whitespace,
|
||||
P.alt(
|
||||
this.createSubObjectGrammar(),
|
||||
this.customPropertyGrammar,
|
||||
Grammar.createAttributeGrammar(this, P.reg(Grammar.Regex.MultipleWordsSymbols)),
|
||||
Grammar.createAttributeGrammar(
|
||||
this,
|
||||
Grammar.attributeNameQuoted,
|
||||
undefined,
|
||||
(values, attributeKey, attributeValue) => {
|
||||
Utility.objectSet(values, [...attributeKey, "quoted"], true)
|
||||
},
|
||||
),
|
||||
this.inlinedArrayEntryGrammar,
|
||||
)
|
||||
)
|
||||
.map(([_0, entry]) => entry)
|
||||
.many(),
|
||||
P.reg(/\s+End +Object/),
|
||||
)
|
||||
.map(([_0, attributes, _2]) => {
|
||||
const values = {}
|
||||
attributes.forEach(attributeSetter => attributeSetter(values))
|
||||
return new this(values)
|
||||
})
|
||||
.label("ObjectEntity")
|
||||
}
|
||||
|
||||
static createSubObjectGrammar() {
|
||||
return P.lazy(() => this.grammar)
|
||||
.map(object =>
|
||||
values => {
|
||||
object.trailing = false
|
||||
values[Configuration.subObjectAttributeNameFromEntity(object)] = object
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* Mirror then name part of the objects contained in this one in ExportPath
|
||||
*/
|
||||
mirrorNameInExportPaths(originalName = this.Name?.toString()) {
|
||||
if (!originalName) {
|
||||
return
|
||||
}
|
||||
const values = [this]
|
||||
for (let i = 0; i < values.length; ++i) {
|
||||
const value = values[i]
|
||||
if (value instanceof ObjectEntity) {
|
||||
values.push(...Object.values(value))
|
||||
if (!value.ExportPath?.valueOf().path.includes(originalName)) {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
const mirroredEntity = /** @type {typeof ObjectEntity} */(value.constructor).attributes.ExportPath
|
||||
let originalExportPath = value.ExportPath
|
||||
value.ExportPath = new mirroredEntity(
|
||||
() => {
|
||||
const exportPath = originalExportPath.valueOf()
|
||||
return new (mirroredEntity.type)(
|
||||
exportPath.type,
|
||||
exportPath.path.replace(originalName, this.Name?.toString() ?? ""),
|
||||
exportPath.full
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {String} */
|
||||
#class
|
||||
getClass() {
|
||||
if (!this.#class) {
|
||||
this.#class = (this.Class?.path ? this.Class.path : this.Class?.type)
|
||||
?? (this.ExportPath?.path ? this.ExportPath.path : 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
|
||||
@@ -427,26 +471,26 @@ 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) {
|
||||
if (dropCounter) {
|
||||
return this.getNameAndCounter()[0]
|
||||
}
|
||||
return this.Name
|
||||
return this.Name.toString()
|
||||
}
|
||||
|
||||
/** @returns {[String, Number]} */
|
||||
getNameAndCounter() {
|
||||
const result = this.getObjectName().match(ObjectEntity.nameRegex)
|
||||
const result = this.getObjectName().match(ObjectEntity.#nameRegex)
|
||||
let name = ""
|
||||
let counter = null
|
||||
return result
|
||||
@@ -509,10 +553,7 @@ export default class ObjectEntity extends IEntity {
|
||||
}
|
||||
|
||||
getCustomproperties(canCreate = false) {
|
||||
if (canCreate && !this.CustomProperties) {
|
||||
this.CustomProperties = []
|
||||
}
|
||||
return this.CustomProperties ?? []
|
||||
return this.CustomProperties.values
|
||||
}
|
||||
|
||||
/** @returns {PinEntity[]} */
|
||||
@@ -557,24 +598,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} */
|
||||
@@ -587,7 +614,7 @@ export default class ObjectEntity extends IEntity {
|
||||
|
||||
isPcg() {
|
||||
return this.getClass() === Configuration.paths.pcgEditorGraphNode
|
||||
|| this.getPcgSubobject()
|
||||
|| this.getPcgSubobject() != null
|
||||
}
|
||||
|
||||
isNiagara() {
|
||||
@@ -629,7 +656,7 @@ export default class ObjectEntity extends IEntity {
|
||||
}
|
||||
|
||||
getDelegatePin() {
|
||||
return this.getCustomproperties().find(pin => pin.PinType.PinCategory === "delegate")
|
||||
return this.getCustomproperties().find(pin => pin.PinType.PinCategory.toString() === "delegate")
|
||||
}
|
||||
|
||||
nodeColor() {
|
||||
@@ -643,4 +670,77 @@ export default class ObjectEntity extends IEntity {
|
||||
additionalPinInserter() {
|
||||
return nodeVariadic(this)
|
||||
}
|
||||
|
||||
/** @param {String} key */
|
||||
showProperty(key) {
|
||||
switch (key) {
|
||||
case "Class":
|
||||
case "Name":
|
||||
case "Archetype":
|
||||
case "ExportPath":
|
||||
case "CustomProperties":
|
||||
// Serielized separately, check doWrite()
|
||||
return false
|
||||
}
|
||||
return super.showProperty(key)
|
||||
}
|
||||
|
||||
/** @param {typeof ObjectEntity} Self */
|
||||
doSerialize(
|
||||
insideString = false,
|
||||
indentation = "",
|
||||
Self = /** @type {typeof ObjectEntity} */(this.constructor),
|
||||
printKey = Self.printKey,
|
||||
keySeparator = Self.keySeparator,
|
||||
attributeSeparator = Self.attributeSeparator,
|
||||
wrap = Self.wrap,
|
||||
) {
|
||||
const isSelfOverriden = Self !== this.constructor
|
||||
const deeperIndentation = indentation + Configuration.indentation
|
||||
const initial_trailing = this.trailing
|
||||
this.trailing = true
|
||||
const content = super.doSerialize(insideString, deeperIndentation, Self, printKey, keySeparator, attributeSeparator, wrap)
|
||||
this.trailing = initial_trailing
|
||||
let result = indentation + "Begin Object"
|
||||
+ ((this.Class?.type || this.Class?.path)
|
||||
// && Self.attributes.Class.ignored !== true
|
||||
// && this.Class.ignored !== true
|
||||
? ` Class${keySeparator}${this.Class.serialize(insideString)}`
|
||||
: ""
|
||||
)
|
||||
+ (this.Name
|
||||
// && Self.attributes.Name.ignored !== true
|
||||
// && this.Name.ignored !== true
|
||||
? ` Name${keySeparator}${this.Name.serialize(insideString)}`
|
||||
: ""
|
||||
)
|
||||
+ (this.Archetype
|
||||
// && Self.attributes.Archetype.ignored !== true
|
||||
// && this.Archetype.ignored !== true
|
||||
? ` Archetype${keySeparator}${this.Archetype.serialize(insideString)}`
|
||||
: ""
|
||||
)
|
||||
+ ((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)}`
|
||||
: ""
|
||||
)
|
||||
+ attributeSeparator
|
||||
+ content
|
||||
+ (Self.attributes.CustomProperties.ignored !== true && this.CustomProperties.ignored !== true
|
||||
? this.getCustomproperties()
|
||||
.map(pin =>
|
||||
deeperIndentation
|
||||
+ printKey("CustomProperties ")
|
||||
+ pin.serialize(insideString)
|
||||
+ attributeSeparator
|
||||
)
|
||||
.join("")
|
||||
: ""
|
||||
)
|
||||
+ indentation + "End Object"
|
||||
+ (isSelfOverriden && Self.trailing || this.trailing ? attributeSeparator : "")
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,80 +1,135 @@
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import P from "parsernostrum"
|
||||
import Utility from "../Utility.js"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
|
||||
export default class ObjectReferenceEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
type: new AttributeInfo({
|
||||
default: "",
|
||||
serialized: true,
|
||||
}),
|
||||
path: new AttributeInfo({
|
||||
default: "",
|
||||
serialized: true,
|
||||
}),
|
||||
_full: new AttributeInfo({
|
||||
ignored: true,
|
||||
}),
|
||||
}
|
||||
static quoted = Parsernostrum.regArray(new RegExp(
|
||||
`'"(${Grammar.Regex.InsideString.source})"'`
|
||||
+ "|"
|
||||
+ `'(${Grammar.Regex.InsideSingleQuotedString.source})'`
|
||||
)).map(([_0, a, b]) => a ?? b)
|
||||
static path = this.quoted.getParser().parser.regexp.source + "|" + Grammar.Regex.Path.source
|
||||
static typeReference = Parsernostrum.reg(
|
||||
static typeReference = P.reg(
|
||||
// @ts-expect-error
|
||||
new RegExp(Grammar.Regex.Path.source + "|" + Grammar.symbol.getParser().regexp.source)
|
||||
)
|
||||
static fullReferenceGrammar = Parsernostrum.regArray(
|
||||
new RegExp(
|
||||
"(" + this.typeReference.getParser().regexp.source + ")"
|
||||
+ "(?:" + this.quoted.getParser().parser.regexp.source + ")"
|
||||
)
|
||||
).map(([_full, type, ...path]) => new this({ type, path: path.find(v => v), _full }))
|
||||
static fullReferenceSerializedGrammar = Parsernostrum.regArray(
|
||||
new RegExp(
|
||||
'"(' + Grammar.Regex.InsideString.source + "?)"
|
||||
+ "(?:'(" + Grammar.Regex.InsideSingleQuotedString.source + `?)')?"`
|
||||
)
|
||||
).map(([_full, type, path]) => new this({ type, path, _full }))
|
||||
static typeReferenceGrammar = this.typeReference.map(v => new this({ type: v, path: "", _full: v }))
|
||||
static fullReferenceGrammar = this.createFullReferenceGrammar()
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
constructor(values = {}) {
|
||||
if (values.constructor === String) {
|
||||
values = {
|
||||
path: values
|
||||
}
|
||||
}
|
||||
super(values)
|
||||
if (!values._full || values._full.length === 0) {
|
||||
this._full = `"${this.type + (this.path ? (`'${this.path}'`) : "")}"`
|
||||
}
|
||||
/** @type {String} */ this.type
|
||||
/** @type {String} */ this.path
|
||||
#type
|
||||
get type() {
|
||||
return this.#type
|
||||
}
|
||||
set type(value) {
|
||||
this.#type = value
|
||||
}
|
||||
|
||||
#path
|
||||
get path() {
|
||||
return this.#path
|
||||
}
|
||||
set path(value) {
|
||||
this.#name = ""
|
||||
this.#path = value
|
||||
}
|
||||
|
||||
#serializer
|
||||
get full() {
|
||||
return this.#serializer
|
||||
}
|
||||
set full(value) {
|
||||
this.#serializer = value
|
||||
}
|
||||
|
||||
#name = ""
|
||||
|
||||
/** @param {(t: String, p: String) => String} serializer */
|
||||
constructor(
|
||||
type = "None",
|
||||
path = "",
|
||||
serializer = type.includes("/") || path
|
||||
? (t, p) => `"${t + (p ? (`'${p}'`) : "")}"`
|
||||
: (t, p) => t) {
|
||||
super()
|
||||
this.#type = type
|
||||
this.#path = path
|
||||
this.#serializer = serializer
|
||||
}
|
||||
|
||||
/** @returns {P<ObjectReferenceEntity>} */
|
||||
static createGrammar() {
|
||||
return Parsernostrum.alt(
|
||||
this.fullReferenceSerializedGrammar,
|
||||
this.fullReferenceGrammar,
|
||||
this.typeReferenceGrammar,
|
||||
)
|
||||
return P.alt(
|
||||
this.createFullReferenceSerializedGrammar(),
|
||||
this.createFullReferenceGrammar(),
|
||||
this.createTypeReferenceGrammar(),
|
||||
).label("ObjectReferenceEntity")
|
||||
}
|
||||
|
||||
/** @returns {P<ObjectReferenceEntity>} */
|
||||
static createFullReferenceGrammar() {
|
||||
return P.regArray(
|
||||
new RegExp(
|
||||
// @ts-expect-error
|
||||
"(" + this.typeReference.getParser().regexp.source + ")"
|
||||
+ "(?:"
|
||||
+ `'"(${Grammar.Regex.InsideString.source})"'`
|
||||
+ "|"
|
||||
+ `'(${Grammar.Regex.InsideSingleQuotedString.source})'`
|
||||
+ ")"
|
||||
)
|
||||
).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>} */
|
||||
static createFullReferenceSerializedGrammar() {
|
||||
return P.regArray(
|
||||
new RegExp(
|
||||
'"(' + Grammar.Regex.InsideString.source + "?)"
|
||||
+ "(?:'(" + Grammar.Regex.InsideSingleQuotedString.source + `?)')?"`
|
||||
)
|
||||
).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, "", (t, p) => t))
|
||||
}
|
||||
|
||||
static createNoneInstance() {
|
||||
return new ObjectReferenceEntity({ type: "None", path: "" })
|
||||
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) {
|
||||
result = Utility.escapeString(result, false)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/** @param {IEntity} other */
|
||||
equals(other) {
|
||||
if (!(other instanceof ObjectReferenceEntity)) {
|
||||
return false
|
||||
}
|
||||
return this.type == other.type && this.path == other.path
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this._full
|
||||
return this.full(this.type, this.path)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
|
||||
export default class PathSymbolEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
value: new AttributeInfo({
|
||||
default: "",
|
||||
}),
|
||||
}
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
static createGrammar() {
|
||||
return Grammar.symbol.map(v => new this(v))
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
if (values.constructor !== Object) {
|
||||
values = {
|
||||
value: values,
|
||||
}
|
||||
}
|
||||
super(values)
|
||||
/** @type {String} */ this.value
|
||||
}
|
||||
|
||||
valueOf() {
|
||||
return this.value
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.value
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
import P from "parsernostrum"
|
||||
import Configuration from "../Configuration.js"
|
||||
import pinColor from "../decoding/pinColor.js"
|
||||
import pinTitle from "../decoding/pinTitle.js"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import AlternativesEntity from "./AlternativesEntity.js"
|
||||
import ArrayEntity from "./ArrayEntity.js"
|
||||
import BooleanEntity from "./BooleanEntity.js"
|
||||
import ByteEntity from "./ByteEntity.js"
|
||||
import ComputedType from "./ComputedType.js"
|
||||
import ComputedTypeEntity from "./ComputedTypeEntity.js"
|
||||
import EnumDisplayValueEntity from "./EnumDisplayValueEntity.js"
|
||||
import EnumEntity from "./EnumEntity.js"
|
||||
import FormatTextEntity from "./FormatTextEntity.js"
|
||||
@@ -15,6 +18,7 @@ import IntegerEntity from "./IntegerEntity.js"
|
||||
import InvariantTextEntity from "./InvariantTextEntity.js"
|
||||
import LinearColorEntity from "./LinearColorEntity.js"
|
||||
import LocalizedTextEntity from "./LocalizedTextEntity.js"
|
||||
import NumberEntity from "./NumberEntity.js"
|
||||
import ObjectReferenceEntity from "./ObjectReferenceEntity.js"
|
||||
import PinReferenceEntity from "./PinReferenceEntity.js"
|
||||
import PinTypeEntity from "./PinTypeEntity.js"
|
||||
@@ -24,84 +28,77 @@ import SimpleSerializationRotatorEntity from "./SimpleSerializationRotatorEntity
|
||||
import SimpleSerializationVector2DEntity from "./SimpleSerializationVector2DEntity.js"
|
||||
import SimpleSerializationVector4DEntity from "./SimpleSerializationVector4DEntity.js"
|
||||
import SimpleSerializationVectorEntity from "./SimpleSerializationVectorEntity.js"
|
||||
import Union from "./Union.js"
|
||||
import StringEntity from "./StringEntity.js"
|
||||
import SymbolEntity from "./SymbolEntity.js"
|
||||
import Vector2DEntity from "./Vector2DEntity.js"
|
||||
import Vector4DEntity from "./Vector4DEntity.js"
|
||||
import VectorEntity from "./VectorEntity.js"
|
||||
|
||||
/** @template {TerminalAttribute} T */
|
||||
const paths = Configuration.paths
|
||||
|
||||
/** @template {IEntity} T */
|
||||
export default class PinEntity extends IEntity {
|
||||
|
||||
static lookbehind = "Pin"
|
||||
static #typeEntityMap = {
|
||||
[Configuration.paths.linearColor]: LinearColorEntity,
|
||||
[Configuration.paths.rotator]: RotatorEntity,
|
||||
[Configuration.paths.vector]: VectorEntity,
|
||||
[Configuration.paths.vector2D]: Vector2DEntity,
|
||||
[Configuration.paths.vector4f]: Vector4DEntity,
|
||||
"bool": Boolean,
|
||||
"bool": BooleanEntity,
|
||||
"byte": ByteEntity,
|
||||
"enum": EnumEntity,
|
||||
"exec": String,
|
||||
"exec": StringEntity,
|
||||
"float": NumberEntity,
|
||||
"int": IntegerEntity,
|
||||
"int64": Integer64Entity,
|
||||
"name": String,
|
||||
"real": Number,
|
||||
"string": String,
|
||||
"name": StringEntity,
|
||||
"real": NumberEntity,
|
||||
"string": StringEntity,
|
||||
[paths.linearColor]: LinearColorEntity,
|
||||
[paths.niagaraBool]: BooleanEntity,
|
||||
[paths.niagaraFloat]: NumberEntity,
|
||||
[paths.niagaraPosition]: VectorEntity,
|
||||
[paths.rotator]: RotatorEntity,
|
||||
[paths.vector]: VectorEntity,
|
||||
[paths.vector2D]: Vector2DEntity,
|
||||
[paths.vector4f]: Vector4DEntity,
|
||||
}
|
||||
static #alternativeTypeEntityMap = {
|
||||
"enum": EnumDisplayValueEntity,
|
||||
"rg": RBSerializationVector2DEntity,
|
||||
[Configuration.paths.rotator]: SimpleSerializationRotatorEntity,
|
||||
[Configuration.paths.vector]: SimpleSerializationVectorEntity,
|
||||
[Configuration.paths.vector2D]: SimpleSerializationVector2DEntity,
|
||||
[Configuration.paths.vector3f]: SimpleSerializationVectorEntity,
|
||||
[Configuration.paths.vector4f]: SimpleSerializationVector4DEntity,
|
||||
[paths.niagaraPosition]: SimpleSerializationVectorEntity.flagAllowShortSerialization(),
|
||||
[paths.rotator]: SimpleSerializationRotatorEntity,
|
||||
[paths.vector]: SimpleSerializationVectorEntity,
|
||||
[paths.vector2D]: SimpleSerializationVector2DEntity,
|
||||
[paths.vector3f]: SimpleSerializationVectorEntity,
|
||||
[paths.vector4f]: SimpleSerializationVector4DEntity,
|
||||
}
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
lookbehind: new AttributeInfo({
|
||||
default: "Pin",
|
||||
ignored: true,
|
||||
}),
|
||||
objectEntity: new AttributeInfo({
|
||||
ignored: true,
|
||||
}),
|
||||
pinIndex: new AttributeInfo({
|
||||
type: Number,
|
||||
ignored: true,
|
||||
}),
|
||||
PinId: new AttributeInfo({
|
||||
type: GuidEntity,
|
||||
default: () => new GuidEntity()
|
||||
}),
|
||||
PinName: AttributeInfo.createValue(""),
|
||||
PinFriendlyName: AttributeInfo.createType(new Union(LocalizedTextEntity, FormatTextEntity, InvariantTextEntity, String)),
|
||||
PinToolTip: AttributeInfo.createType(String),
|
||||
Direction: AttributeInfo.createType(String),
|
||||
PinType: new AttributeInfo({
|
||||
type: PinTypeEntity,
|
||||
default: () => new PinTypeEntity(),
|
||||
inlined: true,
|
||||
}),
|
||||
LinkedTo: AttributeInfo.createType([PinReferenceEntity]),
|
||||
SubPins: AttributeInfo.createType([PinReferenceEntity]),
|
||||
ParentPin: AttributeInfo.createType(PinReferenceEntity),
|
||||
DefaultValue: new AttributeInfo({
|
||||
type: new ComputedType(
|
||||
PinId: GuidEntity.withDefault(),
|
||||
PinName: StringEntity.withDefault(),
|
||||
PinFriendlyName: AlternativesEntity.accepting(
|
||||
LocalizedTextEntity,
|
||||
FormatTextEntity,
|
||||
InvariantTextEntity,
|
||||
StringEntity
|
||||
),
|
||||
PinToolTip: StringEntity,
|
||||
Direction: StringEntity,
|
||||
PinType: PinTypeEntity.withDefault().flagInlined(),
|
||||
LinkedTo: ArrayEntity.of(PinReferenceEntity).withDefault().flagSilent(),
|
||||
SubPins: ArrayEntity.of(PinReferenceEntity),
|
||||
ParentPin: PinReferenceEntity,
|
||||
DefaultValue:
|
||||
ComputedTypeEntity.from(
|
||||
/** @param {PinEntity} pinEntity */
|
||||
pinEntity => pinEntity.getEntityType(true) ?? String
|
||||
pinEntity => pinEntity.getEntityType(true)?.flagSerialized() ?? StringEntity
|
||||
),
|
||||
serialized: true,
|
||||
}),
|
||||
AutogeneratedDefaultValue: AttributeInfo.createType(String),
|
||||
DefaultObject: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
PersistentGuid: AttributeInfo.createType(GuidEntity),
|
||||
bHidden: AttributeInfo.createValue(false),
|
||||
bNotConnectable: AttributeInfo.createValue(false),
|
||||
bDefaultValueIsReadOnly: AttributeInfo.createValue(false),
|
||||
bDefaultValueIsIgnored: AttributeInfo.createValue(false),
|
||||
bAdvancedView: AttributeInfo.createValue(false),
|
||||
bOrphanedPin: AttributeInfo.createValue(false),
|
||||
AutogeneratedDefaultValue: StringEntity,
|
||||
DefaultObject: ObjectReferenceEntity,
|
||||
PersistentGuid: GuidEntity,
|
||||
bHidden: BooleanEntity,
|
||||
bNotConnectable: BooleanEntity,
|
||||
bDefaultValueIsReadOnly: BooleanEntity,
|
||||
bDefaultValueIsIgnored: BooleanEntity,
|
||||
bAdvancedView: BooleanEntity,
|
||||
bOrphanedPin: BooleanEntity,
|
||||
}
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
@@ -113,42 +110,76 @@ export default class PinEntity extends IEntity {
|
||||
return this.#recomputesNodeTitleOnChange
|
||||
}
|
||||
|
||||
/** @type {ObjectEntity} */
|
||||
#objectEntity = null
|
||||
get objectEntity() {
|
||||
try {
|
||||
/*
|
||||
* Why inside a try block ?
|
||||
* It is because of this issue: https://stackoverflow.com/questions/61237153/access-private-method-in-an-overriden-method-called-from-the-base-class-construc
|
||||
* super(values) will call IEntity constructor while this instance is not yet fully constructed
|
||||
* IEntity will call computedEntity.compute(this) to initialize DefaultValue from this class
|
||||
* Which in turn calls pinEntity.getEntityType(true)
|
||||
* Which calls this.getType()
|
||||
* Which calls this.objectEntity?.isPcg()
|
||||
* Which would access #objectEntity through get objectEntity()
|
||||
* And this would violate the private access rule (because this class is not yet constructed)
|
||||
* If this issue in the future will be fixed in all the major browsers, please remove this try catch
|
||||
*/
|
||||
return this.#objectEntity
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
set objectEntity(value) {
|
||||
this.#objectEntity = value
|
||||
}
|
||||
|
||||
#pinIndex
|
||||
get pinIndex() {
|
||||
return this.#pinIndex
|
||||
}
|
||||
set pinIndex(value) {
|
||||
this.#pinIndex = value
|
||||
}
|
||||
|
||||
constructor(values = {}) {
|
||||
super(values)
|
||||
/** @type {InstanceType<typeof PinEntity.attributes.PinId>} */ this.PinId
|
||||
/** @type {InstanceType<typeof PinEntity.attributes.PinName>} */ this.PinName
|
||||
/** @type {InstanceType<typeof PinEntity.attributes.PinFriendlyName>} */ this.PinFriendlyName
|
||||
/** @type {InstanceType<typeof PinEntity.attributes.PinToolTip>} */ this.PinToolTip
|
||||
/** @type {InstanceType<typeof PinEntity.attributes.Direction>} */ this.Direction
|
||||
/** @type {InstanceType<typeof PinEntity.attributes.PinType>} */ this.PinType
|
||||
/** @type {InstanceType<typeof PinEntity.attributes.LinkedTo>} */ this.LinkedTo
|
||||
/** @type {T} */ this.DefaultValue
|
||||
/** @type {InstanceType<typeof PinEntity.attributes.AutogeneratedDefaultValue>} */ this.AutogeneratedDefaultValue
|
||||
/** @type {InstanceType<typeof PinEntity.attributes.DefaultObject>} */ this.DefaultObject
|
||||
/** @type {InstanceType<typeof PinEntity.attributes.PersistentGuid>} */ this.PersistentGuid
|
||||
/** @type {InstanceType<typeof PinEntity.attributes.bHidden>} */ this.bHidden
|
||||
/** @type {InstanceType<typeof PinEntity.attributes.bNotConnectable>} */ this.bNotConnectable
|
||||
/** @type {InstanceType<typeof PinEntity.attributes.bDefaultValueIsReadOnly>} */ this.bDefaultValueIsReadOnly
|
||||
/** @type {InstanceType<typeof PinEntity.attributes.bDefaultValueIsIgnored>} */ this.bDefaultValueIsIgnored
|
||||
/** @type {InstanceType<typeof PinEntity.attributes.bAdvancedView>} */ this.bAdvancedView
|
||||
/** @type {InstanceType<typeof PinEntity.attributes.bOrphanedPin>} */ this.bOrphanedPin
|
||||
/** @type {ObjectEntity} */ this.objectEntity
|
||||
}
|
||||
|
||||
/** @returns {P<PinEntity>} */
|
||||
static createGrammar() {
|
||||
return Grammar.createEntityGrammar(this)
|
||||
}
|
||||
|
||||
constructor(values = {}, suppressWarns = false) {
|
||||
super(values, suppressWarns)
|
||||
/** @type {ObjectEntity} */ this.objectEntity
|
||||
/** @type {Number} */ this.pinIndex
|
||||
/** @type {GuidEntity} */ this.PinId
|
||||
/** @type {String} */ this.PinName
|
||||
/** @type {LocalizedTextEntity | String} */ this.PinFriendlyName
|
||||
/** @type {String} */ this.PinToolTip
|
||||
/** @type {String} */ this.Direction
|
||||
/** @type {PinTypeEntity} */ this.PinType
|
||||
/** @type {PinReferenceEntity[]} */ this.LinkedTo
|
||||
/** @type {T} */ this.DefaultValue
|
||||
/** @type {String} */ this.AutogeneratedDefaultValue
|
||||
/** @type {ObjectReferenceEntity} */ this.DefaultObject
|
||||
/** @type {GuidEntity} */ this.PersistentGuid
|
||||
/** @type {Boolean} */ this.bHidden
|
||||
/** @type {Boolean} */ this.bNotConnectable
|
||||
/** @type {Boolean} */ this.bDefaultValueIsReadOnly
|
||||
/** @type {Boolean} */ this.bDefaultValueIsIgnored
|
||||
/** @type {Boolean} */ this.bAdvancedView
|
||||
/** @type {Boolean} */ this.bOrphanedPin
|
||||
}
|
||||
|
||||
/** @param {ObjectEntity} objectEntity */
|
||||
static fromLegacyObject(objectEntity) {
|
||||
return new PinEntity(objectEntity, true)
|
||||
return new PinEntity(objectEntity)
|
||||
}
|
||||
|
||||
/** @returns {String} */
|
||||
getType() {
|
||||
const category = this.PinType.PinCategory.toLocaleLowerCase()
|
||||
if (category === "struct" || category === "class" || category === "object" || category === "type") {
|
||||
return this.PinType.PinSubCategoryObject.path
|
||||
const category = this.PinType.PinCategory?.toString().toLocaleLowerCase()
|
||||
if (["struct", "class", "object", "type", "statictype"].includes(category)) {
|
||||
return this.PinType.PinSubCategoryObject?.path
|
||||
}
|
||||
if (this.isEnum()) {
|
||||
return "enum"
|
||||
@@ -156,8 +187,8 @@ export default class PinEntity extends IEntity {
|
||||
if (this.objectEntity?.isPcg()) {
|
||||
const pcgSuboject = this.objectEntity.getPcgSubobject()
|
||||
const pinObjectReference = this.isInput()
|
||||
? pcgSuboject.InputPins?.[this.pinIndex]
|
||||
: pcgSuboject.OutputPins?.[this.pinIndex]
|
||||
? pcgSuboject.InputPins?.valueOf()[this.pinIndex]
|
||||
: pcgSuboject.OutputPins?.valueOf()[this.pinIndex]
|
||||
if (pinObjectReference) {
|
||||
/** @type {ObjectEntity} */
|
||||
const pinObject = pcgSuboject[Configuration.subObjectAttributeNameFromReference(pinObjectReference, true)]
|
||||
@@ -170,8 +201,8 @@ export default class PinEntity extends IEntity {
|
||||
}
|
||||
if (allowedTypes) {
|
||||
if (
|
||||
pinObject.Properties.bAllowMultipleData !== false
|
||||
&& pinObject.Properties.bAllowMultipleConnections !== false
|
||||
pinObject.Properties.bAllowMultipleData?.valueOf() !== false
|
||||
&& pinObject.Properties.bAllowMultipleConnections?.valueOf() !== false
|
||||
) {
|
||||
allowedTypes += "[]"
|
||||
}
|
||||
@@ -180,26 +211,28 @@ export default class PinEntity extends IEntity {
|
||||
}
|
||||
}
|
||||
if (category === "optional") {
|
||||
switch (this.PinType.PinSubCategory) {
|
||||
const subCategory = this.PinType.PinSubCategory?.toString()
|
||||
switch (subCategory) {
|
||||
case "red":
|
||||
return "real"
|
||||
case "rg":
|
||||
return "rg"
|
||||
case "rgb":
|
||||
return Configuration.paths.vector
|
||||
return paths.vector
|
||||
case "rgba":
|
||||
return Configuration.paths.linearColor
|
||||
return paths.linearColor
|
||||
default:
|
||||
return this.PinType.PinSubCategory
|
||||
return subCategory
|
||||
}
|
||||
}
|
||||
return category
|
||||
}
|
||||
|
||||
/** @returns {typeof IEntity} */
|
||||
getEntityType(alternative = false) {
|
||||
const typeString = this.getType()
|
||||
const entity = PinEntity.#typeEntityMap[typeString]
|
||||
const alternativeEntity = PinEntity.#alternativeTypeEntityMap[typeString]
|
||||
const type = this.getType()
|
||||
const entity = PinEntity.#typeEntityMap[type]
|
||||
const alternativeEntity = PinEntity.#alternativeTypeEntityMap[type]
|
||||
return alternative && alternativeEntity !== undefined
|
||||
? alternativeEntity
|
||||
: entity
|
||||
@@ -211,52 +244,42 @@ export default class PinEntity extends IEntity {
|
||||
|
||||
/** @param {PinEntity} other */
|
||||
copyTypeFrom(other) {
|
||||
this.PinType.PinCategory = other.PinType.PinCategory
|
||||
this.PinType.PinSubCategory = other.PinType.PinSubCategory
|
||||
this.PinType.PinSubCategoryObject = other.PinType.PinSubCategoryObject
|
||||
this.PinType.PinSubCategoryMemberReference = other.PinType.PinSubCategoryMemberReference
|
||||
this.PinType.PinValueType = other.PinType.PinValueType
|
||||
this.PinType.ContainerType = other.PinType.ContainerType
|
||||
this.PinType.bIsReference = other.PinType.bIsReference
|
||||
this.PinType.bIsConst = other.PinType.bIsConst
|
||||
this.PinType.bIsWeakPointer = other.PinType.bIsWeakPointer
|
||||
this.PinType.bIsUObjectWrapper = other.PinType.bIsUObjectWrapper
|
||||
this.PinType.bSerializeAsSinglePrecisionFloat = other.PinType.bSerializeAsSinglePrecisionFloat
|
||||
this.PinType = other.PinType
|
||||
}
|
||||
|
||||
getDefaultValue(maybeCreate = false) {
|
||||
if (this.DefaultValue === undefined && maybeCreate) {
|
||||
// @ts-expect-error
|
||||
this.DefaultValue = new (this.getEntityType(true))()
|
||||
this.DefaultValue = /** @type {T} */(new (this.getEntityType(true))())
|
||||
}
|
||||
return this.DefaultValue
|
||||
}
|
||||
|
||||
isEnum() {
|
||||
const type = this.PinType.PinSubCategoryObject.type
|
||||
return type === Configuration.paths.enum
|
||||
|| type === Configuration.paths.userDefinedEnum
|
||||
|| type.toLowerCase() === "enum"
|
||||
const type = this.PinType.PinSubCategoryObject?.type
|
||||
return type === paths.enum
|
||||
|| type === paths.userDefinedEnum
|
||||
|| type?.toLowerCase() === "enum"
|
||||
}
|
||||
|
||||
isExecution() {
|
||||
return this.PinType.PinCategory === "exec"
|
||||
return this.PinType.PinCategory.toString() === "exec"
|
||||
|| this.getType() === paths.niagaraParameterMap
|
||||
}
|
||||
|
||||
isHidden() {
|
||||
return this.bHidden
|
||||
return this.bHidden?.valueOf()
|
||||
}
|
||||
|
||||
isInput() {
|
||||
return !this.bHidden && this.Direction != "EGPD_Output"
|
||||
return !this.isHidden() && this.Direction?.toString() != "EGPD_Output"
|
||||
}
|
||||
|
||||
isOutput() {
|
||||
return !this.bHidden && this.Direction == "EGPD_Output"
|
||||
return !this.isHidden() && this.Direction?.toString() == "EGPD_Output"
|
||||
}
|
||||
|
||||
isLinked() {
|
||||
return this.LinkedTo?.length > 0 ?? false
|
||||
return this.LinkedTo?.length > 0
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -265,15 +288,12 @@ export default class PinEntity extends IEntity {
|
||||
* @returns true if it was not already linked to the tarket
|
||||
*/
|
||||
linkTo(targetObjectName, targetPinEntity) {
|
||||
const linkFound = this.LinkedTo?.some(pinReferenceEntity =>
|
||||
const linkFound = this.LinkedTo.values?.some(pinReferenceEntity =>
|
||||
pinReferenceEntity.objectName.toString() == targetObjectName
|
||||
&& pinReferenceEntity.pinGuid.valueOf() == targetPinEntity.PinId.valueOf()
|
||||
&& pinReferenceEntity.pinGuid.toString() == targetPinEntity.PinId.toString()
|
||||
)
|
||||
if (!linkFound) {
|
||||
(this.LinkedTo ??= []).push(new PinReferenceEntity({
|
||||
objectName: targetObjectName,
|
||||
pinGuid: targetPinEntity.PinId,
|
||||
}))
|
||||
this.LinkedTo.values.push(new PinReferenceEntity(new SymbolEntity(targetObjectName), targetPinEntity.PinId))
|
||||
return true
|
||||
}
|
||||
return false // Already linked
|
||||
@@ -285,14 +305,14 @@ export default class PinEntity extends IEntity {
|
||||
* @returns true if it was linked to the target
|
||||
*/
|
||||
unlinkFrom(targetObjectName, targetPinEntity) {
|
||||
const indexElement = this.LinkedTo?.findIndex(pinReferenceEntity => {
|
||||
const indexElement = this.LinkedTo.values?.findIndex(pinReferenceEntity => {
|
||||
return pinReferenceEntity.objectName.toString() == targetObjectName
|
||||
&& pinReferenceEntity.pinGuid.valueOf() == targetPinEntity.PinId.valueOf()
|
||||
&& pinReferenceEntity.pinGuid.toString() == targetPinEntity.PinId.toString()
|
||||
})
|
||||
if (indexElement >= 0) {
|
||||
this.LinkedTo.splice(indexElement, 1)
|
||||
this.LinkedTo.values.splice(indexElement, 1)
|
||||
if (this.LinkedTo.length === 0 && PinEntity.attributes.LinkedTo.default === undefined) {
|
||||
this.LinkedTo = undefined
|
||||
this.LinkedTo.values = []
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -300,7 +320,7 @@ export default class PinEntity extends IEntity {
|
||||
}
|
||||
|
||||
getSubCategory() {
|
||||
return this.PinType.PinSubCategoryObject.path
|
||||
return this.PinType.PinSubCategoryObject?.path
|
||||
}
|
||||
|
||||
pinColor() {
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import P from "parsernostrum"
|
||||
import GuidEntity from "./GuidEntity.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
import PathSymbolEntity from "./PathSymbolEntity.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import SymbolEntity from "./SymbolEntity.js"
|
||||
|
||||
export default class PinReferenceEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
objectName: AttributeInfo.createType(PathSymbolEntity),
|
||||
pinGuid: AttributeInfo.createType(GuidEntity),
|
||||
}
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
static createGrammar() {
|
||||
return Parsernostrum.seq(
|
||||
PathSymbolEntity.grammar,
|
||||
Parsernostrum.whitespace,
|
||||
GuidEntity.grammar
|
||||
).map(
|
||||
([objectName, _1, pinGuid]) => new this({
|
||||
objectName: objectName,
|
||||
pinGuid: pinGuid,
|
||||
})
|
||||
)
|
||||
/**
|
||||
* @param {SymbolEntity} objectName
|
||||
* @param {GuidEntity} pinGuid
|
||||
*/
|
||||
constructor(objectName = null, pinGuid = null) {
|
||||
super()
|
||||
this.objectName = objectName
|
||||
this.pinGuid = pinGuid
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
super(values)
|
||||
/** @type {PathSymbolEntity} */ this.objectName
|
||||
/** @type {GuidEntity} */ this.pinGuid
|
||||
/** @returns {P<PinReferenceEntity>} */
|
||||
static createGrammar() {
|
||||
return P.seq(
|
||||
SymbolEntity.grammar,
|
||||
P.whitespace,
|
||||
GuidEntity.grammar
|
||||
)
|
||||
.map(([objectName, _1, pinGuid]) => new this(objectName, pinGuid))
|
||||
.label("PinReferenceEntity")
|
||||
}
|
||||
|
||||
doSerialize() {
|
||||
return this.objectName.serialize() + " " + this.pinGuid.serialize()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +1,55 @@
|
||||
import P from "parsernostrum"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import BooleanEntity from "./BooleanEntity.js"
|
||||
import FunctionReferenceEntity from "./FunctionReferenceEntity.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
import ObjectReferenceEntity from "./ObjectReferenceEntity.js"
|
||||
import PathSymbolEntity from "./PathSymbolEntity.js"
|
||||
import StringEntity from "./StringEntity.js"
|
||||
import SymbolEntity from "./SymbolEntity.js"
|
||||
|
||||
export default class PinTypeEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
PinCategory: AttributeInfo.createValue(""),
|
||||
PinSubCategory: AttributeInfo.createValue(""),
|
||||
PinSubCategoryObject: new AttributeInfo({
|
||||
type: ObjectReferenceEntity,
|
||||
default: () => ObjectReferenceEntity.createNoneInstance(),
|
||||
}),
|
||||
PinSubCategoryMemberReference: new AttributeInfo({
|
||||
type: FunctionReferenceEntity,
|
||||
default: null,
|
||||
}),
|
||||
PinValueType: new AttributeInfo({
|
||||
type: PinTypeEntity,
|
||||
default: null,
|
||||
}),
|
||||
ContainerType: AttributeInfo.createType(PathSymbolEntity),
|
||||
bIsReference: AttributeInfo.createValue(false),
|
||||
bIsConst: AttributeInfo.createValue(false),
|
||||
bIsWeakPointer: AttributeInfo.createValue(false),
|
||||
bIsUObjectWrapper: AttributeInfo.createValue(false),
|
||||
bSerializeAsSinglePrecisionFloat: AttributeInfo.createValue(false),
|
||||
PinCategory: StringEntity.withDefault(),
|
||||
PinSubCategory: StringEntity,
|
||||
PinSubCategoryObject: ObjectReferenceEntity,
|
||||
PinSubCategoryMemberReference: FunctionReferenceEntity,
|
||||
ContainerType: SymbolEntity,
|
||||
bIsReference: BooleanEntity,
|
||||
bIsConst: BooleanEntity,
|
||||
bIsWeakPointer: BooleanEntity,
|
||||
bIsUObjectWrapper: BooleanEntity,
|
||||
bSerializeAsSinglePrecisionFloat: BooleanEntity,
|
||||
}
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
static createGrammar() {
|
||||
return Grammar.createEntityGrammar(this)
|
||||
constructor(values = {}) {
|
||||
super(values)
|
||||
/** @type {InstanceType<typeof PinTypeEntity.attributes.PinCategory>} */ this.PinCategory
|
||||
/** @type {InstanceType<typeof PinTypeEntity.attributes.PinSubCategory>} */ this.PinSubCategory
|
||||
/** @type {InstanceType<typeof PinTypeEntity.attributes.PinSubCategoryObject>} */ this.PinSubCategoryObject
|
||||
/** @type {InstanceType<typeof PinTypeEntity.attributes.PinSubCategoryMemberReference>} */ this.PinSubCategoryMemberReference
|
||||
/** @type {InstanceType<typeof PinTypeEntity.attributes.ContainerType>} */ this.ContainerType
|
||||
/** @type {InstanceType<typeof PinTypeEntity.attributes.bIsReference>} */ this.bIsReference
|
||||
/** @type {InstanceType<typeof PinTypeEntity.attributes.bIsConst>} */ this.bIsConst
|
||||
/** @type {InstanceType<typeof PinTypeEntity.attributes.bIsWeakPointer>} */ this.bIsWeakPointer
|
||||
/** @type {InstanceType<typeof PinTypeEntity.attributes.bIsUObjectWrapper>} */ this.bIsUObjectWrapper
|
||||
/** @type {InstanceType<typeof PinTypeEntity.attributes.bIsUObjectWrapper>} */ this.bIsUObjectWrapper
|
||||
/** @type {InstanceType<typeof PinTypeEntity.attributes.bSerializeAsSinglePrecisionFloat>} */ this.bSerializeAsSinglePrecisionFloat
|
||||
}
|
||||
|
||||
constructor(values = {}, suppressWarns = false) {
|
||||
super(values, suppressWarns)
|
||||
/** @type {String} */ this.PinCategory
|
||||
/** @type {String} */ this.PinSubCategory
|
||||
/** @type {ObjectReferenceEntity} */ this.PinSubCategoryObject
|
||||
/** @type {FunctionReferenceEntity} */ this.PinSubCategoryMemberReference
|
||||
/** @type {PinTypeEntity} */ this.PinValueType
|
||||
/** @type {PathSymbolEntity} */ this.ContainerType
|
||||
/** @type {Boolean} */ this.bIsReference
|
||||
/** @type {Boolean} */ this.bIsConst
|
||||
/** @type {Boolean} */ this.bIsWeakPointer
|
||||
/** @type {Boolean} */ this.bIsUObjectWrapper
|
||||
/** @type {Boolean} */ this.bIsUObjectWrapper
|
||||
/** @type {Boolean} */ this.bSerializeAsSinglePrecisionFloat
|
||||
/** @returns {P<PinTypeEntity>} */
|
||||
static createGrammar() {
|
||||
return Grammar.createEntityGrammar(this).label("PinTypeEntity")
|
||||
}
|
||||
|
||||
/** @param {PinTypeEntity} other */
|
||||
copyTypeFrom(other) {
|
||||
this.PinCategory = other.PinCategory
|
||||
this.PinSubCategory = other.PinSubCategory
|
||||
this.PinSubCategoryObject = other.PinSubCategoryObject
|
||||
this.PinSubCategoryMemberReference = other.PinSubCategoryMemberReference
|
||||
this.PinValueType = other.PinValueType
|
||||
this.ContainerType = other.ContainerType
|
||||
this.bIsReference = other.bIsReference
|
||||
this.bIsConst = other.bIsConst
|
||||
this.bIsWeakPointer = other.bIsWeakPointer
|
||||
this.bIsUObjectWrapper = other.bIsUObjectWrapper
|
||||
this.bSerializeAsSinglePrecisionFloat = other.bSerializeAsSinglePrecisionFloat
|
||||
for (const key of this.keys) {
|
||||
if (other[key] !== undefined) {
|
||||
this[key] = other[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
23
js/entity/RBSerializationVector2DEntity.js
Normal file → Executable file
23
js/entity/RBSerializationVector2DEntity.js
Normal file → Executable file
@@ -1,21 +1,26 @@
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import P from "parsernostrum"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import Vector2DEntity from "./Vector2DEntity.js"
|
||||
|
||||
export default class RBSerializationVector2DEntity extends Vector2DEntity {
|
||||
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
/** @returns {P<RBSerializationVector2DEntity>} */
|
||||
static createGrammar() {
|
||||
return Parsernostrum.alt(
|
||||
Parsernostrum.regArray(new RegExp(
|
||||
/X\s*=\s*/.source + "(?<x>" + Parsernostrum.number.getParser().parser.regexp.source + ")"
|
||||
return P.alt(
|
||||
P.regArray(new RegExp(
|
||||
/X\s*=\s*/.source + "(?<x>" + Grammar.numberRegexSource + ")"
|
||||
+ "\\s+"
|
||||
+ /Y\s*=\s*/.source + "(?<y>" + Parsernostrum.number.getParser().parser.regexp.source + ")"
|
||||
+ /Y\s*=\s*/.source + "(?<y>" + Grammar.numberRegexSource + ")"
|
||||
)).map(({ groups: { x, y } }) => new this({
|
||||
X: Number(x),
|
||||
Y: Number(y),
|
||||
X: new (Vector2DEntity.attributes.X)(x),
|
||||
Y: new (Vector2DEntity.attributes.Y)(y),
|
||||
})),
|
||||
Vector2DEntity.grammar
|
||||
)
|
||||
Vector2DEntity.grammar.map(v => new this({
|
||||
X: v.X,
|
||||
Y: v.Y,
|
||||
}))
|
||||
).label("RBSerializationVector2DEntity")
|
||||
}
|
||||
}
|
||||
|
||||
33
js/entity/RotatorEntity.js
Normal file → Executable file
33
js/entity/RotatorEntity.js
Normal file → Executable file
@@ -1,35 +1,28 @@
|
||||
import P from "parsernostrum"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
import NumberEntity from "./NumberEntity.js"
|
||||
|
||||
export default class RotatorEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
R: new AttributeInfo({
|
||||
default: 0,
|
||||
expected: true,
|
||||
}),
|
||||
P: new AttributeInfo({
|
||||
default: 0,
|
||||
expected: true,
|
||||
}),
|
||||
Y: new AttributeInfo({
|
||||
default: 0,
|
||||
expected: true,
|
||||
}),
|
||||
R: NumberEntity.withDefault(),
|
||||
P: NumberEntity.withDefault(),
|
||||
Y: NumberEntity.withDefault(),
|
||||
}
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
static createGrammar() {
|
||||
return Grammar.createEntityGrammar(this, false)
|
||||
}
|
||||
|
||||
constructor(values) {
|
||||
super(values)
|
||||
/** @type {Number} */ this.R
|
||||
/** @type {Number} */ this.P
|
||||
/** @type {Number} */ this.Y
|
||||
/** @type {InstanceType<typeof RotatorEntity.attributes.R>} */ this.R
|
||||
/** @type {InstanceType<typeof RotatorEntity.attributes.P>} */ this.P
|
||||
/** @type {InstanceType<typeof RotatorEntity.attributes.Y>} */ this.Y
|
||||
}
|
||||
|
||||
/** @returns {P<RotatorEntity>} */
|
||||
static createGrammar() {
|
||||
return Grammar.createEntityGrammar(this, Grammar.commaSeparation, 1).label("RotatorEntity")
|
||||
}
|
||||
|
||||
getRoll() {
|
||||
|
||||
19
js/entity/ScriptVariableEntity.js
Normal file → Executable file
19
js/entity/ScriptVariableEntity.js
Normal file → Executable file
@@ -1,5 +1,5 @@
|
||||
import P from "parsernostrum"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import GuidEntity from "./GuidEntity.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
import ObjectReferenceEntity from "./ObjectReferenceEntity.js"
|
||||
@@ -8,18 +8,19 @@ export default class ScriptVariableEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
ScriptVariable: AttributeInfo.createType(ObjectReferenceEntity),
|
||||
OriginalChangeId: AttributeInfo.createType(GuidEntity),
|
||||
ScriptVariable: ObjectReferenceEntity,
|
||||
OriginalChangeId: GuidEntity,
|
||||
}
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
static createGrammar() {
|
||||
return Grammar.createEntityGrammar(this)
|
||||
constructor(values = {}) {
|
||||
super(values)
|
||||
/** @type {InstanceType<typeof ScriptVariableEntity.attributes.ScriptVariable>} */ this.ScriptVariable
|
||||
/** @type {InstanceType<typeof ScriptVariableEntity.attributes.OriginalChangeId>} */ this.OriginalChangeId
|
||||
}
|
||||
|
||||
constructor(values = {}, suppressWarns = false) {
|
||||
super(values, suppressWarns)
|
||||
/** @type {ObjectReferenceEntity} */ this.ScriptVariable
|
||||
/** @type {GuidEntity} */ this.OriginalChangeId
|
||||
/** @returns {P<ScriptVariableEntity>} */
|
||||
static createGrammar() {
|
||||
return Grammar.createEntityGrammar(this).label("ScriptVariableEntity")
|
||||
}
|
||||
}
|
||||
|
||||
45
js/entity/SimpleSerializationRotatorEntity.js
Normal file → Executable file
45
js/entity/SimpleSerializationRotatorEntity.js
Normal file → Executable file
@@ -1,25 +1,40 @@
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import P from "parsernostrum"
|
||||
import NumberEntity from "./NumberEntity.js"
|
||||
import RotatorEntity from "./RotatorEntity.js"
|
||||
|
||||
export default class SimpleSerializationRotatorEntity extends RotatorEntity {
|
||||
|
||||
static attributeSeparator = ", "
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
/** @returns {P<SimpleSerializationRotatorEntity>} */
|
||||
static createGrammar() {
|
||||
const number = Parsernostrum.number.getParser().parser.regexp.source
|
||||
return Parsernostrum.alt(
|
||||
Parsernostrum.regArray(new RegExp(
|
||||
"(" + number + ")"
|
||||
+ "\\s*,\\s*"
|
||||
+ "(" + number + ")"
|
||||
+ "\\s*,\\s*"
|
||||
+ "(" + number + ")"
|
||||
)).map(([_, p, y, r]) => new this({
|
||||
R: Number(r),
|
||||
P: Number(p),
|
||||
Y: Number(y),
|
||||
return P.alt(
|
||||
P.regArray(new RegExp(
|
||||
`(${NumberEntity.numberRegexSource})`
|
||||
+ String.raw`\s*,\s*`
|
||||
+ `(${NumberEntity.numberRegexSource})`
|
||||
+ String.raw`\s*,\s*`
|
||||
+ `(${NumberEntity.numberRegexSource})`
|
||||
)).map(([_, p, pPrecision, y, yPrecision, r, rPrecision]) => new this({
|
||||
R: new (RotatorEntity.attributes.R)(r, rPrecision?.length),
|
||||
P: new (RotatorEntity.attributes.P)(p, pPrecision?.length),
|
||||
Y: new (RotatorEntity.attributes.Y)(y, yPrecision?.length),
|
||||
})),
|
||||
RotatorEntity.grammar
|
||||
)
|
||||
RotatorEntity.grammar.map(v => new this({
|
||||
R: v.R,
|
||||
P: v.P,
|
||||
Y: v.Y,
|
||||
}))
|
||||
).label("SimpleSerializationRotatorEntity")
|
||||
}
|
||||
|
||||
doSerialize() {
|
||||
const attributeSeparator = /** @type {typeof SimpleSerializationRotatorEntity} */(
|
||||
this.constructor
|
||||
).attributeSeparator
|
||||
return this.P.serialize() + attributeSeparator
|
||||
+ this.Y.serialize() + attributeSeparator
|
||||
+ this.R.serialize() + (this.trailing ? attributeSeparator : "")
|
||||
}
|
||||
}
|
||||
|
||||
37
js/entity/SimpleSerializationVector2DEntity.js
Normal file → Executable file
37
js/entity/SimpleSerializationVector2DEntity.js
Normal file → Executable file
@@ -1,22 +1,35 @@
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import P from "parsernostrum"
|
||||
import NumberEntity from "./NumberEntity.js"
|
||||
import Vector2DEntity from "./Vector2DEntity.js"
|
||||
|
||||
export default class SimpleSerializationVector2DEntity extends Vector2DEntity {
|
||||
|
||||
static attributeSeparator = ", "
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
/** @returns {P<SimpleSerializationVector2DEntity>} */
|
||||
static createGrammar() {
|
||||
const number = Parsernostrum.number.getParser().parser.regexp.source
|
||||
return Parsernostrum.alt(
|
||||
Parsernostrum.regArray(new RegExp(
|
||||
"(" + number + ")"
|
||||
+ "\\s*,\\s*"
|
||||
+ "(" + number + ")"
|
||||
)).map(([_, x, y]) => new this({
|
||||
X: Number(x),
|
||||
Y: Number(y),
|
||||
return P.alt(
|
||||
P.regArray(new RegExp(
|
||||
`(${NumberEntity.numberRegexSource})`
|
||||
+ String.raw`\s*,\s*`
|
||||
+ `(${NumberEntity.numberRegexSource})`
|
||||
)).map(([_, x, xPrecision, y, yPrecision]) => new this({
|
||||
X: new (Vector2DEntity.attributes.X)(x, xPrecision?.length),
|
||||
Y: new (Vector2DEntity.attributes.Y)(y, yPrecision?.length),
|
||||
})),
|
||||
Vector2DEntity.grammar
|
||||
)
|
||||
Vector2DEntity.grammar.map(v => new this({
|
||||
X: v.X,
|
||||
Y: v.Y,
|
||||
}))
|
||||
).label("SimpleSerializationVector2DEntity")
|
||||
}
|
||||
|
||||
doSerialize() {
|
||||
const attributeSeparator = /** @type {typeof SimpleSerializationVector2DEntity} */(
|
||||
this.constructor
|
||||
).attributeSeparator
|
||||
return this.X.serialize() + attributeSeparator
|
||||
+ this.Y.serialize() + (this.trailing ? attributeSeparator : "")
|
||||
}
|
||||
}
|
||||
|
||||
32
js/entity/SimpleSerializationVector4DEntity.js
Normal file → Executable file
32
js/entity/SimpleSerializationVector4DEntity.js
Normal file → Executable file
@@ -1,27 +1,29 @@
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import P from "parsernostrum"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import Vector4DEntity from "./Vector4DEntity.js"
|
||||
|
||||
export default class SimpleSerializationVector4DEntity extends Vector4DEntity {
|
||||
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
/** @returns {P<SimpleSerializationVector4DEntity> } */
|
||||
static createGrammar() {
|
||||
const number = Parsernostrum.number.getParser().parser.regexp.source
|
||||
return Parsernostrum.alt(
|
||||
Parsernostrum.regArray(new RegExp(
|
||||
"(" + number + ")"
|
||||
+ "\\s*,\\s*"
|
||||
+ "(" + number + ")"
|
||||
+ "\\s*,\\s*"
|
||||
+ "(" + number + ")"
|
||||
+ "\\s*,\\s*"
|
||||
+ "(" + number + ")"
|
||||
const number = Grammar.numberRegexSource
|
||||
return P.alt(
|
||||
P.regArray(new RegExp(
|
||||
`(${Grammar.numberRegexSource})`
|
||||
+ String.raw`\s*,\s*`
|
||||
+ `(${Grammar.numberRegexSource})`
|
||||
+ String.raw`\s*,\s*`
|
||||
+ `(${Grammar.numberRegexSource})`
|
||||
+ String.raw`\s*,\s*`
|
||||
+ `(${Grammar.numberRegexSource})`
|
||||
))
|
||||
.map(([_0, x, y, z, w]) => new this({
|
||||
X: Number(x),
|
||||
Y: Number(y),
|
||||
Z: Number(z),
|
||||
W: Number(w),
|
||||
X: new (Vector4DEntity.attributes.X)(x),
|
||||
Y: new (Vector4DEntity.attributes.Y)(y),
|
||||
Z: new (Vector4DEntity.attributes.Z)(z),
|
||||
W: new (Vector4DEntity.attributes.W)(w),
|
||||
})),
|
||||
Vector4DEntity.grammar
|
||||
)
|
||||
|
||||
61
js/entity/SimpleSerializationVectorEntity.js
Normal file → Executable file
61
js/entity/SimpleSerializationVectorEntity.js
Normal file → Executable file
@@ -1,26 +1,59 @@
|
||||
import Parsernostrum from "parsernostrum"
|
||||
import P from "parsernostrum"
|
||||
import NumberEntity from "./NumberEntity.js"
|
||||
import VectorEntity from "./VectorEntity.js"
|
||||
|
||||
export default class SimpleSerializationVectorEntity extends VectorEntity {
|
||||
|
||||
static allowShortSerialization = false
|
||||
static attributeSeparator = ", "
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
/** @returns {P<SimpleSerializationVectorEntity>} */
|
||||
static createGrammar() {
|
||||
const number = Parsernostrum.number.getParser().parser.regexp.source
|
||||
return Parsernostrum.alt(
|
||||
Parsernostrum.regArray(new RegExp(
|
||||
"(" + number + ")"
|
||||
+ "\\s*,\\s*"
|
||||
+ "(" + number + ")"
|
||||
+ "\\s*,\\s*"
|
||||
+ "(" + number + ")"
|
||||
return P.alt(
|
||||
P.regArray(new RegExp(
|
||||
`(${NumberEntity.numberRegexSource})`
|
||||
// If allow simple serialization then it can parse only a single number ...
|
||||
+ (this.allowShortSerialization ? `(?:` : "")
|
||||
+ String.raw`\s*,\s*`
|
||||
+ `(${NumberEntity.numberRegexSource})`
|
||||
+ String.raw`\s*,\s*`
|
||||
+ `(${NumberEntity.numberRegexSource})`
|
||||
// ... that will be assigned to X and the rest is optional and set to 0
|
||||
+ (this.allowShortSerialization ? `)?` : "")
|
||||
))
|
||||
.map(([_0, x, y, z]) => new this({
|
||||
X: Number(x),
|
||||
Y: Number(y),
|
||||
Z: Number(z),
|
||||
.map(([_, x, xPrecision, y, yPrecision, z, zPrecision]) => new this({
|
||||
X: new (VectorEntity.attributes.X)(x, xPrecision?.length),
|
||||
Y: new (VectorEntity.attributes.Y)(y, yPrecision?.length),
|
||||
Z: new (VectorEntity.attributes.Z)(z, zPrecision?.length),
|
||||
})),
|
||||
VectorEntity.grammar
|
||||
VectorEntity.grammar.map(v => new this({
|
||||
X: v.X,
|
||||
Y: v.Y,
|
||||
Z: v.Z,
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {typeof SimpleSerializationVectorEntity} T
|
||||
* @this {T}
|
||||
*/
|
||||
static flagAllowShortSerialization(value = true) {
|
||||
const result = this.asUniqueClass()
|
||||
if (value !== result.allowShortSerialization) {
|
||||
result.allowShortSerialization = value
|
||||
result.grammar = result.createGrammar()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
doSerialize() {
|
||||
const attributeSeparator = /** @type {typeof SimpleSerializationVectorEntity} */(
|
||||
this.constructor
|
||||
).attributeSeparator
|
||||
return this.X.serialize() + attributeSeparator
|
||||
+ this.Y.serialize() + attributeSeparator
|
||||
+ this.Z.serialize() + (this.trailing ? attributeSeparator : "")
|
||||
}
|
||||
}
|
||||
|
||||
57
js/entity/StringEntity.js
Executable file
57
js/entity/StringEntity.js
Executable file
@@ -0,0 +1,57 @@
|
||||
import P from "parsernostrum"
|
||||
import IEntity from "./IEntity.js"
|
||||
|
||||
export default class StringEntity extends IEntity {
|
||||
|
||||
static grammar = this.createGrammar()
|
||||
static escapedCharacters = /['"\\]/g
|
||||
static unescapedBackslash = /(?<=(?:[^\\]|^)(?:\\\\)*)\\(?!\\)/
|
||||
|
||||
constructor(value = "") {
|
||||
super()
|
||||
this.value = value
|
||||
}
|
||||
|
||||
/** @returns {P<StringEntity>} */
|
||||
static createGrammar() {
|
||||
return P.doubleQuotedString
|
||||
.map(insideString => new this(StringEntity.unescape(insideString)))
|
||||
.label("StringEntity")
|
||||
}
|
||||
|
||||
|
||||
/** @param {String} value */
|
||||
static escape(value, inline = true) {
|
||||
let result = value.replaceAll(new RegExp(`(${StringEntity.escapedCharacters.source})`, "g"), '\\$1')
|
||||
if (inline) {
|
||||
result = result
|
||||
.replaceAll("\n", "\\n") // Replace newline with \n
|
||||
.replaceAll("\t", "\\t") // Replace tab with \t
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/** @param {String} value */
|
||||
static unescape(value) {
|
||||
return value
|
||||
.replaceAll(new RegExp(StringEntity.unescapedBackslash.source + "t", "g"), "\t") // Replace tab with \t
|
||||
.replaceAll(new RegExp(StringEntity.unescapedBackslash.source + "n", "g"), "\n") // Replace newline with \n
|
||||
.replaceAll(new RegExp(`\\\\(${StringEntity.escapedCharacters.source})`, "g"), "$1")
|
||||
}
|
||||
|
||||
doSerialize(insideString = false) {
|
||||
let result = `"${StringEntity.escape(this.value)}"`
|
||||
if (insideString) {
|
||||
result = StringEntity.escape(result, false)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
valueOf() {
|
||||
return this.value
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.value
|
||||
}
|
||||
}
|
||||
37
js/entity/SymbolEntity.js
Normal file → Executable file
37
js/entity/SymbolEntity.js
Normal file → Executable file
@@ -1,32 +1,35 @@
|
||||
import P from "parsernostrum"
|
||||
import Grammar from "../serialization/Grammar.js"
|
||||
import AttributeInfo from "./AttributeInfo.js"
|
||||
import IEntity from "./IEntity.js"
|
||||
|
||||
export default class SymbolEntity extends IEntity {
|
||||
|
||||
static attributes = {
|
||||
...super.attributes,
|
||||
value: AttributeInfo.createValue(""),
|
||||
static attributeConverter = {
|
||||
fromAttribute: (value, type) => new this(value),
|
||||
toAttribute: (value, type) => value.toString()
|
||||
}
|
||||
static grammar = this.createGrammar()
|
||||
|
||||
/** @returns {P<SymbolEntity>} */
|
||||
static createGrammar() {
|
||||
return Grammar.symbol.map(v => new this(v))
|
||||
return Grammar.symbol.map(v => new this(v)).label("SymbolEntity")
|
||||
}
|
||||
|
||||
/** @param {String | Object} values */
|
||||
constructor(values) {
|
||||
if (values.constructor !== Object) {
|
||||
values = {
|
||||
value: values,
|
||||
}
|
||||
constructor(value = "") {
|
||||
super()
|
||||
this.value = value
|
||||
}
|
||||
|
||||
serialize(
|
||||
insideString = false,
|
||||
indentation = "",
|
||||
Self = /** @type {typeof IEntity} */(this.constructor),
|
||||
) {
|
||||
let result = this.value
|
||||
if (Self.serialized) {
|
||||
result = `"${result}"`
|
||||
}
|
||||
super(values)
|
||||
/** @type {String} */ this.value
|
||||
}
|
||||
|
||||
valueOf() {
|
||||
return this.value
|
||||
return result
|
||||
}
|
||||
|
||||
toString() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user