@cyclonium/2d 0.0.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/editor.d.ts +2 -0
- package/lib/editor.js +1 -0
- package/lib/index.d.ts +7 -0
- package/lib/index.js +6 -0
- package/lib/sort-settings.d.ts +17 -0
- package/lib/sort-settings.js +75 -0
- package/lib/sortable.d.ts +16 -0
- package/lib/sortable.js +19 -0
- package/lib/sorting-group.component.d.ts +10 -0
- package/lib/sorting-group.component.js +36 -0
- package/lib/sorting-layer.d.ts +4 -0
- package/lib/sorting-layer.js +2 -0
- package/lib/sorting-tree.d.ts +38 -0
- package/lib/sorting-tree.js +417 -0
- package/lib/sprite-renderer.component.d.ts +63 -0
- package/lib/sprite-renderer.component.js +432 -0
- package/lib/sprite-renderer.editor.d.ts +6 -0
- package/lib/sprite-renderer.editor.js +39 -0
- package/lib/sprite.d.ts +2 -0
- package/lib/sprite.js +1 -0
- package/package.json +46 -0
package/lib/editor.d.ts
ADDED
package/lib/editor.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SpriteRendererEditor } from './sprite-renderer.editor.js';
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { MAX_ORDER_IN_LAYER, MIN_ORDER_IN_LAYER, SortableRenderer, computeLocalSortingKey, } from './sortable.js';
|
|
2
|
+
export { SortSettings } from './sort-settings.js';
|
|
3
|
+
export { SortingGroup } from './sorting-group.component.js';
|
|
4
|
+
export { SortingLayer } from './sorting-layer.js';
|
|
5
|
+
export { SpriteRenderer, SpriteRenderType } from './sprite-renderer.component.js';
|
|
6
|
+
export { Sprite } from './sprite.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { MAX_ORDER_IN_LAYER, MIN_ORDER_IN_LAYER, SortableRenderer, computeLocalSortingKey, } from './sortable.js';
|
|
2
|
+
export { SortSettings } from './sort-settings.js';
|
|
3
|
+
export { SortingGroup } from './sorting-group.component.js';
|
|
4
|
+
export { SortingLayer } from './sorting-layer.js';
|
|
5
|
+
export { SpriteRenderer, SpriteRenderType } from './sprite-renderer.component.js';
|
|
6
|
+
export { Sprite } from './sprite.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { SortableRenderer } from './sortable.js';
|
|
2
|
+
import type { SortingGroup } from './sorting-group.component.js';
|
|
3
|
+
export declare class SortSettings {
|
|
4
|
+
get sortingLayer(): number;
|
|
5
|
+
set sortingLayer(value: number);
|
|
6
|
+
get orderInLayer(): number;
|
|
7
|
+
set orderInLayer(value: number);
|
|
8
|
+
get connected(): boolean;
|
|
9
|
+
get sortingKey(): number;
|
|
10
|
+
connect(sortableRenderer: SortableRenderer | SortingGroup): void;
|
|
11
|
+
disconnect(): void;
|
|
12
|
+
private _sortingLayer;
|
|
13
|
+
private _orderInLayer;
|
|
14
|
+
private _sortingTreeNode;
|
|
15
|
+
private _updateRenderOrder;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=sort-settings.d.ts.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { cycloClass, editable, idem, designType, serializable } from '@cyclonium/core/legacy-decorator';
|
|
8
|
+
import { CCInteger } from 'cc';
|
|
9
|
+
import { MIN_ORDER_IN_LAYER, MAX_ORDER_IN_LAYER, SortableRenderer } from './sortable.js';
|
|
10
|
+
import { SortingLayer } from './sorting-layer.js';
|
|
11
|
+
import { SortingTreeNode } from './sorting-tree.js';
|
|
12
|
+
let SortSettings = class SortSettings {
|
|
13
|
+
get sortingLayer() {
|
|
14
|
+
return this._sortingLayer;
|
|
15
|
+
}
|
|
16
|
+
set sortingLayer(value) {
|
|
17
|
+
this._sortingLayer = value;
|
|
18
|
+
this._updateRenderOrder();
|
|
19
|
+
}
|
|
20
|
+
get orderInLayer() {
|
|
21
|
+
return this._orderInLayer;
|
|
22
|
+
}
|
|
23
|
+
set orderInLayer(value) {
|
|
24
|
+
this._orderInLayer = value;
|
|
25
|
+
this._updateRenderOrder();
|
|
26
|
+
}
|
|
27
|
+
get connected() {
|
|
28
|
+
return this._sortingTreeNode !== undefined;
|
|
29
|
+
}
|
|
30
|
+
get sortingKey() {
|
|
31
|
+
return this._sortingTreeNode?.sortingKeyStart ?? 0;
|
|
32
|
+
}
|
|
33
|
+
connect(sortableRenderer) {
|
|
34
|
+
if (this._sortingTreeNode) {
|
|
35
|
+
throw new Error('Already connected to a sorting tree node');
|
|
36
|
+
}
|
|
37
|
+
this._sortingTreeNode = SortingTreeNode.create(sortableRenderer);
|
|
38
|
+
}
|
|
39
|
+
disconnect() {
|
|
40
|
+
this._sortingTreeNode?.destroy();
|
|
41
|
+
this._sortingTreeNode = undefined;
|
|
42
|
+
}
|
|
43
|
+
_sortingLayer = 0;
|
|
44
|
+
_orderInLayer = 0;
|
|
45
|
+
_sortingTreeNode = undefined;
|
|
46
|
+
_updateRenderOrder() {
|
|
47
|
+
if (!this._sortingTreeNode) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
this._sortingTreeNode.update();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
__decorate([
|
|
54
|
+
editable,
|
|
55
|
+
idem,
|
|
56
|
+
designType(SortingLayer)
|
|
57
|
+
], SortSettings.prototype, "sortingLayer", null);
|
|
58
|
+
__decorate([
|
|
59
|
+
editable({
|
|
60
|
+
type: CCInteger,
|
|
61
|
+
min: MIN_ORDER_IN_LAYER,
|
|
62
|
+
max: MAX_ORDER_IN_LAYER,
|
|
63
|
+
}),
|
|
64
|
+
idem
|
|
65
|
+
], SortSettings.prototype, "orderInLayer", null);
|
|
66
|
+
__decorate([
|
|
67
|
+
serializable
|
|
68
|
+
], SortSettings.prototype, "_sortingLayer", void 0);
|
|
69
|
+
__decorate([
|
|
70
|
+
serializable
|
|
71
|
+
], SortSettings.prototype, "_orderInLayer", void 0);
|
|
72
|
+
SortSettings = __decorate([
|
|
73
|
+
cycloClass(`cyclo.SortSettings`)
|
|
74
|
+
], SortSettings);
|
|
75
|
+
export { SortSettings };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Component } from 'cc';
|
|
2
|
+
import type { SortSettings } from './sort-settings.js';
|
|
3
|
+
export interface SortableRenderer extends Component {
|
|
4
|
+
readonly [SortableRenderer.Tags.sortSettings]: SortSettings;
|
|
5
|
+
setSortingKey(sortingKey: number): void;
|
|
6
|
+
}
|
|
7
|
+
export declare namespace SortableRenderer {
|
|
8
|
+
namespace Tags {
|
|
9
|
+
const sortSettings: unique symbol;
|
|
10
|
+
}
|
|
11
|
+
function is(component: Component): component is SortableRenderer;
|
|
12
|
+
}
|
|
13
|
+
export declare const MIN_ORDER_IN_LAYER: number;
|
|
14
|
+
export declare const MAX_ORDER_IN_LAYER: number;
|
|
15
|
+
export declare function computeLocalSortingKey(sortSettings: SortSettings): number;
|
|
16
|
+
//# sourceMappingURL=sortable.d.ts.map
|
package/lib/sortable.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
2
|
+
export var SortableRenderer;
|
|
3
|
+
(function (SortableRenderer) {
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
5
|
+
let Tags;
|
|
6
|
+
(function (Tags) {
|
|
7
|
+
Tags.sortSettings = Symbol('isSortableRenderer');
|
|
8
|
+
})(Tags = SortableRenderer.Tags || (SortableRenderer.Tags = {}));
|
|
9
|
+
function is(component) {
|
|
10
|
+
return Tags.sortSettings in component;
|
|
11
|
+
}
|
|
12
|
+
SortableRenderer.is = is;
|
|
13
|
+
})(SortableRenderer || (SortableRenderer = {}));
|
|
14
|
+
const MAX_ORDER_IN_LAYER_POW = 16;
|
|
15
|
+
export const MIN_ORDER_IN_LAYER = -(2 ** (MAX_ORDER_IN_LAYER_POW - 1));
|
|
16
|
+
export const MAX_ORDER_IN_LAYER = 2 ** (MAX_ORDER_IN_LAYER_POW - 1);
|
|
17
|
+
export function computeLocalSortingKey(sortSettings) {
|
|
18
|
+
return (sortSettings.sortingLayer << MAX_ORDER_IN_LAYER_POW) + (sortSettings.orderInLayer + MAX_ORDER_IN_LAYER);
|
|
19
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { CycloComponent } from '@cyclonium/core/framework';
|
|
2
|
+
import { SortSettings } from './sort-settings.js';
|
|
3
|
+
export declare class SortingGroup extends CycloComponent {
|
|
4
|
+
get sortSettings(): SortSettings;
|
|
5
|
+
protected onDestroy(): void;
|
|
6
|
+
protected onEnabled(): void;
|
|
7
|
+
protected onDisabled(): void;
|
|
8
|
+
private _sortSettings;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=sorting-group.component.d.ts.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/// <meta "uuid"="d716c0b9-0a04-4247-b7a1-21f76bbf0e3b"/>
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
import { CycloComponent } from '@cyclonium/core/framework';
|
|
9
|
+
import { editable, executeInEditMode, cycloClass, serializable } from '@cyclonium/core/legacy-decorator';
|
|
10
|
+
import { SortSettings } from './sort-settings.js';
|
|
11
|
+
let SortingGroup = class SortingGroup extends CycloComponent {
|
|
12
|
+
get sortSettings() {
|
|
13
|
+
return this._sortSettings;
|
|
14
|
+
}
|
|
15
|
+
onDestroy() {
|
|
16
|
+
this._sortSettings.disconnect();
|
|
17
|
+
}
|
|
18
|
+
onEnabled() {
|
|
19
|
+
this._sortSettings.connect(this);
|
|
20
|
+
}
|
|
21
|
+
onDisabled() {
|
|
22
|
+
this._sortSettings.disconnect();
|
|
23
|
+
}
|
|
24
|
+
_sortSettings = new SortSettings();
|
|
25
|
+
};
|
|
26
|
+
__decorate([
|
|
27
|
+
editable
|
|
28
|
+
], SortingGroup.prototype, "sortSettings", null);
|
|
29
|
+
__decorate([
|
|
30
|
+
serializable
|
|
31
|
+
], SortingGroup.prototype, "_sortSettings", void 0);
|
|
32
|
+
SortingGroup = __decorate([
|
|
33
|
+
cycloClass(`cyclo.SortingGroup`),
|
|
34
|
+
executeInEditMode
|
|
35
|
+
], SortingGroup);
|
|
36
|
+
export { SortingGroup };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { SortingGroup } from './sorting-group.component.js';
|
|
2
|
+
import { SortableRenderer } from './sortable.js';
|
|
3
|
+
export declare class SortingTreeNode {
|
|
4
|
+
private readonly _component;
|
|
5
|
+
private readonly _treeNodeMap;
|
|
6
|
+
static create(component: SortingGroup | SortableRenderer): SortingTreeNode;
|
|
7
|
+
get sortingKeyStart(): number;
|
|
8
|
+
destroy(): void;
|
|
9
|
+
update(): void;
|
|
10
|
+
private static _sceneSortingRecordMap;
|
|
11
|
+
private static _printSortingTree;
|
|
12
|
+
private static _computeLCAs;
|
|
13
|
+
private static _computeLCA;
|
|
14
|
+
private static _computeLocalSortingKey;
|
|
15
|
+
private static _compareLocalSortingKey;
|
|
16
|
+
private static _ensureSceneSortingRecord;
|
|
17
|
+
private static _getOrCreateParent;
|
|
18
|
+
private static _getOrCreateNode;
|
|
19
|
+
private static _createComponentSortingTreeNode;
|
|
20
|
+
private static _constructSubTree;
|
|
21
|
+
private static _queueUpdate;
|
|
22
|
+
private constructor();
|
|
23
|
+
private _parent;
|
|
24
|
+
private _children;
|
|
25
|
+
private _sortingKeyStart;
|
|
26
|
+
private _sortingKeyEnd;
|
|
27
|
+
private _sortSettings;
|
|
28
|
+
private _childrenSorted;
|
|
29
|
+
private _ensureChildrenSorted;
|
|
30
|
+
private _addChildSlightly;
|
|
31
|
+
private _removeChildSlightly;
|
|
32
|
+
private _transferChildren;
|
|
33
|
+
private _updateAll;
|
|
34
|
+
private _updateChild;
|
|
35
|
+
private _updateChildRangeSlightly;
|
|
36
|
+
private _updateChildFrom;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=sorting-tree.d.ts.map
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
import { Node, Scene } from 'cc';
|
|
2
|
+
import { SortingGroup } from './sorting-group.component.js';
|
|
3
|
+
import { assert } from '@cyclonium/core/utils';
|
|
4
|
+
import { logger } from '@cyclonium/core/log';
|
|
5
|
+
import { EDITOR_NOT_IN_PREVIEW } from 'cc/env';
|
|
6
|
+
import { computeLocalSortingKey, SortableRenderer } from './sortable.js';
|
|
7
|
+
const ENABLE_SORTING_TREE_DEBUG = false && !EDITOR_NOT_IN_PREVIEW;
|
|
8
|
+
const debugLog = ENABLE_SORTING_TREE_DEBUG
|
|
9
|
+
? (() => {
|
|
10
|
+
let indent = 0;
|
|
11
|
+
return {
|
|
12
|
+
incIndent() {
|
|
13
|
+
indent++;
|
|
14
|
+
},
|
|
15
|
+
decIndent() {
|
|
16
|
+
indent--;
|
|
17
|
+
},
|
|
18
|
+
log: (head, ...args) => {
|
|
19
|
+
const tag = '[SortingTree]';
|
|
20
|
+
if (typeof head === 'string') {
|
|
21
|
+
logger.info(`${tag} ${' '.repeat(indent)}${head}`, ...args);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
logger.info(tag, ...args);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
})()
|
|
29
|
+
: undefined;
|
|
30
|
+
export class SortingTreeNode {
|
|
31
|
+
_component;
|
|
32
|
+
_treeNodeMap;
|
|
33
|
+
static create(component) {
|
|
34
|
+
if (ENABLE_SORTING_TREE_DEBUG) {
|
|
35
|
+
debugLog?.log(`Request creating sorting tree node: ${component.name}`);
|
|
36
|
+
}
|
|
37
|
+
const sceneSortingRecord = this._ensureSceneSortingRecord(component);
|
|
38
|
+
const sortingTreeNode = this._getOrCreateNode(sceneSortingRecord, component);
|
|
39
|
+
let nodeToUpdate;
|
|
40
|
+
const { rootTreeNode, treeChangeContext: { dirtyTreeNodes, rootNodeInvolved, }, } = sceneSortingRecord;
|
|
41
|
+
if (rootNodeInvolved) {
|
|
42
|
+
nodeToUpdate = rootTreeNode;
|
|
43
|
+
}
|
|
44
|
+
else if (dirtyTreeNodes.length === 0) {
|
|
45
|
+
nodeToUpdate = undefined;
|
|
46
|
+
if (ENABLE_SORTING_TREE_DEBUG) {
|
|
47
|
+
debugLog?.log(`No dirty tree nodes, not updating any node`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
const lca = this._computeLCAs(sceneSortingRecord.rootTreeNode, dirtyTreeNodes);
|
|
52
|
+
nodeToUpdate = lca;
|
|
53
|
+
}
|
|
54
|
+
nodeToUpdate?._updateAll();
|
|
55
|
+
return sortingTreeNode;
|
|
56
|
+
}
|
|
57
|
+
get sortingKeyStart() {
|
|
58
|
+
return this._sortingKeyStart;
|
|
59
|
+
}
|
|
60
|
+
destroy() {
|
|
61
|
+
const parent = this._parent;
|
|
62
|
+
assert(parent);
|
|
63
|
+
if (this._component) {
|
|
64
|
+
this._treeNodeMap?.delete(this._component);
|
|
65
|
+
}
|
|
66
|
+
parent._removeChildSlightly(this);
|
|
67
|
+
const children = this._children;
|
|
68
|
+
this._children = [];
|
|
69
|
+
parent._transferChildren(children);
|
|
70
|
+
parent._updateAll();
|
|
71
|
+
}
|
|
72
|
+
update() {
|
|
73
|
+
this._parent?._updateChild(this);
|
|
74
|
+
if (ENABLE_SORTING_TREE_DEBUG) {
|
|
75
|
+
let root;
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
77
|
+
for (let node = this;;) {
|
|
78
|
+
if (!node._parent) {
|
|
79
|
+
root = node;
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
node = node._parent;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
SortingTreeNode._printSortingTree?.(root);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
static _sceneSortingRecordMap = new WeakMap();
|
|
90
|
+
static _printSortingTree = (() => {
|
|
91
|
+
if (!ENABLE_SORTING_TREE_DEBUG) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
return (root) => {
|
|
95
|
+
let output = '== Sorting Tree ==\n';
|
|
96
|
+
function print(node, depth = 0) {
|
|
97
|
+
const sortingRange = node._sortingKeyEnd - node._sortingKeyStart === 1
|
|
98
|
+
? `${node._sortingKeyStart}`
|
|
99
|
+
: `${node._sortingKeyStart}-${node._sortingKeyEnd}`;
|
|
100
|
+
output += `${' '.repeat(depth)}[${sortingRange}]${node._component?.name ?? '<root>'}`;
|
|
101
|
+
if (node._sortSettings) {
|
|
102
|
+
const sortSettings = node._sortSettings;
|
|
103
|
+
output += ` (SortingLayer: ${sortSettings.sortingLayer}, OrderInLayer: ${sortSettings.orderInLayer})`;
|
|
104
|
+
}
|
|
105
|
+
output += `\n`;
|
|
106
|
+
node._children.forEach((child) => print(child, depth + 1));
|
|
107
|
+
}
|
|
108
|
+
print(root);
|
|
109
|
+
debugLog?.log(output);
|
|
110
|
+
};
|
|
111
|
+
})();
|
|
112
|
+
static _computeLCAs(root, nodes) {
|
|
113
|
+
assert(nodes.length > 0);
|
|
114
|
+
if (nodes.length === 1) {
|
|
115
|
+
return nodes[0];
|
|
116
|
+
}
|
|
117
|
+
const lca = nodes[0];
|
|
118
|
+
for (let i = 1; i < nodes.length; i++) {
|
|
119
|
+
const which = this._computeLCA(lca, nodes[i]);
|
|
120
|
+
if (!which) {
|
|
121
|
+
return root;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return lca;
|
|
125
|
+
}
|
|
126
|
+
static _computeLCA(a, b) {
|
|
127
|
+
// 实现很垃圾,需要优化
|
|
128
|
+
const ancestors = new Set();
|
|
129
|
+
for (let current = a; current; current = current._parent) {
|
|
130
|
+
ancestors.add(current);
|
|
131
|
+
}
|
|
132
|
+
for (let current = b; current; current = current._parent) {
|
|
133
|
+
if (ancestors.has(current)) {
|
|
134
|
+
return current;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
static _computeLocalSortingKey(sortingNode) {
|
|
140
|
+
assert(sortingNode._sortSettings);
|
|
141
|
+
return computeLocalSortingKey(sortingNode._sortSettings);
|
|
142
|
+
}
|
|
143
|
+
static _compareLocalSortingKey(a, b) {
|
|
144
|
+
return this._computeLocalSortingKey(a) - this._computeLocalSortingKey(b);
|
|
145
|
+
}
|
|
146
|
+
static _ensureSceneSortingRecord(component) {
|
|
147
|
+
const scene = component.node.scene;
|
|
148
|
+
let sceneSortingRecord = this._sceneSortingRecordMap.get(scene);
|
|
149
|
+
if (sceneSortingRecord) {
|
|
150
|
+
sceneSortingRecord.treeChangeContext.rootNodeInvolved = false;
|
|
151
|
+
sceneSortingRecord.treeChangeContext.dirtyTreeNodes.length = 0;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
const treeNodeMap = new WeakMap();
|
|
155
|
+
if (ENABLE_SORTING_TREE_DEBUG) {
|
|
156
|
+
debugLog?.log(`Create(PASSIVE) sorting tree root`);
|
|
157
|
+
}
|
|
158
|
+
const rootTreeNode = new SortingTreeNode(undefined, undefined);
|
|
159
|
+
sceneSortingRecord = {
|
|
160
|
+
rootTreeNode,
|
|
161
|
+
treeNodeMap,
|
|
162
|
+
treeChangeContext: {
|
|
163
|
+
dirtyTreeNodes: [],
|
|
164
|
+
rootNodeInvolved: true,
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
this._sceneSortingRecordMap.set(scene, sceneSortingRecord);
|
|
168
|
+
if (ENABLE_SORTING_TREE_DEBUG) {
|
|
169
|
+
debugLog?.log(`[NOTICE] Creating subtrees from <root> top-to-bottom`);
|
|
170
|
+
}
|
|
171
|
+
debugLog?.incIndent();
|
|
172
|
+
this._constructSubTree(sceneSortingRecord, rootTreeNode, scene);
|
|
173
|
+
debugLog?.decIndent();
|
|
174
|
+
if (ENABLE_SORTING_TREE_DEBUG) {
|
|
175
|
+
Object.defineProperty(globalThis, '__printSortingTree__', {
|
|
176
|
+
value: this._printSortingTree?.bind(this, rootTreeNode),
|
|
177
|
+
writable: true,
|
|
178
|
+
configurable: true,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return sceneSortingRecord;
|
|
183
|
+
}
|
|
184
|
+
static _getOrCreateParent(sceneSortingRecord, component) {
|
|
185
|
+
const startSceneNode = component instanceof SortingGroup ? component.node.parent : component.node;
|
|
186
|
+
for (let currentNode = startSceneNode; currentNode; currentNode = currentNode.parent) {
|
|
187
|
+
const sortingGroup = currentNode.getComponent(SortingGroup);
|
|
188
|
+
if (sortingGroup && sortingGroup.enabledInHierarchy) {
|
|
189
|
+
const sortingTreeNode = this._getOrCreateNode(sceneSortingRecord, sortingGroup);
|
|
190
|
+
return sortingTreeNode;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return sceneSortingRecord.rootTreeNode;
|
|
194
|
+
}
|
|
195
|
+
static _getOrCreateNode(sceneSortingRecord, component) {
|
|
196
|
+
{
|
|
197
|
+
const existingTreeNode = sceneSortingRecord.treeNodeMap.get(component);
|
|
198
|
+
if (existingTreeNode) {
|
|
199
|
+
return existingTreeNode;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
debugLog?.incIndent();
|
|
203
|
+
const parentTreeNode = this._getOrCreateParent(sceneSortingRecord, component);
|
|
204
|
+
debugLog?.decIndent();
|
|
205
|
+
{
|
|
206
|
+
const existingTreeNode = sceneSortingRecord.treeNodeMap.get(component);
|
|
207
|
+
if (existingTreeNode) {
|
|
208
|
+
return existingTreeNode;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (ENABLE_SORTING_TREE_DEBUG) {
|
|
212
|
+
debugLog?.log(`[DYNAMIC] Create sorting tree node for ${component.name}`);
|
|
213
|
+
}
|
|
214
|
+
const newSortingTreeNode = this._createComponentSortingTreeNode(sceneSortingRecord, component);
|
|
215
|
+
parentTreeNode._addChildSlightly(newSortingTreeNode);
|
|
216
|
+
SortingTreeNode._queueUpdate(sceneSortingRecord, parentTreeNode);
|
|
217
|
+
if (component instanceof SortingGroup) {
|
|
218
|
+
debugLog?.log(`[NOTICE] Creating subtrees from ${component.name} top-to-bottom`);
|
|
219
|
+
this._constructSubTree(sceneSortingRecord, newSortingTreeNode, component.node);
|
|
220
|
+
}
|
|
221
|
+
sceneSortingRecord.treeChangeContext.dirtyTreeNodes.push(parentTreeNode);
|
|
222
|
+
return newSortingTreeNode;
|
|
223
|
+
}
|
|
224
|
+
static _createComponentSortingTreeNode(sceneSortingRecord, component) {
|
|
225
|
+
const sortingTreeNode = new SortingTreeNode(component, sceneSortingRecord.treeNodeMap);
|
|
226
|
+
sceneSortingRecord.treeNodeMap.set(component, sortingTreeNode);
|
|
227
|
+
return sortingTreeNode;
|
|
228
|
+
}
|
|
229
|
+
static _constructSubTree(sceneSortingRecord, sortingNode, sceneNode) {
|
|
230
|
+
// All sortable renderers of CURRENT node are our children.
|
|
231
|
+
for (const component of sceneNode.components) {
|
|
232
|
+
if (SortableRenderer.is(component) && component.enabledInHierarchy) {
|
|
233
|
+
let childTreeNode = sceneSortingRecord.treeNodeMap.get(component);
|
|
234
|
+
if (childTreeNode) {
|
|
235
|
+
// Add component to a node already involved in sorting tree.
|
|
236
|
+
assert(childTreeNode._parent);
|
|
237
|
+
childTreeNode._parent._removeChildSlightly(childTreeNode);
|
|
238
|
+
SortingTreeNode._queueUpdate(sceneSortingRecord, childTreeNode._parent);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
// Add a brand new sub tree.
|
|
242
|
+
if (ENABLE_SORTING_TREE_DEBUG) {
|
|
243
|
+
debugLog?.log(`Create(PASSIVE) sorting tree node for ${component.name}`);
|
|
244
|
+
}
|
|
245
|
+
childTreeNode = SortingTreeNode._createComponentSortingTreeNode(sceneSortingRecord, component);
|
|
246
|
+
}
|
|
247
|
+
sortingNode._addChildSlightly(childTreeNode);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// For child scene nodes,
|
|
251
|
+
for (const childSceneNode of sceneNode.children) {
|
|
252
|
+
// If child scene node is a sorting group, then itself is our child,
|
|
253
|
+
// but its child scene nodes are not our children.
|
|
254
|
+
const childSortingGroup = childSceneNode.getComponent(SortingGroup);
|
|
255
|
+
if (childSortingGroup && childSortingGroup.enabledInHierarchy) {
|
|
256
|
+
let childTreeNode = sceneSortingRecord.treeNodeMap.get(childSortingGroup);
|
|
257
|
+
if (childTreeNode) {
|
|
258
|
+
// Add component to a node already involved in sorting tree.
|
|
259
|
+
assert(childTreeNode._parent);
|
|
260
|
+
childTreeNode._parent._removeChildSlightly(childTreeNode);
|
|
261
|
+
SortingTreeNode._queueUpdate(sceneSortingRecord, childTreeNode._parent);
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
// Add a brand new sub tree.
|
|
265
|
+
if (ENABLE_SORTING_TREE_DEBUG) {
|
|
266
|
+
debugLog?.log(`Create(PASSIVE) sorting tree node for ${childSortingGroup.name}`);
|
|
267
|
+
}
|
|
268
|
+
childTreeNode = SortingTreeNode._createComponentSortingTreeNode(sceneSortingRecord, childSortingGroup);
|
|
269
|
+
debugLog?.incIndent();
|
|
270
|
+
this._constructSubTree(sceneSortingRecord, childTreeNode, childSceneNode);
|
|
271
|
+
debugLog?.decIndent();
|
|
272
|
+
}
|
|
273
|
+
sortingNode._addChildSlightly(childTreeNode);
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
// Otherwise, the child scene node is our child.
|
|
277
|
+
this._constructSubTree(sceneSortingRecord, sortingNode, childSceneNode);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
static _queueUpdate(sceneSortingRecord, sortingNode) {
|
|
281
|
+
const { rootTreeNode, treeChangeContext: { rootNodeInvolved, dirtyTreeNodes, }, } = sceneSortingRecord;
|
|
282
|
+
if (rootNodeInvolved) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (sortingNode === rootTreeNode) {
|
|
286
|
+
sceneSortingRecord.treeChangeContext.rootNodeInvolved = true;
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
dirtyTreeNodes.push(sortingNode);
|
|
290
|
+
}
|
|
291
|
+
constructor(_component, _treeNodeMap) {
|
|
292
|
+
this._component = _component;
|
|
293
|
+
this._treeNodeMap = _treeNodeMap;
|
|
294
|
+
this._sortSettings = _component instanceof SortingGroup
|
|
295
|
+
? _component.sortSettings
|
|
296
|
+
: _component
|
|
297
|
+
? _component[SortableRenderer.Tags.sortSettings]
|
|
298
|
+
: undefined;
|
|
299
|
+
}
|
|
300
|
+
_parent = undefined;
|
|
301
|
+
_children = [];
|
|
302
|
+
_sortingKeyStart = 0;
|
|
303
|
+
_sortingKeyEnd = 0;
|
|
304
|
+
_sortSettings = undefined;
|
|
305
|
+
_childrenSorted = true;
|
|
306
|
+
_ensureChildrenSorted() {
|
|
307
|
+
if (!this._childrenSorted) {
|
|
308
|
+
this._children.sort(SortingTreeNode._compareLocalSortingKey.bind(SortingTreeNode));
|
|
309
|
+
this._childrenSorted = true;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
_addChildSlightly(child) {
|
|
313
|
+
if (ENABLE_SORTING_TREE_DEBUG) {
|
|
314
|
+
debugLog?.log(`${this._component?.name ?? '<root>'} ·---> ${child._component?.name ?? '<root>'}`);
|
|
315
|
+
}
|
|
316
|
+
assert(!child._parent);
|
|
317
|
+
child._parent = this;
|
|
318
|
+
this._children.push(child);
|
|
319
|
+
this._childrenSorted = false;
|
|
320
|
+
}
|
|
321
|
+
_removeChildSlightly(child) {
|
|
322
|
+
assert(child._parent === this);
|
|
323
|
+
child._parent = undefined;
|
|
324
|
+
this._children = this._children.filter((c) => c !== child);
|
|
325
|
+
}
|
|
326
|
+
_transferChildren(children) {
|
|
327
|
+
children.forEach((child) => {
|
|
328
|
+
child._parent = this;
|
|
329
|
+
});
|
|
330
|
+
this._children.push(...children);
|
|
331
|
+
this._childrenSorted = false;
|
|
332
|
+
}
|
|
333
|
+
_updateAll() {
|
|
334
|
+
// Full sort
|
|
335
|
+
this._ensureChildrenSorted();
|
|
336
|
+
this._updateChildFrom(0);
|
|
337
|
+
if (ENABLE_SORTING_TREE_DEBUG) {
|
|
338
|
+
SortingTreeNode._printSortingTree?.(this);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
_updateChild(child) {
|
|
342
|
+
const childIndex = this._children.indexOf(child);
|
|
343
|
+
if (childIndex < 0) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
const affectingChildIndex = resort(this._children, childIndex, SortingTreeNode._compareLocalSortingKey.bind(SortingTreeNode));
|
|
347
|
+
this._updateChildFrom(affectingChildIndex);
|
|
348
|
+
}
|
|
349
|
+
_updateChildRangeSlightly(startChildIndex) {
|
|
350
|
+
let sortingKey = startChildIndex === 0 ? this._sortingKeyStart : this._children[startChildIndex - 1]._sortingKeyEnd;
|
|
351
|
+
for (let iChild = startChildIndex; iChild < this._children.length; iChild++) {
|
|
352
|
+
const child = this._children[iChild];
|
|
353
|
+
child._ensureChildrenSorted();
|
|
354
|
+
child._sortingKeyStart = sortingKey;
|
|
355
|
+
if (child._component && SortableRenderer.is(child._component)) {
|
|
356
|
+
assert(child._children.length === 0);
|
|
357
|
+
child._sortingKeyEnd = sortingKey + 1;
|
|
358
|
+
child._component.setSortingKey(sortingKey);
|
|
359
|
+
sortingKey = child._sortingKeyEnd;
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
child._updateChildRangeSlightly(0);
|
|
363
|
+
sortingKey = child._sortingKeyEnd;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
this._sortingKeyEnd = sortingKey;
|
|
367
|
+
}
|
|
368
|
+
_updateChildFrom(startChildIndex) {
|
|
369
|
+
const oldSortingKey = this._sortingKeyEnd;
|
|
370
|
+
this._updateChildRangeSlightly(startChildIndex);
|
|
371
|
+
// Our elements keep the same sorting orders.
|
|
372
|
+
if (oldSortingKey !== this._sortingKeyEnd) {
|
|
373
|
+
// Notify parent we're changed.
|
|
374
|
+
this._parent?._updateChild(this);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* 重新排序已经排序的数组的元素,只有 `index` 会被重新排序。
|
|
380
|
+
* 返回插入位置。
|
|
381
|
+
*/
|
|
382
|
+
function resort(array, index, compare) {
|
|
383
|
+
const bound = lowerBoundIgnore(array, index, compare);
|
|
384
|
+
if (bound === index) {
|
|
385
|
+
return index;
|
|
386
|
+
}
|
|
387
|
+
else if (bound < index) {
|
|
388
|
+
const element = array[index];
|
|
389
|
+
for (let i = index - 1; i >= bound; i--) {
|
|
390
|
+
array[i + 1] = array[i];
|
|
391
|
+
}
|
|
392
|
+
array[bound] = element;
|
|
393
|
+
return bound;
|
|
394
|
+
}
|
|
395
|
+
else { // bound > index
|
|
396
|
+
const element = array[index];
|
|
397
|
+
for (let i = index; i < bound; i++) {
|
|
398
|
+
array[i] = array[i + 1];
|
|
399
|
+
}
|
|
400
|
+
array[bound - 1] = element;
|
|
401
|
+
return index;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
function lowerBoundIgnore(array, index, compare) {
|
|
405
|
+
const length = array.length;
|
|
406
|
+
let iFirstGreaterThan = length;
|
|
407
|
+
for (let i = 0; i < length; i++) {
|
|
408
|
+
if (i === index) {
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
if (compare(array[i], array[index]) > 0) {
|
|
412
|
+
iFirstGreaterThan = i;
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return iFirstGreaterThan;
|
|
417
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { CycloComponent } from '@cyclonium/core/framework';
|
|
2
|
+
import { Texture2D, Material, Color, componentEditorTraits, geometry } from 'cc';
|
|
3
|
+
import { Bounds2D } from '@cyclonium/core/math/bounds-2d';
|
|
4
|
+
import { SortableRenderer } from './sortable.js';
|
|
5
|
+
import { SortSettings } from './sort-settings.js';
|
|
6
|
+
export declare enum SpriteRenderType {
|
|
7
|
+
simple = "simple"
|
|
8
|
+
}
|
|
9
|
+
export declare class SpriteRenderer extends CycloComponent implements SortableRenderer {
|
|
10
|
+
static prepare(): Promise<void>;
|
|
11
|
+
get renderType(): SpriteRenderType;
|
|
12
|
+
set renderType(value: SpriteRenderType);
|
|
13
|
+
get color(): Color;
|
|
14
|
+
set color(value: Color);
|
|
15
|
+
get sprite(): Texture2D | undefined;
|
|
16
|
+
set sprite(value: Texture2D | undefined);
|
|
17
|
+
debugShowNormals: boolean;
|
|
18
|
+
get sortSettings(): SortSettings;
|
|
19
|
+
get [SortableRenderer.Tags.sortSettings](): SortSettings;
|
|
20
|
+
get pixelsPerUnit(): number;
|
|
21
|
+
set pixelsPerUnit(value: number);
|
|
22
|
+
get material(): Material | null;
|
|
23
|
+
set material(value: Material | null);
|
|
24
|
+
/**
|
|
25
|
+
* The renderer's quad bounds in node-local space. The returned object should be treated as readonly.
|
|
26
|
+
* When no sprite is assigned, this returns the default placeholder quad bounds from -1..1 on both axes.
|
|
27
|
+
*/
|
|
28
|
+
get localBounds(): Bounds2D;
|
|
29
|
+
setSortingKey(sortingKey: number): void;
|
|
30
|
+
[componentEditorTraits.BoundingComponent.Tags.getBoundingBox](): geometry.AABB;
|
|
31
|
+
protected onAwake(): void;
|
|
32
|
+
protected onDestroy(): void;
|
|
33
|
+
protected onEnabled(): void;
|
|
34
|
+
protected onDisabled(): void;
|
|
35
|
+
protected onUpdate(): void;
|
|
36
|
+
private _renderType;
|
|
37
|
+
private readonly _color;
|
|
38
|
+
private _sprite;
|
|
39
|
+
private _pixelsPerUnit;
|
|
40
|
+
private _material;
|
|
41
|
+
private _sortSettings;
|
|
42
|
+
private _renderRecord;
|
|
43
|
+
private _effectiveMaterial;
|
|
44
|
+
private _localBounds;
|
|
45
|
+
private _getMaterial;
|
|
46
|
+
private _initializeEffectiveMaterial;
|
|
47
|
+
private _createDefaultEffectMaterialAndSetAsEffective;
|
|
48
|
+
private _setEffectiveMaterial;
|
|
49
|
+
private _destroyRenderRecord;
|
|
50
|
+
private _constructRenderRecord;
|
|
51
|
+
private _createRenderingSubMeshes;
|
|
52
|
+
private _createRenderingSubMeshesInSimpleRenderType;
|
|
53
|
+
private _updateAllMaterialProperties;
|
|
54
|
+
private _updateMaterialColor;
|
|
55
|
+
private _updateMaterialSprite;
|
|
56
|
+
private _updateMaterialPixelsPerUnit;
|
|
57
|
+
private _updateModelBoundingShape;
|
|
58
|
+
private _updateLocalBounds;
|
|
59
|
+
private _getBoundingHalfSize;
|
|
60
|
+
private _syncRenderRecordEnabled;
|
|
61
|
+
private _shouldRenderRecordBeEnabled;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=sprite-renderer.component.d.ts.map
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
/// <meta "uuid"="1ca96b17-efb6-4805-961e-17c292d0840f"/>
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
import { CycloComponent } from '@cyclonium/core/framework';
|
|
9
|
+
import { designType, editable, executeInEditMode, cycloClass, idem, serializable } from '@cyclonium/core/legacy-decorator';
|
|
10
|
+
import { Texture2D, Material, Color, renderer, RenderingSubMesh, gfx, EffectAsset, Vec2, Vec3, componentEditorTraits, geometry, assetManager } from 'cc';
|
|
11
|
+
import { assert, markEnum } from '@cyclonium/core/utils';
|
|
12
|
+
import { logger } from '@cyclonium/core/log';
|
|
13
|
+
import { Bounds2D } from '@cyclonium/core/math/bounds-2d';
|
|
14
|
+
import { Sprite } from './sprite.js';
|
|
15
|
+
import { SortableRenderer } from './sortable.js';
|
|
16
|
+
import { SortSettings } from './sort-settings.js';
|
|
17
|
+
const ENABLE_RENDERING = true;
|
|
18
|
+
export var SpriteRenderType;
|
|
19
|
+
(function (SpriteRenderType) {
|
|
20
|
+
SpriteRenderType["simple"] = "simple";
|
|
21
|
+
})(SpriteRenderType || (SpriteRenderType = {}));
|
|
22
|
+
markEnum(SpriteRenderType);
|
|
23
|
+
const loadDefaultEffect = (() => {
|
|
24
|
+
let defaultEffect;
|
|
25
|
+
return () => {
|
|
26
|
+
if (defaultEffect instanceof Promise) {
|
|
27
|
+
return defaultEffect;
|
|
28
|
+
}
|
|
29
|
+
if (!defaultEffect) {
|
|
30
|
+
defaultEffect = (async () => {
|
|
31
|
+
const loadedEffect = await loadAsset('230b4535-10a9-475d-843a-05cbf87b2227');
|
|
32
|
+
defaultEffect = loadedEffect;
|
|
33
|
+
return loadedEffect;
|
|
34
|
+
})().catch((error) => {
|
|
35
|
+
logger.error('Failed to load default effect', error);
|
|
36
|
+
return undefined;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return defaultEffect;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
let SpriteRenderer = class SpriteRenderer extends CycloComponent {
|
|
43
|
+
static async prepare() {
|
|
44
|
+
const effect = await loadDefaultEffect();
|
|
45
|
+
if (!effect) {
|
|
46
|
+
throw new Error('Failed to load default sprite renderer effect');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
get renderType() {
|
|
50
|
+
return this._renderType;
|
|
51
|
+
}
|
|
52
|
+
set renderType(value) {
|
|
53
|
+
this._renderType = value;
|
|
54
|
+
this._constructRenderRecord();
|
|
55
|
+
}
|
|
56
|
+
get color() {
|
|
57
|
+
return this._color;
|
|
58
|
+
}
|
|
59
|
+
set color(value) {
|
|
60
|
+
this._color.set(value);
|
|
61
|
+
if (this._effectiveMaterial) {
|
|
62
|
+
this._updateMaterialColor(this._effectiveMaterial);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
get sprite() {
|
|
66
|
+
return this._sprite;
|
|
67
|
+
}
|
|
68
|
+
set sprite(value) {
|
|
69
|
+
this._sprite = value;
|
|
70
|
+
if (this._effectiveMaterial) {
|
|
71
|
+
this._updateMaterialSprite(this._effectiveMaterial);
|
|
72
|
+
}
|
|
73
|
+
if (this._renderRecord) {
|
|
74
|
+
this._updateModelBoundingShape(this._renderRecord.model);
|
|
75
|
+
this._syncRenderRecordEnabled();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
debugShowNormals = false;
|
|
79
|
+
get sortSettings() {
|
|
80
|
+
return this._sortSettings;
|
|
81
|
+
}
|
|
82
|
+
get [SortableRenderer.Tags.sortSettings]() {
|
|
83
|
+
return this._sortSettings;
|
|
84
|
+
}
|
|
85
|
+
get pixelsPerUnit() {
|
|
86
|
+
return this._pixelsPerUnit;
|
|
87
|
+
}
|
|
88
|
+
set pixelsPerUnit(value) {
|
|
89
|
+
this._pixelsPerUnit = value;
|
|
90
|
+
if (this._effectiveMaterial) {
|
|
91
|
+
this._updateMaterialPixelsPerUnit(this._effectiveMaterial);
|
|
92
|
+
}
|
|
93
|
+
if (this._renderRecord) {
|
|
94
|
+
this._updateModelBoundingShape(this._renderRecord.model);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
get material() {
|
|
98
|
+
return this._material;
|
|
99
|
+
}
|
|
100
|
+
set material(value) {
|
|
101
|
+
this._material = value;
|
|
102
|
+
if (value) {
|
|
103
|
+
this._setEffectiveMaterial(value);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
this._effectiveMaterial = undefined;
|
|
107
|
+
void this._initializeEffectiveMaterial();
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The renderer's quad bounds in node-local space. The returned object should be treated as readonly.
|
|
111
|
+
* When no sprite is assigned, this returns the default placeholder quad bounds from -1..1 on both axes.
|
|
112
|
+
*/
|
|
113
|
+
get localBounds() {
|
|
114
|
+
return this._updateLocalBounds(this._localBounds ??= new Bounds2D());
|
|
115
|
+
}
|
|
116
|
+
setSortingKey(sortingKey) {
|
|
117
|
+
if (this._renderRecord) {
|
|
118
|
+
this._renderRecord.model.priority = sortingKey;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
[componentEditorTraits.BoundingComponent.Tags.getBoundingBox]() {
|
|
122
|
+
const localBounds = this.localBounds;
|
|
123
|
+
const bounds = geometry.AABB.fromPoints(new geometry.AABB(), vec2ToVec3(localBounds.min), vec2ToVec3(localBounds.max));
|
|
124
|
+
bounds.transform(this.node.worldMatrix, null, null, null, bounds);
|
|
125
|
+
return bounds;
|
|
126
|
+
}
|
|
127
|
+
onAwake() {
|
|
128
|
+
const material = this._getMaterial();
|
|
129
|
+
void material;
|
|
130
|
+
}
|
|
131
|
+
onDestroy() {
|
|
132
|
+
super.onDestroy();
|
|
133
|
+
this._destroyRenderRecord();
|
|
134
|
+
}
|
|
135
|
+
onEnabled() {
|
|
136
|
+
this._sortSettings.connect(this);
|
|
137
|
+
this._syncRenderRecordEnabled();
|
|
138
|
+
}
|
|
139
|
+
onDisabled() {
|
|
140
|
+
this._sortSettings.disconnect();
|
|
141
|
+
if (this._renderRecord) {
|
|
142
|
+
this._renderRecord.model.priority = 0;
|
|
143
|
+
}
|
|
144
|
+
this._syncRenderRecordEnabled();
|
|
145
|
+
}
|
|
146
|
+
onUpdate() {
|
|
147
|
+
if (!ENABLE_RENDERING) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
switch (this.renderType) {
|
|
151
|
+
case SpriteRenderType.simple:
|
|
152
|
+
if (this._renderRecord) {
|
|
153
|
+
// nothing
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
this._destroyRenderRecord();
|
|
157
|
+
this._constructRenderRecord();
|
|
158
|
+
}
|
|
159
|
+
break;
|
|
160
|
+
default:
|
|
161
|
+
this._destroyRenderRecord();
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
_renderType = SpriteRenderType.simple;
|
|
166
|
+
_color = new Color(Color.WHITE);
|
|
167
|
+
_sprite = undefined;
|
|
168
|
+
_pixelsPerUnit = 100;
|
|
169
|
+
_material = null;
|
|
170
|
+
_sortSettings = new SortSettings();
|
|
171
|
+
_renderRecord = null;
|
|
172
|
+
_effectiveMaterial;
|
|
173
|
+
_localBounds;
|
|
174
|
+
_getMaterial() {
|
|
175
|
+
if (this._effectiveMaterial) {
|
|
176
|
+
return this._effectiveMaterial;
|
|
177
|
+
}
|
|
178
|
+
return this._initializeEffectiveMaterial();
|
|
179
|
+
}
|
|
180
|
+
_initializeEffectiveMaterial() {
|
|
181
|
+
assert(!this._effectiveMaterial);
|
|
182
|
+
if (this._material) {
|
|
183
|
+
return this._setEffectiveMaterial(this._material);
|
|
184
|
+
}
|
|
185
|
+
const defaultEffect = loadDefaultEffect();
|
|
186
|
+
if (defaultEffect instanceof EffectAsset) {
|
|
187
|
+
return this._createDefaultEffectMaterialAndSetAsEffective(defaultEffect);
|
|
188
|
+
}
|
|
189
|
+
const material = new Material();
|
|
190
|
+
material.reset({
|
|
191
|
+
effectName: 'builtin-unlit',
|
|
192
|
+
});
|
|
193
|
+
void defaultEffect.then((effect) => {
|
|
194
|
+
if (!effect) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (this._effectiveMaterial !== material) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
this._createDefaultEffectMaterialAndSetAsEffective(effect);
|
|
201
|
+
material.destroy();
|
|
202
|
+
});
|
|
203
|
+
return this._setEffectiveMaterial(material);
|
|
204
|
+
}
|
|
205
|
+
_createDefaultEffectMaterialAndSetAsEffective(effect) {
|
|
206
|
+
const material = new Material();
|
|
207
|
+
material.reset({
|
|
208
|
+
effectAsset: effect,
|
|
209
|
+
});
|
|
210
|
+
return this._setEffectiveMaterial(material);
|
|
211
|
+
}
|
|
212
|
+
_setEffectiveMaterial(material) {
|
|
213
|
+
this._effectiveMaterial = material;
|
|
214
|
+
this._updateAllMaterialProperties(material);
|
|
215
|
+
const renderRecord = this._renderRecord;
|
|
216
|
+
if (renderRecord) {
|
|
217
|
+
renderRecord.model.subModels.forEach((_, subModelIndex) => {
|
|
218
|
+
renderRecord.model.setSubModelMaterial(subModelIndex, material);
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
return material;
|
|
222
|
+
}
|
|
223
|
+
_destroyRenderRecord() {
|
|
224
|
+
if (!this._renderRecord) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const { renderScene, renderingSubMeshes, model, } = this._renderRecord;
|
|
228
|
+
this._renderRecord = null;
|
|
229
|
+
renderScene.removeModel(model);
|
|
230
|
+
renderingSubMeshes.forEach((subMesh) => {
|
|
231
|
+
subMesh.destroy();
|
|
232
|
+
});
|
|
233
|
+
model.destroy();
|
|
234
|
+
}
|
|
235
|
+
_constructRenderRecord() {
|
|
236
|
+
const renderScene = this._getRenderScene();
|
|
237
|
+
const device = renderScene.root.device;
|
|
238
|
+
const renderingSubMeshes = this._createRenderingSubMeshes(device);
|
|
239
|
+
const model = new renderer.scene.Model();
|
|
240
|
+
model.node = this.node;
|
|
241
|
+
model.transform = this.node;
|
|
242
|
+
this._updateModelBoundingShape(model);
|
|
243
|
+
const material = this._getMaterial();
|
|
244
|
+
renderingSubMeshes.forEach((subMesh, iSubModel) => {
|
|
245
|
+
model.initSubModel(iSubModel, subMesh, material);
|
|
246
|
+
});
|
|
247
|
+
model.visFlags = this.node.layer;
|
|
248
|
+
model.enabled = this._shouldRenderRecordBeEnabled();
|
|
249
|
+
renderScene.addModel(model);
|
|
250
|
+
this._renderRecord = {
|
|
251
|
+
renderingSubMeshes: renderingSubMeshes,
|
|
252
|
+
model: model,
|
|
253
|
+
renderScene,
|
|
254
|
+
};
|
|
255
|
+
if (this._sortSettings.connected) {
|
|
256
|
+
model.priority = this._sortSettings.sortingKey;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
_createRenderingSubMeshes(device) {
|
|
260
|
+
switch (this._renderType) {
|
|
261
|
+
case SpriteRenderType.simple:
|
|
262
|
+
return this._createRenderingSubMeshesInSimpleRenderType(device);
|
|
263
|
+
default:
|
|
264
|
+
throw new Error('Unsupported render type');
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
_createRenderingSubMeshesInSimpleRenderType(device) {
|
|
268
|
+
const { vertexCount, positions, normals, indices, } = simpleSpriteQuad;
|
|
269
|
+
const offsetPositionF = 0;
|
|
270
|
+
const stridePositionF = 2;
|
|
271
|
+
const offsetNormalF = offsetPositionF + stridePositionF;
|
|
272
|
+
const strideNormalF = 2;
|
|
273
|
+
const strideF = offsetNormalF + strideNormalF;
|
|
274
|
+
const localVertexBuffer = new Float32Array(vertexCount * strideF);
|
|
275
|
+
let pVerticesF = 0;
|
|
276
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
277
|
+
localVertexBuffer[pVerticesF + offsetPositionF] = positions[i * 2];
|
|
278
|
+
localVertexBuffer[pVerticesF + offsetPositionF + 1] = positions[i * 2 + 1];
|
|
279
|
+
localVertexBuffer[pVerticesF + offsetNormalF] = normals[i * 2];
|
|
280
|
+
localVertexBuffer[pVerticesF + offsetNormalF + 1] = normals[i * 2 + 1];
|
|
281
|
+
pVerticesF += strideF;
|
|
282
|
+
}
|
|
283
|
+
const vertexBuffer = device.createBuffer(new gfx.BufferInfo(gfx.BufferUsageBit.VERTEX, gfx.MemoryUsageBit.DEVICE, localVertexBuffer.byteLength, Float32Array.BYTES_PER_ELEMENT * strideF, gfx.BufferFlagBit.NONE));
|
|
284
|
+
updateGfxBuffer(vertexBuffer, localVertexBuffer);
|
|
285
|
+
const indexBuffer = device.createBuffer(new gfx.BufferInfo(gfx.BufferUsageBit.INDEX, gfx.MemoryUsageBit.DEVICE, indices.byteLength, indices.BYTES_PER_ELEMENT, gfx.BufferFlagBit.NONE));
|
|
286
|
+
updateGfxBuffer(indexBuffer, indices);
|
|
287
|
+
const renderingSubMesh = new RenderingSubMesh([vertexBuffer], [
|
|
288
|
+
new gfx.Attribute(gfx.AttributeName.ATTR_POSITION, gfx.Format.RGB32F, false, undefined, false, undefined),
|
|
289
|
+
new gfx.Attribute(gfx.AttributeName.ATTR_NORMAL, gfx.Format.RG32F, false, undefined, false, undefined),
|
|
290
|
+
new gfx.Attribute(gfx.AttributeName.ATTR_TEX_COORD, gfx.Format.RG32F, false, undefined, false, undefined),
|
|
291
|
+
], gfx.PrimitiveMode.TRIANGLE_LIST, indexBuffer, null, true);
|
|
292
|
+
return [renderingSubMesh];
|
|
293
|
+
}
|
|
294
|
+
_updateAllMaterialProperties(material) {
|
|
295
|
+
this._updateMaterialColor(material);
|
|
296
|
+
this._updateMaterialSprite(material);
|
|
297
|
+
this._updateMaterialPixelsPerUnit(material);
|
|
298
|
+
}
|
|
299
|
+
_updateMaterialColor(material) {
|
|
300
|
+
material.setProperty('mainColor', this.color);
|
|
301
|
+
}
|
|
302
|
+
_updateMaterialSprite(material) {
|
|
303
|
+
const pixelsPerUnit = this._pixelsPerUnit;
|
|
304
|
+
let mainTexture = null;
|
|
305
|
+
const spriteSize = new Vec2();
|
|
306
|
+
if (this.sprite) {
|
|
307
|
+
mainTexture = this.sprite;
|
|
308
|
+
spriteSize.set(this.sprite.width / 2, this.sprite.height / 2);
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
mainTexture = null;
|
|
312
|
+
spriteSize.set(pixelsPerUnit, pixelsPerUnit);
|
|
313
|
+
}
|
|
314
|
+
material.setProperty('mainTexture', mainTexture);
|
|
315
|
+
material.setProperty('spriteSize', spriteSize);
|
|
316
|
+
}
|
|
317
|
+
_updateMaterialPixelsPerUnit(material) {
|
|
318
|
+
material.setProperty('pixelsPerUnit', this._pixelsPerUnit);
|
|
319
|
+
}
|
|
320
|
+
_updateModelBoundingShape(model) {
|
|
321
|
+
const localBounds = this.localBounds;
|
|
322
|
+
model.createBoundingShape(new Vec3(localBounds.xMin, localBounds.yMin, 0), new Vec3(localBounds.xMax, localBounds.yMax, 0));
|
|
323
|
+
model.updateWorldBound();
|
|
324
|
+
}
|
|
325
|
+
_updateLocalBounds(bounds) {
|
|
326
|
+
const halfSize = this._getBoundingHalfSize();
|
|
327
|
+
bounds.min.set(-halfSize.x, -halfSize.y);
|
|
328
|
+
bounds.max.set(halfSize.x, halfSize.y);
|
|
329
|
+
return bounds;
|
|
330
|
+
}
|
|
331
|
+
_getBoundingHalfSize() {
|
|
332
|
+
if (!this.sprite) {
|
|
333
|
+
return new Vec2(1, 1);
|
|
334
|
+
}
|
|
335
|
+
return new Vec2(this.sprite.width / 2 / this.pixelsPerUnit, this.sprite.height / 2 / this.pixelsPerUnit);
|
|
336
|
+
}
|
|
337
|
+
_syncRenderRecordEnabled() {
|
|
338
|
+
if (this._renderRecord) {
|
|
339
|
+
this._renderRecord.model.enabled = this._shouldRenderRecordBeEnabled();
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
_shouldRenderRecordBeEnabled() {
|
|
343
|
+
return this.enabledInHierarchy && !!this.sprite;
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
__decorate([
|
|
347
|
+
editable,
|
|
348
|
+
serializable,
|
|
349
|
+
designType(SpriteRenderType)
|
|
350
|
+
], SpriteRenderer.prototype, "renderType", null);
|
|
351
|
+
__decorate([
|
|
352
|
+
editable
|
|
353
|
+
], SpriteRenderer.prototype, "color", null);
|
|
354
|
+
__decorate([
|
|
355
|
+
editable(Sprite)
|
|
356
|
+
], SpriteRenderer.prototype, "sprite", null);
|
|
357
|
+
__decorate([
|
|
358
|
+
editable,
|
|
359
|
+
serializable
|
|
360
|
+
], SpriteRenderer.prototype, "debugShowNormals", void 0);
|
|
361
|
+
__decorate([
|
|
362
|
+
editable
|
|
363
|
+
], SpriteRenderer.prototype, "sortSettings", null);
|
|
364
|
+
__decorate([
|
|
365
|
+
editable
|
|
366
|
+
], SpriteRenderer.prototype, "pixelsPerUnit", null);
|
|
367
|
+
__decorate([
|
|
368
|
+
editable(Material),
|
|
369
|
+
idem
|
|
370
|
+
], SpriteRenderer.prototype, "material", null);
|
|
371
|
+
__decorate([
|
|
372
|
+
serializable
|
|
373
|
+
], SpriteRenderer.prototype, "_renderType", void 0);
|
|
374
|
+
__decorate([
|
|
375
|
+
serializable
|
|
376
|
+
], SpriteRenderer.prototype, "_color", void 0);
|
|
377
|
+
__decorate([
|
|
378
|
+
serializable
|
|
379
|
+
], SpriteRenderer.prototype, "_sprite", void 0);
|
|
380
|
+
__decorate([
|
|
381
|
+
serializable
|
|
382
|
+
], SpriteRenderer.prototype, "_pixelsPerUnit", void 0);
|
|
383
|
+
__decorate([
|
|
384
|
+
serializable
|
|
385
|
+
], SpriteRenderer.prototype, "_material", void 0);
|
|
386
|
+
__decorate([
|
|
387
|
+
serializable
|
|
388
|
+
], SpriteRenderer.prototype, "_sortSettings", void 0);
|
|
389
|
+
SpriteRenderer = __decorate([
|
|
390
|
+
cycloClass('cyclo.SpriteRenderer'),
|
|
391
|
+
executeInEditMode
|
|
392
|
+
], SpriteRenderer);
|
|
393
|
+
export { SpriteRenderer };
|
|
394
|
+
const simpleSpriteQuad = (() => {
|
|
395
|
+
const positions = new Float32Array([
|
|
396
|
+
-1, -1, // left bottom
|
|
397
|
+
-1, 1, // left top
|
|
398
|
+
1, 1, // right top
|
|
399
|
+
1, -1, // right bottom
|
|
400
|
+
]);
|
|
401
|
+
const n = Math.sqrt(2);
|
|
402
|
+
const normals = positions.map((position) => n * position);
|
|
403
|
+
const indices = new Uint16Array([
|
|
404
|
+
0, 1, 2,
|
|
405
|
+
0, 2, 3,
|
|
406
|
+
]);
|
|
407
|
+
return {
|
|
408
|
+
vertexCount: 4,
|
|
409
|
+
positions,
|
|
410
|
+
normals,
|
|
411
|
+
indices,
|
|
412
|
+
};
|
|
413
|
+
})();
|
|
414
|
+
function loadAsset(uuid) {
|
|
415
|
+
return new Promise((resolve, reject) => {
|
|
416
|
+
assetManager.loadAny(uuid, (error, asset) => {
|
|
417
|
+
if (error) {
|
|
418
|
+
reject(error);
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
resolve(asset);
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
function updateGfxBuffer(buffer, source) {
|
|
427
|
+
// The `gfx.BufferSource` type does not contains ArrayBufferView, whereas in actual this works as expected.
|
|
428
|
+
buffer.update(source);
|
|
429
|
+
}
|
|
430
|
+
function vec2ToVec3(input) {
|
|
431
|
+
return new Vec3(input.x, input.y, 0);
|
|
432
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ComponentEditor, type SceneGuiContext } from '@cyclonium/editor';
|
|
2
|
+
import { SpriteRenderer } from './sprite-renderer.component.js';
|
|
3
|
+
export declare class SpriteRendererEditor extends ComponentEditor<SpriteRenderer> {
|
|
4
|
+
protected onSceneGUI(ctx: SceneGuiContext): void;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=sprite-renderer.editor.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { Vec2 } from '@cyclonium/core/math/vec2';
|
|
8
|
+
import { ComponentEditor } from '@cyclonium/editor';
|
|
9
|
+
import { Color, componentEditorTraits } from 'cc';
|
|
10
|
+
import { SpriteRenderer } from './sprite-renderer.component.js';
|
|
11
|
+
let SpriteRendererEditor = class SpriteRendererEditor extends ComponentEditor {
|
|
12
|
+
onSceneGUI(ctx) {
|
|
13
|
+
const bounds = this.target[componentEditorTraits.BoundingComponent.Tags.getBoundingBox]();
|
|
14
|
+
if (!bounds) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const { center, halfExtents } = bounds;
|
|
18
|
+
const outlineHalfExtents = new Vec2(halfExtents.x + lineThickness * 0.5, halfExtents.y + lineThickness * 0.5);
|
|
19
|
+
ctx.handles.color = boundingFrameColor;
|
|
20
|
+
ctx.handles.drawPolyline2D({
|
|
21
|
+
points: [
|
|
22
|
+
new Vec2(center.x - outlineHalfExtents.x, center.y - outlineHalfExtents.y),
|
|
23
|
+
new Vec2(center.x - outlineHalfExtents.x, center.y + outlineHalfExtents.y),
|
|
24
|
+
new Vec2(center.x + outlineHalfExtents.x, center.y + outlineHalfExtents.y),
|
|
25
|
+
new Vec2(center.x + outlineHalfExtents.x, center.y - outlineHalfExtents.y),
|
|
26
|
+
],
|
|
27
|
+
close: true,
|
|
28
|
+
thickness: lineThickness,
|
|
29
|
+
z: center.z + zOffset,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
SpriteRendererEditor = __decorate([
|
|
34
|
+
ComponentEditor.LegacyDecorators.attachTo(SpriteRenderer)
|
|
35
|
+
], SpriteRendererEditor);
|
|
36
|
+
export { SpriteRendererEditor };
|
|
37
|
+
const boundingFrameColor = new Color(255, 190, 82, 255);
|
|
38
|
+
const lineThickness = 0.02;
|
|
39
|
+
const zOffset = 0.01;
|
package/lib/sprite.d.ts
ADDED
package/lib/sprite.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Texture2D as Sprite } from 'cc';
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cyclonium/2d",
|
|
3
|
+
"version": "0.0.100",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"files": [
|
|
6
|
+
"lib/**/*{.js,.d.ts}"
|
|
7
|
+
],
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/index.d.ts",
|
|
11
|
+
"default": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./editor": {
|
|
14
|
+
"types": "./lib/editor.d.ts",
|
|
15
|
+
"default": "./lib/editor.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"@cyclonium/core": "0.0.100",
|
|
20
|
+
"@cyclonium/editor": "0.0.100"
|
|
21
|
+
},
|
|
22
|
+
"peerDependenciesMeta": {
|
|
23
|
+
"@cyclonium/editor": {
|
|
24
|
+
"optional": true
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^25.9.2",
|
|
29
|
+
"@vitest/browser-playwright": "^4.1.5",
|
|
30
|
+
"playwright": "^1.59.1",
|
|
31
|
+
"typescript": "^6.0.2",
|
|
32
|
+
"vitest": "^4.1.5",
|
|
33
|
+
"@cyclonium/workflow": "0.0.100",
|
|
34
|
+
"@cyclonium/editor": "0.0.100",
|
|
35
|
+
"@cyclonium/cc-test": "0.0.100",
|
|
36
|
+
"@cyclonium/types-cc": "0.0.100"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc -b",
|
|
40
|
+
"dev": "tsc -b --watch",
|
|
41
|
+
"test": "npm run test:compile && npm run t",
|
|
42
|
+
"test:compile": "tsc -b test/tsconfig.json",
|
|
43
|
+
"t": "vitest run --browser.headless=true",
|
|
44
|
+
"test:browser:dev": "vitest"
|
|
45
|
+
}
|
|
46
|
+
}
|