@cyclonium/handles 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.
@@ -0,0 +1,38 @@
1
+ import { HandleInputContext, HandleMouseButton, type HandleHostInput } from './handle-input.js';
2
+ import { HandleRenderer } from './handle-renderer.js';
3
+ import { Node } from 'cc';
4
+ export declare class HandleContext {
5
+ constructor(hostInput: HandleHostInput, opts: {
6
+ renderer: {
7
+ node: Node;
8
+ };
9
+ });
10
+ get now(): number;
11
+ get input(): HandleInputContext;
12
+ get renderer(): HandleRenderer;
13
+ destroy_internal(): void;
14
+ forEach<T>(iterable: ReadonlyArray<T>, keyGenerator: (item: T) => string, callback: (item: T, index: number) => void): void;
15
+ allocateSequentialKey(): HandleKey;
16
+ pushScope(key: string): void;
17
+ popScope(): void;
18
+ startFrame_internal(opts: HandleContext.FrameStartOptions): void;
19
+ endFrame_internal(): void;
20
+ queueFrameEndTask_internal(callback: () => void): void;
21
+ private readonly _currentScope;
22
+ private _inputContext;
23
+ private _now;
24
+ private _endFrameCallbacks;
25
+ private _renderer;
26
+ private _pushScope;
27
+ private _popScope;
28
+ }
29
+ export declare namespace HandleContext {
30
+ export import MouseButton = HandleMouseButton;
31
+ interface FrameStartOptions {
32
+ }
33
+ }
34
+ export type HandleKey = string & {
35
+ readonly __handleKey: unique symbol;
36
+ };
37
+ export declare function HandleKey(): HandleKey;
38
+ //# sourceMappingURL=handle-context.d.ts.map
@@ -0,0 +1,87 @@
1
+ import { HandleInputContext, HandleMouseButton } from './handle-input.js';
2
+ import { HandleRenderer } from './handle-renderer.js';
3
+ import { Node } from 'cc';
4
+ export class HandleContext {
5
+ constructor(hostInput, opts) {
6
+ this._inputContext = new HandleInputContext(hostInput);
7
+ this._pushScope('');
8
+ this._renderer = new HandleRenderer(opts.renderer);
9
+ }
10
+ get now() {
11
+ return this._now;
12
+ }
13
+ get input() {
14
+ return this._inputContext;
15
+ }
16
+ get renderer() {
17
+ return this._renderer;
18
+ }
19
+ destroy_internal() {
20
+ this._renderer.destroy_internal();
21
+ }
22
+ forEach(iterable, keyGenerator, callback) {
23
+ for (const [index, item] of iterable.entries()) {
24
+ const key = keyGenerator(item);
25
+ this._pushScope(key);
26
+ try {
27
+ callback(item, index);
28
+ }
29
+ finally {
30
+ this._popScope();
31
+ }
32
+ }
33
+ }
34
+ allocateSequentialKey() {
35
+ const scope = this._currentScope[this._currentScope.length - 1];
36
+ const sequentialId = scope.nextSequentialIndex++;
37
+ return `${scope.key}/${sequentialId}`;
38
+ }
39
+ pushScope(key) {
40
+ this._pushScope(key);
41
+ }
42
+ popScope() {
43
+ this._popScope();
44
+ }
45
+ startFrame_internal(opts) {
46
+ this._now = Date.now();
47
+ this._inputContext.startFrame_internal(opts);
48
+ this._renderer.startFrame();
49
+ }
50
+ endFrame_internal() {
51
+ for (const callback of this._endFrameCallbacks) {
52
+ callback();
53
+ }
54
+ this._endFrameCallbacks.length = 0;
55
+ this._currentScope.length = 1;
56
+ this._inputContext.endFrame_internal();
57
+ this._renderer.endFrame();
58
+ }
59
+ queueFrameEndTask_internal(callback) {
60
+ this._endFrameCallbacks.push(callback);
61
+ }
62
+ _currentScope = [];
63
+ _inputContext;
64
+ _now = Date.now();
65
+ _endFrameCallbacks = [];
66
+ _renderer;
67
+ _pushScope(key) {
68
+ this._currentScope.push({ key, nextSequentialIndex: 0 });
69
+ }
70
+ _popScope() {
71
+ if (this._currentScope.length <= 1) {
72
+ throw new Error('Mismatched scope push/pop');
73
+ }
74
+ this._currentScope.pop();
75
+ }
76
+ }
77
+ Object.defineProperties(HandleContext, {
78
+ MouseButton: {
79
+ value: HandleMouseButton,
80
+ writable: true,
81
+ enumerable: false,
82
+ configurable: true,
83
+ },
84
+ });
85
+ export function HandleKey() {
86
+ return '';
87
+ }
@@ -0,0 +1,34 @@
1
+ import { Vec2 } from '@cyclonium/core/math/vec2';
2
+ import type { HandleContext } from './handle-context.js';
3
+ import type { geometry } from 'cc';
4
+ export declare enum HandleMouseButton {
5
+ left = 0,
6
+ right = 1,
7
+ middle = 2
8
+ }
9
+ export declare class HandleInputContext {
10
+ private _hostInput;
11
+ constructor(_hostInput: HandleHostInput);
12
+ get mousePosition(): Vec2;
13
+ mouseButtonDown(button: HandleMouseButton): boolean;
14
+ mouseRay(): geometry.Ray;
15
+ get mouseAltKey(): boolean;
16
+ get mouseCtrlKey(): boolean;
17
+ get mouseShiftKey(): boolean;
18
+ startFrame_internal(_opts: HandleContext.FrameStartOptions): void;
19
+ endFrame_internal(): void;
20
+ captureMouse(): void;
21
+ }
22
+ export interface HandleHostInput {
23
+ readonly mouse: HandleHostMouseInput;
24
+ }
25
+ export interface HandleHostMouseInput {
26
+ wantCapture: boolean;
27
+ readonly position: Vec2;
28
+ readonly buttons: number;
29
+ mouseRay(): geometry.Ray;
30
+ readonly altKey: boolean;
31
+ readonly ctrlKey: boolean;
32
+ readonly shiftKey: boolean;
33
+ }
34
+ //# sourceMappingURL=handle-input.d.ts.map
@@ -0,0 +1,40 @@
1
+ import { Vec2 } from '@cyclonium/core/math/vec2';
2
+ export var HandleMouseButton;
3
+ (function (HandleMouseButton) {
4
+ HandleMouseButton[HandleMouseButton["left"] = 0] = "left";
5
+ HandleMouseButton[HandleMouseButton["right"] = 1] = "right";
6
+ HandleMouseButton[HandleMouseButton["middle"] = 2] = "middle";
7
+ })(HandleMouseButton || (HandleMouseButton = {}));
8
+ export class HandleInputContext {
9
+ _hostInput;
10
+ constructor(_hostInput) {
11
+ this._hostInput = _hostInput;
12
+ }
13
+ get mousePosition() {
14
+ return this._hostInput.mouse.position;
15
+ }
16
+ mouseButtonDown(button) {
17
+ return (this._hostInput.mouse.buttons & (1 << button)) !== 0;
18
+ }
19
+ mouseRay() {
20
+ return this._hostInput.mouse.mouseRay();
21
+ }
22
+ get mouseAltKey() {
23
+ return this._hostInput.mouse.altKey;
24
+ }
25
+ get mouseCtrlKey() {
26
+ return this._hostInput.mouse.ctrlKey;
27
+ }
28
+ get mouseShiftKey() {
29
+ return this._hostInput.mouse.shiftKey;
30
+ }
31
+ startFrame_internal(_opts) {
32
+ this._hostInput.mouse.wantCapture = false;
33
+ }
34
+ endFrame_internal() {
35
+ // ...
36
+ }
37
+ captureMouse() {
38
+ this._hostInput.mouse.wantCapture = true;
39
+ }
40
+ }
@@ -0,0 +1,3 @@
1
+ export declare class HandleProvider {
2
+ }
3
+ //# sourceMappingURL=handle-provider.d.ts.map
@@ -0,0 +1,2 @@
1
+ export class HandleProvider {
2
+ }
@@ -0,0 +1,41 @@
1
+ import type { Vec2 } from '@cyclonium/core/math/vec2';
2
+ import { Color, Node, Vec3 } from 'cc';
3
+ export declare class HandleRenderer {
4
+ constructor(opts: {
5
+ node: Node;
6
+ });
7
+ destroy_internal(): void;
8
+ startFrame(): void;
9
+ endFrame(): void;
10
+ drawRect(_opts: {
11
+ center: Vec3;
12
+ halfExtent: Vec2;
13
+ right?: Vec3;
14
+ up?: Vec3;
15
+ color?: Color;
16
+ unlit?: boolean;
17
+ wireframe?: boolean;
18
+ }): void;
19
+ drawLine2D(opts: {
20
+ from: Vec2;
21
+ to: Vec2;
22
+ color?: Color;
23
+ thickness?: number;
24
+ z?: number;
25
+ }): void;
26
+ drawPolyline2D({ points, close, color, thickness, z, }: {
27
+ points: Vec2[];
28
+ close?: boolean;
29
+ color?: Color;
30
+ thickness?: number;
31
+ z?: number;
32
+ }): void;
33
+ drawBox2D(opts: {
34
+ center: Vec3;
35
+ halfExtent: number | Vec2;
36
+ color?: Color;
37
+ wireframe?: boolean;
38
+ }): void;
39
+ private _canvas;
40
+ }
41
+ //# sourceMappingURL=handle-renderer.d.ts.map
@@ -0,0 +1,55 @@
1
+ import { Color, Node, Vec3 } from 'cc';
2
+ import { Canvas3D } from '@cyclonium/canvas-3d';
3
+ export class HandleRenderer {
4
+ constructor(opts) {
5
+ this._canvas = new Canvas3D(opts);
6
+ }
7
+ destroy_internal() {
8
+ this._canvas.destroy();
9
+ }
10
+ startFrame() {
11
+ }
12
+ endFrame() {
13
+ this._canvas.commit();
14
+ }
15
+ drawRect(_opts) {
16
+ }
17
+ drawLine2D(opts) {
18
+ this._canvas.beginPath();
19
+ this._canvas.moveTo(new Vec3(opts.from.x, opts.from.y, opts.z ?? 0));
20
+ this._canvas.lineTo(new Vec3(opts.to.x, opts.to.y, opts.z ?? 0));
21
+ this._canvas.strokeColor = opts.color ?? Color.WHITE;
22
+ this._canvas.lineWidth = opts.thickness ?? 1;
23
+ this._canvas.stroke();
24
+ }
25
+ drawPolyline2D({ points, close = false, color = Color.WHITE, thickness = 1, z = 0, }) {
26
+ if (points.length === 0) {
27
+ return;
28
+ }
29
+ this._canvas.beginPath();
30
+ this._canvas.moveTo(new Vec3(points[0].x, points[0].y, z));
31
+ for (let i = 1; i < points.length; i++) {
32
+ const point = points[i];
33
+ this._canvas.lineTo(new Vec3(point.x, point.y, z));
34
+ }
35
+ if (close) {
36
+ this._canvas.closePath();
37
+ }
38
+ this._canvas.strokeColor = color;
39
+ this._canvas.lineWidth = thickness;
40
+ this._canvas.stroke();
41
+ }
42
+ drawBox2D(opts) {
43
+ this._canvas.beginPath();
44
+ this._canvas.box(opts.center, typeof opts.halfExtent === 'number' ? opts.halfExtent : new Vec3(opts.halfExtent.x, opts.halfExtent.y, 0));
45
+ if (opts.wireframe) {
46
+ this._canvas.strokeColor = opts.color ?? Color.WHITE;
47
+ this._canvas.stroke();
48
+ }
49
+ else {
50
+ this._canvas.fillColor = opts.color ?? Color.WHITE;
51
+ this._canvas.fill();
52
+ }
53
+ }
54
+ _canvas;
55
+ }
@@ -0,0 +1,29 @@
1
+ import { HandleProvider } from '../handle-provider.js';
2
+ import type { Bounds2D } from '@cyclonium/core/math/bounds-2d';
3
+ import type { HandleContext } from '../handle-context.js';
4
+ export declare enum RectHandleLockFlag {
5
+ lockTopEdge = 1,
6
+ lockBottomEdge = 2,
7
+ lockLeftEdge = 4,
8
+ lockRightEdge = 8,
9
+ lockTopLeft = 5,
10
+ lockTopRight = 9,
11
+ lockBottomLeft = 6,
12
+ lockBottomRight = 10,
13
+ lockAllEdges = 15,
14
+ lockCenter = 16,
15
+ lockAspectRatio = 32
16
+ }
17
+ export declare class Bounds2DHandleProvider extends HandleProvider {
18
+ constructor();
19
+ draw(ctx: HandleContext, bounds: Bounds2D, opts?: {
20
+ lock?: RectHandleLockFlag;
21
+ }): boolean;
22
+ private readonly _pointHandleProvider;
23
+ private readonly _lineSegmentHandleProvider;
24
+ private readonly _cacheEssentials;
25
+ private _drawPoint;
26
+ private _drawEdge;
27
+ private _applyMirror;
28
+ }
29
+ //# sourceMappingURL=bounds-2d-handle.d.ts.map
@@ -0,0 +1,196 @@
1
+ import { HandleProvider } from '../handle-provider.js';
2
+ import { Vec2 } from '@cyclonium/core/math/vec2';
3
+ import { PointHandleProvider } from './point-handle.js';
4
+ import { LineSegmentHandleLockFlag, LineSegmentHandleProvider } from './line-segment-handle.js';
5
+ import { Ref } from '../ref.js';
6
+ import { Color, Quat, Vec3 } from 'cc';
7
+ export var RectHandleLockFlag;
8
+ (function (RectHandleLockFlag) {
9
+ RectHandleLockFlag[RectHandleLockFlag["lockTopEdge"] = 1] = "lockTopEdge";
10
+ RectHandleLockFlag[RectHandleLockFlag["lockBottomEdge"] = 2] = "lockBottomEdge";
11
+ RectHandleLockFlag[RectHandleLockFlag["lockLeftEdge"] = 4] = "lockLeftEdge";
12
+ RectHandleLockFlag[RectHandleLockFlag["lockRightEdge"] = 8] = "lockRightEdge";
13
+ RectHandleLockFlag[RectHandleLockFlag["lockTopLeft"] = 5] = "lockTopLeft";
14
+ RectHandleLockFlag[RectHandleLockFlag["lockTopRight"] = 9] = "lockTopRight";
15
+ RectHandleLockFlag[RectHandleLockFlag["lockBottomLeft"] = 6] = "lockBottomLeft";
16
+ RectHandleLockFlag[RectHandleLockFlag["lockBottomRight"] = 10] = "lockBottomRight";
17
+ RectHandleLockFlag[RectHandleLockFlag["lockAllEdges"] = 15] = "lockAllEdges";
18
+ RectHandleLockFlag[RectHandleLockFlag["lockCenter"] = 16] = "lockCenter";
19
+ RectHandleLockFlag[RectHandleLockFlag["lockAspectRatio"] = 32] = "lockAspectRatio";
20
+ })(RectHandleLockFlag || (RectHandleLockFlag = {}));
21
+ const cornerPointStyle = {
22
+ color: new Color(Color.BLACK),
23
+ wireframe: true,
24
+ };
25
+ const centerPointStyle = cornerPointStyle;
26
+ export class Bounds2DHandleProvider extends HandleProvider {
27
+ constructor() {
28
+ super();
29
+ this._pointHandleProvider = new PointHandleProvider();
30
+ this._lineSegmentHandleProvider = new LineSegmentHandleProvider();
31
+ this._cacheEssentials = {
32
+ centerPoint: new Vec2(),
33
+ leftTopPoint: new Vec2(),
34
+ rightTopPoint: new Vec2(),
35
+ leftBottomPoint: new Vec2(),
36
+ rightBottomPoint: new Vec2(),
37
+ topEdge: new Ref(0),
38
+ bottomEdge: new Ref(0),
39
+ leftEdge: new Ref(0),
40
+ rightEdge: new Ref(0),
41
+ };
42
+ }
43
+ draw(ctx, bounds, opts) {
44
+ const essentials = this._cacheEssentials;
45
+ const { centerPoint, leftTopPoint, rightTopPoint, leftBottomPoint, rightBottomPoint, topEdge, bottomEdge, leftEdge, rightEdge, } = essentials;
46
+ const { lock = 0, } = opts || {};
47
+ const rotationQuat = undefined;
48
+ const baseRight = Vec3.UNIT_X;
49
+ const baseUp = Vec3.UNIT_Y;
50
+ ctx.renderer.drawRect({
51
+ center: new Vec3(bounds.center.x, bounds.center.y, 0),
52
+ halfExtent: bounds.size.mulScalar(0.5),
53
+ right: rotationQuat ? Vec3.transformQuat(new Vec3(), baseRight, rotationQuat) : baseRight,
54
+ up: rotationQuat ? Vec3.transformQuat(new Vec3(), baseUp, rotationQuat) : baseUp,
55
+ color: new Color(255, 255, 255, 128),
56
+ unlit: true,
57
+ wireframe: false,
58
+ });
59
+ const pointSize = Math.min(bounds.size.x, bounds.size.y) * 0.1;
60
+ const edgeWidth = pointSize * 0.5;
61
+ const mirrorOperation = ctx.input.mouseAltKey && ctx.input.mouseCtrlKey && ctx.input.mouseShiftKey;
62
+ let centerChanged = false;
63
+ centerPoint.copyFrom(bounds.center);
64
+ if (this._drawPoint(ctx, centerPoint, centerPointStyle, pointSize, !!(lock & RectHandleLockFlag.lockCenter))) {
65
+ bounds.center = centerPoint;
66
+ centerChanged = true;
67
+ }
68
+ const min = new Vec2().copyFrom(bounds.min);
69
+ const max = new Vec2().copyFrom(bounds.max);
70
+ let minMaxChanged = false;
71
+ {
72
+ const pointModel = leftTopPoint;
73
+ const inputX = min.x;
74
+ const inputY = max.y;
75
+ pointModel.set(inputX, inputY);
76
+ if (this._drawPoint(ctx, pointModel, cornerPointStyle, pointSize, !!(lock & RectHandleLockFlag.lockTopLeft))) {
77
+ const { x, y } = pointModel;
78
+ if (mirrorOperation) {
79
+ const dx = x - inputX;
80
+ const dy = y - inputY;
81
+ this._applyMirror(min, max, -dx, dy);
82
+ }
83
+ else {
84
+ min.x = x;
85
+ max.y = y;
86
+ }
87
+ minMaxChanged = true;
88
+ }
89
+ }
90
+ {
91
+ const pointModel = rightTopPoint;
92
+ const inputX = max.x;
93
+ const inputY = max.y;
94
+ pointModel.set(inputX, inputY);
95
+ if (this._drawPoint(ctx, pointModel, cornerPointStyle, pointSize, !!(lock & RectHandleLockFlag.lockTopRight))) {
96
+ const { x, y } = pointModel;
97
+ if (mirrorOperation) {
98
+ const dx = x - inputX;
99
+ const dy = y - inputY;
100
+ this._applyMirror(min, max, dx, dy);
101
+ }
102
+ else {
103
+ max.x = x;
104
+ max.y = y;
105
+ }
106
+ minMaxChanged = true;
107
+ }
108
+ }
109
+ {
110
+ const pointModel = rightBottomPoint;
111
+ const inputX = max.x;
112
+ const inputY = min.y;
113
+ pointModel.set(inputX, inputY);
114
+ if (this._drawPoint(ctx, pointModel, cornerPointStyle, pointSize, !!(lock & RectHandleLockFlag.lockBottomRight))) {
115
+ const { x, y } = pointModel;
116
+ if (mirrorOperation) {
117
+ const dx = x - inputX;
118
+ const dy = y - inputY;
119
+ this._applyMirror(min, max, dx, -dy);
120
+ }
121
+ else {
122
+ max.x = x;
123
+ min.y = y;
124
+ }
125
+ minMaxChanged = true;
126
+ }
127
+ }
128
+ {
129
+ const pointModel = leftBottomPoint;
130
+ const inputX = min.x;
131
+ const inputY = min.y;
132
+ pointModel.set(inputX, inputY);
133
+ if (this._drawPoint(ctx, pointModel, cornerPointStyle, pointSize, !!(lock & RectHandleLockFlag.lockBottomLeft))) {
134
+ const { x, y } = pointModel;
135
+ if (mirrorOperation) {
136
+ const dx = x - inputX;
137
+ const dy = y - inputY;
138
+ this._applyMirror(min, max, -dx, -dy);
139
+ }
140
+ else {
141
+ min.x = x;
142
+ min.y = y;
143
+ }
144
+ minMaxChanged = true;
145
+ }
146
+ }
147
+ topEdge.value = min.y;
148
+ if (this._drawEdge(ctx, leftTopPoint, rightTopPoint, topEdge, !!(lock & RectHandleLockFlag.lockTopEdge), edgeWidth)) {
149
+ min.y = topEdge.value;
150
+ minMaxChanged = true;
151
+ }
152
+ bottomEdge.value = max.y;
153
+ if (this._drawEdge(ctx, leftBottomPoint, rightBottomPoint, bottomEdge, !!(lock & RectHandleLockFlag.lockBottomEdge), edgeWidth)) {
154
+ max.y = bottomEdge.value;
155
+ minMaxChanged = true;
156
+ }
157
+ leftEdge.value = min.x;
158
+ if (this._drawEdge(ctx, leftTopPoint, leftBottomPoint, leftEdge, !!(lock & RectHandleLockFlag.lockLeftEdge), edgeWidth)) {
159
+ min.x = leftEdge.value;
160
+ minMaxChanged = true;
161
+ }
162
+ rightEdge.value = max.x;
163
+ if (this._drawEdge(ctx, rightTopPoint, rightBottomPoint, rightEdge, !!(lock & RectHandleLockFlag.lockRightEdge), edgeWidth)) {
164
+ max.x = rightEdge.value;
165
+ minMaxChanged = true;
166
+ }
167
+ if (minMaxChanged) {
168
+ bounds.setMinMax(min, max);
169
+ }
170
+ return centerChanged || minMaxChanged;
171
+ }
172
+ _pointHandleProvider;
173
+ _lineSegmentHandleProvider;
174
+ _cacheEssentials;
175
+ _drawPoint(ctx, point, style, size, locked) {
176
+ return this._pointHandleProvider.drawSquaredPoint2D(ctx, point, {
177
+ lock: locked,
178
+ style,
179
+ size,
180
+ });
181
+ }
182
+ _drawEdge(ctx, start, end, edge, locked, edgeWidth) {
183
+ return this._lineSegmentHandleProvider.drawEdge2D(ctx, edge, undefined, {
184
+ start,
185
+ end,
186
+ lock: locked ? true : LineSegmentHandleLockFlag.lockForwardBackward,
187
+ width: edgeWidth,
188
+ });
189
+ }
190
+ _applyMirror(min, max, dxMax, dyMax) {
191
+ max.x += dxMax;
192
+ max.y += dyMax;
193
+ min.x -= dxMax;
194
+ min.y -= dyMax;
195
+ }
196
+ }
@@ -0,0 +1,29 @@
1
+ import type { Vec3 } from 'cc';
2
+ import { HandleProvider } from '../handle-provider.js';
3
+ import type { HandleContext } from '../handle-context.js';
4
+ import type { Ref } from '../ref.js';
5
+ import { Vec2 } from '@cyclonium/core/math/vec2';
6
+ export declare enum LineSegmentHandleLockFlag {
7
+ lockForward = 1,
8
+ lockBackward = 2,
9
+ lockOrthogonalLeft = 4,
10
+ lockOrthogonalRight = 8,
11
+ lockForwardBackward = 3,
12
+ lockOrthogonal = 12,
13
+ lockAll = 15
14
+ }
15
+ export declare class LineSegmentHandleProvider extends HandleProvider {
16
+ constructor();
17
+ draw(_ctx: HandleContext, _model: {
18
+ start: Vec3;
19
+ end: Vec3;
20
+ }): boolean;
21
+ drawEdge2D(ctx: HandleContext, _modelTangent: Ref<number> | undefined, _modelOrthogonal: Ref<number> | undefined, props: {
22
+ start: Vec2;
23
+ end: Vec2;
24
+ width?: number;
25
+ lock?: boolean | LineSegmentHandleLockFlag;
26
+ }): boolean;
27
+ private _virtualBox2DHandleProvider;
28
+ }
29
+ //# sourceMappingURL=line-segment-handle.d.ts.map
@@ -0,0 +1,88 @@
1
+ import { HandleProvider } from '../handle-provider.js';
2
+ import { Vec2 } from '@cyclonium/core/math/vec2';
3
+ import { VirtualBox2DHandleProvider } from './virtual-box-2d-handle.js';
4
+ export var LineSegmentHandleLockFlag;
5
+ (function (LineSegmentHandleLockFlag) {
6
+ LineSegmentHandleLockFlag[LineSegmentHandleLockFlag["lockForward"] = 1] = "lockForward";
7
+ LineSegmentHandleLockFlag[LineSegmentHandleLockFlag["lockBackward"] = 2] = "lockBackward";
8
+ LineSegmentHandleLockFlag[LineSegmentHandleLockFlag["lockOrthogonalLeft"] = 4] = "lockOrthogonalLeft";
9
+ LineSegmentHandleLockFlag[LineSegmentHandleLockFlag["lockOrthogonalRight"] = 8] = "lockOrthogonalRight";
10
+ LineSegmentHandleLockFlag[LineSegmentHandleLockFlag["lockForwardBackward"] = 3] = "lockForwardBackward";
11
+ LineSegmentHandleLockFlag[LineSegmentHandleLockFlag["lockOrthogonal"] = 12] = "lockOrthogonal";
12
+ LineSegmentHandleLockFlag[LineSegmentHandleLockFlag["lockAll"] = 15] = "lockAll";
13
+ })(LineSegmentHandleLockFlag || (LineSegmentHandleLockFlag = {}));
14
+ export class LineSegmentHandleProvider extends HandleProvider {
15
+ constructor() {
16
+ super();
17
+ }
18
+ draw(_ctx, _model) {
19
+ return false;
20
+ }
21
+ drawEdge2D(ctx, _modelTangent, _modelOrthogonal, props) {
22
+ const { start, end } = props;
23
+ const lockProp = props?.lock;
24
+ const lock = typeof lockProp === 'boolean'
25
+ ? (lockProp ? LineSegmentHandleLockFlag.lockAll : 0)
26
+ : (lockProp ?? 0);
27
+ const moved = false;
28
+ const center = start.add(end).mulSelfScalar(0.5);
29
+ const dir = end.sub(start);
30
+ const length = dir.magnitude;
31
+ dir.normalizeSelf();
32
+ const rotation = dir.atan2();
33
+ const width = props?.width ?? 1;
34
+ this._virtualBox2DHandleProvider.drawVirtualBox(ctx, center, {
35
+ size: new Vec2(length, width),
36
+ rotation,
37
+ onWantMove: (newPosition) => {
38
+ if ((lock & LineSegmentHandleLockFlag.lockAll) === LineSegmentHandleLockFlag.lockAll) {
39
+ return;
40
+ }
41
+ const movement = newPosition.sub(center);
42
+ const tangent = dir;
43
+ const { b, lengthA, lengthB } = decomposeCacheResult(movement, dir);
44
+ if (lengthA > 0) {
45
+ if (!(lock & LineSegmentHandleLockFlag.lockForward)) {
46
+ center.addMulScalarSelf(tangent, lengthA);
47
+ }
48
+ }
49
+ else if (lengthA < 0) {
50
+ if (!(lock & LineSegmentHandleLockFlag.lockBackward)) {
51
+ center.addMulScalarSelf(tangent, -lengthA);
52
+ }
53
+ }
54
+ if (lengthB > 0) {
55
+ if (!(lock & LineSegmentHandleLockFlag.lockOrthogonalLeft)) {
56
+ center.addMulScalarSelf(b, lengthB);
57
+ }
58
+ }
59
+ else if (lengthB < 0) {
60
+ if (!(lock & LineSegmentHandleLockFlag.lockOrthogonalRight)) {
61
+ center.addMulScalarSelf(b, lengthB);
62
+ }
63
+ }
64
+ },
65
+ });
66
+ if (moved) {
67
+ return true;
68
+ }
69
+ return false;
70
+ }
71
+ _virtualBox2DHandleProvider = new VirtualBox2DHandleProvider();
72
+ }
73
+ const decomposeCacheResult = (() => {
74
+ const cacheOrthogonal = new Vec2();
75
+ const cacheResult = {
76
+ lengthA: 0,
77
+ lengthB: 0,
78
+ b: cacheOrthogonal,
79
+ };
80
+ return (input, dir) => {
81
+ const orthonormal = cacheOrthogonal.copyFrom(dir.orthonormal());
82
+ const lengthA = Vec2.dot(input, dir);
83
+ const lengthB = Vec2.dot(input, orthonormal);
84
+ cacheResult.lengthA = lengthA;
85
+ cacheResult.lengthB = lengthB;
86
+ return cacheResult;
87
+ };
88
+ })();
@@ -0,0 +1,24 @@
1
+ import { Vec2 } from '@cyclonium/core/math/vec2';
2
+ import type { HandleContext } from '../handle-context.js';
3
+ import { HandleProvider } from '../handle-provider.js';
4
+ import type { Vec3 } from 'cc';
5
+ import type { Color } from 'cc';
6
+ export declare enum PointHandleLockFlag {
7
+ x = 1,
8
+ y = 2,
9
+ z = 4,
10
+ all = 7
11
+ }
12
+ export declare class PointHandleProvider extends HandleProvider {
13
+ constructor();
14
+ draw(_ctx: HandleContext, _model: Vec3): boolean;
15
+ drawSquaredPoint2D(ctx: HandleContext, model: Vec2, props?: {
16
+ size?: number;
17
+ lock?: boolean | PointHandleLockFlag;
18
+ style?: {
19
+ color?: Color;
20
+ };
21
+ }): boolean;
22
+ private _virtualBox2DHandleProvider;
23
+ }
24
+ //# sourceMappingURL=point-handle.d.ts.map
@@ -0,0 +1,46 @@
1
+ import { Vec2 } from '@cyclonium/core/math/vec2';
2
+ import { HandleProvider } from '../handle-provider.js';
3
+ import { VirtualBox2DHandleProvider } from './virtual-box-2d-handle.js';
4
+ export var PointHandleLockFlag;
5
+ (function (PointHandleLockFlag) {
6
+ PointHandleLockFlag[PointHandleLockFlag["x"] = 1] = "x";
7
+ PointHandleLockFlag[PointHandleLockFlag["y"] = 2] = "y";
8
+ PointHandleLockFlag[PointHandleLockFlag["z"] = 4] = "z";
9
+ PointHandleLockFlag[PointHandleLockFlag["all"] = 7] = "all";
10
+ })(PointHandleLockFlag || (PointHandleLockFlag = {}));
11
+ export class PointHandleProvider extends HandleProvider {
12
+ constructor() {
13
+ super();
14
+ }
15
+ draw(_ctx, _model) {
16
+ return false;
17
+ }
18
+ drawSquaredPoint2D(ctx, model, props) {
19
+ const lockProp = props?.lock;
20
+ const lock = typeof lockProp === 'boolean'
21
+ ? (lockProp ? PointHandleLockFlag.all : 0)
22
+ : (lockProp ?? 0);
23
+ const sizeProps = props?.size;
24
+ const size = sizeProps ? new Vec2(sizeProps, sizeProps) : Vec2.ONE;
25
+ let moved = false;
26
+ this._virtualBox2DHandleProvider.drawVirtualBox(ctx, model, {
27
+ size,
28
+ style: props?.style,
29
+ onWantMove: (newPosition) => {
30
+ if (!(lock & PointHandleLockFlag.x)) {
31
+ model.x = newPosition.x;
32
+ moved = true;
33
+ }
34
+ if (!(lock & PointHandleLockFlag.y)) {
35
+ model.y = newPosition.y;
36
+ moved = true;
37
+ }
38
+ },
39
+ });
40
+ if (moved) {
41
+ return true;
42
+ }
43
+ return false;
44
+ }
45
+ _virtualBox2DHandleProvider = new VirtualBox2DHandleProvider();
46
+ }
@@ -0,0 +1,28 @@
1
+ import { type HandleContext } from '../handle-context.js';
2
+ import { HandleProvider } from '../handle-provider.js';
3
+ import { Vec2 } from '@cyclonium/core/math/vec2';
4
+ import { Color } from 'cc';
5
+ import { HandleMouseButton } from '../handle-input.js';
6
+ export declare class VirtualBox2DHandleProvider extends HandleProvider {
7
+ constructor();
8
+ drawVirtualBox(ctx: HandleContext, center: Vec2, opts: {
9
+ size: Vec2;
10
+ rotation?: number;
11
+ style?: {
12
+ color?: Color;
13
+ wireframe?: boolean;
14
+ };
15
+ onMouseEnter?: () => void;
16
+ onMouseExit?: () => void;
17
+ onMouseMove?: () => void;
18
+ onMouseDown?: (button: HandleMouseButton) => void;
19
+ onMouseUp?: (button: HandleMouseButton) => void;
20
+ onWantMove?: (newPosition: Vec2) => void;
21
+ }): boolean;
22
+ private _livingRecords;
23
+ private _pendingRecords;
24
+ private _queuedFrameEndTask;
25
+ private _onFrameEnd;
26
+ private _processInput;
27
+ }
28
+ //# sourceMappingURL=virtual-box-2d-handle.d.ts.map
@@ -0,0 +1,149 @@
1
+ import { Bounds2D } from '@cyclonium/core/math/bounds-2d';
2
+ import {} from '../handle-context.js';
3
+ import { HandleProvider } from '../handle-provider.js';
4
+ import { Vec2 } from '@cyclonium/core/math/vec2';
5
+ import { Color, geometry, Quat, Vec3 } from 'cc';
6
+ import { logger } from '@cyclonium/core/log';
7
+ import { HandleMouseButton } from '../handle-input.js';
8
+ export class VirtualBox2DHandleProvider extends HandleProvider {
9
+ constructor() {
10
+ super();
11
+ }
12
+ drawVirtualBox(ctx, center, opts) {
13
+ const handleKey = ctx.allocateSequentialKey();
14
+ let livingRecord;
15
+ const pendingRecord = this._pendingRecords.get(handleKey);
16
+ if (pendingRecord) {
17
+ this._pendingRecords.delete(handleKey);
18
+ livingRecord = pendingRecord;
19
+ }
20
+ else {
21
+ livingRecord = new VirtualBoxRecord();
22
+ logger.verbose(`Create virtual box handle ${handleKey}`);
23
+ }
24
+ ++livingRecord.liveFrames;
25
+ livingRecord.bounds.setCenterSize(center, opts.size);
26
+ this._livingRecords.set(handleKey, livingRecord);
27
+ const mouseState = livingRecord.mouse;
28
+ for (const [buttonId_, buttonState] of Object.entries(mouseState.buttons)) {
29
+ const buttonId = Number(buttonId_);
30
+ const down = ctx.input.mouseButtonDown(buttonId);
31
+ buttonState.previousDown = buttonState.down;
32
+ if (down !== buttonState.down) {
33
+ buttonState.down = down;
34
+ buttonState.alterTime = ctx.now;
35
+ if (down) {
36
+ opts.onMouseDown?.(buttonId);
37
+ }
38
+ else {
39
+ opts.onMouseUp?.(buttonId);
40
+ }
41
+ }
42
+ }
43
+ const positionMoved = this._processInput(ctx, livingRecord, center, opts);
44
+ const rotationQuat = opts.rotation
45
+ ? Quat.fromAxisAngle(new Quat(), Vec3.UNIT_Z, opts.rotation)
46
+ : undefined;
47
+ const baseRight = Vec3.UNIT_X;
48
+ const baseUp = Vec3.UNIT_Y;
49
+ ctx.renderer.drawRect({
50
+ center: new Vec3(center.x, center.y, 0),
51
+ halfExtent: opts.size.mulScalar(0.5),
52
+ right: rotationQuat ? Vec3.transformQuat(new Vec3(), baseRight, rotationQuat) : baseRight,
53
+ up: rotationQuat ? Vec3.transformQuat(new Vec3(), baseUp, rotationQuat) : baseUp,
54
+ color: opts.style?.color ?? Color.WHITE,
55
+ unlit: true,
56
+ wireframe: opts.style?.wireframe ?? false,
57
+ });
58
+ if (!this._queuedFrameEndTask) {
59
+ this._queuedFrameEndTask = true;
60
+ ctx.queueFrameEndTask_internal(this._onFrameEnd.bind(this));
61
+ }
62
+ return positionMoved;
63
+ }
64
+ _livingRecords = new Map();
65
+ _pendingRecords = new Map();
66
+ _queuedFrameEndTask = false;
67
+ _onFrameEnd() {
68
+ this._queuedFrameEndTask = false;
69
+ [this._livingRecords, this._pendingRecords] = [this._pendingRecords, this._livingRecords];
70
+ this._livingRecords.clear();
71
+ }
72
+ _processInput(ctx, record, center, opts) {
73
+ let positionMoved = false;
74
+ const currentMousePosition = ctx.input.mousePosition;
75
+ const mouseRay = ctx.input.mouseRay();
76
+ const mouseState = record.mouse;
77
+ mouseState.position.copyFrom(currentMousePosition);
78
+ if (record.dragRecord.started) {
79
+ if (!mouseState.buttons[HandleMouseButton.left].down) {
80
+ record.dragRecord.started = false;
81
+ }
82
+ else {
83
+ ctx.input.captureMouse();
84
+ const toi = geometry.intersect.rayPlane(mouseRay, record.dragRecord.startPlane);
85
+ const hasHit = toi !== 0;
86
+ if (hasHit) {
87
+ const hitPoint = new Vec3();
88
+ mouseRay.computeHit(hitPoint, toi);
89
+ opts.onWantMove?.(new Vec2(hitPoint.x, hitPoint.y));
90
+ positionMoved = true;
91
+ }
92
+ }
93
+ }
94
+ else {
95
+ const aabb = new geometry.AABB();
96
+ aabb.center = new Vec3(center.x, center.y, 0);
97
+ const halfSize = opts.size.mulScalar(0.5);
98
+ aabb.halfExtents = new Vec3(halfSize.x, halfSize.y, 0);
99
+ const toi = geometry.intersect.rayAABB(mouseRay, aabb);
100
+ const entered = toi !== 0;
101
+ if (entered) {
102
+ ctx.input.captureMouse();
103
+ if (!mouseState.entered) {
104
+ opts.onMouseEnter?.();
105
+ }
106
+ const hitPosition = new Vec3();
107
+ mouseRay.computeHit(hitPosition, toi);
108
+ if (mouseState.buttons[HandleMouseButton.left].down && !mouseState.buttons[HandleMouseButton.left].previousDown) {
109
+ record.dragRecord.started = true;
110
+ record.dragRecord.startPosition.set(hitPosition);
111
+ record.dragRecord.startPlane.n.set(mouseRay.d);
112
+ record.dragRecord.startPlane.n.negative();
113
+ record.dragRecord.startPlane.d = Vec3.len(hitPosition);
114
+ }
115
+ }
116
+ else {
117
+ if (mouseState.entered) {
118
+ opts.onMouseExit?.();
119
+ }
120
+ }
121
+ }
122
+ return positionMoved;
123
+ }
124
+ }
125
+ class VirtualBoxRecord {
126
+ bounds = new Bounds2D();
127
+ liveFrames = 0;
128
+ mouse = new VirtualBoxButtonState();
129
+ dragRecord = new DragRecord();
130
+ }
131
+ class DragRecord {
132
+ started = false;
133
+ startPosition = new Vec3();
134
+ startPlane = new geometry.Plane();
135
+ }
136
+ class VirtualBoxButtonState {
137
+ entered = false;
138
+ position = new Vec2();
139
+ buttons = {
140
+ [HandleMouseButton.left]: new MouseButtonState(),
141
+ [HandleMouseButton.right]: new MouseButtonState(),
142
+ [HandleMouseButton.middle]: new MouseButtonState(),
143
+ };
144
+ }
145
+ class MouseButtonState {
146
+ down = false;
147
+ alterTime = 0;
148
+ previousDown = false;
149
+ }
@@ -0,0 +1,34 @@
1
+ import { HandleContext } from './handle-context.js';
2
+ import { Bounds2DHandleProvider } from './handles/bounds-2d-handle.js';
3
+ import { PointHandleProvider } from './handles/point-handle.js';
4
+ import { Color } from 'cc';
5
+ import type { HandleRenderer } from './handle-renderer.js';
6
+ export declare class Handles {
7
+ constructor(...args: ConstructorParameters<typeof HandleContext>);
8
+ get input(): import("./handle-input.js").HandleInputContext;
9
+ get color(): Color;
10
+ set color(value: Color);
11
+ destroy_internal(): void;
12
+ pushScope(key: string): void;
13
+ popScope(): void;
14
+ /**
15
+ * Draw an operable rectangle in scene,
16
+ * will modify the bounds in place according to user operation,
17
+ * @returns true if the rectangle is modified.
18
+ */
19
+ bounds2DHandle(...args: Handles.Slice1<Parameters<Bounds2DHandleProvider['draw']>>): boolean;
20
+ squaredPoint2DHandle(...args: Handles.Slice1<Parameters<PointHandleProvider['drawSquaredPoint2D']>>): boolean;
21
+ drawLine2D(opt: Omit<Parameters<HandleRenderer['drawLine2D']>[0], 'color'>): void;
22
+ drawPolyline2D(opt: Omit<Parameters<HandleRenderer['drawPolyline2D']>[0], 'color'>): void;
23
+ drawBox2D(opt: Omit<Parameters<HandleRenderer['drawBox2D']>[0], 'color'>): void;
24
+ startFrame_internal(opts: HandleContext.FrameStartOptions): void;
25
+ endFrame_internal(): void;
26
+ private _handleContext;
27
+ private _bounds2DHandleProvider;
28
+ private _pointHandleProvider;
29
+ private readonly _color;
30
+ }
31
+ export declare namespace Handles {
32
+ type Slice1<T> = T extends readonly [infer _U, ...infer Rest] ? Rest : never;
33
+ }
34
+ //# sourceMappingURL=handles.d.ts.map
package/lib/handles.js ADDED
@@ -0,0 +1,66 @@
1
+ import { HandleContext } from './handle-context.js';
2
+ import { Bounds2DHandleProvider } from './handles/bounds-2d-handle.js';
3
+ import { PointHandleProvider } from './handles/point-handle.js';
4
+ import { Color } from 'cc';
5
+ export class Handles {
6
+ constructor(...args) {
7
+ this._handleContext = new HandleContext(...args);
8
+ }
9
+ get input() {
10
+ return this._handleContext.input;
11
+ }
12
+ get color() {
13
+ return this._color;
14
+ }
15
+ set color(value) {
16
+ this._color.set(value);
17
+ }
18
+ destroy_internal() {
19
+ this._handleContext.destroy_internal();
20
+ }
21
+ pushScope(key) {
22
+ this._handleContext.pushScope(key);
23
+ }
24
+ popScope() {
25
+ this._handleContext.popScope();
26
+ }
27
+ /**
28
+ * Draw an operable rectangle in scene,
29
+ * will modify the bounds in place according to user operation,
30
+ * @returns true if the rectangle is modified.
31
+ */
32
+ bounds2DHandle(...args) {
33
+ return (this._bounds2DHandleProvider ??= new Bounds2DHandleProvider()).draw(this._handleContext, ...args);
34
+ }
35
+ squaredPoint2DHandle(...args) {
36
+ return (this._pointHandleProvider ??= new PointHandleProvider()).drawSquaredPoint2D(this._handleContext, ...args);
37
+ }
38
+ drawLine2D(opt) {
39
+ this._handleContext.renderer.drawLine2D({
40
+ ...opt,
41
+ color: this._color,
42
+ });
43
+ }
44
+ drawPolyline2D(opt) {
45
+ this._handleContext.renderer.drawPolyline2D({
46
+ ...opt,
47
+ color: this._color,
48
+ });
49
+ }
50
+ drawBox2D(opt) {
51
+ this._handleContext.renderer.drawBox2D({
52
+ ...opt,
53
+ color: this._color,
54
+ });
55
+ }
56
+ startFrame_internal(opts) {
57
+ this._handleContext.startFrame_internal(opts);
58
+ }
59
+ endFrame_internal() {
60
+ this._handleContext.endFrame_internal();
61
+ }
62
+ _handleContext;
63
+ _bounds2DHandleProvider;
64
+ _pointHandleProvider;
65
+ _color = Color.WHITE.clone();
66
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ export { HandleContext, HandleKey } from './handle-context.js';
2
+ export { HandleInputContext, HandleMouseButton } from './handle-input.js';
3
+ export type { HandleHostInput, HandleHostMouseInput } from './handle-input.js';
4
+ export { HandleProvider } from './handle-provider.js';
5
+ export { HandleRenderer } from './handle-renderer.js';
6
+ export { Handles } from './handles.js';
7
+ export { Ref } from './ref.js';
8
+ export { Bounds2DHandleProvider, RectHandleLockFlag } from './handles/bounds-2d-handle.js';
9
+ export { LineSegmentHandleLockFlag, LineSegmentHandleProvider } from './handles/line-segment-handle.js';
10
+ export { PointHandleLockFlag, PointHandleProvider } from './handles/point-handle.js';
11
+ export { VirtualBox2DHandleProvider } from './handles/virtual-box-2d-handle.js';
12
+ //# sourceMappingURL=index.d.ts.map
package/lib/index.js ADDED
@@ -0,0 +1,10 @@
1
+ export { HandleContext, HandleKey } from './handle-context.js';
2
+ export { HandleInputContext, HandleMouseButton } from './handle-input.js';
3
+ export { HandleProvider } from './handle-provider.js';
4
+ export { HandleRenderer } from './handle-renderer.js';
5
+ export { Handles } from './handles.js';
6
+ export { Ref } from './ref.js';
7
+ export { Bounds2DHandleProvider, RectHandleLockFlag } from './handles/bounds-2d-handle.js';
8
+ export { LineSegmentHandleLockFlag, LineSegmentHandleProvider } from './handles/line-segment-handle.js';
9
+ export { PointHandleLockFlag, PointHandleProvider } from './handles/point-handle.js';
10
+ export { VirtualBox2DHandleProvider } from './handles/virtual-box-2d-handle.js';
package/lib/ref.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export declare class Ref<T> {
2
+ value: T;
3
+ constructor(value: T);
4
+ }
5
+ //# sourceMappingURL=ref.d.ts.map
package/lib/ref.js ADDED
@@ -0,0 +1,6 @@
1
+ export class Ref {
2
+ value;
3
+ constructor(value) {
4
+ this.value = value;
5
+ }
6
+ }
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@cyclonium/handles",
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
+ },
14
+ "peerDependencies": {
15
+ "@cyclonium/core": "0.0.100"
16
+ },
17
+ "dependencies": {
18
+ "@cyclonium/canvas-3d": "0.0.100"
19
+ },
20
+ "devDependencies": {
21
+ "typescript": "^6.0.2",
22
+ "@cyclonium/types-cc": "0.0.100",
23
+ "@cyclonium/workflow": "0.0.100"
24
+ },
25
+ "scripts": {
26
+ "build": "tsc -b",
27
+ "dev": "tsc -b --watch"
28
+ }
29
+ }