brilliantsole 0.0.28 → 0.0.30
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/build/brilliantsole.cjs +6078 -444
- package/build/brilliantsole.cjs.map +1 -1
- package/build/brilliantsole.js +21734 -3031
- package/build/brilliantsole.js.map +1 -1
- package/build/brilliantsole.ls.js +23228 -5817
- package/build/brilliantsole.ls.js.map +1 -1
- package/build/brilliantsole.min.js +1 -1
- package/build/brilliantsole.min.js.map +1 -1
- package/build/brilliantsole.module.d.ts +1216 -72
- package/build/brilliantsole.module.js +21697 -3032
- package/build/brilliantsole.module.js.map +1 -1
- package/build/brilliantsole.module.min.d.ts +1216 -72
- package/build/brilliantsole.module.min.js +1 -1
- package/build/brilliantsole.module.min.js.map +1 -1
- package/build/brilliantsole.node.module.d.ts +927 -68
- package/build/brilliantsole.node.module.js +6053 -445
- package/build/brilliantsole.node.module.js.map +1 -1
- package/build/dts/BS.d.ts +21 -1
- package/build/dts/Device.d.ts +152 -12
- package/build/dts/DeviceManager.d.ts +3 -3
- package/build/dts/DisplayManager.d.ts +320 -0
- package/build/dts/FileTransferManager.d.ts +10 -4
- package/build/dts/MicrophoneManager.d.ts +88 -0
- package/build/dts/connection/BaseConnectionManager.d.ts +2 -2
- package/build/dts/connection/bluetooth/BluetoothUUID.d.ts +12 -0
- package/build/dts/devicePair/DevicePair.d.ts +5 -5
- package/build/dts/sensor/SensorConfigurationManager.d.ts +2 -1
- package/build/dts/sensor/SensorDataManager.d.ts +3 -3
- package/build/dts/server/BaseClient.d.ts +4 -4
- package/build/dts/server/udp/UDPUtils.d.ts +1 -1
- package/build/dts/utils/ArrayBufferUtils.d.ts +1 -0
- package/build/dts/utils/AudioUtils.d.ts +2 -0
- package/build/dts/utils/BitmapUtils.d.ts +17 -0
- package/build/dts/utils/ColorUtils.d.ts +5 -0
- package/build/dts/utils/DisplayBitmapUtils.d.ts +47 -0
- package/build/dts/utils/DisplayCanvasHelper.d.ts +270 -0
- package/build/dts/utils/DisplayContextCommand.d.ts +300 -0
- package/build/dts/utils/DisplayContextState.d.ts +51 -0
- package/build/dts/utils/DisplayContextStateHelper.d.ts +9 -0
- package/build/dts/utils/DisplayManagerInterface.d.ts +173 -0
- package/build/dts/utils/DisplaySpriteSheetUtils.d.ts +72 -0
- package/build/dts/utils/DisplayUtils.d.ts +70 -0
- package/build/dts/utils/MathUtils.d.ts +16 -0
- package/build/dts/utils/PathUtils.d.ts +4 -0
- package/build/dts/utils/RangeHelper.d.ts +7 -0
- package/build/dts/utils/SpriteSheetUtils.d.ts +20 -0
- package/build/index.d.ts +1216 -72
- package/build/index.node.d.ts +927 -68
- package/examples/3d-generic/index.html +5 -0
- package/examples/3d-generic/script.js +1 -0
- package/examples/basic/index.html +443 -53
- package/examples/basic/script.js +1551 -24
- package/examples/camera/barcode-detector.js +109 -0
- package/examples/camera/depth-estimation.js +71 -0
- package/examples/camera/face-detector.js +119 -0
- package/examples/camera/face-landmark.js +111 -0
- package/examples/camera/gesture-recognition.js +97 -0
- package/examples/camera/hand-landmark.js +74 -0
- package/examples/camera/image-segmentation.js +98 -0
- package/examples/camera/image-to-text.js +43 -0
- package/examples/camera/image-upscale.js +75 -0
- package/examples/camera/index.html +129 -0
- package/examples/camera/object-detection.js +98 -0
- package/examples/camera/pose-landmark.js +60 -0
- package/examples/camera/script.js +316 -0
- package/examples/camera/utils.js +165 -0
- package/examples/camera/yolo-tiny.js +54 -0
- package/examples/camera/yolo.js +119 -0
- package/examples/display-3d/index.html +195 -0
- package/examples/display-3d/script.js +1235 -0
- package/examples/display-canvas/aframe.js +42950 -0
- package/examples/display-canvas/index.html +245 -0
- package/examples/display-canvas/script.js +2312 -0
- package/examples/display-image/index.html +189 -0
- package/examples/display-image/script.js +1093 -0
- package/examples/display-spritesheet/index.html +960 -0
- package/examples/display-spritesheet/script.js +4243 -0
- package/examples/display-text/index.html +195 -0
- package/examples/display-text/script.js +1418 -0
- package/examples/display-wireframe/index.html +204 -0
- package/examples/display-wireframe/script.js +1167 -0
- package/examples/edge-impulse/script.js +23 -5
- package/examples/glasses-gestures/README.md +11 -0
- package/examples/glasses-gestures/edge-impulse-standalone.js +7228 -0
- package/examples/glasses-gestures/edge-impulse-standalone.wasm +0 -0
- package/examples/glasses-gestures/index.html +74 -0
- package/examples/glasses-gestures/run-impulse.js +135 -0
- package/examples/glasses-gestures/script.js +228 -0
- package/examples/microphone/index.html +104 -0
- package/examples/microphone/script.js +394 -0
- package/examples/microphone/utils.js +45 -0
- package/examples/microphone/whisper-realtime.js +166 -0
- package/examples/microphone/whisper.js +132 -0
- package/examples/punch/index.html +4 -1
- package/examples/server/script.js +0 -1
- package/examples/ukaton-firmware-update/merged-firmware.bin +0 -0
- package/examples/webxr-3/components/bs-camera.js +65 -0
- package/examples/webxr-3/index.html +134 -0
- package/examples/webxr-3/script.js +432 -0
- package/package.json +10 -2
- package/src/BS.ts +101 -1
- package/src/CameraManager.ts +10 -8
- package/src/Device.ts +652 -11
- package/src/DisplayManager.ts +2989 -0
- package/src/FileTransferManager.ts +79 -26
- package/src/InformationManager.ts +8 -7
- package/src/MicrophoneManager.ts +606 -0
- package/src/TfliteManager.ts +4 -2
- package/src/WifiManager.ts +4 -1
- package/src/connection/BaseConnectionManager.ts +4 -0
- package/src/connection/bluetooth/bluetoothUUIDs.ts +36 -1
- package/src/devicePair/DevicePairPressureSensorDataManager.ts +1 -1
- package/src/scanner/NobleScanner.ts +1 -1
- package/src/sensor/SensorConfigurationManager.ts +16 -8
- package/src/sensor/SensorDataManager.ts +5 -0
- package/src/server/udp/UDPServer.ts +4 -4
- package/src/server/udp/UDPUtils.ts +1 -1
- package/src/server/websocket/WebSocketClient.ts +50 -1
- package/src/utils/ArrayBufferUtils.ts +23 -5
- package/src/utils/AudioUtils.ts +65 -0
- package/src/utils/ColorUtils.ts +66 -0
- package/src/utils/DisplayBitmapUtils.ts +695 -0
- package/src/utils/DisplayCanvasHelper.ts +4222 -0
- package/src/utils/DisplayContextCommand.ts +1566 -0
- package/src/utils/DisplayContextState.ts +138 -0
- package/src/utils/DisplayContextStateHelper.ts +48 -0
- package/src/utils/DisplayManagerInterface.ts +1356 -0
- package/src/utils/DisplaySpriteSheetUtils.ts +782 -0
- package/src/utils/DisplayUtils.ts +529 -0
- package/src/utils/EventDispatcher.ts +59 -14
- package/src/utils/MathUtils.ts +88 -2
- package/src/utils/ObjectUtils.ts +6 -1
- package/src/utils/PathUtils.ts +192 -0
- package/src/utils/RangeHelper.ts +15 -3
- package/src/utils/Timer.ts +1 -1
- package/src/utils/environment.ts +15 -6
|
@@ -0,0 +1,2989 @@
|
|
|
1
|
+
import Device, { SendMessageCallback } from "./Device.ts";
|
|
2
|
+
import {
|
|
3
|
+
concatenateArrayBuffers,
|
|
4
|
+
UInt8ByteBuffer,
|
|
5
|
+
} from "./utils/ArrayBufferUtils.ts";
|
|
6
|
+
import { createConsole } from "./utils/Console.ts";
|
|
7
|
+
import EventDispatcher from "./utils/EventDispatcher.ts";
|
|
8
|
+
import autoBind from "auto-bind";
|
|
9
|
+
import {
|
|
10
|
+
clamp,
|
|
11
|
+
degToRad,
|
|
12
|
+
normalizeRadians,
|
|
13
|
+
Vector2,
|
|
14
|
+
} from "./utils/MathUtils.ts";
|
|
15
|
+
import { rgbToHex, stringToRGB } from "./utils/ColorUtils.ts";
|
|
16
|
+
import DisplayContextStateHelper from "./utils/DisplayContextStateHelper.ts";
|
|
17
|
+
import {
|
|
18
|
+
assertValidColor,
|
|
19
|
+
assertValidDisplayBrightness,
|
|
20
|
+
assertValidSegmentCap,
|
|
21
|
+
DisplayScaleDirection,
|
|
22
|
+
DisplayBitmapScaleDirectionToCommandType,
|
|
23
|
+
DisplayColorRGB,
|
|
24
|
+
DisplayCropDirection,
|
|
25
|
+
DisplayCropDirections,
|
|
26
|
+
DisplayCropDirectionToCommandType,
|
|
27
|
+
DisplayCropDirectionToStateKey,
|
|
28
|
+
DisplayRotationCropDirectionToCommandType,
|
|
29
|
+
DisplayRotationCropDirectionToStateKey,
|
|
30
|
+
maxDisplayScale,
|
|
31
|
+
roundScale,
|
|
32
|
+
DisplaySpriteScaleDirectionToCommandType,
|
|
33
|
+
minDisplayScale,
|
|
34
|
+
assertValidAlignment,
|
|
35
|
+
DisplayAlignmentDirectionToCommandType,
|
|
36
|
+
DisplayAlignmentDirectionToStateKey,
|
|
37
|
+
assertValidDirection,
|
|
38
|
+
assertValidAlignmentDirection,
|
|
39
|
+
assertValidWireframe,
|
|
40
|
+
trimWireframe,
|
|
41
|
+
assertValidNumberOfControlPoints,
|
|
42
|
+
assertValidPathNumberOfControlPoints,
|
|
43
|
+
assertValidPath,
|
|
44
|
+
} from "./utils/DisplayUtils.ts";
|
|
45
|
+
import {
|
|
46
|
+
assertValidBitmapPixels,
|
|
47
|
+
drawBitmapHeaderLength,
|
|
48
|
+
getBitmapNumberOfBytes,
|
|
49
|
+
imageToBitmap,
|
|
50
|
+
quantizeImage,
|
|
51
|
+
resizeAndQuantizeImage,
|
|
52
|
+
} from "./utils/DisplayBitmapUtils.ts";
|
|
53
|
+
import {
|
|
54
|
+
DefaultDisplayContextState,
|
|
55
|
+
DisplayAlignment,
|
|
56
|
+
DisplayAlignmentDirection,
|
|
57
|
+
DisplayContextState,
|
|
58
|
+
DisplayContextStateKey,
|
|
59
|
+
DisplayDirection,
|
|
60
|
+
DisplaySegmentCap,
|
|
61
|
+
PartialDisplayContextState,
|
|
62
|
+
} from "./utils/DisplayContextState.ts";
|
|
63
|
+
import {
|
|
64
|
+
DisplayContextCommand,
|
|
65
|
+
DisplayContextCommandType,
|
|
66
|
+
DisplayContextCommandTypes,
|
|
67
|
+
serializeContextCommand,
|
|
68
|
+
} from "./utils/DisplayContextCommand.ts";
|
|
69
|
+
import {
|
|
70
|
+
assertAnySelectedSpriteSheet,
|
|
71
|
+
assertLoadedSpriteSheet,
|
|
72
|
+
assertSelectedSpriteSheet,
|
|
73
|
+
assertSprite,
|
|
74
|
+
assertSpritePaletteSwap,
|
|
75
|
+
assertSpriteSheetPalette,
|
|
76
|
+
assertSpriteSheetPaletteSwap,
|
|
77
|
+
DisplayManagerInterface,
|
|
78
|
+
drawSpriteFromSpriteSheet,
|
|
79
|
+
getSprite,
|
|
80
|
+
getSpritePaletteSwap,
|
|
81
|
+
getSpriteSheetPalette,
|
|
82
|
+
getSpriteSheetPaletteSwap,
|
|
83
|
+
runDisplayContextCommand,
|
|
84
|
+
runDisplayContextCommands,
|
|
85
|
+
selectSpritePaletteSwap,
|
|
86
|
+
selectSpriteSheetPalette,
|
|
87
|
+
selectSpriteSheetPaletteSwap,
|
|
88
|
+
} from "./utils/DisplayManagerInterface.ts";
|
|
89
|
+
import { SendFileCallback } from "./FileTransferManager.ts";
|
|
90
|
+
import { textDecoder, textEncoder } from "./utils/Text.ts";
|
|
91
|
+
import {
|
|
92
|
+
DisplaySprite,
|
|
93
|
+
DisplaySpritePaletteSwap,
|
|
94
|
+
DisplaySpriteSheetPalette,
|
|
95
|
+
DisplaySpriteSheetPaletteSwap,
|
|
96
|
+
serializeSpriteSheet,
|
|
97
|
+
DisplaySpriteSheet,
|
|
98
|
+
DisplaySpriteLines,
|
|
99
|
+
stringToSpriteLines,
|
|
100
|
+
DisplaySpriteSerializedSubLine,
|
|
101
|
+
DisplaySpriteSerializedLine,
|
|
102
|
+
DisplaySpriteSerializedLines,
|
|
103
|
+
} from "./utils/DisplaySpriteSheetUtils.ts";
|
|
104
|
+
import { wait } from "./utils/Timer.ts";
|
|
105
|
+
|
|
106
|
+
const _console = createConsole("DisplayManager", { log: true });
|
|
107
|
+
|
|
108
|
+
export const DefaultNumberOfDisplayColors = 16;
|
|
109
|
+
|
|
110
|
+
export const DisplayCommands = ["sleep", "wake"] as const;
|
|
111
|
+
export type DisplayCommand = (typeof DisplayCommands)[number];
|
|
112
|
+
|
|
113
|
+
export const DisplayStatuses = ["awake", "asleep"] as const;
|
|
114
|
+
export type DisplayStatus = (typeof DisplayStatuses)[number];
|
|
115
|
+
|
|
116
|
+
export const DisplayInformationTypes = [
|
|
117
|
+
"type",
|
|
118
|
+
"width",
|
|
119
|
+
"height",
|
|
120
|
+
"pixelDepth",
|
|
121
|
+
] as const;
|
|
122
|
+
export type DisplayInformationType = (typeof DisplayInformationTypes)[number];
|
|
123
|
+
|
|
124
|
+
export const DisplayTypes = [
|
|
125
|
+
"none",
|
|
126
|
+
"generic",
|
|
127
|
+
"monocularLeft",
|
|
128
|
+
"monocularRight",
|
|
129
|
+
"binocular",
|
|
130
|
+
] as const;
|
|
131
|
+
export type DisplayType = (typeof DisplayTypes)[number];
|
|
132
|
+
|
|
133
|
+
export const DisplayPixelDepths = ["1", "2", "4"] as const;
|
|
134
|
+
export type DisplayPixelDepth = (typeof DisplayPixelDepths)[number];
|
|
135
|
+
|
|
136
|
+
export const DisplayBrightnesses = [
|
|
137
|
+
"veryLow",
|
|
138
|
+
"low",
|
|
139
|
+
"medium",
|
|
140
|
+
"high",
|
|
141
|
+
"veryHigh",
|
|
142
|
+
] as const;
|
|
143
|
+
export type DisplayBrightness = (typeof DisplayBrightnesses)[number];
|
|
144
|
+
|
|
145
|
+
export const DisplayMessageTypes = [
|
|
146
|
+
"isDisplayAvailable",
|
|
147
|
+
"displayStatus",
|
|
148
|
+
"displayInformation",
|
|
149
|
+
"displayCommand",
|
|
150
|
+
"getDisplayBrightness",
|
|
151
|
+
"setDisplayBrightness",
|
|
152
|
+
"displayContextCommands",
|
|
153
|
+
"displayReady",
|
|
154
|
+
"getSpriteSheetName",
|
|
155
|
+
"setSpriteSheetName",
|
|
156
|
+
"spriteSheetIndex",
|
|
157
|
+
] as const;
|
|
158
|
+
export type DisplayMessageType = (typeof DisplayMessageTypes)[number];
|
|
159
|
+
|
|
160
|
+
export type DisplaySize = {
|
|
161
|
+
width: number;
|
|
162
|
+
height: number;
|
|
163
|
+
};
|
|
164
|
+
export type DisplayInformation = {
|
|
165
|
+
type: DisplayType;
|
|
166
|
+
width: number;
|
|
167
|
+
height: number;
|
|
168
|
+
pixelDepth: DisplayPixelDepth;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
export type DisplayBitmapColorPair = {
|
|
172
|
+
bitmapColorIndex: number;
|
|
173
|
+
colorIndex: number;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export type DisplaySpriteColorPair = {
|
|
177
|
+
spriteColorIndex: number;
|
|
178
|
+
colorIndex: number;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
export type DisplayWireframeEdge = {
|
|
182
|
+
startIndex: number;
|
|
183
|
+
endIndex: number;
|
|
184
|
+
};
|
|
185
|
+
export type DisplaySegment = {
|
|
186
|
+
start: Vector2;
|
|
187
|
+
end: Vector2;
|
|
188
|
+
};
|
|
189
|
+
export type DisplayWireframe = {
|
|
190
|
+
points: Vector2[];
|
|
191
|
+
edges: DisplayWireframeEdge[];
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
export const DisplayBezierCurveTypes = [
|
|
195
|
+
"segment",
|
|
196
|
+
"quadratic",
|
|
197
|
+
"cubic",
|
|
198
|
+
] as const;
|
|
199
|
+
export type DisplayBezierCurveType = (typeof DisplayBezierCurveTypes)[number];
|
|
200
|
+
export type DisplayBezierCurve = {
|
|
201
|
+
type: DisplayBezierCurveType;
|
|
202
|
+
controlPoints: Vector2[];
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
export const displayCurveTypeBitWidth = 2;
|
|
206
|
+
export const displayCurveTypesPerByte = 8 / displayCurveTypeBitWidth;
|
|
207
|
+
|
|
208
|
+
export const DisplayPointDataTypes = ["int8", "int16", "float"] as const;
|
|
209
|
+
export type DisplayPointDataType = (typeof DisplayPointDataTypes)[number];
|
|
210
|
+
export const displayPointDataTypeToSize: Record<DisplayPointDataType, number> =
|
|
211
|
+
{
|
|
212
|
+
int8: 1 * 2,
|
|
213
|
+
int16: 2 * 2,
|
|
214
|
+
float: 4 * 2,
|
|
215
|
+
};
|
|
216
|
+
export const displayPointDataTypeToRange: Record<
|
|
217
|
+
DisplayPointDataType,
|
|
218
|
+
{ min: number; max: number }
|
|
219
|
+
> = {
|
|
220
|
+
int8: { min: -(2 ** 7), max: 2 ** 7 - 1 },
|
|
221
|
+
int16: { min: -(2 ** 15), max: 2 ** 15 - 1 },
|
|
222
|
+
float: { min: -Infinity, max: Infinity },
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export const DisplayInformationValues = {
|
|
226
|
+
type: DisplayTypes,
|
|
227
|
+
pixelDepth: DisplayPixelDepths,
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
export const RequiredDisplayMessageTypes: DisplayMessageType[] = [
|
|
231
|
+
"isDisplayAvailable",
|
|
232
|
+
"displayInformation",
|
|
233
|
+
"displayStatus",
|
|
234
|
+
"getDisplayBrightness",
|
|
235
|
+
] as const;
|
|
236
|
+
|
|
237
|
+
export const DisplayEventTypes = [
|
|
238
|
+
...DisplayMessageTypes,
|
|
239
|
+
"displayContextState",
|
|
240
|
+
"displayColor",
|
|
241
|
+
"displayColorOpacity",
|
|
242
|
+
"displayOpacity",
|
|
243
|
+
"displaySpriteSheetUploadStart",
|
|
244
|
+
"displaySpriteSheetUploadProgress",
|
|
245
|
+
"displaySpriteSheetUploadComplete",
|
|
246
|
+
] as const;
|
|
247
|
+
export type DisplayEventType = (typeof DisplayEventTypes)[number];
|
|
248
|
+
|
|
249
|
+
export interface DisplayEventMessages {
|
|
250
|
+
isDisplayAvailable: { isDisplayAvailable: boolean };
|
|
251
|
+
displayStatus: {
|
|
252
|
+
displayStatus: DisplayStatus;
|
|
253
|
+
previousDisplayStatus: DisplayStatus;
|
|
254
|
+
};
|
|
255
|
+
displayInformation: {
|
|
256
|
+
displayInformation: DisplayInformation;
|
|
257
|
+
};
|
|
258
|
+
getDisplayBrightness: {
|
|
259
|
+
displayBrightness: DisplayBrightness;
|
|
260
|
+
};
|
|
261
|
+
displayContextState: {
|
|
262
|
+
displayContextState: DisplayContextState;
|
|
263
|
+
differences: DisplayContextStateKey[];
|
|
264
|
+
};
|
|
265
|
+
displayColor: {
|
|
266
|
+
colorIndex: number;
|
|
267
|
+
colorRGB: DisplayColorRGB;
|
|
268
|
+
colorHex: string;
|
|
269
|
+
};
|
|
270
|
+
displayColorOpacity: {
|
|
271
|
+
opacity: number;
|
|
272
|
+
colorIndex: number;
|
|
273
|
+
};
|
|
274
|
+
displayOpacity: {
|
|
275
|
+
opacity: number;
|
|
276
|
+
};
|
|
277
|
+
displayReady: {};
|
|
278
|
+
getSpriteSheetName: {
|
|
279
|
+
spriteSheetName: string;
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
displaySpriteSheetUploadStart: {
|
|
283
|
+
spriteSheetName: string;
|
|
284
|
+
spriteSheet: DisplaySpriteSheet;
|
|
285
|
+
};
|
|
286
|
+
displaySpriteSheetUploadProgress: {
|
|
287
|
+
spriteSheetName: string;
|
|
288
|
+
spriteSheet: DisplaySpriteSheet;
|
|
289
|
+
progress: number;
|
|
290
|
+
};
|
|
291
|
+
displaySpriteSheetUploadComplete: {
|
|
292
|
+
spriteSheetName: string;
|
|
293
|
+
spriteSheet: DisplaySpriteSheet;
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export type DisplayEventDispatcher = EventDispatcher<
|
|
298
|
+
Device,
|
|
299
|
+
DisplayEventType,
|
|
300
|
+
DisplayEventMessages
|
|
301
|
+
>;
|
|
302
|
+
export type SendDisplayMessageCallback =
|
|
303
|
+
SendMessageCallback<DisplayMessageType>;
|
|
304
|
+
|
|
305
|
+
export const MinSpriteSheetNameLength = 1;
|
|
306
|
+
export const MaxSpriteSheetNameLength = 30;
|
|
307
|
+
|
|
308
|
+
export type DisplayBitmap = {
|
|
309
|
+
width: number;
|
|
310
|
+
height: number;
|
|
311
|
+
numberOfColors: number;
|
|
312
|
+
pixels: number[];
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
class DisplayManager implements DisplayManagerInterface {
|
|
316
|
+
constructor() {
|
|
317
|
+
autoBind(this);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
sendMessage!: SendDisplayMessageCallback;
|
|
321
|
+
|
|
322
|
+
eventDispatcher!: DisplayEventDispatcher;
|
|
323
|
+
get #dispatchEvent() {
|
|
324
|
+
return this.eventDispatcher.dispatchEvent;
|
|
325
|
+
}
|
|
326
|
+
get waitForEvent() {
|
|
327
|
+
return this.eventDispatcher.waitForEvent;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
requestRequiredInformation() {
|
|
331
|
+
_console.log("requesting required display information");
|
|
332
|
+
const messages = RequiredDisplayMessageTypes.map((messageType) => ({
|
|
333
|
+
type: messageType,
|
|
334
|
+
}));
|
|
335
|
+
this.sendMessage(messages, false);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// IS DISPLAY AVAILABLE
|
|
339
|
+
#isAvailable = false;
|
|
340
|
+
get isAvailable() {
|
|
341
|
+
return this.#isAvailable;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
#assertDisplayIsAvailable() {
|
|
345
|
+
_console.assertWithError(this.#isAvailable, "display is not available");
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
#parseIsDisplayAvailable(dataView: DataView) {
|
|
349
|
+
const newIsDisplayAvailable = dataView.getUint8(0) == 1;
|
|
350
|
+
this.#isAvailable = newIsDisplayAvailable;
|
|
351
|
+
_console.log({ isDisplayAvailable: this.#isAvailable });
|
|
352
|
+
this.#dispatchEvent("isDisplayAvailable", {
|
|
353
|
+
isDisplayAvailable: this.#isAvailable,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// DISPLAY CONTEXT STATE
|
|
358
|
+
#contextStateHelper = new DisplayContextStateHelper();
|
|
359
|
+
get contextState() {
|
|
360
|
+
return this.#contextStateHelper.state;
|
|
361
|
+
}
|
|
362
|
+
#onContextStateUpdate(differences: DisplayContextStateKey[]) {
|
|
363
|
+
this.#dispatchEvent("displayContextState", {
|
|
364
|
+
displayContextState: structuredClone(this.contextState),
|
|
365
|
+
differences,
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
async setContextState(
|
|
369
|
+
newState: PartialDisplayContextState,
|
|
370
|
+
sendImmediately?: boolean
|
|
371
|
+
) {
|
|
372
|
+
const differences = this.#contextStateHelper.diff(newState);
|
|
373
|
+
if (differences.length == 0) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
differences.forEach((difference) => {
|
|
377
|
+
switch (difference) {
|
|
378
|
+
case "backgroundColorIndex":
|
|
379
|
+
this.selectBackgroundColor(newState.backgroundColorIndex!);
|
|
380
|
+
break;
|
|
381
|
+
case "fillBackground":
|
|
382
|
+
this.setFillBackground(newState.fillBackground!);
|
|
383
|
+
break;
|
|
384
|
+
case "ignoreFill":
|
|
385
|
+
this.setIgnoreFill(newState.ignoreFill!);
|
|
386
|
+
break;
|
|
387
|
+
case "ignoreLine":
|
|
388
|
+
this.setIgnoreLine(newState.ignoreLine!);
|
|
389
|
+
break;
|
|
390
|
+
case "fillColorIndex":
|
|
391
|
+
this.selectFillColor(newState.fillColorIndex!);
|
|
392
|
+
break;
|
|
393
|
+
case "lineColorIndex":
|
|
394
|
+
this.selectLineColor(newState.lineColorIndex!);
|
|
395
|
+
break;
|
|
396
|
+
case "lineWidth":
|
|
397
|
+
this.setLineWidth(newState.lineWidth!);
|
|
398
|
+
break;
|
|
399
|
+
case "horizontalAlignment":
|
|
400
|
+
this.setHorizontalAlignment(newState.horizontalAlignment!);
|
|
401
|
+
break;
|
|
402
|
+
case "verticalAlignment":
|
|
403
|
+
this.setVerticalAlignment(newState.verticalAlignment!);
|
|
404
|
+
break;
|
|
405
|
+
case "rotation":
|
|
406
|
+
this.setRotation(newState.rotation!, true);
|
|
407
|
+
break;
|
|
408
|
+
case "segmentStartCap":
|
|
409
|
+
this.setSegmentStartCap(newState.segmentStartCap!);
|
|
410
|
+
break;
|
|
411
|
+
case "segmentEndCap":
|
|
412
|
+
this.setSegmentEndCap(newState.segmentEndCap!);
|
|
413
|
+
break;
|
|
414
|
+
case "segmentStartRadius":
|
|
415
|
+
this.setSegmentStartRadius(newState.segmentStartRadius!);
|
|
416
|
+
break;
|
|
417
|
+
case "segmentEndRadius":
|
|
418
|
+
this.setSegmentEndRadius(newState.segmentEndRadius!);
|
|
419
|
+
break;
|
|
420
|
+
case "cropTop":
|
|
421
|
+
this.setCropTop(newState.cropTop!);
|
|
422
|
+
break;
|
|
423
|
+
case "cropRight":
|
|
424
|
+
this.setCropRight(newState.cropRight!);
|
|
425
|
+
break;
|
|
426
|
+
case "cropBottom":
|
|
427
|
+
this.setCropBottom(newState.cropBottom!);
|
|
428
|
+
break;
|
|
429
|
+
case "cropLeft":
|
|
430
|
+
this.setCropLeft(newState.cropLeft!);
|
|
431
|
+
break;
|
|
432
|
+
case "rotationCropTop":
|
|
433
|
+
this.setRotationCropTop(newState.rotationCropTop!);
|
|
434
|
+
break;
|
|
435
|
+
case "rotationCropRight":
|
|
436
|
+
this.setRotationCropRight(newState.rotationCropRight!);
|
|
437
|
+
break;
|
|
438
|
+
case "rotationCropBottom":
|
|
439
|
+
this.setRotationCropBottom(newState.rotationCropBottom!);
|
|
440
|
+
break;
|
|
441
|
+
case "rotationCropLeft":
|
|
442
|
+
this.setRotationCropLeft(newState.rotationCropLeft!);
|
|
443
|
+
break;
|
|
444
|
+
case "bitmapColorIndices":
|
|
445
|
+
const bitmapColors: DisplayBitmapColorPair[] = [];
|
|
446
|
+
newState.bitmapColorIndices!.forEach(
|
|
447
|
+
(colorIndex, bitmapColorIndex) => {
|
|
448
|
+
bitmapColors.push({ bitmapColorIndex, colorIndex });
|
|
449
|
+
}
|
|
450
|
+
);
|
|
451
|
+
this.selectBitmapColors(bitmapColors);
|
|
452
|
+
break;
|
|
453
|
+
case "bitmapScaleX":
|
|
454
|
+
this.setBitmapScaleX(newState.bitmapScaleX!);
|
|
455
|
+
break;
|
|
456
|
+
case "bitmapScaleY":
|
|
457
|
+
this.setBitmapScaleY(newState.bitmapScaleY!);
|
|
458
|
+
break;
|
|
459
|
+
case "spriteColorIndices":
|
|
460
|
+
const spriteColors: DisplaySpriteColorPair[] = [];
|
|
461
|
+
newState.spriteColorIndices!.forEach(
|
|
462
|
+
(colorIndex, spriteColorIndex) => {
|
|
463
|
+
spriteColors.push({ spriteColorIndex, colorIndex });
|
|
464
|
+
}
|
|
465
|
+
);
|
|
466
|
+
this.selectSpriteColors(spriteColors);
|
|
467
|
+
break;
|
|
468
|
+
case "spriteScaleX":
|
|
469
|
+
this.setSpriteScaleX(newState.spriteScaleX!);
|
|
470
|
+
break;
|
|
471
|
+
case "spriteScaleY":
|
|
472
|
+
this.setSpriteScaleY(newState.spriteScaleY!);
|
|
473
|
+
break;
|
|
474
|
+
case "spritesLineHeight":
|
|
475
|
+
this.setSpritesLineHeight(newState.spritesLineHeight!);
|
|
476
|
+
break;
|
|
477
|
+
case "spritesDirection":
|
|
478
|
+
this.setSpritesDirection(newState.spritesDirection!);
|
|
479
|
+
break;
|
|
480
|
+
case "spritesLineDirection":
|
|
481
|
+
this.setSpritesLineDirection(newState.spritesLineDirection!);
|
|
482
|
+
break;
|
|
483
|
+
case "spritesSpacing":
|
|
484
|
+
this.setSpritesSpacing(newState.spritesSpacing!);
|
|
485
|
+
break;
|
|
486
|
+
case "spritesLineSpacing":
|
|
487
|
+
this.setSpritesLineSpacing(newState.spritesLineSpacing!);
|
|
488
|
+
break;
|
|
489
|
+
case "spritesAlignment":
|
|
490
|
+
this.setSpritesAlignment(newState.spritesAlignment!);
|
|
491
|
+
break;
|
|
492
|
+
case "spritesLineAlignment":
|
|
493
|
+
this.setSpritesLineAlignment(newState.spritesLineAlignment!);
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
if (sendImmediately) {
|
|
498
|
+
await this.#sendContextCommands();
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// DISPLAY STATUS
|
|
503
|
+
#displayStatus!: DisplayStatus;
|
|
504
|
+
get displayStatus() {
|
|
505
|
+
return this.#displayStatus;
|
|
506
|
+
}
|
|
507
|
+
get isDisplayAwake() {
|
|
508
|
+
return this.#displayStatus == "awake";
|
|
509
|
+
}
|
|
510
|
+
#parseDisplayStatus(dataView: DataView) {
|
|
511
|
+
const displayStatusIndex = dataView.getUint8(0);
|
|
512
|
+
const newDisplayStatus = DisplayStatuses[displayStatusIndex];
|
|
513
|
+
this.#updateDisplayStatus(newDisplayStatus);
|
|
514
|
+
}
|
|
515
|
+
#updateDisplayStatus(newDisplayStatus: DisplayStatus) {
|
|
516
|
+
_console.assertEnumWithError(newDisplayStatus, DisplayStatuses);
|
|
517
|
+
if (newDisplayStatus == this.#displayStatus) {
|
|
518
|
+
_console.log(`redundant displayStatus ${newDisplayStatus}`);
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
const previousDisplayStatus = this.#displayStatus;
|
|
522
|
+
this.#displayStatus = newDisplayStatus;
|
|
523
|
+
_console.log(`updated displayStatus to "${this.displayStatus}"`);
|
|
524
|
+
this.#dispatchEvent("displayStatus", {
|
|
525
|
+
displayStatus: this.displayStatus,
|
|
526
|
+
previousDisplayStatus,
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// DISPLAY COMMAND
|
|
531
|
+
async #sendDisplayCommand(
|
|
532
|
+
command: DisplayCommand,
|
|
533
|
+
sendImmediately?: boolean
|
|
534
|
+
) {
|
|
535
|
+
_console.assertEnumWithError(command, DisplayCommands);
|
|
536
|
+
_console.log(`sending display command "${command}"`);
|
|
537
|
+
|
|
538
|
+
const promise = this.waitForEvent("displayStatus");
|
|
539
|
+
_console.log(`setting command "${command}"`);
|
|
540
|
+
const commandEnum = DisplayCommands.indexOf(command);
|
|
541
|
+
|
|
542
|
+
this.sendMessage(
|
|
543
|
+
[
|
|
544
|
+
{
|
|
545
|
+
type: "displayCommand",
|
|
546
|
+
data: UInt8ByteBuffer(commandEnum),
|
|
547
|
+
},
|
|
548
|
+
],
|
|
549
|
+
sendImmediately
|
|
550
|
+
);
|
|
551
|
+
|
|
552
|
+
await promise;
|
|
553
|
+
}
|
|
554
|
+
#assertIsAwake() {
|
|
555
|
+
_console.assertWithError(
|
|
556
|
+
this.#displayStatus == "awake",
|
|
557
|
+
`display is not awake - currently ${this.#displayStatus}`
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
#assertIsNotAwake() {
|
|
561
|
+
_console.assertWithError(
|
|
562
|
+
this.#displayStatus != "awake",
|
|
563
|
+
`display is awake`
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
async wake() {
|
|
568
|
+
this.#assertIsNotAwake();
|
|
569
|
+
await this.#sendDisplayCommand("wake");
|
|
570
|
+
}
|
|
571
|
+
async sleep() {
|
|
572
|
+
this.#assertIsAwake();
|
|
573
|
+
await this.#sendDisplayCommand("sleep");
|
|
574
|
+
}
|
|
575
|
+
async toggle() {
|
|
576
|
+
switch (this.displayStatus) {
|
|
577
|
+
case "asleep":
|
|
578
|
+
this.wake();
|
|
579
|
+
break;
|
|
580
|
+
case "awake":
|
|
581
|
+
this.sleep();
|
|
582
|
+
break;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
get numberOfColors() {
|
|
587
|
+
return 2 ** Number(this.pixelDepth!);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// INFORMATION
|
|
591
|
+
#displayInformation?: DisplayInformation;
|
|
592
|
+
get displayInformation() {
|
|
593
|
+
return this.#displayInformation!;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
get pixelDepth() {
|
|
597
|
+
return this.#displayInformation?.pixelDepth!;
|
|
598
|
+
}
|
|
599
|
+
get width() {
|
|
600
|
+
return this.#displayInformation?.width!;
|
|
601
|
+
}
|
|
602
|
+
get height() {
|
|
603
|
+
return this.#displayInformation?.width!;
|
|
604
|
+
}
|
|
605
|
+
get size() {
|
|
606
|
+
return {
|
|
607
|
+
width: this.width!,
|
|
608
|
+
height: this.height!,
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
get type() {
|
|
612
|
+
return this.#displayInformation?.type!;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
#parseDisplayInformation(dataView: DataView) {
|
|
616
|
+
// @ts-expect-error
|
|
617
|
+
const parsedDisplayInformation: DisplayInformation = {};
|
|
618
|
+
|
|
619
|
+
let byteOffset = 0;
|
|
620
|
+
while (byteOffset < dataView.byteLength) {
|
|
621
|
+
const displayInformationTypeIndex = dataView.getUint8(byteOffset++);
|
|
622
|
+
const displayInformationType =
|
|
623
|
+
DisplayInformationTypes[displayInformationTypeIndex];
|
|
624
|
+
_console.assertWithError(
|
|
625
|
+
displayInformationType,
|
|
626
|
+
`invalid displayInformationTypeIndex ${displayInformationType}`
|
|
627
|
+
);
|
|
628
|
+
_console.log({ displayInformationType });
|
|
629
|
+
|
|
630
|
+
switch (displayInformationType) {
|
|
631
|
+
case "width":
|
|
632
|
+
case "height":
|
|
633
|
+
{
|
|
634
|
+
const value = dataView.getUint16(byteOffset, true);
|
|
635
|
+
parsedDisplayInformation[displayInformationType] = value;
|
|
636
|
+
byteOffset += 2;
|
|
637
|
+
}
|
|
638
|
+
break;
|
|
639
|
+
case "pixelDepth":
|
|
640
|
+
case "type":
|
|
641
|
+
{
|
|
642
|
+
const values = DisplayInformationValues[displayInformationType];
|
|
643
|
+
let rawValue = dataView.getUint8(byteOffset++);
|
|
644
|
+
const value = values[rawValue];
|
|
645
|
+
_console.assertEnumWithError(value, values);
|
|
646
|
+
// @ts-expect-error
|
|
647
|
+
parsedDisplayInformation[displayInformationType] = value;
|
|
648
|
+
}
|
|
649
|
+
break;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
_console.log({ parsedDisplayInformation });
|
|
654
|
+
const missingDisplayInformationType = DisplayInformationTypes.find(
|
|
655
|
+
(type) => !(type in parsedDisplayInformation)
|
|
656
|
+
);
|
|
657
|
+
_console.assertWithError(
|
|
658
|
+
!missingDisplayInformationType,
|
|
659
|
+
`missingDisplayInformationType ${missingDisplayInformationType}`
|
|
660
|
+
);
|
|
661
|
+
this.#displayInformation = parsedDisplayInformation;
|
|
662
|
+
this.#colors = new Array(this.numberOfColors).fill("#000000");
|
|
663
|
+
this.#opacities = new Array(this.numberOfColors).fill(1);
|
|
664
|
+
this.contextState.bitmapColorIndices = new Array(this.numberOfColors).fill(
|
|
665
|
+
0
|
|
666
|
+
);
|
|
667
|
+
this.contextState.spriteColorIndices = new Array(this.numberOfColors).fill(
|
|
668
|
+
0
|
|
669
|
+
);
|
|
670
|
+
this.#dispatchEvent("displayInformation", {
|
|
671
|
+
displayInformation: this.#displayInformation,
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// DISPLAY BRIGHTNESS
|
|
676
|
+
#brightness!: DisplayBrightness;
|
|
677
|
+
get brightness() {
|
|
678
|
+
return this.#brightness;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
#parseDisplayBrightness(dataView: DataView) {
|
|
682
|
+
const newDisplayBrightnessEnum = dataView.getUint8(0);
|
|
683
|
+
const newDisplayBrightness = DisplayBrightnesses[newDisplayBrightnessEnum];
|
|
684
|
+
assertValidDisplayBrightness(newDisplayBrightness);
|
|
685
|
+
|
|
686
|
+
this.#brightness = newDisplayBrightness;
|
|
687
|
+
_console.log({ displayBrightness: this.#brightness });
|
|
688
|
+
this.#dispatchEvent("getDisplayBrightness", {
|
|
689
|
+
displayBrightness: this.#brightness,
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
async setBrightness(
|
|
694
|
+
newDisplayBrightness: DisplayBrightness,
|
|
695
|
+
sendImmediately?: boolean
|
|
696
|
+
) {
|
|
697
|
+
this.#assertDisplayIsAvailable();
|
|
698
|
+
assertValidDisplayBrightness(newDisplayBrightness);
|
|
699
|
+
if (this.brightness == newDisplayBrightness) {
|
|
700
|
+
_console.log(`redundant displayBrightness ${newDisplayBrightness}`);
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
const newDisplayBrightnessEnum =
|
|
704
|
+
DisplayBrightnesses.indexOf(newDisplayBrightness);
|
|
705
|
+
const newDisplayBrightnessData = UInt8ByteBuffer(newDisplayBrightnessEnum);
|
|
706
|
+
|
|
707
|
+
const promise = this.waitForEvent("getDisplayBrightness");
|
|
708
|
+
this.sendMessage(
|
|
709
|
+
[{ type: "setDisplayBrightness", data: newDisplayBrightnessData }],
|
|
710
|
+
sendImmediately
|
|
711
|
+
);
|
|
712
|
+
await promise;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// DISPLAY CONTEXT
|
|
716
|
+
#assertValidDisplayContextCommand(
|
|
717
|
+
displayContextCommand: DisplayContextCommandType
|
|
718
|
+
) {
|
|
719
|
+
_console.assertEnumWithError(
|
|
720
|
+
displayContextCommand,
|
|
721
|
+
DisplayContextCommandTypes
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
get #maxCommandDataLength() {
|
|
726
|
+
return this.mtu - 7;
|
|
727
|
+
}
|
|
728
|
+
#displayContextCommandBuffers: ArrayBuffer[] = [];
|
|
729
|
+
async #sendDisplayContextCommand(
|
|
730
|
+
displayContextCommand: DisplayContextCommandType,
|
|
731
|
+
arrayBuffer?: ArrayBuffer,
|
|
732
|
+
sendImmediately?: boolean
|
|
733
|
+
) {
|
|
734
|
+
this.#assertValidDisplayContextCommand(displayContextCommand);
|
|
735
|
+
_console.log(
|
|
736
|
+
"sendDisplayContextCommand",
|
|
737
|
+
{ displayContextCommand, sendImmediately },
|
|
738
|
+
arrayBuffer
|
|
739
|
+
);
|
|
740
|
+
const displayContextCommandEnum = DisplayContextCommandTypes.indexOf(
|
|
741
|
+
displayContextCommand
|
|
742
|
+
);
|
|
743
|
+
const _arrayBuffer = concatenateArrayBuffers(
|
|
744
|
+
UInt8ByteBuffer(displayContextCommandEnum),
|
|
745
|
+
arrayBuffer
|
|
746
|
+
);
|
|
747
|
+
const newLength = this.#displayContextCommandBuffers.reduce(
|
|
748
|
+
(sum, buffer) => sum + buffer.byteLength,
|
|
749
|
+
_arrayBuffer.byteLength
|
|
750
|
+
);
|
|
751
|
+
if (newLength > this.#maxCommandDataLength) {
|
|
752
|
+
_console.log("displayContextCommandBuffers too full - sending now");
|
|
753
|
+
await this.#sendContextCommands();
|
|
754
|
+
}
|
|
755
|
+
this.#displayContextCommandBuffers.push(_arrayBuffer);
|
|
756
|
+
if (sendImmediately) {
|
|
757
|
+
await this.#sendContextCommands();
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
async #sendContextCommands() {
|
|
761
|
+
if (this.#displayContextCommandBuffers.length == 0) {
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
const data = concatenateArrayBuffers(this.#displayContextCommandBuffers);
|
|
765
|
+
_console.log(
|
|
766
|
+
`sending displayContextCommands`,
|
|
767
|
+
this.#displayContextCommandBuffers.slice(),
|
|
768
|
+
data
|
|
769
|
+
);
|
|
770
|
+
this.#displayContextCommandBuffers.length = 0;
|
|
771
|
+
await this.sendMessage([{ type: "displayContextCommands", data }], true);
|
|
772
|
+
}
|
|
773
|
+
async flushContextCommands() {
|
|
774
|
+
await this.#sendContextCommands();
|
|
775
|
+
}
|
|
776
|
+
async show(sendImmediately = true) {
|
|
777
|
+
_console.log("showDisplay");
|
|
778
|
+
this.#isReady = false;
|
|
779
|
+
this.#lastShowRequestTime = Date.now();
|
|
780
|
+
await this.#sendDisplayContextCommand("show", undefined, sendImmediately);
|
|
781
|
+
}
|
|
782
|
+
async clear(sendImmediately = true) {
|
|
783
|
+
_console.log("clearDisplay");
|
|
784
|
+
this.#isReady = false;
|
|
785
|
+
this.#lastShowRequestTime = Date.now();
|
|
786
|
+
await this.#sendDisplayContextCommand("clear", undefined, sendImmediately);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
assertValidColorIndex(colorIndex: number) {
|
|
790
|
+
_console.assertRangeWithError(
|
|
791
|
+
"colorIndex",
|
|
792
|
+
colorIndex,
|
|
793
|
+
0,
|
|
794
|
+
this.numberOfColors
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
#colors: string[] = [];
|
|
798
|
+
get colors() {
|
|
799
|
+
return this.#colors;
|
|
800
|
+
}
|
|
801
|
+
async setColor(
|
|
802
|
+
colorIndex: number,
|
|
803
|
+
color: DisplayColorRGB | string,
|
|
804
|
+
sendImmediately?: boolean
|
|
805
|
+
) {
|
|
806
|
+
let colorRGB: DisplayColorRGB;
|
|
807
|
+
if (typeof color == "string") {
|
|
808
|
+
colorRGB = stringToRGB(color);
|
|
809
|
+
} else {
|
|
810
|
+
colorRGB = color;
|
|
811
|
+
}
|
|
812
|
+
const colorHex = rgbToHex(colorRGB);
|
|
813
|
+
if (this.colors[colorIndex] == colorHex) {
|
|
814
|
+
_console.log(`redundant color #${colorIndex} ${colorHex}`);
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
//_console.log(`setting color #${colorIndex}`, colorRGB);
|
|
819
|
+
this.assertValidColorIndex(colorIndex);
|
|
820
|
+
assertValidColor(colorRGB);
|
|
821
|
+
const dataView = new DataView(new ArrayBuffer(4));
|
|
822
|
+
dataView.setUint8(0, colorIndex);
|
|
823
|
+
dataView.setUint8(1, colorRGB.r);
|
|
824
|
+
dataView.setUint8(2, colorRGB.g);
|
|
825
|
+
dataView.setUint8(3, colorRGB.b);
|
|
826
|
+
await this.#sendDisplayContextCommand(
|
|
827
|
+
"setColor",
|
|
828
|
+
dataView.buffer,
|
|
829
|
+
sendImmediately
|
|
830
|
+
);
|
|
831
|
+
this.colors[colorIndex] = colorHex;
|
|
832
|
+
this.#dispatchEvent("displayColor", {
|
|
833
|
+
colorIndex,
|
|
834
|
+
colorRGB,
|
|
835
|
+
colorHex,
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
#opacities: number[] = [];
|
|
839
|
+
get opacities() {
|
|
840
|
+
return this.#opacities;
|
|
841
|
+
}
|
|
842
|
+
async setColorOpacity(
|
|
843
|
+
colorIndex: number,
|
|
844
|
+
opacity: number,
|
|
845
|
+
sendImmediately?: boolean
|
|
846
|
+
) {
|
|
847
|
+
const commandType: DisplayContextCommandType = "setColorOpacity";
|
|
848
|
+
const dataView = serializeContextCommand(this, {
|
|
849
|
+
type: commandType,
|
|
850
|
+
colorIndex,
|
|
851
|
+
opacity,
|
|
852
|
+
});
|
|
853
|
+
if (!dataView) {
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
await this.#sendDisplayContextCommand(
|
|
857
|
+
commandType,
|
|
858
|
+
dataView.buffer,
|
|
859
|
+
sendImmediately
|
|
860
|
+
);
|
|
861
|
+
this.#opacities[colorIndex] = opacity;
|
|
862
|
+
this.#dispatchEvent("displayColorOpacity", { colorIndex, opacity });
|
|
863
|
+
}
|
|
864
|
+
async setOpacity(opacity: number, sendImmediately?: boolean) {
|
|
865
|
+
const commandType: DisplayContextCommandType = "setOpacity";
|
|
866
|
+
const dataView = serializeContextCommand(this, {
|
|
867
|
+
type: commandType,
|
|
868
|
+
opacity,
|
|
869
|
+
});
|
|
870
|
+
if (!dataView) {
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
await this.#sendDisplayContextCommand(
|
|
874
|
+
commandType,
|
|
875
|
+
dataView.buffer,
|
|
876
|
+
sendImmediately
|
|
877
|
+
);
|
|
878
|
+
this.#opacities.fill(opacity);
|
|
879
|
+
this.#dispatchEvent("displayOpacity", { opacity });
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
async saveContext(sendImmediately?: boolean) {
|
|
883
|
+
const commandType: DisplayContextCommandType = "saveContext";
|
|
884
|
+
const dataView = serializeContextCommand(this, { type: commandType });
|
|
885
|
+
await this.#sendDisplayContextCommand(
|
|
886
|
+
commandType,
|
|
887
|
+
dataView?.buffer,
|
|
888
|
+
sendImmediately
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
async restoreContext(sendImmediately?: boolean) {
|
|
892
|
+
const commandType: DisplayContextCommandType = "restoreContext";
|
|
893
|
+
const dataView = serializeContextCommand(this, { type: commandType });
|
|
894
|
+
await this.#sendDisplayContextCommand(
|
|
895
|
+
commandType,
|
|
896
|
+
dataView?.buffer,
|
|
897
|
+
sendImmediately
|
|
898
|
+
);
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
async selectFillColor(fillColorIndex: number, sendImmediately?: boolean) {
|
|
902
|
+
this.assertValidColorIndex(fillColorIndex);
|
|
903
|
+
const differences = this.#contextStateHelper.update({
|
|
904
|
+
fillColorIndex,
|
|
905
|
+
});
|
|
906
|
+
if (differences.length == 0) {
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
const commandType: DisplayContextCommandType = "selectFillColor";
|
|
910
|
+
const dataView = serializeContextCommand(this, {
|
|
911
|
+
type: commandType,
|
|
912
|
+
fillColorIndex,
|
|
913
|
+
});
|
|
914
|
+
if (!dataView) {
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
await this.#sendDisplayContextCommand(
|
|
918
|
+
commandType,
|
|
919
|
+
dataView.buffer,
|
|
920
|
+
sendImmediately
|
|
921
|
+
);
|
|
922
|
+
this.#onContextStateUpdate(differences);
|
|
923
|
+
}
|
|
924
|
+
async selectBackgroundColor(
|
|
925
|
+
backgroundColorIndex: number,
|
|
926
|
+
sendImmediately?: boolean
|
|
927
|
+
) {
|
|
928
|
+
this.assertValidColorIndex(backgroundColorIndex);
|
|
929
|
+
const differences = this.#contextStateHelper.update({
|
|
930
|
+
backgroundColorIndex,
|
|
931
|
+
});
|
|
932
|
+
if (differences.length == 0) {
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
const commandType: DisplayContextCommandType = "selectBackgroundColor";
|
|
936
|
+
const dataView = serializeContextCommand(this, {
|
|
937
|
+
type: commandType,
|
|
938
|
+
backgroundColorIndex,
|
|
939
|
+
});
|
|
940
|
+
if (!dataView) {
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
await this.#sendDisplayContextCommand(
|
|
944
|
+
commandType,
|
|
945
|
+
dataView.buffer,
|
|
946
|
+
sendImmediately
|
|
947
|
+
);
|
|
948
|
+
this.#onContextStateUpdate(differences);
|
|
949
|
+
}
|
|
950
|
+
async selectLineColor(lineColorIndex: number, sendImmediately?: boolean) {
|
|
951
|
+
this.assertValidColorIndex(lineColorIndex);
|
|
952
|
+
const differences = this.#contextStateHelper.update({
|
|
953
|
+
lineColorIndex,
|
|
954
|
+
});
|
|
955
|
+
if (differences.length == 0) {
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
const commandType: DisplayContextCommandType = "selectLineColor";
|
|
959
|
+
const dataView = serializeContextCommand(this, {
|
|
960
|
+
type: commandType,
|
|
961
|
+
lineColorIndex,
|
|
962
|
+
});
|
|
963
|
+
if (!dataView) {
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
await this.#sendDisplayContextCommand(
|
|
967
|
+
commandType,
|
|
968
|
+
dataView.buffer,
|
|
969
|
+
sendImmediately
|
|
970
|
+
);
|
|
971
|
+
this.#onContextStateUpdate(differences);
|
|
972
|
+
}
|
|
973
|
+
async setIgnoreFill(ignoreFill: boolean, sendImmediately?: boolean) {
|
|
974
|
+
const differences = this.#contextStateHelper.update({
|
|
975
|
+
ignoreFill,
|
|
976
|
+
});
|
|
977
|
+
if (differences.length == 0) {
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
const commandType: DisplayContextCommandType = "setIgnoreFill";
|
|
981
|
+
const dataView = serializeContextCommand(this, {
|
|
982
|
+
type: commandType,
|
|
983
|
+
ignoreFill,
|
|
984
|
+
});
|
|
985
|
+
if (!dataView) {
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
await this.#sendDisplayContextCommand(
|
|
989
|
+
commandType,
|
|
990
|
+
dataView.buffer,
|
|
991
|
+
sendImmediately
|
|
992
|
+
);
|
|
993
|
+
this.#onContextStateUpdate(differences);
|
|
994
|
+
}
|
|
995
|
+
async setIgnoreLine(ignoreLine: boolean, sendImmediately?: boolean) {
|
|
996
|
+
const differences = this.#contextStateHelper.update({
|
|
997
|
+
ignoreLine,
|
|
998
|
+
});
|
|
999
|
+
if (differences.length == 0) {
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
const commandType: DisplayContextCommandType = "setIgnoreLine";
|
|
1003
|
+
const dataView = serializeContextCommand(this, {
|
|
1004
|
+
type: commandType,
|
|
1005
|
+
ignoreLine,
|
|
1006
|
+
});
|
|
1007
|
+
if (!dataView) {
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
await this.#sendDisplayContextCommand(
|
|
1011
|
+
commandType,
|
|
1012
|
+
dataView.buffer,
|
|
1013
|
+
sendImmediately
|
|
1014
|
+
);
|
|
1015
|
+
this.#onContextStateUpdate(differences);
|
|
1016
|
+
}
|
|
1017
|
+
async setFillBackground(fillBackground: boolean, sendImmediately?: boolean) {
|
|
1018
|
+
const differences = this.#contextStateHelper.update({
|
|
1019
|
+
fillBackground,
|
|
1020
|
+
});
|
|
1021
|
+
if (differences.length == 0) {
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
const commandType: DisplayContextCommandType = "setFillBackground";
|
|
1025
|
+
const dataView = serializeContextCommand(this, {
|
|
1026
|
+
type: commandType,
|
|
1027
|
+
fillBackground,
|
|
1028
|
+
});
|
|
1029
|
+
if (!dataView) {
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
await this.#sendDisplayContextCommand(
|
|
1033
|
+
commandType,
|
|
1034
|
+
dataView.buffer,
|
|
1035
|
+
sendImmediately
|
|
1036
|
+
);
|
|
1037
|
+
this.#onContextStateUpdate(differences);
|
|
1038
|
+
}
|
|
1039
|
+
assertValidLineWidth(lineWidth: number) {
|
|
1040
|
+
_console.assertRangeWithError(
|
|
1041
|
+
"lineWidth",
|
|
1042
|
+
lineWidth,
|
|
1043
|
+
0,
|
|
1044
|
+
Math.max(this.width, this.height)
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
async setLineWidth(lineWidth: number, sendImmediately?: boolean) {
|
|
1048
|
+
this.assertValidLineWidth(lineWidth);
|
|
1049
|
+
const differences = this.#contextStateHelper.update({
|
|
1050
|
+
lineWidth,
|
|
1051
|
+
});
|
|
1052
|
+
if (differences.length == 0) {
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
const commandType: DisplayContextCommandType = "setLineWidth";
|
|
1056
|
+
const dataView = serializeContextCommand(this, {
|
|
1057
|
+
type: commandType,
|
|
1058
|
+
lineWidth,
|
|
1059
|
+
});
|
|
1060
|
+
if (!dataView) {
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
await this.#sendDisplayContextCommand(
|
|
1064
|
+
commandType,
|
|
1065
|
+
dataView.buffer,
|
|
1066
|
+
sendImmediately
|
|
1067
|
+
);
|
|
1068
|
+
this.#onContextStateUpdate(differences);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
async setAlignment(
|
|
1072
|
+
alignmentDirection: DisplayAlignmentDirection,
|
|
1073
|
+
alignment: DisplayAlignment,
|
|
1074
|
+
sendImmediately?: boolean
|
|
1075
|
+
) {
|
|
1076
|
+
assertValidAlignmentDirection(alignmentDirection);
|
|
1077
|
+
const alignmentCommand =
|
|
1078
|
+
DisplayAlignmentDirectionToCommandType[alignmentDirection];
|
|
1079
|
+
const alignmentKey =
|
|
1080
|
+
DisplayAlignmentDirectionToStateKey[alignmentDirection];
|
|
1081
|
+
const differences = this.#contextStateHelper.update({
|
|
1082
|
+
[alignmentKey]: alignment,
|
|
1083
|
+
});
|
|
1084
|
+
_console.log({ alignmentKey, alignment, differences });
|
|
1085
|
+
if (differences.length == 0) {
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
// @ts-ignore
|
|
1089
|
+
const dataView = serializeContextCommand(this, {
|
|
1090
|
+
type: alignmentCommand,
|
|
1091
|
+
[alignmentKey]: alignment,
|
|
1092
|
+
});
|
|
1093
|
+
if (!dataView) {
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
await this.#sendDisplayContextCommand(
|
|
1097
|
+
alignmentCommand,
|
|
1098
|
+
dataView.buffer,
|
|
1099
|
+
sendImmediately
|
|
1100
|
+
);
|
|
1101
|
+
this.#onContextStateUpdate(differences);
|
|
1102
|
+
}
|
|
1103
|
+
async setHorizontalAlignment(
|
|
1104
|
+
horizontalAlignment: DisplayAlignment,
|
|
1105
|
+
sendImmediately?: boolean
|
|
1106
|
+
) {
|
|
1107
|
+
await this.setAlignment("horizontal", horizontalAlignment, sendImmediately);
|
|
1108
|
+
}
|
|
1109
|
+
async setVerticalAlignment(
|
|
1110
|
+
verticalAlignment: DisplayAlignment,
|
|
1111
|
+
sendImmediately?: boolean
|
|
1112
|
+
) {
|
|
1113
|
+
await this.setAlignment("vertical", verticalAlignment, sendImmediately);
|
|
1114
|
+
}
|
|
1115
|
+
async resetAlignment(sendImmediately?: boolean) {
|
|
1116
|
+
const differences = this.#contextStateHelper.update({
|
|
1117
|
+
verticalAlignment: DefaultDisplayContextState.verticalAlignment,
|
|
1118
|
+
horizontalAlignment: DefaultDisplayContextState.horizontalAlignment,
|
|
1119
|
+
});
|
|
1120
|
+
if (differences.length == 0) {
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
const commandType: DisplayContextCommandType = "resetAlignment";
|
|
1124
|
+
const dataView = serializeContextCommand(this, {
|
|
1125
|
+
type: commandType,
|
|
1126
|
+
});
|
|
1127
|
+
if (!dataView) {
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
await this.#sendDisplayContextCommand(
|
|
1131
|
+
commandType,
|
|
1132
|
+
dataView?.buffer,
|
|
1133
|
+
sendImmediately
|
|
1134
|
+
);
|
|
1135
|
+
this.#onContextStateUpdate(differences);
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
async setRotation(
|
|
1139
|
+
rotation: number,
|
|
1140
|
+
isRadians?: boolean,
|
|
1141
|
+
sendImmediately?: boolean
|
|
1142
|
+
) {
|
|
1143
|
+
rotation = isRadians ? rotation : degToRad(rotation);
|
|
1144
|
+
rotation = normalizeRadians(rotation);
|
|
1145
|
+
isRadians = true;
|
|
1146
|
+
const differences = this.#contextStateHelper.update({
|
|
1147
|
+
rotation,
|
|
1148
|
+
});
|
|
1149
|
+
if (differences.length == 0) {
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
const commandType: DisplayContextCommandType = "setRotation";
|
|
1153
|
+
const dataView = serializeContextCommand(this, {
|
|
1154
|
+
type: commandType,
|
|
1155
|
+
rotation,
|
|
1156
|
+
isRadians,
|
|
1157
|
+
});
|
|
1158
|
+
if (!dataView) {
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
await this.#sendDisplayContextCommand(
|
|
1162
|
+
commandType,
|
|
1163
|
+
dataView.buffer,
|
|
1164
|
+
sendImmediately
|
|
1165
|
+
);
|
|
1166
|
+
|
|
1167
|
+
this.#onContextStateUpdate(differences);
|
|
1168
|
+
}
|
|
1169
|
+
async clearRotation(sendImmediately?: boolean) {
|
|
1170
|
+
const differences = this.#contextStateHelper.update({
|
|
1171
|
+
rotation: 0,
|
|
1172
|
+
});
|
|
1173
|
+
if (differences.length == 0) {
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1176
|
+
const commandType: DisplayContextCommandType = "clearRotation";
|
|
1177
|
+
const dataView = serializeContextCommand(this, { type: commandType });
|
|
1178
|
+
if (!dataView) {
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
await this.#sendDisplayContextCommand(
|
|
1182
|
+
commandType,
|
|
1183
|
+
dataView.buffer,
|
|
1184
|
+
sendImmediately
|
|
1185
|
+
);
|
|
1186
|
+
this.#onContextStateUpdate(differences);
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
async setSegmentStartCap(
|
|
1190
|
+
segmentStartCap: DisplaySegmentCap,
|
|
1191
|
+
sendImmediately?: boolean
|
|
1192
|
+
) {
|
|
1193
|
+
assertValidSegmentCap(segmentStartCap);
|
|
1194
|
+
const differences = this.#contextStateHelper.update({
|
|
1195
|
+
segmentStartCap,
|
|
1196
|
+
});
|
|
1197
|
+
if (differences.length == 0) {
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
const commandType: DisplayContextCommandType = "setSegmentStartCap";
|
|
1201
|
+
const dataView = serializeContextCommand(this, {
|
|
1202
|
+
type: commandType,
|
|
1203
|
+
segmentStartCap,
|
|
1204
|
+
});
|
|
1205
|
+
if (!dataView) {
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1208
|
+
await this.#sendDisplayContextCommand(
|
|
1209
|
+
commandType,
|
|
1210
|
+
dataView.buffer,
|
|
1211
|
+
sendImmediately
|
|
1212
|
+
);
|
|
1213
|
+
this.#onContextStateUpdate(differences);
|
|
1214
|
+
}
|
|
1215
|
+
async setSegmentEndCap(
|
|
1216
|
+
segmentEndCap: DisplaySegmentCap,
|
|
1217
|
+
sendImmediately?: boolean
|
|
1218
|
+
) {
|
|
1219
|
+
assertValidSegmentCap(segmentEndCap);
|
|
1220
|
+
const differences = this.#contextStateHelper.update({
|
|
1221
|
+
segmentEndCap,
|
|
1222
|
+
});
|
|
1223
|
+
if (differences.length == 0) {
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
const commandType: DisplayContextCommandType = "setSegmentEndCap";
|
|
1227
|
+
const dataView = serializeContextCommand(this, {
|
|
1228
|
+
type: commandType,
|
|
1229
|
+
segmentEndCap,
|
|
1230
|
+
});
|
|
1231
|
+
if (!dataView) {
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
await this.#sendDisplayContextCommand(
|
|
1235
|
+
commandType,
|
|
1236
|
+
dataView.buffer,
|
|
1237
|
+
sendImmediately
|
|
1238
|
+
);
|
|
1239
|
+
this.#onContextStateUpdate(differences);
|
|
1240
|
+
}
|
|
1241
|
+
async setSegmentCap(
|
|
1242
|
+
segmentCap: DisplaySegmentCap,
|
|
1243
|
+
sendImmediately?: boolean
|
|
1244
|
+
) {
|
|
1245
|
+
assertValidSegmentCap(segmentCap);
|
|
1246
|
+
const differences = this.#contextStateHelper.update({
|
|
1247
|
+
segmentStartCap: segmentCap,
|
|
1248
|
+
segmentEndCap: segmentCap,
|
|
1249
|
+
});
|
|
1250
|
+
if (differences.length == 0) {
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
const commandType: DisplayContextCommandType = "setSegmentCap";
|
|
1254
|
+
const dataView = serializeContextCommand(this, {
|
|
1255
|
+
type: commandType,
|
|
1256
|
+
segmentCap,
|
|
1257
|
+
});
|
|
1258
|
+
if (!dataView) {
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
await this.#sendDisplayContextCommand(
|
|
1262
|
+
commandType,
|
|
1263
|
+
dataView.buffer,
|
|
1264
|
+
sendImmediately
|
|
1265
|
+
);
|
|
1266
|
+
this.#onContextStateUpdate(differences);
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
async setSegmentStartRadius(
|
|
1270
|
+
segmentStartRadius: number,
|
|
1271
|
+
sendImmediately?: boolean
|
|
1272
|
+
) {
|
|
1273
|
+
const differences = this.#contextStateHelper.update({
|
|
1274
|
+
segmentStartRadius,
|
|
1275
|
+
});
|
|
1276
|
+
if (differences.length == 0) {
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
const commandType: DisplayContextCommandType = "setSegmentStartRadius";
|
|
1280
|
+
const dataView = serializeContextCommand(this, {
|
|
1281
|
+
type: commandType,
|
|
1282
|
+
segmentStartRadius,
|
|
1283
|
+
});
|
|
1284
|
+
if (!dataView) {
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
await this.#sendDisplayContextCommand(
|
|
1288
|
+
commandType,
|
|
1289
|
+
dataView.buffer,
|
|
1290
|
+
sendImmediately
|
|
1291
|
+
);
|
|
1292
|
+
this.#onContextStateUpdate(differences);
|
|
1293
|
+
}
|
|
1294
|
+
async setSegmentEndRadius(
|
|
1295
|
+
segmentEndRadius: number,
|
|
1296
|
+
sendImmediately?: boolean
|
|
1297
|
+
) {
|
|
1298
|
+
const differences = this.#contextStateHelper.update({
|
|
1299
|
+
segmentEndRadius,
|
|
1300
|
+
});
|
|
1301
|
+
if (differences.length == 0) {
|
|
1302
|
+
return;
|
|
1303
|
+
}
|
|
1304
|
+
const commandType: DisplayContextCommandType = "setSegmentEndRadius";
|
|
1305
|
+
const dataView = serializeContextCommand(this, {
|
|
1306
|
+
type: commandType,
|
|
1307
|
+
segmentEndRadius,
|
|
1308
|
+
});
|
|
1309
|
+
if (!dataView) {
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
await this.#sendDisplayContextCommand(
|
|
1313
|
+
commandType,
|
|
1314
|
+
dataView.buffer,
|
|
1315
|
+
sendImmediately
|
|
1316
|
+
);
|
|
1317
|
+
this.#onContextStateUpdate(differences);
|
|
1318
|
+
}
|
|
1319
|
+
async setSegmentRadius(segmentRadius: number, sendImmediately?: boolean) {
|
|
1320
|
+
const differences = this.#contextStateHelper.update({
|
|
1321
|
+
segmentStartRadius: segmentRadius,
|
|
1322
|
+
segmentEndRadius: segmentRadius,
|
|
1323
|
+
});
|
|
1324
|
+
if (differences.length == 0) {
|
|
1325
|
+
return;
|
|
1326
|
+
}
|
|
1327
|
+
const commandType: DisplayContextCommandType = "setSegmentRadius";
|
|
1328
|
+
const dataView = serializeContextCommand(this, {
|
|
1329
|
+
type: commandType,
|
|
1330
|
+
segmentRadius,
|
|
1331
|
+
});
|
|
1332
|
+
if (!dataView) {
|
|
1333
|
+
return;
|
|
1334
|
+
}
|
|
1335
|
+
await this.#sendDisplayContextCommand(
|
|
1336
|
+
commandType,
|
|
1337
|
+
dataView.buffer,
|
|
1338
|
+
sendImmediately
|
|
1339
|
+
);
|
|
1340
|
+
this.#onContextStateUpdate(differences);
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
async setCrop(
|
|
1344
|
+
cropDirection: DisplayCropDirection,
|
|
1345
|
+
crop: number,
|
|
1346
|
+
sendImmediately?: boolean
|
|
1347
|
+
) {
|
|
1348
|
+
_console.assertEnumWithError(cropDirection, DisplayCropDirections);
|
|
1349
|
+
crop = Math.max(0, crop);
|
|
1350
|
+
const cropCommand = DisplayCropDirectionToCommandType[cropDirection];
|
|
1351
|
+
const cropKey = DisplayCropDirectionToStateKey[cropDirection];
|
|
1352
|
+
const differences = this.#contextStateHelper.update({
|
|
1353
|
+
[cropKey]: crop,
|
|
1354
|
+
});
|
|
1355
|
+
if (differences.length == 0) {
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1358
|
+
// @ts-ignore
|
|
1359
|
+
const dataView = serializeContextCommand(this, {
|
|
1360
|
+
type: cropCommand,
|
|
1361
|
+
[cropKey]: crop,
|
|
1362
|
+
});
|
|
1363
|
+
if (!dataView) {
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
await this.#sendDisplayContextCommand(
|
|
1367
|
+
cropCommand,
|
|
1368
|
+
dataView.buffer,
|
|
1369
|
+
sendImmediately
|
|
1370
|
+
);
|
|
1371
|
+
this.#onContextStateUpdate(differences);
|
|
1372
|
+
}
|
|
1373
|
+
async setCropTop(cropTop: number, sendImmediately?: boolean) {
|
|
1374
|
+
await this.setCrop("top", cropTop, sendImmediately);
|
|
1375
|
+
}
|
|
1376
|
+
async setCropRight(cropRight: number, sendImmediately?: boolean) {
|
|
1377
|
+
await this.setCrop("right", cropRight, sendImmediately);
|
|
1378
|
+
}
|
|
1379
|
+
async setCropBottom(cropBottom: number, sendImmediately?: boolean) {
|
|
1380
|
+
await this.setCrop("bottom", cropBottom, sendImmediately);
|
|
1381
|
+
}
|
|
1382
|
+
async setCropLeft(cropLeft: number, sendImmediately?: boolean) {
|
|
1383
|
+
await this.setCrop("left", cropLeft, sendImmediately);
|
|
1384
|
+
}
|
|
1385
|
+
async clearCrop(sendImmediately?: boolean) {
|
|
1386
|
+
const differences = this.#contextStateHelper.update({
|
|
1387
|
+
cropTop: 0,
|
|
1388
|
+
cropRight: 0,
|
|
1389
|
+
cropBottom: 0,
|
|
1390
|
+
cropLeft: 0,
|
|
1391
|
+
});
|
|
1392
|
+
if (differences.length == 0) {
|
|
1393
|
+
return;
|
|
1394
|
+
}
|
|
1395
|
+
const commandType: DisplayContextCommandType = "clearCrop";
|
|
1396
|
+
const dataView = serializeContextCommand(this, { type: commandType });
|
|
1397
|
+
await this.#sendDisplayContextCommand(
|
|
1398
|
+
commandType,
|
|
1399
|
+
dataView?.buffer,
|
|
1400
|
+
sendImmediately
|
|
1401
|
+
);
|
|
1402
|
+
this.#onContextStateUpdate(differences);
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
async setRotationCrop(
|
|
1406
|
+
cropDirection: DisplayCropDirection,
|
|
1407
|
+
crop: number,
|
|
1408
|
+
sendImmediately?: boolean
|
|
1409
|
+
) {
|
|
1410
|
+
_console.assertEnumWithError(cropDirection, DisplayCropDirections);
|
|
1411
|
+
const cropCommand =
|
|
1412
|
+
DisplayRotationCropDirectionToCommandType[cropDirection];
|
|
1413
|
+
const cropKey = DisplayRotationCropDirectionToStateKey[cropDirection];
|
|
1414
|
+
const differences = this.#contextStateHelper.update({
|
|
1415
|
+
[cropKey]: crop,
|
|
1416
|
+
});
|
|
1417
|
+
if (differences.length == 0) {
|
|
1418
|
+
return;
|
|
1419
|
+
}
|
|
1420
|
+
// @ts-ignore
|
|
1421
|
+
const dataView = serializeContextCommand(this, {
|
|
1422
|
+
type: cropCommand,
|
|
1423
|
+
[cropKey]: crop,
|
|
1424
|
+
});
|
|
1425
|
+
if (!dataView) {
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
await this.#sendDisplayContextCommand(
|
|
1429
|
+
cropCommand,
|
|
1430
|
+
dataView.buffer,
|
|
1431
|
+
sendImmediately
|
|
1432
|
+
);
|
|
1433
|
+
this.#onContextStateUpdate(differences);
|
|
1434
|
+
}
|
|
1435
|
+
async setRotationCropTop(rotationCropTop: number, sendImmediately?: boolean) {
|
|
1436
|
+
await this.setRotationCrop("top", rotationCropTop, sendImmediately);
|
|
1437
|
+
}
|
|
1438
|
+
async setRotationCropRight(
|
|
1439
|
+
rotationCropRight: number,
|
|
1440
|
+
sendImmediately?: boolean
|
|
1441
|
+
) {
|
|
1442
|
+
await this.setRotationCrop("right", rotationCropRight, sendImmediately);
|
|
1443
|
+
}
|
|
1444
|
+
async setRotationCropBottom(
|
|
1445
|
+
rotationCropBottom: number,
|
|
1446
|
+
sendImmediately?: boolean
|
|
1447
|
+
) {
|
|
1448
|
+
await this.setRotationCrop("bottom", rotationCropBottom, sendImmediately);
|
|
1449
|
+
}
|
|
1450
|
+
async setRotationCropLeft(
|
|
1451
|
+
rotationCropLeft: number,
|
|
1452
|
+
sendImmediately?: boolean
|
|
1453
|
+
) {
|
|
1454
|
+
await this.setRotationCrop("left", rotationCropLeft, sendImmediately);
|
|
1455
|
+
}
|
|
1456
|
+
async clearRotationCrop(sendImmediately?: boolean) {
|
|
1457
|
+
const differences = this.#contextStateHelper.update({
|
|
1458
|
+
rotationCropTop: 0,
|
|
1459
|
+
rotationCropRight: 0,
|
|
1460
|
+
rotationCropBottom: 0,
|
|
1461
|
+
rotationCropLeft: 0,
|
|
1462
|
+
});
|
|
1463
|
+
if (differences.length == 0) {
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1466
|
+
const commandType: DisplayContextCommandType = "clearRotationCrop";
|
|
1467
|
+
const dataView = serializeContextCommand(this, {
|
|
1468
|
+
type: commandType,
|
|
1469
|
+
});
|
|
1470
|
+
await this.#sendDisplayContextCommand(
|
|
1471
|
+
commandType,
|
|
1472
|
+
dataView?.buffer,
|
|
1473
|
+
sendImmediately
|
|
1474
|
+
);
|
|
1475
|
+
this.#onContextStateUpdate(differences);
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
async selectBitmapColor(
|
|
1479
|
+
bitmapColorIndex: number,
|
|
1480
|
+
colorIndex: number,
|
|
1481
|
+
sendImmediately?: boolean
|
|
1482
|
+
) {
|
|
1483
|
+
this.assertValidColorIndex(bitmapColorIndex);
|
|
1484
|
+
this.assertValidColorIndex(colorIndex);
|
|
1485
|
+
const bitmapColorIndices = this.contextState.bitmapColorIndices.slice();
|
|
1486
|
+
bitmapColorIndices[bitmapColorIndex] = colorIndex;
|
|
1487
|
+
const differences = this.#contextStateHelper.update({
|
|
1488
|
+
bitmapColorIndices,
|
|
1489
|
+
});
|
|
1490
|
+
if (differences.length == 0) {
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1493
|
+
const commandType: DisplayContextCommandType = "selectBitmapColor";
|
|
1494
|
+
const dataView = serializeContextCommand(this, {
|
|
1495
|
+
type: commandType,
|
|
1496
|
+
bitmapColorIndex,
|
|
1497
|
+
colorIndex,
|
|
1498
|
+
});
|
|
1499
|
+
if (!dataView) {
|
|
1500
|
+
return;
|
|
1501
|
+
}
|
|
1502
|
+
await this.#sendDisplayContextCommand(
|
|
1503
|
+
commandType,
|
|
1504
|
+
dataView.buffer,
|
|
1505
|
+
sendImmediately
|
|
1506
|
+
);
|
|
1507
|
+
this.#onContextStateUpdate(differences);
|
|
1508
|
+
}
|
|
1509
|
+
get bitmapColorIndices() {
|
|
1510
|
+
return this.contextState.bitmapColorIndices;
|
|
1511
|
+
}
|
|
1512
|
+
get bitmapColors() {
|
|
1513
|
+
return this.bitmapColorIndices.map((colorIndex) => this.colors[colorIndex]);
|
|
1514
|
+
}
|
|
1515
|
+
async selectBitmapColors(
|
|
1516
|
+
bitmapColorPairs: DisplayBitmapColorPair[],
|
|
1517
|
+
sendImmediately?: boolean
|
|
1518
|
+
) {
|
|
1519
|
+
_console.assertRangeWithError(
|
|
1520
|
+
"bitmapColors",
|
|
1521
|
+
bitmapColorPairs.length,
|
|
1522
|
+
1,
|
|
1523
|
+
this.numberOfColors
|
|
1524
|
+
);
|
|
1525
|
+
const bitmapColorIndices = this.contextState.bitmapColorIndices.slice();
|
|
1526
|
+
bitmapColorPairs.forEach(({ bitmapColorIndex, colorIndex }) => {
|
|
1527
|
+
this.assertValidColorIndex(bitmapColorIndex);
|
|
1528
|
+
this.assertValidColorIndex(colorIndex);
|
|
1529
|
+
bitmapColorIndices[bitmapColorIndex] = colorIndex;
|
|
1530
|
+
});
|
|
1531
|
+
|
|
1532
|
+
const differences = this.#contextStateHelper.update({
|
|
1533
|
+
bitmapColorIndices,
|
|
1534
|
+
});
|
|
1535
|
+
if (differences.length == 0) {
|
|
1536
|
+
return;
|
|
1537
|
+
}
|
|
1538
|
+
const commandType: DisplayContextCommandType = "selectBitmapColors";
|
|
1539
|
+
const dataView = serializeContextCommand(this, {
|
|
1540
|
+
type: commandType,
|
|
1541
|
+
bitmapColorPairs,
|
|
1542
|
+
});
|
|
1543
|
+
if (!dataView) {
|
|
1544
|
+
return;
|
|
1545
|
+
}
|
|
1546
|
+
await this.#sendDisplayContextCommand(
|
|
1547
|
+
commandType,
|
|
1548
|
+
dataView.buffer,
|
|
1549
|
+
sendImmediately
|
|
1550
|
+
);
|
|
1551
|
+
this.#onContextStateUpdate(differences);
|
|
1552
|
+
}
|
|
1553
|
+
async setBitmapColor(
|
|
1554
|
+
bitmapColorIndex: number,
|
|
1555
|
+
color: DisplayColorRGB | string,
|
|
1556
|
+
sendImmediately?: boolean
|
|
1557
|
+
) {
|
|
1558
|
+
return this.setColor(
|
|
1559
|
+
this.bitmapColorIndices[bitmapColorIndex],
|
|
1560
|
+
color,
|
|
1561
|
+
sendImmediately
|
|
1562
|
+
);
|
|
1563
|
+
}
|
|
1564
|
+
async setBitmapColorOpacity(
|
|
1565
|
+
bitmapColorIndex: number,
|
|
1566
|
+
opacity: number,
|
|
1567
|
+
sendImmediately?: boolean
|
|
1568
|
+
) {
|
|
1569
|
+
return this.setColorOpacity(
|
|
1570
|
+
this.bitmapColorIndices[bitmapColorIndex],
|
|
1571
|
+
opacity,
|
|
1572
|
+
sendImmediately
|
|
1573
|
+
);
|
|
1574
|
+
}
|
|
1575
|
+
async setBitmapScaleDirection(
|
|
1576
|
+
direction: DisplayScaleDirection,
|
|
1577
|
+
bitmapScale: number,
|
|
1578
|
+
sendImmediately?: boolean
|
|
1579
|
+
) {
|
|
1580
|
+
bitmapScale = clamp(bitmapScale, minDisplayScale, maxDisplayScale);
|
|
1581
|
+
bitmapScale = roundScale(bitmapScale);
|
|
1582
|
+
const commandType = DisplayBitmapScaleDirectionToCommandType[direction];
|
|
1583
|
+
_console.log({ [commandType]: bitmapScale });
|
|
1584
|
+
const newState: PartialDisplayContextState = {};
|
|
1585
|
+
let command: DisplayContextCommand;
|
|
1586
|
+
switch (direction) {
|
|
1587
|
+
case "all":
|
|
1588
|
+
newState.bitmapScaleX = bitmapScale;
|
|
1589
|
+
newState.bitmapScaleY = bitmapScale;
|
|
1590
|
+
command = { type: "setBitmapScale", bitmapScale };
|
|
1591
|
+
break;
|
|
1592
|
+
case "x":
|
|
1593
|
+
newState.bitmapScaleX = bitmapScale;
|
|
1594
|
+
command = { type: "setBitmapScaleX", bitmapScaleX: bitmapScale };
|
|
1595
|
+
break;
|
|
1596
|
+
case "y":
|
|
1597
|
+
newState.bitmapScaleY = bitmapScale;
|
|
1598
|
+
command = { type: "setBitmapScaleY", bitmapScaleY: bitmapScale };
|
|
1599
|
+
break;
|
|
1600
|
+
}
|
|
1601
|
+
const differences = this.#contextStateHelper.update(newState);
|
|
1602
|
+
if (differences.length == 0) {
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1605
|
+
const dataView = serializeContextCommand(this, command);
|
|
1606
|
+
if (!dataView) {
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
await this.#sendDisplayContextCommand(
|
|
1610
|
+
commandType,
|
|
1611
|
+
dataView.buffer,
|
|
1612
|
+
sendImmediately
|
|
1613
|
+
);
|
|
1614
|
+
|
|
1615
|
+
this.#onContextStateUpdate(differences);
|
|
1616
|
+
}
|
|
1617
|
+
async setBitmapScaleX(bitmapScaleX: number, sendImmediately?: boolean) {
|
|
1618
|
+
return this.setBitmapScaleDirection("x", bitmapScaleX, sendImmediately);
|
|
1619
|
+
}
|
|
1620
|
+
async setBitmapScaleY(bitmapScaleY: number, sendImmediately?: boolean) {
|
|
1621
|
+
return this.setBitmapScaleDirection("y", bitmapScaleY, sendImmediately);
|
|
1622
|
+
}
|
|
1623
|
+
async setBitmapScale(bitmapScale: number, sendImmediately?: boolean) {
|
|
1624
|
+
return this.setBitmapScaleDirection("all", bitmapScale, sendImmediately);
|
|
1625
|
+
}
|
|
1626
|
+
async resetBitmapScale(sendImmediately?: boolean) {
|
|
1627
|
+
//return this.setBitmapScaleDirection("all", 1, sendImmediately);
|
|
1628
|
+
|
|
1629
|
+
const differences = this.#contextStateHelper.update({
|
|
1630
|
+
bitmapScaleX: 1,
|
|
1631
|
+
bitmapScaleY: 1,
|
|
1632
|
+
});
|
|
1633
|
+
if (differences.length == 0) {
|
|
1634
|
+
return;
|
|
1635
|
+
}
|
|
1636
|
+
const commandType: DisplayContextCommandType = "resetBitmapScale";
|
|
1637
|
+
const dataView = serializeContextCommand(this, {
|
|
1638
|
+
type: commandType,
|
|
1639
|
+
});
|
|
1640
|
+
await this.#sendDisplayContextCommand(
|
|
1641
|
+
commandType,
|
|
1642
|
+
dataView?.buffer,
|
|
1643
|
+
sendImmediately
|
|
1644
|
+
);
|
|
1645
|
+
this.#onContextStateUpdate(differences);
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
async selectSpriteColor(
|
|
1649
|
+
spriteColorIndex: number,
|
|
1650
|
+
colorIndex: number,
|
|
1651
|
+
sendImmediately?: boolean
|
|
1652
|
+
) {
|
|
1653
|
+
this.assertValidColorIndex(spriteColorIndex);
|
|
1654
|
+
this.assertValidColorIndex(colorIndex);
|
|
1655
|
+
const spriteColorIndices = this.contextState.spriteColorIndices.slice();
|
|
1656
|
+
spriteColorIndices[spriteColorIndex] = colorIndex;
|
|
1657
|
+
const differences = this.#contextStateHelper.update({
|
|
1658
|
+
spriteColorIndices,
|
|
1659
|
+
});
|
|
1660
|
+
if (differences.length == 0) {
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
const commandType: DisplayContextCommandType = "selectSpriteColor";
|
|
1664
|
+
const dataView = serializeContextCommand(this, {
|
|
1665
|
+
type: commandType,
|
|
1666
|
+
spriteColorIndex,
|
|
1667
|
+
colorIndex,
|
|
1668
|
+
});
|
|
1669
|
+
if (!dataView) {
|
|
1670
|
+
return;
|
|
1671
|
+
}
|
|
1672
|
+
await this.#sendDisplayContextCommand(
|
|
1673
|
+
commandType,
|
|
1674
|
+
dataView.buffer,
|
|
1675
|
+
sendImmediately
|
|
1676
|
+
);
|
|
1677
|
+
this.#onContextStateUpdate(differences);
|
|
1678
|
+
}
|
|
1679
|
+
get spriteColorIndices() {
|
|
1680
|
+
return this.contextState.spriteColorIndices;
|
|
1681
|
+
}
|
|
1682
|
+
get spriteColors() {
|
|
1683
|
+
return this.spriteColorIndices.map((colorIndex) => this.colors[colorIndex]);
|
|
1684
|
+
}
|
|
1685
|
+
async selectSpriteColors(
|
|
1686
|
+
spriteColorPairs: DisplaySpriteColorPair[],
|
|
1687
|
+
sendImmediately?: boolean
|
|
1688
|
+
) {
|
|
1689
|
+
_console.assertRangeWithError(
|
|
1690
|
+
"spriteColors",
|
|
1691
|
+
spriteColorPairs.length,
|
|
1692
|
+
1,
|
|
1693
|
+
this.numberOfColors
|
|
1694
|
+
);
|
|
1695
|
+
const spriteColorIndices = this.contextState.spriteColorIndices.slice();
|
|
1696
|
+
spriteColorPairs.forEach(({ spriteColorIndex, colorIndex }) => {
|
|
1697
|
+
this.assertValidColorIndex(spriteColorIndex);
|
|
1698
|
+
this.assertValidColorIndex(colorIndex);
|
|
1699
|
+
spriteColorIndices[spriteColorIndex] = colorIndex;
|
|
1700
|
+
});
|
|
1701
|
+
|
|
1702
|
+
const differences = this.#contextStateHelper.update({
|
|
1703
|
+
spriteColorIndices,
|
|
1704
|
+
});
|
|
1705
|
+
if (differences.length == 0) {
|
|
1706
|
+
return;
|
|
1707
|
+
}
|
|
1708
|
+
const commandType: DisplayContextCommandType = "selectSpriteColors";
|
|
1709
|
+
const dataView = serializeContextCommand(this, {
|
|
1710
|
+
type: commandType,
|
|
1711
|
+
spriteColorPairs,
|
|
1712
|
+
});
|
|
1713
|
+
if (!dataView) {
|
|
1714
|
+
return;
|
|
1715
|
+
}
|
|
1716
|
+
await this.#sendDisplayContextCommand(
|
|
1717
|
+
commandType,
|
|
1718
|
+
dataView.buffer,
|
|
1719
|
+
sendImmediately
|
|
1720
|
+
);
|
|
1721
|
+
this.#onContextStateUpdate(differences);
|
|
1722
|
+
}
|
|
1723
|
+
async setSpriteColor(
|
|
1724
|
+
spriteColorIndex: number,
|
|
1725
|
+
color: DisplayColorRGB | string,
|
|
1726
|
+
sendImmediately?: boolean
|
|
1727
|
+
) {
|
|
1728
|
+
return this.setColor(
|
|
1729
|
+
this.spriteColorIndices[spriteColorIndex],
|
|
1730
|
+
color,
|
|
1731
|
+
sendImmediately
|
|
1732
|
+
);
|
|
1733
|
+
}
|
|
1734
|
+
async setSpriteColorOpacity(
|
|
1735
|
+
spriteColorIndex: number,
|
|
1736
|
+
opacity: number,
|
|
1737
|
+
sendImmediately?: boolean
|
|
1738
|
+
) {
|
|
1739
|
+
return this.setColorOpacity(
|
|
1740
|
+
this.spriteColorIndices[spriteColorIndex],
|
|
1741
|
+
opacity,
|
|
1742
|
+
sendImmediately
|
|
1743
|
+
);
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
async resetSpriteColors(sendImmediately?: boolean) {
|
|
1747
|
+
const spriteColorIndices = new Array(this.numberOfColors).fill(0);
|
|
1748
|
+
const differences = this.#contextStateHelper.update({
|
|
1749
|
+
spriteColorIndices,
|
|
1750
|
+
});
|
|
1751
|
+
if (differences.length == 0) {
|
|
1752
|
+
return;
|
|
1753
|
+
}
|
|
1754
|
+
const commandType: DisplayContextCommandType = "resetSpriteColors";
|
|
1755
|
+
const dataView = serializeContextCommand(this, {
|
|
1756
|
+
type: commandType,
|
|
1757
|
+
});
|
|
1758
|
+
await this.#sendDisplayContextCommand(
|
|
1759
|
+
commandType,
|
|
1760
|
+
dataView?.buffer,
|
|
1761
|
+
sendImmediately
|
|
1762
|
+
);
|
|
1763
|
+
this.#onContextStateUpdate(differences);
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
async setSpriteScaleDirection(
|
|
1767
|
+
direction: DisplayScaleDirection,
|
|
1768
|
+
spriteScale: number,
|
|
1769
|
+
sendImmediately?: boolean
|
|
1770
|
+
) {
|
|
1771
|
+
spriteScale = clamp(spriteScale, minDisplayScale, maxDisplayScale);
|
|
1772
|
+
spriteScale = roundScale(spriteScale);
|
|
1773
|
+
const commandType = DisplaySpriteScaleDirectionToCommandType[direction];
|
|
1774
|
+
_console.log({ [commandType]: spriteScale });
|
|
1775
|
+
const newState: PartialDisplayContextState = {};
|
|
1776
|
+
let command: DisplayContextCommand;
|
|
1777
|
+
switch (direction) {
|
|
1778
|
+
case "all":
|
|
1779
|
+
newState.spriteScaleX = spriteScale;
|
|
1780
|
+
newState.spriteScaleY = spriteScale;
|
|
1781
|
+
command = { type: "setSpriteScale", spriteScale };
|
|
1782
|
+
break;
|
|
1783
|
+
case "x":
|
|
1784
|
+
newState.spriteScaleX = spriteScale;
|
|
1785
|
+
command = { type: "setSpriteScaleX", spriteScaleX: spriteScale };
|
|
1786
|
+
break;
|
|
1787
|
+
case "y":
|
|
1788
|
+
newState.spriteScaleY = spriteScale;
|
|
1789
|
+
command = { type: "setSpriteScaleY", spriteScaleY: spriteScale };
|
|
1790
|
+
break;
|
|
1791
|
+
}
|
|
1792
|
+
const differences = this.#contextStateHelper.update(newState);
|
|
1793
|
+
if (differences.length == 0) {
|
|
1794
|
+
return;
|
|
1795
|
+
}
|
|
1796
|
+
const dataView = serializeContextCommand(this, command);
|
|
1797
|
+
if (!dataView) {
|
|
1798
|
+
return;
|
|
1799
|
+
}
|
|
1800
|
+
await this.#sendDisplayContextCommand(
|
|
1801
|
+
commandType,
|
|
1802
|
+
dataView.buffer,
|
|
1803
|
+
sendImmediately
|
|
1804
|
+
);
|
|
1805
|
+
|
|
1806
|
+
this.#onContextStateUpdate(differences);
|
|
1807
|
+
}
|
|
1808
|
+
async setSpriteScaleX(spriteScaleX: number, sendImmediately?: boolean) {
|
|
1809
|
+
return this.setSpriteScaleDirection("x", spriteScaleX, sendImmediately);
|
|
1810
|
+
}
|
|
1811
|
+
async setSpriteScaleY(spriteScaleY: number, sendImmediately?: boolean) {
|
|
1812
|
+
return this.setSpriteScaleDirection("y", spriteScaleY, sendImmediately);
|
|
1813
|
+
}
|
|
1814
|
+
async setSpriteScale(spriteScale: number, sendImmediately?: boolean) {
|
|
1815
|
+
return this.setSpriteScaleDirection("all", spriteScale, sendImmediately);
|
|
1816
|
+
}
|
|
1817
|
+
async resetSpriteScale(sendImmediately?: boolean) {
|
|
1818
|
+
//return this.setSpriteScaleDirection("all", 1, sendImmediately);
|
|
1819
|
+
|
|
1820
|
+
const differences = this.#contextStateHelper.update({
|
|
1821
|
+
spriteScaleX: 1,
|
|
1822
|
+
spriteScaleY: 1,
|
|
1823
|
+
});
|
|
1824
|
+
if (differences.length == 0) {
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1827
|
+
const commandType: DisplayContextCommandType = "resetSpriteScale";
|
|
1828
|
+
const dataView = serializeContextCommand(this, {
|
|
1829
|
+
type: commandType,
|
|
1830
|
+
});
|
|
1831
|
+
await this.#sendDisplayContextCommand(
|
|
1832
|
+
commandType,
|
|
1833
|
+
dataView?.buffer,
|
|
1834
|
+
sendImmediately
|
|
1835
|
+
);
|
|
1836
|
+
this.#onContextStateUpdate(differences);
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
async setSpritesLineHeight(
|
|
1840
|
+
spritesLineHeight: number,
|
|
1841
|
+
sendImmediately?: boolean
|
|
1842
|
+
) {
|
|
1843
|
+
this.assertValidLineWidth(spritesLineHeight);
|
|
1844
|
+
const differences = this.#contextStateHelper.update({
|
|
1845
|
+
spritesLineHeight,
|
|
1846
|
+
});
|
|
1847
|
+
if (differences.length == 0) {
|
|
1848
|
+
return;
|
|
1849
|
+
}
|
|
1850
|
+
const commandType: DisplayContextCommandType = "setSpritesLineHeight";
|
|
1851
|
+
const dataView = serializeContextCommand(this, {
|
|
1852
|
+
type: commandType,
|
|
1853
|
+
spritesLineHeight,
|
|
1854
|
+
});
|
|
1855
|
+
if (!dataView) {
|
|
1856
|
+
return;
|
|
1857
|
+
}
|
|
1858
|
+
await this.#sendDisplayContextCommand(
|
|
1859
|
+
commandType,
|
|
1860
|
+
dataView.buffer,
|
|
1861
|
+
sendImmediately
|
|
1862
|
+
);
|
|
1863
|
+
this.#onContextStateUpdate(differences);
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
async setSpritesDirectionGeneric(
|
|
1867
|
+
direction: DisplayDirection,
|
|
1868
|
+
isOrthogonal: boolean,
|
|
1869
|
+
sendImmediately?: boolean
|
|
1870
|
+
) {
|
|
1871
|
+
assertValidDirection(direction);
|
|
1872
|
+
const stateKey: DisplayContextStateKey = isOrthogonal
|
|
1873
|
+
? "spritesLineDirection"
|
|
1874
|
+
: "spritesDirection";
|
|
1875
|
+
const commandType: DisplayContextCommandType = isOrthogonal
|
|
1876
|
+
? "setSpritesLineDirection"
|
|
1877
|
+
: "setSpritesDirection";
|
|
1878
|
+
|
|
1879
|
+
const differences = this.#contextStateHelper.update({
|
|
1880
|
+
[stateKey]: direction,
|
|
1881
|
+
});
|
|
1882
|
+
if (differences.length == 0) {
|
|
1883
|
+
return;
|
|
1884
|
+
}
|
|
1885
|
+
// @ts-expect-error
|
|
1886
|
+
const dataView = serializeContextCommand(this, {
|
|
1887
|
+
type: commandType,
|
|
1888
|
+
[stateKey]: direction,
|
|
1889
|
+
});
|
|
1890
|
+
if (!dataView) {
|
|
1891
|
+
return;
|
|
1892
|
+
}
|
|
1893
|
+
await this.#sendDisplayContextCommand(
|
|
1894
|
+
commandType,
|
|
1895
|
+
dataView.buffer,
|
|
1896
|
+
sendImmediately
|
|
1897
|
+
);
|
|
1898
|
+
this.#onContextStateUpdate(differences);
|
|
1899
|
+
}
|
|
1900
|
+
async setSpritesDirection(
|
|
1901
|
+
spritesDirection: DisplayDirection,
|
|
1902
|
+
sendImmediately?: boolean
|
|
1903
|
+
) {
|
|
1904
|
+
await this.setSpritesDirectionGeneric(
|
|
1905
|
+
spritesDirection,
|
|
1906
|
+
false,
|
|
1907
|
+
sendImmediately
|
|
1908
|
+
);
|
|
1909
|
+
}
|
|
1910
|
+
async setSpritesLineDirection(
|
|
1911
|
+
spritesLineDirection: DisplayDirection,
|
|
1912
|
+
sendImmediately?: boolean
|
|
1913
|
+
) {
|
|
1914
|
+
await this.setSpritesDirectionGeneric(
|
|
1915
|
+
spritesLineDirection,
|
|
1916
|
+
true,
|
|
1917
|
+
sendImmediately
|
|
1918
|
+
);
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
async setSpritesSpacingGeneric(
|
|
1922
|
+
spacing: number,
|
|
1923
|
+
isOrthogonal: boolean,
|
|
1924
|
+
sendImmediately?: boolean
|
|
1925
|
+
) {
|
|
1926
|
+
const stateKey: DisplayContextStateKey = isOrthogonal
|
|
1927
|
+
? "spritesLineSpacing"
|
|
1928
|
+
: "spritesSpacing";
|
|
1929
|
+
const commandType: DisplayContextCommandType = isOrthogonal
|
|
1930
|
+
? "setSpritesLineSpacing"
|
|
1931
|
+
: "setSpritesSpacing";
|
|
1932
|
+
|
|
1933
|
+
const differences = this.#contextStateHelper.update({
|
|
1934
|
+
[stateKey]: spacing,
|
|
1935
|
+
});
|
|
1936
|
+
if (differences.length == 0) {
|
|
1937
|
+
return;
|
|
1938
|
+
}
|
|
1939
|
+
// @ts-expect-error
|
|
1940
|
+
const dataView = serializeContextCommand(this, {
|
|
1941
|
+
type: commandType,
|
|
1942
|
+
[stateKey]: spacing,
|
|
1943
|
+
});
|
|
1944
|
+
if (!dataView) {
|
|
1945
|
+
return;
|
|
1946
|
+
}
|
|
1947
|
+
await this.#sendDisplayContextCommand(
|
|
1948
|
+
commandType,
|
|
1949
|
+
dataView.buffer,
|
|
1950
|
+
sendImmediately
|
|
1951
|
+
);
|
|
1952
|
+
this.#onContextStateUpdate(differences);
|
|
1953
|
+
}
|
|
1954
|
+
async setSpritesSpacing(spritesSpacing: number, sendImmediately?: boolean) {
|
|
1955
|
+
await this.setSpritesSpacingGeneric(spritesSpacing, false, sendImmediately);
|
|
1956
|
+
}
|
|
1957
|
+
async setSpritesLineSpacing(
|
|
1958
|
+
spritesSpacing: number,
|
|
1959
|
+
sendImmediately?: boolean
|
|
1960
|
+
) {
|
|
1961
|
+
await this.setSpritesSpacingGeneric(spritesSpacing, true, sendImmediately);
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
async setSpritesAlignmentGeneric(
|
|
1965
|
+
alignment: DisplayAlignment,
|
|
1966
|
+
isOrthogonal: boolean,
|
|
1967
|
+
sendImmediately?: boolean
|
|
1968
|
+
) {
|
|
1969
|
+
assertValidAlignment(alignment);
|
|
1970
|
+
const stateKey: DisplayContextStateKey = isOrthogonal
|
|
1971
|
+
? "spritesLineAlignment"
|
|
1972
|
+
: "spritesAlignment";
|
|
1973
|
+
const commandType: DisplayContextCommandType = isOrthogonal
|
|
1974
|
+
? "setSpritesLineAlignment"
|
|
1975
|
+
: "setSpritesAlignment";
|
|
1976
|
+
const differences = this.#contextStateHelper.update({
|
|
1977
|
+
[stateKey]: alignment,
|
|
1978
|
+
});
|
|
1979
|
+
if (differences.length == 0) {
|
|
1980
|
+
return;
|
|
1981
|
+
}
|
|
1982
|
+
// @ts-expect-error
|
|
1983
|
+
const dataView = serializeContextCommand(this, {
|
|
1984
|
+
type: commandType,
|
|
1985
|
+
[stateKey]: alignment,
|
|
1986
|
+
});
|
|
1987
|
+
if (!dataView) {
|
|
1988
|
+
return;
|
|
1989
|
+
}
|
|
1990
|
+
await this.#sendDisplayContextCommand(
|
|
1991
|
+
commandType,
|
|
1992
|
+
dataView.buffer,
|
|
1993
|
+
sendImmediately
|
|
1994
|
+
);
|
|
1995
|
+
this.#onContextStateUpdate(differences);
|
|
1996
|
+
}
|
|
1997
|
+
async setSpritesAlignment(
|
|
1998
|
+
spritesAlignment: DisplayAlignment,
|
|
1999
|
+
sendImmediately?: boolean
|
|
2000
|
+
) {
|
|
2001
|
+
await this.setSpritesAlignmentGeneric(
|
|
2002
|
+
spritesAlignment,
|
|
2003
|
+
false,
|
|
2004
|
+
sendImmediately
|
|
2005
|
+
);
|
|
2006
|
+
}
|
|
2007
|
+
async setSpritesLineAlignment(
|
|
2008
|
+
spritesLineAlignment: DisplayAlignment,
|
|
2009
|
+
sendImmediately?: boolean
|
|
2010
|
+
) {
|
|
2011
|
+
await this.setSpritesAlignmentGeneric(
|
|
2012
|
+
spritesLineAlignment,
|
|
2013
|
+
true,
|
|
2014
|
+
sendImmediately
|
|
2015
|
+
);
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
async clearRect(
|
|
2019
|
+
x: number,
|
|
2020
|
+
y: number,
|
|
2021
|
+
width: number,
|
|
2022
|
+
height: number,
|
|
2023
|
+
sendImmediately?: boolean
|
|
2024
|
+
) {
|
|
2025
|
+
const commandType: DisplayContextCommandType = "clearRect";
|
|
2026
|
+
const dataView = serializeContextCommand(this, {
|
|
2027
|
+
type: commandType,
|
|
2028
|
+
x,
|
|
2029
|
+
y,
|
|
2030
|
+
width,
|
|
2031
|
+
height,
|
|
2032
|
+
});
|
|
2033
|
+
if (!dataView) {
|
|
2034
|
+
return;
|
|
2035
|
+
}
|
|
2036
|
+
await this.#sendDisplayContextCommand(
|
|
2037
|
+
commandType,
|
|
2038
|
+
dataView.buffer,
|
|
2039
|
+
sendImmediately
|
|
2040
|
+
);
|
|
2041
|
+
}
|
|
2042
|
+
async drawRect(
|
|
2043
|
+
offsetX: number,
|
|
2044
|
+
offsetY: number,
|
|
2045
|
+
width: number,
|
|
2046
|
+
height: number,
|
|
2047
|
+
sendImmediately?: boolean
|
|
2048
|
+
) {
|
|
2049
|
+
const commandType: DisplayContextCommandType = "drawRect";
|
|
2050
|
+
const dataView = serializeContextCommand(this, {
|
|
2051
|
+
type: commandType,
|
|
2052
|
+
offsetX,
|
|
2053
|
+
offsetY,
|
|
2054
|
+
width,
|
|
2055
|
+
height,
|
|
2056
|
+
});
|
|
2057
|
+
if (!dataView) {
|
|
2058
|
+
return;
|
|
2059
|
+
}
|
|
2060
|
+
await this.#sendDisplayContextCommand(
|
|
2061
|
+
commandType,
|
|
2062
|
+
dataView.buffer,
|
|
2063
|
+
sendImmediately
|
|
2064
|
+
);
|
|
2065
|
+
}
|
|
2066
|
+
async drawRoundRect(
|
|
2067
|
+
offsetX: number,
|
|
2068
|
+
offsetY: number,
|
|
2069
|
+
width: number,
|
|
2070
|
+
height: number,
|
|
2071
|
+
borderRadius: number,
|
|
2072
|
+
sendImmediately?: boolean
|
|
2073
|
+
) {
|
|
2074
|
+
const commandType: DisplayContextCommandType = "drawRoundRect";
|
|
2075
|
+
const dataView = serializeContextCommand(this, {
|
|
2076
|
+
type: commandType,
|
|
2077
|
+
offsetX,
|
|
2078
|
+
offsetY,
|
|
2079
|
+
width,
|
|
2080
|
+
height,
|
|
2081
|
+
borderRadius,
|
|
2082
|
+
});
|
|
2083
|
+
if (!dataView) {
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
2086
|
+
await this.#sendDisplayContextCommand(
|
|
2087
|
+
commandType,
|
|
2088
|
+
dataView.buffer,
|
|
2089
|
+
sendImmediately
|
|
2090
|
+
);
|
|
2091
|
+
}
|
|
2092
|
+
async drawCircle(
|
|
2093
|
+
offsetX: number,
|
|
2094
|
+
offsetY: number,
|
|
2095
|
+
radius: number,
|
|
2096
|
+
sendImmediately?: boolean
|
|
2097
|
+
) {
|
|
2098
|
+
const commandType: DisplayContextCommandType = "drawCircle";
|
|
2099
|
+
const dataView = serializeContextCommand(this, {
|
|
2100
|
+
type: commandType,
|
|
2101
|
+
offsetX,
|
|
2102
|
+
offsetY,
|
|
2103
|
+
radius,
|
|
2104
|
+
});
|
|
2105
|
+
if (!dataView) {
|
|
2106
|
+
return;
|
|
2107
|
+
}
|
|
2108
|
+
await this.#sendDisplayContextCommand(
|
|
2109
|
+
commandType,
|
|
2110
|
+
dataView.buffer,
|
|
2111
|
+
sendImmediately
|
|
2112
|
+
);
|
|
2113
|
+
}
|
|
2114
|
+
async drawEllipse(
|
|
2115
|
+
offsetX: number,
|
|
2116
|
+
offsetY: number,
|
|
2117
|
+
radiusX: number,
|
|
2118
|
+
radiusY: number,
|
|
2119
|
+
sendImmediately?: boolean
|
|
2120
|
+
) {
|
|
2121
|
+
const commandType: DisplayContextCommandType = "drawEllipse";
|
|
2122
|
+
const dataView = serializeContextCommand(this, {
|
|
2123
|
+
type: commandType,
|
|
2124
|
+
offsetX,
|
|
2125
|
+
offsetY,
|
|
2126
|
+
radiusX,
|
|
2127
|
+
radiusY,
|
|
2128
|
+
});
|
|
2129
|
+
if (!dataView) {
|
|
2130
|
+
return;
|
|
2131
|
+
}
|
|
2132
|
+
await this.#sendDisplayContextCommand(
|
|
2133
|
+
commandType,
|
|
2134
|
+
dataView.buffer,
|
|
2135
|
+
sendImmediately
|
|
2136
|
+
);
|
|
2137
|
+
}
|
|
2138
|
+
async drawRegularPolygon(
|
|
2139
|
+
offsetX: number,
|
|
2140
|
+
offsetY: number,
|
|
2141
|
+
radius: number,
|
|
2142
|
+
numberOfSides: number,
|
|
2143
|
+
sendImmediately?: boolean
|
|
2144
|
+
) {
|
|
2145
|
+
const commandType: DisplayContextCommandType = "drawRegularPolygon";
|
|
2146
|
+
const dataView = serializeContextCommand(this, {
|
|
2147
|
+
type: commandType,
|
|
2148
|
+
offsetX,
|
|
2149
|
+
offsetY,
|
|
2150
|
+
radius,
|
|
2151
|
+
numberOfSides,
|
|
2152
|
+
});
|
|
2153
|
+
if (!dataView) {
|
|
2154
|
+
return;
|
|
2155
|
+
}
|
|
2156
|
+
await this.#sendDisplayContextCommand(
|
|
2157
|
+
commandType,
|
|
2158
|
+
dataView.buffer,
|
|
2159
|
+
sendImmediately
|
|
2160
|
+
);
|
|
2161
|
+
}
|
|
2162
|
+
async drawPolygon(points: Vector2[], sendImmediately?: boolean) {
|
|
2163
|
+
_console.assertRangeWithError("numberOfPoints", points.length, 2, 255);
|
|
2164
|
+
const commandType: DisplayContextCommandType = "drawPolygon";
|
|
2165
|
+
const dataView = serializeContextCommand(this, {
|
|
2166
|
+
type: commandType,
|
|
2167
|
+
points,
|
|
2168
|
+
});
|
|
2169
|
+
if (!dataView) {
|
|
2170
|
+
return;
|
|
2171
|
+
}
|
|
2172
|
+
await this.#sendDisplayContextCommand(
|
|
2173
|
+
commandType,
|
|
2174
|
+
dataView.buffer,
|
|
2175
|
+
sendImmediately
|
|
2176
|
+
);
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
async drawWireframe(wireframe: DisplayWireframe, sendImmediately?: boolean) {
|
|
2180
|
+
assertValidWireframe(wireframe);
|
|
2181
|
+
const trimmedWireframe = trimWireframe(wireframe);
|
|
2182
|
+
const commandType: DisplayContextCommandType = "drawWireframe";
|
|
2183
|
+
const dataView = serializeContextCommand(this, {
|
|
2184
|
+
type: commandType,
|
|
2185
|
+
wireframe: trimmedWireframe,
|
|
2186
|
+
});
|
|
2187
|
+
if (!dataView) {
|
|
2188
|
+
return;
|
|
2189
|
+
}
|
|
2190
|
+
if (dataView.byteLength > this.#maxCommandDataLength) {
|
|
2191
|
+
_console.error(
|
|
2192
|
+
`wireframe data ${dataView.byteLength} too large (max ${
|
|
2193
|
+
this.#maxCommandDataLength
|
|
2194
|
+
})`
|
|
2195
|
+
);
|
|
2196
|
+
return;
|
|
2197
|
+
}
|
|
2198
|
+
await this.#sendDisplayContextCommand(
|
|
2199
|
+
commandType,
|
|
2200
|
+
dataView.buffer,
|
|
2201
|
+
sendImmediately
|
|
2202
|
+
);
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
async drawCurve(
|
|
2206
|
+
curveType: DisplayBezierCurveType,
|
|
2207
|
+
controlPoints: Vector2[],
|
|
2208
|
+
sendImmediately?: boolean
|
|
2209
|
+
) {
|
|
2210
|
+
assertValidNumberOfControlPoints(curveType, controlPoints);
|
|
2211
|
+
const commandType: DisplayContextCommandType =
|
|
2212
|
+
curveType == "cubic"
|
|
2213
|
+
? "drawCubicBezierCurve"
|
|
2214
|
+
: "drawQuadraticBezierCurve";
|
|
2215
|
+
const dataView = serializeContextCommand(this, {
|
|
2216
|
+
type: commandType,
|
|
2217
|
+
controlPoints,
|
|
2218
|
+
});
|
|
2219
|
+
if (!dataView) {
|
|
2220
|
+
return;
|
|
2221
|
+
}
|
|
2222
|
+
await this.#sendDisplayContextCommand(
|
|
2223
|
+
commandType,
|
|
2224
|
+
dataView.buffer,
|
|
2225
|
+
sendImmediately
|
|
2226
|
+
);
|
|
2227
|
+
}
|
|
2228
|
+
async drawCurves(
|
|
2229
|
+
curveType: DisplayBezierCurveType,
|
|
2230
|
+
controlPoints: Vector2[],
|
|
2231
|
+
sendImmediately?: boolean
|
|
2232
|
+
) {
|
|
2233
|
+
assertValidPathNumberOfControlPoints(curveType, controlPoints);
|
|
2234
|
+
const commandType: DisplayContextCommandType =
|
|
2235
|
+
curveType == "cubic"
|
|
2236
|
+
? "drawCubicBezierCurves"
|
|
2237
|
+
: "drawQuadraticBezierCurves";
|
|
2238
|
+
const dataView = serializeContextCommand(this, {
|
|
2239
|
+
type: commandType,
|
|
2240
|
+
controlPoints,
|
|
2241
|
+
});
|
|
2242
|
+
if (!dataView) {
|
|
2243
|
+
return;
|
|
2244
|
+
}
|
|
2245
|
+
if (dataView.byteLength > this.#maxCommandDataLength) {
|
|
2246
|
+
_console.error(
|
|
2247
|
+
`curve data ${dataView.byteLength} too large (max ${
|
|
2248
|
+
this.#maxCommandDataLength
|
|
2249
|
+
})`
|
|
2250
|
+
);
|
|
2251
|
+
// FILL - split into multiple curves
|
|
2252
|
+
return;
|
|
2253
|
+
}
|
|
2254
|
+
await this.#sendDisplayContextCommand(
|
|
2255
|
+
commandType,
|
|
2256
|
+
dataView.buffer,
|
|
2257
|
+
sendImmediately
|
|
2258
|
+
);
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
async drawQuadraticBezierCurve(
|
|
2262
|
+
controlPoints: Vector2[],
|
|
2263
|
+
sendImmediately?: boolean
|
|
2264
|
+
) {
|
|
2265
|
+
await this.drawCurve("quadratic", controlPoints, sendImmediately);
|
|
2266
|
+
}
|
|
2267
|
+
async drawQuadraticBezierCurves(
|
|
2268
|
+
controlPoints: Vector2[],
|
|
2269
|
+
sendImmediately?: boolean
|
|
2270
|
+
) {
|
|
2271
|
+
await this.drawCurves("quadratic", controlPoints, sendImmediately);
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
async drawCubicBezierCurve(
|
|
2275
|
+
controlPoints: Vector2[],
|
|
2276
|
+
sendImmediately?: boolean
|
|
2277
|
+
) {
|
|
2278
|
+
await this.drawCurve("cubic", controlPoints, sendImmediately);
|
|
2279
|
+
}
|
|
2280
|
+
async drawCubicBezierCurves(
|
|
2281
|
+
controlPoints: Vector2[],
|
|
2282
|
+
sendImmediately?: boolean
|
|
2283
|
+
) {
|
|
2284
|
+
await this.drawCurves("cubic", controlPoints, sendImmediately);
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2287
|
+
async _drawPath(
|
|
2288
|
+
isClosed: boolean,
|
|
2289
|
+
curves: DisplayBezierCurve[],
|
|
2290
|
+
sendImmediately?: boolean
|
|
2291
|
+
) {
|
|
2292
|
+
assertValidPath(curves);
|
|
2293
|
+
|
|
2294
|
+
const commandType: DisplayContextCommandType = isClosed
|
|
2295
|
+
? "drawClosedPath"
|
|
2296
|
+
: "drawPath";
|
|
2297
|
+
const dataView = serializeContextCommand(this, {
|
|
2298
|
+
type: commandType,
|
|
2299
|
+
curves,
|
|
2300
|
+
});
|
|
2301
|
+
if (!dataView) {
|
|
2302
|
+
return;
|
|
2303
|
+
}
|
|
2304
|
+
if (dataView.byteLength > this.#maxCommandDataLength) {
|
|
2305
|
+
_console.error(
|
|
2306
|
+
`path data ${dataView.byteLength} too large (max ${
|
|
2307
|
+
this.#maxCommandDataLength
|
|
2308
|
+
})`
|
|
2309
|
+
);
|
|
2310
|
+
// FILL - split into multiple paths
|
|
2311
|
+
return;
|
|
2312
|
+
}
|
|
2313
|
+
await this.#sendDisplayContextCommand(
|
|
2314
|
+
commandType,
|
|
2315
|
+
dataView.buffer,
|
|
2316
|
+
sendImmediately
|
|
2317
|
+
);
|
|
2318
|
+
}
|
|
2319
|
+
async drawPath(curves: DisplayBezierCurve[], sendImmediately?: boolean) {
|
|
2320
|
+
await this._drawPath(false, curves, sendImmediately);
|
|
2321
|
+
}
|
|
2322
|
+
async drawClosedPath(
|
|
2323
|
+
curves: DisplayBezierCurve[],
|
|
2324
|
+
sendImmediately?: boolean
|
|
2325
|
+
) {
|
|
2326
|
+
await this._drawPath(true, curves, sendImmediately);
|
|
2327
|
+
}
|
|
2328
|
+
|
|
2329
|
+
async drawSegment(
|
|
2330
|
+
startX: number,
|
|
2331
|
+
startY: number,
|
|
2332
|
+
endX: number,
|
|
2333
|
+
endY: number,
|
|
2334
|
+
sendImmediately?: boolean
|
|
2335
|
+
) {
|
|
2336
|
+
const commandType: DisplayContextCommandType = "drawSegment";
|
|
2337
|
+
const dataView = serializeContextCommand(this, {
|
|
2338
|
+
type: commandType,
|
|
2339
|
+
startX,
|
|
2340
|
+
startY,
|
|
2341
|
+
endX,
|
|
2342
|
+
endY,
|
|
2343
|
+
});
|
|
2344
|
+
if (!dataView) {
|
|
2345
|
+
return;
|
|
2346
|
+
}
|
|
2347
|
+
await this.#sendDisplayContextCommand(
|
|
2348
|
+
commandType,
|
|
2349
|
+
dataView.buffer,
|
|
2350
|
+
sendImmediately
|
|
2351
|
+
);
|
|
2352
|
+
}
|
|
2353
|
+
async drawSegments(points: Vector2[], sendImmediately?: boolean) {
|
|
2354
|
+
_console.assertRangeWithError("numberOfPoints", points.length, 2, 255);
|
|
2355
|
+
const commandType: DisplayContextCommandType = "drawSegments";
|
|
2356
|
+
const dataView = serializeContextCommand(this, {
|
|
2357
|
+
type: commandType,
|
|
2358
|
+
points,
|
|
2359
|
+
});
|
|
2360
|
+
if (!dataView) {
|
|
2361
|
+
return;
|
|
2362
|
+
}
|
|
2363
|
+
if (dataView.byteLength > this.#maxCommandDataLength) {
|
|
2364
|
+
const mid = Math.floor(points.length / 2);
|
|
2365
|
+
const firstHalf = points.slice(0, mid + 1);
|
|
2366
|
+
const secondHalf = points.slice(mid);
|
|
2367
|
+
_console.log({ firstHalf, secondHalf });
|
|
2368
|
+
_console.log("sending first half", firstHalf);
|
|
2369
|
+
await this.drawSegments(firstHalf, false);
|
|
2370
|
+
_console.log("sending second half", secondHalf);
|
|
2371
|
+
await this.drawSegments(secondHalf, sendImmediately);
|
|
2372
|
+
return;
|
|
2373
|
+
}
|
|
2374
|
+
await this.#sendDisplayContextCommand(
|
|
2375
|
+
commandType,
|
|
2376
|
+
dataView.buffer,
|
|
2377
|
+
sendImmediately
|
|
2378
|
+
);
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
async drawArc(
|
|
2382
|
+
offsetX: number,
|
|
2383
|
+
offsetY: number,
|
|
2384
|
+
radius: number,
|
|
2385
|
+
startAngle: number,
|
|
2386
|
+
angleOffset: number,
|
|
2387
|
+
isRadians?: boolean,
|
|
2388
|
+
sendImmediately?: boolean
|
|
2389
|
+
) {
|
|
2390
|
+
const commandType: DisplayContextCommandType = "drawArc";
|
|
2391
|
+
const dataView = serializeContextCommand(this, {
|
|
2392
|
+
type: commandType,
|
|
2393
|
+
offsetX,
|
|
2394
|
+
offsetY,
|
|
2395
|
+
radius,
|
|
2396
|
+
startAngle,
|
|
2397
|
+
angleOffset,
|
|
2398
|
+
isRadians,
|
|
2399
|
+
});
|
|
2400
|
+
if (!dataView) {
|
|
2401
|
+
return;
|
|
2402
|
+
}
|
|
2403
|
+
await this.#sendDisplayContextCommand(
|
|
2404
|
+
commandType,
|
|
2405
|
+
dataView.buffer,
|
|
2406
|
+
sendImmediately
|
|
2407
|
+
);
|
|
2408
|
+
}
|
|
2409
|
+
async drawArcEllipse(
|
|
2410
|
+
offsetX: number,
|
|
2411
|
+
offsetY: number,
|
|
2412
|
+
radiusX: number,
|
|
2413
|
+
radiusY: number,
|
|
2414
|
+
startAngle: number,
|
|
2415
|
+
angleOffset: number,
|
|
2416
|
+
isRadians?: boolean,
|
|
2417
|
+
sendImmediately?: boolean
|
|
2418
|
+
) {
|
|
2419
|
+
const commandType: DisplayContextCommandType = "drawArcEllipse";
|
|
2420
|
+
const dataView = serializeContextCommand(this, {
|
|
2421
|
+
type: commandType,
|
|
2422
|
+
offsetX,
|
|
2423
|
+
offsetY,
|
|
2424
|
+
radiusX,
|
|
2425
|
+
radiusY,
|
|
2426
|
+
startAngle,
|
|
2427
|
+
angleOffset,
|
|
2428
|
+
isRadians,
|
|
2429
|
+
});
|
|
2430
|
+
if (!dataView) {
|
|
2431
|
+
return;
|
|
2432
|
+
}
|
|
2433
|
+
await this.#sendDisplayContextCommand(
|
|
2434
|
+
commandType,
|
|
2435
|
+
dataView.buffer,
|
|
2436
|
+
sendImmediately
|
|
2437
|
+
);
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
assertValidNumberOfColors(numberOfColors: number) {
|
|
2441
|
+
_console.assertRangeWithError(
|
|
2442
|
+
"numberOfColors",
|
|
2443
|
+
numberOfColors,
|
|
2444
|
+
2,
|
|
2445
|
+
this.numberOfColors
|
|
2446
|
+
);
|
|
2447
|
+
}
|
|
2448
|
+
|
|
2449
|
+
assertValidBitmap(bitmap: DisplayBitmap, checkSize?: boolean) {
|
|
2450
|
+
this.assertValidNumberOfColors(bitmap.numberOfColors);
|
|
2451
|
+
assertValidBitmapPixels(bitmap);
|
|
2452
|
+
if (checkSize) {
|
|
2453
|
+
this.#assertValidBitmapSize(bitmap);
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2456
|
+
#assertValidBitmapSize(bitmap: DisplayBitmap) {
|
|
2457
|
+
const pixelDataLength = getBitmapNumberOfBytes(bitmap);
|
|
2458
|
+
_console.assertRangeWithError(
|
|
2459
|
+
"bitmap.pixels.length",
|
|
2460
|
+
pixelDataLength,
|
|
2461
|
+
1,
|
|
2462
|
+
this.#maxCommandDataLength - drawBitmapHeaderLength
|
|
2463
|
+
);
|
|
2464
|
+
}
|
|
2465
|
+
async drawBitmap(
|
|
2466
|
+
offsetX: number,
|
|
2467
|
+
offsetY: number,
|
|
2468
|
+
bitmap: DisplayBitmap,
|
|
2469
|
+
sendImmediately?: boolean
|
|
2470
|
+
) {
|
|
2471
|
+
this.assertValidBitmap(bitmap, true);
|
|
2472
|
+
const commandType: DisplayContextCommandType = "drawBitmap";
|
|
2473
|
+
const dataView = serializeContextCommand(this, {
|
|
2474
|
+
type: commandType,
|
|
2475
|
+
offsetX,
|
|
2476
|
+
offsetY,
|
|
2477
|
+
bitmap,
|
|
2478
|
+
});
|
|
2479
|
+
if (!dataView) {
|
|
2480
|
+
return;
|
|
2481
|
+
}
|
|
2482
|
+
await this.#sendDisplayContextCommand(
|
|
2483
|
+
commandType,
|
|
2484
|
+
dataView.buffer,
|
|
2485
|
+
sendImmediately
|
|
2486
|
+
);
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2489
|
+
async imageToBitmap(
|
|
2490
|
+
image: HTMLImageElement,
|
|
2491
|
+
width: number,
|
|
2492
|
+
height: number,
|
|
2493
|
+
numberOfColors?: number
|
|
2494
|
+
) {
|
|
2495
|
+
return imageToBitmap(
|
|
2496
|
+
image,
|
|
2497
|
+
width,
|
|
2498
|
+
height,
|
|
2499
|
+
this.colors,
|
|
2500
|
+
this.bitmapColorIndices,
|
|
2501
|
+
numberOfColors
|
|
2502
|
+
);
|
|
2503
|
+
}
|
|
2504
|
+
async quantizeImage(
|
|
2505
|
+
image: HTMLImageElement,
|
|
2506
|
+
width: number,
|
|
2507
|
+
height: number,
|
|
2508
|
+
numberOfColors: number
|
|
2509
|
+
) {
|
|
2510
|
+
return quantizeImage(image, width, height, numberOfColors);
|
|
2511
|
+
}
|
|
2512
|
+
async resizeAndQuantizeImage(
|
|
2513
|
+
image: HTMLImageElement,
|
|
2514
|
+
width: number,
|
|
2515
|
+
height: number,
|
|
2516
|
+
numberOfColors: number,
|
|
2517
|
+
colors?: string[]
|
|
2518
|
+
) {
|
|
2519
|
+
return resizeAndQuantizeImage(image, width, height, numberOfColors, colors);
|
|
2520
|
+
}
|
|
2521
|
+
|
|
2522
|
+
// CONTEXT COMMANDS
|
|
2523
|
+
|
|
2524
|
+
async runContextCommand(
|
|
2525
|
+
command: DisplayContextCommand,
|
|
2526
|
+
sendImmediately?: boolean
|
|
2527
|
+
) {
|
|
2528
|
+
return runDisplayContextCommand(this, command, sendImmediately);
|
|
2529
|
+
}
|
|
2530
|
+
async runContextCommands(
|
|
2531
|
+
commands: DisplayContextCommand[],
|
|
2532
|
+
sendImmediately?: boolean
|
|
2533
|
+
) {
|
|
2534
|
+
return runDisplayContextCommands(this, commands, sendImmediately);
|
|
2535
|
+
}
|
|
2536
|
+
|
|
2537
|
+
#isReady = true;
|
|
2538
|
+
get isReady() {
|
|
2539
|
+
return this.isAvailable && this.#isReady;
|
|
2540
|
+
}
|
|
2541
|
+
#lastReadyTime = 0;
|
|
2542
|
+
#lastShowRequestTime = 0;
|
|
2543
|
+
#minReadyInterval = 100; // Forced delay due to Frame's fpga timing...
|
|
2544
|
+
#waitBeforeReady = false;
|
|
2545
|
+
async #parseDisplayReady(dataView: DataView) {
|
|
2546
|
+
const now = Date.now();
|
|
2547
|
+
const timeSinceLastDraw = now - this.#lastShowRequestTime;
|
|
2548
|
+
const timeSinceLastReady = now - this.#lastReadyTime;
|
|
2549
|
+
//_console.log(`${timeSinceLastReady}ms since last render`);
|
|
2550
|
+
_console.log(`${timeSinceLastDraw}ms draw time`);
|
|
2551
|
+
if (this.#waitBeforeReady && timeSinceLastReady < this.#minReadyInterval) {
|
|
2552
|
+
const timeToWait = this.#minReadyInterval - timeSinceLastReady;
|
|
2553
|
+
_console.log(`waiting ${timeToWait}ms`);
|
|
2554
|
+
await wait(timeToWait);
|
|
2555
|
+
}
|
|
2556
|
+
this.#isReady = true;
|
|
2557
|
+
this.#lastReadyTime = Date.now();
|
|
2558
|
+
this.#dispatchEvent("displayReady", {});
|
|
2559
|
+
}
|
|
2560
|
+
|
|
2561
|
+
// SPRITE SHEET
|
|
2562
|
+
#spriteSheets: Record<string, DisplaySpriteSheet> = {};
|
|
2563
|
+
#spriteSheetIndices: Record<string, number> = {};
|
|
2564
|
+
get spriteSheets() {
|
|
2565
|
+
return this.#spriteSheets;
|
|
2566
|
+
}
|
|
2567
|
+
get spriteSheetIndices() {
|
|
2568
|
+
return this.#spriteSheetIndices;
|
|
2569
|
+
}
|
|
2570
|
+
async #setSpriteSheetName(
|
|
2571
|
+
spriteSheetName: string,
|
|
2572
|
+
sendImmediately?: boolean
|
|
2573
|
+
) {
|
|
2574
|
+
_console.assertTypeWithError(spriteSheetName, "string");
|
|
2575
|
+
_console.assertRangeWithError(
|
|
2576
|
+
"newName",
|
|
2577
|
+
spriteSheetName.length,
|
|
2578
|
+
MinSpriteSheetNameLength,
|
|
2579
|
+
MaxSpriteSheetNameLength
|
|
2580
|
+
);
|
|
2581
|
+
const setSpriteSheetNameData = textEncoder.encode(spriteSheetName);
|
|
2582
|
+
_console.log({ setSpriteSheetNameData });
|
|
2583
|
+
|
|
2584
|
+
const promise = this.waitForEvent("getSpriteSheetName");
|
|
2585
|
+
this.sendMessage(
|
|
2586
|
+
[{ type: "setSpriteSheetName", data: setSpriteSheetNameData.buffer }],
|
|
2587
|
+
sendImmediately
|
|
2588
|
+
);
|
|
2589
|
+
await promise;
|
|
2590
|
+
}
|
|
2591
|
+
#pendingSpriteSheet?: DisplaySpriteSheet;
|
|
2592
|
+
get pendingSpriteSheet() {
|
|
2593
|
+
return this.#pendingSpriteSheet;
|
|
2594
|
+
}
|
|
2595
|
+
#pendingSpriteSheetName?: string;
|
|
2596
|
+
get pendingSpriteSheetName() {
|
|
2597
|
+
return this.#pendingSpriteSheetName;
|
|
2598
|
+
}
|
|
2599
|
+
#updateSpriteSheetName(updatedSpriteSheetName: string) {
|
|
2600
|
+
_console.assertTypeWithError(updatedSpriteSheetName, "string");
|
|
2601
|
+
this.#pendingSpriteSheetName = updatedSpriteSheetName;
|
|
2602
|
+
_console.log({ updatedSpriteSheetName: this.#pendingSpriteSheetName });
|
|
2603
|
+
this.#dispatchEvent("getSpriteSheetName", {
|
|
2604
|
+
spriteSheetName: this.#pendingSpriteSheetName,
|
|
2605
|
+
});
|
|
2606
|
+
}
|
|
2607
|
+
sendFile!: SendFileCallback;
|
|
2608
|
+
serializeSpriteSheet(spriteSheet: DisplaySpriteSheet): ArrayBuffer {
|
|
2609
|
+
return serializeSpriteSheet(this, spriteSheet);
|
|
2610
|
+
}
|
|
2611
|
+
async uploadSpriteSheet(spriteSheet: DisplaySpriteSheet) {
|
|
2612
|
+
spriteSheet = structuredClone(spriteSheet);
|
|
2613
|
+
this.#pendingSpriteSheet = spriteSheet;
|
|
2614
|
+
const buffer = this.serializeSpriteSheet(this.#pendingSpriteSheet);
|
|
2615
|
+
await this.#setSpriteSheetName(this.#pendingSpriteSheet.name);
|
|
2616
|
+
const promise = this.waitForEvent("displaySpriteSheetUploadComplete");
|
|
2617
|
+
this.sendFile("spriteSheet", buffer, true);
|
|
2618
|
+
await promise;
|
|
2619
|
+
}
|
|
2620
|
+
async uploadSpriteSheets(spriteSheets: DisplaySpriteSheet[]) {
|
|
2621
|
+
for (const spriteSheet of spriteSheets) {
|
|
2622
|
+
await this.uploadSpriteSheet(spriteSheet);
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
assertLoadedSpriteSheet(spriteSheetName: string) {
|
|
2626
|
+
assertLoadedSpriteSheet(this, spriteSheetName);
|
|
2627
|
+
}
|
|
2628
|
+
assertSelectedSpriteSheet(spriteSheetName: string) {
|
|
2629
|
+
assertSelectedSpriteSheet(this, spriteSheetName);
|
|
2630
|
+
}
|
|
2631
|
+
assertAnySelectedSpriteSheet() {
|
|
2632
|
+
assertAnySelectedSpriteSheet(this);
|
|
2633
|
+
}
|
|
2634
|
+
assertSprite(spriteName: string) {
|
|
2635
|
+
return assertSprite(this, spriteName);
|
|
2636
|
+
}
|
|
2637
|
+
getSprite(spriteName: string): DisplaySprite | undefined {
|
|
2638
|
+
return getSprite(this, spriteName);
|
|
2639
|
+
}
|
|
2640
|
+
getSpriteSheetPalette(
|
|
2641
|
+
paletteName: string
|
|
2642
|
+
): DisplaySpriteSheetPalette | undefined {
|
|
2643
|
+
return getSpriteSheetPalette(this, paletteName);
|
|
2644
|
+
}
|
|
2645
|
+
getSpriteSheetPaletteSwap(
|
|
2646
|
+
paletteSwapName: string
|
|
2647
|
+
): DisplaySpriteSheetPaletteSwap | undefined {
|
|
2648
|
+
return getSpriteSheetPaletteSwap(this, paletteSwapName);
|
|
2649
|
+
}
|
|
2650
|
+
getSpritePaletteSwap(
|
|
2651
|
+
spriteName: string,
|
|
2652
|
+
paletteSwapName: string
|
|
2653
|
+
): DisplaySpritePaletteSwap | undefined {
|
|
2654
|
+
return getSpritePaletteSwap(this, spriteName, paletteSwapName);
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
get selectedSpriteSheet() {
|
|
2658
|
+
if (this.contextState.spriteSheetName) {
|
|
2659
|
+
return this.#spriteSheets[this.contextState.spriteSheetName];
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
get selectedSpriteSheetName() {
|
|
2663
|
+
return this.selectedSpriteSheet?.name;
|
|
2664
|
+
}
|
|
2665
|
+
async selectSpriteSheet(spriteSheetName: string, sendImmediately?: boolean) {
|
|
2666
|
+
this.assertLoadedSpriteSheet(spriteSheetName);
|
|
2667
|
+
const differences = this.#contextStateHelper.update({
|
|
2668
|
+
spriteSheetName,
|
|
2669
|
+
});
|
|
2670
|
+
if (differences.length == 0) {
|
|
2671
|
+
return;
|
|
2672
|
+
}
|
|
2673
|
+
const spriteSheetIndex = this.spriteSheetIndices[spriteSheetName];
|
|
2674
|
+
const commandType: DisplayContextCommandType = "selectSpriteSheet";
|
|
2675
|
+
const dataView = serializeContextCommand(this, {
|
|
2676
|
+
type: commandType,
|
|
2677
|
+
spriteSheetIndex,
|
|
2678
|
+
});
|
|
2679
|
+
if (!dataView) {
|
|
2680
|
+
return;
|
|
2681
|
+
}
|
|
2682
|
+
await this.#sendDisplayContextCommand(
|
|
2683
|
+
commandType,
|
|
2684
|
+
dataView.buffer,
|
|
2685
|
+
sendImmediately
|
|
2686
|
+
);
|
|
2687
|
+
this.#onContextStateUpdate(differences);
|
|
2688
|
+
}
|
|
2689
|
+
async drawSprite(
|
|
2690
|
+
offsetX: number,
|
|
2691
|
+
offsetY: number,
|
|
2692
|
+
spriteName: string,
|
|
2693
|
+
sendImmediately?: boolean
|
|
2694
|
+
) {
|
|
2695
|
+
_console.assertWithError(
|
|
2696
|
+
this.selectedSpriteSheet,
|
|
2697
|
+
"no spriteSheet selected"
|
|
2698
|
+
);
|
|
2699
|
+
let spriteIndex = this.selectedSpriteSheet!.sprites.findIndex(
|
|
2700
|
+
(sprite) => sprite.name == spriteName
|
|
2701
|
+
);
|
|
2702
|
+
_console.assertWithError(
|
|
2703
|
+
spriteIndex != -1,
|
|
2704
|
+
`sprite "${spriteName}" not found`
|
|
2705
|
+
);
|
|
2706
|
+
spriteIndex = spriteIndex!;
|
|
2707
|
+
const commandType: DisplayContextCommandType = "drawSprite";
|
|
2708
|
+
const dataView = serializeContextCommand(this, {
|
|
2709
|
+
type: commandType,
|
|
2710
|
+
offsetX,
|
|
2711
|
+
offsetY,
|
|
2712
|
+
spriteIndex,
|
|
2713
|
+
use2Bytes: this.selectedSpriteSheet!.sprites.length > 255,
|
|
2714
|
+
});
|
|
2715
|
+
if (!dataView) {
|
|
2716
|
+
return;
|
|
2717
|
+
}
|
|
2718
|
+
await this.#sendDisplayContextCommand(
|
|
2719
|
+
commandType,
|
|
2720
|
+
dataView.buffer,
|
|
2721
|
+
sendImmediately
|
|
2722
|
+
);
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2725
|
+
async drawSprites(
|
|
2726
|
+
offsetX: number,
|
|
2727
|
+
offsetY: number,
|
|
2728
|
+
spriteLines: DisplaySpriteLines,
|
|
2729
|
+
sendImmediately?: boolean
|
|
2730
|
+
) {
|
|
2731
|
+
const spriteSerializedLines: DisplaySpriteSerializedLines = [];
|
|
2732
|
+
spriteLines.forEach((spriteLine) => {
|
|
2733
|
+
const serializedLine: DisplaySpriteSerializedLine = [];
|
|
2734
|
+
spriteLine.forEach((spriteSubLine) => {
|
|
2735
|
+
this.assertLoadedSpriteSheet(spriteSubLine.spriteSheetName);
|
|
2736
|
+
const spriteSheet = this.spriteSheets[spriteSubLine.spriteSheetName];
|
|
2737
|
+
const spriteSheetIndex = this.spriteSheetIndices[spriteSheet.name];
|
|
2738
|
+
const serializedSubLine: DisplaySpriteSerializedSubLine = {
|
|
2739
|
+
spriteSheetIndex,
|
|
2740
|
+
spriteIndices: [],
|
|
2741
|
+
use2Bytes: spriteSheet.sprites.length > 255,
|
|
2742
|
+
};
|
|
2743
|
+
spriteSubLine.spriteNames.forEach((spriteName) => {
|
|
2744
|
+
let spriteIndex = spriteSheet.sprites.findIndex(
|
|
2745
|
+
(sprite) => sprite.name == spriteName
|
|
2746
|
+
);
|
|
2747
|
+
_console.assertWithError(
|
|
2748
|
+
spriteIndex != -1,
|
|
2749
|
+
`sprite "${spriteName}" not found`
|
|
2750
|
+
);
|
|
2751
|
+
spriteIndex = spriteIndex!;
|
|
2752
|
+
serializedSubLine.spriteIndices.push(spriteIndex);
|
|
2753
|
+
});
|
|
2754
|
+
serializedLine.push(serializedSubLine);
|
|
2755
|
+
});
|
|
2756
|
+
spriteSerializedLines.push(serializedLine);
|
|
2757
|
+
});
|
|
2758
|
+
_console.log("spriteSerializedLines", spriteSerializedLines);
|
|
2759
|
+
const commandType: DisplayContextCommandType = "drawSprites";
|
|
2760
|
+
const dataView = serializeContextCommand(this, {
|
|
2761
|
+
type: commandType,
|
|
2762
|
+
offsetX,
|
|
2763
|
+
offsetY,
|
|
2764
|
+
spriteSerializedLines: spriteSerializedLines,
|
|
2765
|
+
});
|
|
2766
|
+
if (!dataView) {
|
|
2767
|
+
return;
|
|
2768
|
+
}
|
|
2769
|
+
await this.#sendDisplayContextCommand(
|
|
2770
|
+
commandType,
|
|
2771
|
+
dataView.buffer,
|
|
2772
|
+
sendImmediately
|
|
2773
|
+
);
|
|
2774
|
+
}
|
|
2775
|
+
|
|
2776
|
+
async drawSpritesString(
|
|
2777
|
+
offsetX: number,
|
|
2778
|
+
offsetY: number,
|
|
2779
|
+
string: string,
|
|
2780
|
+
requireAll?: boolean,
|
|
2781
|
+
maxLineBreadth?: number,
|
|
2782
|
+
separators?: string[],
|
|
2783
|
+
sendImmediately?: boolean
|
|
2784
|
+
) {
|
|
2785
|
+
const spriteLines = this.stringToSpriteLines(
|
|
2786
|
+
string,
|
|
2787
|
+
requireAll,
|
|
2788
|
+
maxLineBreadth,
|
|
2789
|
+
separators
|
|
2790
|
+
);
|
|
2791
|
+
await this.drawSprites(offsetX, offsetY, spriteLines, sendImmediately);
|
|
2792
|
+
}
|
|
2793
|
+
stringToSpriteLines(
|
|
2794
|
+
string: string,
|
|
2795
|
+
requireAll?: boolean,
|
|
2796
|
+
maxLineBreadth?: number,
|
|
2797
|
+
separators?: string[]
|
|
2798
|
+
): DisplaySpriteLines {
|
|
2799
|
+
return stringToSpriteLines(
|
|
2800
|
+
string,
|
|
2801
|
+
this.spriteSheets,
|
|
2802
|
+
this.contextState,
|
|
2803
|
+
requireAll,
|
|
2804
|
+
maxLineBreadth,
|
|
2805
|
+
separators
|
|
2806
|
+
);
|
|
2807
|
+
}
|
|
2808
|
+
|
|
2809
|
+
async drawSpriteFromSpriteSheet(
|
|
2810
|
+
offsetX: number,
|
|
2811
|
+
offsetY: number,
|
|
2812
|
+
spriteName: string,
|
|
2813
|
+
spriteSheet: DisplaySpriteSheet,
|
|
2814
|
+
paletteName?: string,
|
|
2815
|
+
sendImmediately?: boolean
|
|
2816
|
+
) {
|
|
2817
|
+
return drawSpriteFromSpriteSheet(
|
|
2818
|
+
this,
|
|
2819
|
+
offsetX,
|
|
2820
|
+
offsetY,
|
|
2821
|
+
spriteName,
|
|
2822
|
+
spriteSheet,
|
|
2823
|
+
paletteName,
|
|
2824
|
+
sendImmediately
|
|
2825
|
+
);
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2828
|
+
#parseSpriteSheetIndex(dataView: DataView) {
|
|
2829
|
+
const spriteSheetIndex = dataView.getUint8(0);
|
|
2830
|
+
_console.log({
|
|
2831
|
+
pendingSpriteSheet: this.#pendingSpriteSheet,
|
|
2832
|
+
spriteSheetName: this.#pendingSpriteSheetName,
|
|
2833
|
+
spriteSheetIndex,
|
|
2834
|
+
});
|
|
2835
|
+
if (this.isServerSide) {
|
|
2836
|
+
return;
|
|
2837
|
+
}
|
|
2838
|
+
_console.assertWithError(
|
|
2839
|
+
this.#pendingSpriteSheetName != undefined,
|
|
2840
|
+
"expected spriteSheetName when receiving spriteSheetIndex"
|
|
2841
|
+
);
|
|
2842
|
+
_console.assertWithError(
|
|
2843
|
+
this.#pendingSpriteSheet != undefined,
|
|
2844
|
+
"expected pendingSpriteSheet when receiving spriteSheetIndex"
|
|
2845
|
+
);
|
|
2846
|
+
this.#spriteSheets[this.#pendingSpriteSheetName!] =
|
|
2847
|
+
this.#pendingSpriteSheet!;
|
|
2848
|
+
this.#spriteSheetIndices[this.#pendingSpriteSheetName!] = spriteSheetIndex;
|
|
2849
|
+
this.#dispatchEvent("displaySpriteSheetUploadComplete", {
|
|
2850
|
+
spriteSheetName: this.#pendingSpriteSheetName!,
|
|
2851
|
+
spriteSheet: this.#pendingSpriteSheet!,
|
|
2852
|
+
});
|
|
2853
|
+
this.#pendingSpriteSheet = undefined;
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
// MESSAGE
|
|
2857
|
+
parseMessage(messageType: DisplayMessageType, dataView: DataView) {
|
|
2858
|
+
_console.log({ messageType, dataView });
|
|
2859
|
+
|
|
2860
|
+
switch (messageType) {
|
|
2861
|
+
case "isDisplayAvailable":
|
|
2862
|
+
this.#parseIsDisplayAvailable(dataView);
|
|
2863
|
+
break;
|
|
2864
|
+
case "displayStatus":
|
|
2865
|
+
this.#parseDisplayStatus(dataView);
|
|
2866
|
+
break;
|
|
2867
|
+
case "displayInformation":
|
|
2868
|
+
this.#parseDisplayInformation(dataView);
|
|
2869
|
+
break;
|
|
2870
|
+
case "getDisplayBrightness":
|
|
2871
|
+
case "setDisplayBrightness":
|
|
2872
|
+
this.#parseDisplayBrightness(dataView);
|
|
2873
|
+
break;
|
|
2874
|
+
case "displayReady":
|
|
2875
|
+
this.#parseDisplayReady(dataView);
|
|
2876
|
+
break;
|
|
2877
|
+
case "getSpriteSheetName":
|
|
2878
|
+
case "setSpriteSheetName":
|
|
2879
|
+
const spriteSheetName = textDecoder.decode(dataView.buffer);
|
|
2880
|
+
_console.log({ spriteSheetName });
|
|
2881
|
+
this.#updateSpriteSheetName(spriteSheetName);
|
|
2882
|
+
break;
|
|
2883
|
+
case "spriteSheetIndex":
|
|
2884
|
+
this.#parseSpriteSheetIndex(dataView);
|
|
2885
|
+
break;
|
|
2886
|
+
default:
|
|
2887
|
+
throw Error(`uncaught messageType ${messageType}`);
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
|
|
2891
|
+
// SPRITE SHEET PALETTES
|
|
2892
|
+
|
|
2893
|
+
assertSpriteSheetPalette(paletteName: string) {
|
|
2894
|
+
assertSpriteSheetPalette(this, paletteName);
|
|
2895
|
+
}
|
|
2896
|
+
assertSpriteSheetPaletteSwap(paletteSwapName: string) {
|
|
2897
|
+
assertSpriteSheetPaletteSwap(this, paletteSwapName);
|
|
2898
|
+
}
|
|
2899
|
+
assertSpritePaletteSwap(spriteName: string, paletteSwapName: string) {
|
|
2900
|
+
assertSpritePaletteSwap(this, spriteName, paletteSwapName);
|
|
2901
|
+
}
|
|
2902
|
+
async selectSpriteSheetPalette(
|
|
2903
|
+
paletteName: string,
|
|
2904
|
+
offset?: number,
|
|
2905
|
+
sendImmediately?: boolean
|
|
2906
|
+
) {
|
|
2907
|
+
await selectSpriteSheetPalette(this, paletteName, offset, sendImmediately);
|
|
2908
|
+
}
|
|
2909
|
+
async selectSpriteSheetPaletteSwap(
|
|
2910
|
+
paletteSwapName: string,
|
|
2911
|
+
offset?: number,
|
|
2912
|
+
sendImmediately?: boolean
|
|
2913
|
+
) {
|
|
2914
|
+
await selectSpriteSheetPaletteSwap(
|
|
2915
|
+
this,
|
|
2916
|
+
paletteSwapName,
|
|
2917
|
+
offset,
|
|
2918
|
+
sendImmediately
|
|
2919
|
+
);
|
|
2920
|
+
}
|
|
2921
|
+
async selectSpritePaletteSwap(
|
|
2922
|
+
spriteName: string,
|
|
2923
|
+
paletteSwapName: string,
|
|
2924
|
+
offset?: number,
|
|
2925
|
+
sendImmediately?: boolean
|
|
2926
|
+
) {
|
|
2927
|
+
await selectSpritePaletteSwap(
|
|
2928
|
+
this,
|
|
2929
|
+
spriteName,
|
|
2930
|
+
paletteSwapName,
|
|
2931
|
+
offset,
|
|
2932
|
+
sendImmediately
|
|
2933
|
+
);
|
|
2934
|
+
}
|
|
2935
|
+
|
|
2936
|
+
reset() {
|
|
2937
|
+
_console.log("clearing displayManager");
|
|
2938
|
+
// @ts-ignore
|
|
2939
|
+
this.#displayStatus = undefined;
|
|
2940
|
+
this.#isAvailable = false;
|
|
2941
|
+
this.#displayInformation = undefined;
|
|
2942
|
+
// @ts-ignore
|
|
2943
|
+
this.#brightness = undefined;
|
|
2944
|
+
this.#displayContextCommandBuffers = [];
|
|
2945
|
+
this.#isAvailable = false;
|
|
2946
|
+
|
|
2947
|
+
this.#contextStateHelper.reset();
|
|
2948
|
+
this.#colors.length = 0;
|
|
2949
|
+
this.#opacities.length = 0;
|
|
2950
|
+
|
|
2951
|
+
this.#isReady = true;
|
|
2952
|
+
this.#pendingSpriteSheet = undefined;
|
|
2953
|
+
this.#pendingSpriteSheetName = undefined;
|
|
2954
|
+
|
|
2955
|
+
this.isServerSide = false;
|
|
2956
|
+
|
|
2957
|
+
Object.keys(this.#spriteSheetIndices).forEach(
|
|
2958
|
+
(spriteSheetName) => delete this.#spriteSheetIndices[spriteSheetName]
|
|
2959
|
+
);
|
|
2960
|
+
Object.keys(this.#spriteSheets).forEach(
|
|
2961
|
+
(spriteSheetName) => delete this.#spriteSheets[spriteSheetName]
|
|
2962
|
+
);
|
|
2963
|
+
}
|
|
2964
|
+
|
|
2965
|
+
// MTU
|
|
2966
|
+
#mtu!: number;
|
|
2967
|
+
get mtu() {
|
|
2968
|
+
return this.#mtu;
|
|
2969
|
+
}
|
|
2970
|
+
set mtu(newMtu: number) {
|
|
2971
|
+
this.#mtu = newMtu;
|
|
2972
|
+
}
|
|
2973
|
+
|
|
2974
|
+
// SERVER SIDE
|
|
2975
|
+
#isServerSide = false;
|
|
2976
|
+
get isServerSide() {
|
|
2977
|
+
return this.#isServerSide;
|
|
2978
|
+
}
|
|
2979
|
+
set isServerSide(newIsServerSide) {
|
|
2980
|
+
if (this.#isServerSide == newIsServerSide) {
|
|
2981
|
+
_console.log("redundant isServerSide assignment");
|
|
2982
|
+
return;
|
|
2983
|
+
}
|
|
2984
|
+
_console.log({ newIsServerSide });
|
|
2985
|
+
this.#isServerSide = newIsServerSide;
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
|
|
2989
|
+
export default DisplayManager;
|