@sequent-org/moodboard 1.0.0

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.
Files changed (123) hide show
  1. package/package.json +44 -0
  2. package/src/assets/icons/README.md +105 -0
  3. package/src/assets/icons/attachments.svg +3 -0
  4. package/src/assets/icons/clear.svg +5 -0
  5. package/src/assets/icons/comments.svg +3 -0
  6. package/src/assets/icons/emoji.svg +6 -0
  7. package/src/assets/icons/frame.svg +3 -0
  8. package/src/assets/icons/image.svg +3 -0
  9. package/src/assets/icons/note.svg +3 -0
  10. package/src/assets/icons/pan.svg +3 -0
  11. package/src/assets/icons/pencil.svg +3 -0
  12. package/src/assets/icons/redo.svg +3 -0
  13. package/src/assets/icons/select.svg +9 -0
  14. package/src/assets/icons/shapes.svg +3 -0
  15. package/src/assets/icons/text-add.svg +3 -0
  16. package/src/assets/icons/topbar/README.md +39 -0
  17. package/src/assets/icons/topbar/grid-cross.svg +6 -0
  18. package/src/assets/icons/topbar/grid-dot.svg +3 -0
  19. package/src/assets/icons/topbar/grid-line.svg +3 -0
  20. package/src/assets/icons/topbar/grid-off.svg +3 -0
  21. package/src/assets/icons/topbar/paint.svg +3 -0
  22. package/src/assets/icons/undo.svg +3 -0
  23. package/src/core/ApiClient.js +309 -0
  24. package/src/core/EventBus.js +42 -0
  25. package/src/core/HistoryManager.js +261 -0
  26. package/src/core/KeyboardManager.js +710 -0
  27. package/src/core/PixiEngine.js +439 -0
  28. package/src/core/SaveManager.js +381 -0
  29. package/src/core/StateManager.js +64 -0
  30. package/src/core/commands/BaseCommand.js +68 -0
  31. package/src/core/commands/CopyObjectCommand.js +44 -0
  32. package/src/core/commands/CreateObjectCommand.js +46 -0
  33. package/src/core/commands/DeleteObjectCommand.js +146 -0
  34. package/src/core/commands/EditFileNameCommand.js +107 -0
  35. package/src/core/commands/GroupMoveCommand.js +47 -0
  36. package/src/core/commands/GroupReorderZCommand.js +74 -0
  37. package/src/core/commands/GroupResizeCommand.js +37 -0
  38. package/src/core/commands/GroupRotateCommand.js +41 -0
  39. package/src/core/commands/MoveObjectCommand.js +89 -0
  40. package/src/core/commands/PasteObjectCommand.js +103 -0
  41. package/src/core/commands/ReorderZCommand.js +45 -0
  42. package/src/core/commands/ResizeObjectCommand.js +135 -0
  43. package/src/core/commands/RotateObjectCommand.js +70 -0
  44. package/src/core/commands/index.js +14 -0
  45. package/src/core/events/Events.js +147 -0
  46. package/src/core/index.js +1632 -0
  47. package/src/core/rendering/GeometryUtils.js +89 -0
  48. package/src/core/rendering/HitTestManager.js +186 -0
  49. package/src/core/rendering/LayerManager.js +137 -0
  50. package/src/core/rendering/ObjectRenderer.js +363 -0
  51. package/src/core/rendering/PixiRenderer.js +140 -0
  52. package/src/core/rendering/index.js +9 -0
  53. package/src/grid/BaseGrid.js +164 -0
  54. package/src/grid/CrossGrid.js +75 -0
  55. package/src/grid/DotGrid.js +148 -0
  56. package/src/grid/GridFactory.js +173 -0
  57. package/src/grid/LineGrid.js +115 -0
  58. package/src/index.js +2 -0
  59. package/src/moodboard/ActionHandler.js +114 -0
  60. package/src/moodboard/DataManager.js +114 -0
  61. package/src/moodboard/MoodBoard.js +359 -0
  62. package/src/moodboard/WorkspaceManager.js +103 -0
  63. package/src/objects/BaseObject.js +1 -0
  64. package/src/objects/CommentObject.js +115 -0
  65. package/src/objects/DrawingObject.js +114 -0
  66. package/src/objects/EmojiObject.js +98 -0
  67. package/src/objects/FileObject.js +318 -0
  68. package/src/objects/FrameObject.js +127 -0
  69. package/src/objects/ImageObject.js +72 -0
  70. package/src/objects/NoteObject.js +227 -0
  71. package/src/objects/ObjectFactory.js +61 -0
  72. package/src/objects/ShapeObject.js +134 -0
  73. package/src/objects/StampObject.js +0 -0
  74. package/src/objects/StickerObject.js +0 -0
  75. package/src/objects/TextObject.js +123 -0
  76. package/src/services/BoardService.js +85 -0
  77. package/src/services/FileUploadService.js +398 -0
  78. package/src/services/FrameService.js +138 -0
  79. package/src/services/ImageUploadService.js +246 -0
  80. package/src/services/ZOrderManager.js +50 -0
  81. package/src/services/ZoomPanController.js +78 -0
  82. package/src/src.7z +0 -0
  83. package/src/src.zip +0 -0
  84. package/src/src2.zip +0 -0
  85. package/src/tools/AlignmentGuides.js +326 -0
  86. package/src/tools/BaseTool.js +257 -0
  87. package/src/tools/ResizeHandles.js +381 -0
  88. package/src/tools/ToolManager.js +580 -0
  89. package/src/tools/board-tools/PanTool.js +43 -0
  90. package/src/tools/board-tools/ZoomTool.js +393 -0
  91. package/src/tools/object-tools/DrawingTool.js +404 -0
  92. package/src/tools/object-tools/PlacementTool.js +1005 -0
  93. package/src/tools/object-tools/SelectTool.js +2183 -0
  94. package/src/tools/object-tools/TextTool.js +416 -0
  95. package/src/tools/object-tools/selection/BoxSelectController.js +105 -0
  96. package/src/tools/object-tools/selection/GeometryUtils.js +101 -0
  97. package/src/tools/object-tools/selection/GroupDragController.js +61 -0
  98. package/src/tools/object-tools/selection/GroupResizeController.js +90 -0
  99. package/src/tools/object-tools/selection/GroupRotateController.js +61 -0
  100. package/src/tools/object-tools/selection/HandlesSync.js +96 -0
  101. package/src/tools/object-tools/selection/ResizeController.js +68 -0
  102. package/src/tools/object-tools/selection/RotateController.js +58 -0
  103. package/src/tools/object-tools/selection/SelectionModel.js +42 -0
  104. package/src/tools/object-tools/selection/SimpleDragController.js +45 -0
  105. package/src/ui/CommentPopover.js +187 -0
  106. package/src/ui/ContextMenu.js +340 -0
  107. package/src/ui/FilePropertiesPanel.js +298 -0
  108. package/src/ui/FramePropertiesPanel.js +462 -0
  109. package/src/ui/HtmlHandlesLayer.js +778 -0
  110. package/src/ui/HtmlTextLayer.js +279 -0
  111. package/src/ui/MapPanel.js +290 -0
  112. package/src/ui/NotePropertiesPanel.js +502 -0
  113. package/src/ui/SaveStatus.js +250 -0
  114. package/src/ui/TextPropertiesPanel.js +911 -0
  115. package/src/ui/Toolbar.js +1118 -0
  116. package/src/ui/Topbar.js +220 -0
  117. package/src/ui/ZoomPanel.js +116 -0
  118. package/src/ui/styles/workspace.css +854 -0
  119. package/src/utils/colors.js +0 -0
  120. package/src/utils/geometry.js +0 -0
  121. package/src/utils/iconLoader.js +270 -0
  122. package/src/utils/objectIdGenerator.js +17 -0
  123. package/src/utils/topbarIconLoader.js +114 -0
@@ -0,0 +1,363 @@
1
+ import * as PIXI from 'pixi.js';
2
+ import { ObjectFactory } from '../../objects/ObjectFactory.js';
3
+ import { GeometryUtils } from './GeometryUtils.js';
4
+
5
+ /**
6
+ * Рендерер объектов
7
+ * Отвечает за создание, обновление и удаление PIXI объектов
8
+ */
9
+ export class ObjectRenderer {
10
+ constructor(objectsMap) {
11
+ this.objects = objectsMap; // Map<id, pixiObject> из PixiEngine
12
+ }
13
+
14
+ /**
15
+ * Создать объект на холсте
16
+ * @param {Object} objectData - Данные объекта
17
+ * @returns {PIXI.DisplayObject|null} Созданный PIXI объект или null
18
+ */
19
+ createObject(objectData) {
20
+ let pixiObject;
21
+
22
+ // Создаем объект через фабрику
23
+ const instance = ObjectFactory.create(objectData.type, objectData);
24
+ if (instance) {
25
+ pixiObject = instance.getPixi();
26
+ this._setupObjectMetadata(pixiObject, objectData, instance);
27
+ } else {
28
+ console.warn(`Unknown object type: ${objectData.type}`);
29
+ pixiObject = this._createDefaultObject(objectData);
30
+ }
31
+
32
+ if (pixiObject) {
33
+ this._setupObjectProperties(pixiObject, objectData);
34
+ this._setupObjectTransform(pixiObject, objectData);
35
+ // Объект добавляется в слой через PixiEngine, здесь только сохраняем в Map
36
+ this.objects.set(objectData.id, pixiObject);
37
+ }
38
+
39
+ return pixiObject;
40
+ }
41
+
42
+ /**
43
+ * Настройка метаданных объекта
44
+ * @param {PIXI.DisplayObject} pixiObject - PIXI объект
45
+ * @param {Object} objectData - Данные объекта
46
+ * @param {Object} instance - Экземпляр объекта
47
+ * @private
48
+ */
49
+ _setupObjectMetadata(pixiObject, objectData, instance) {
50
+ const prevMb = pixiObject._mb || {};
51
+ pixiObject._mb = {
52
+ ...prevMb,
53
+ objectId: objectData.id,
54
+ type: objectData.type,
55
+ properties: objectData.properties || {},
56
+ instance
57
+ };
58
+ }
59
+
60
+ /**
61
+ * Настройка базовых свойств объекта
62
+ * @param {PIXI.DisplayObject} pixiObject - PIXI объект
63
+ * @param {Object} objectData - Данные объекта
64
+ * @private
65
+ */
66
+ _setupObjectProperties(pixiObject, objectData) {
67
+ pixiObject.x = objectData.position.x;
68
+ pixiObject.y = objectData.position.y;
69
+ pixiObject.eventMode = 'static';
70
+ pixiObject.cursor = 'pointer';
71
+ }
72
+
73
+ /**
74
+ * Настройка трансформации объекта
75
+ * @param {PIXI.DisplayObject} pixiObject - PIXI объект
76
+ * @param {Object} objectData - Данные объекта
77
+ * @private
78
+ */
79
+ _setupObjectTransform(pixiObject, objectData) {
80
+ // Устанавливаем центр вращения
81
+ if (pixiObject.anchor !== undefined) {
82
+ pixiObject.anchor.set(0.5, 0.5);
83
+ } else if (pixiObject instanceof PIXI.Graphics) {
84
+ const bounds = pixiObject.getBounds();
85
+ const pivotX = bounds.width / 2;
86
+ const pivotY = bounds.height / 2;
87
+ pixiObject.pivot.set(pivotX, pivotY);
88
+
89
+ // Компенсируем смещение pivot
90
+ const needsCompensation = !objectData.transform || !objectData.transform.pivotCompensated;
91
+ if (needsCompensation) {
92
+ pixiObject.x += pivotX;
93
+ pixiObject.y += pivotY;
94
+ }
95
+ }
96
+
97
+ // Применяем поворот
98
+ if (objectData.transform && objectData.transform.rotation !== undefined) {
99
+ const angleRadians = GeometryUtils.degreesToRadians(objectData.transform.rotation);
100
+ pixiObject.rotation = angleRadians;
101
+ }
102
+ }
103
+
104
+
105
+
106
+ /**
107
+ * Создать объект по умолчанию для неизвестных типов
108
+ * @param {Object} objectData - Данные объекта
109
+ * @returns {PIXI.Graphics} PIXI Graphics объект
110
+ * @private
111
+ */
112
+ _createDefaultObject(objectData) {
113
+ const graphics = new PIXI.Graphics();
114
+ graphics.beginFill(0xFF0000, 0.5);
115
+ graphics.drawRect(0, 0, objectData.width || 100, objectData.height || 100);
116
+ graphics.endFill();
117
+ return graphics;
118
+ }
119
+
120
+ /**
121
+ * Удалить объект с холста
122
+ * @param {string} objectId - ID объекта
123
+ */
124
+ removeObject(objectId) {
125
+ const pixiObject = this.objects.get(objectId);
126
+ if (pixiObject) {
127
+ // Объект удаляется из слоя через PixiEngine, здесь только удаляем из Map
128
+ this.objects.delete(objectId);
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Обновить размер объекта
134
+ * @param {string} objectId - ID объекта
135
+ * @param {Object} size - Новый размер {width, height}
136
+ * @param {string} objectType - Тип объекта
137
+ */
138
+ updateObjectSize(objectId, size, objectType = null) {
139
+ const pixiObject = this.objects.get(objectId);
140
+ if (!pixiObject) return;
141
+
142
+ console.log(`🎨 Обновляем размер объекта ${objectId}, тип: ${objectType}`);
143
+
144
+ // Делегируем изменение размера объекту
145
+ const meta = pixiObject._mb || {};
146
+ if (meta.instance && typeof meta.instance.updateSize === 'function') {
147
+ meta.instance.updateSize(size);
148
+ } else if (pixiObject instanceof PIXI.Graphics) {
149
+ this._recreateGraphicsObject(pixiObject, size, objectType);
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Пересоздать Graphics объект с новым размером
155
+ * @param {PIXI.Graphics} pixiObject - PIXI Graphics объект
156
+ * @param {Object} size - Новый размер
157
+ * @param {string} objectType - Тип объекта
158
+ * @private
159
+ */
160
+ _recreateGraphicsObject(pixiObject, size, objectType = null) {
161
+ pixiObject.clear();
162
+
163
+ console.log(`🔄 Пересоздаем Graphics объект, тип: ${objectType}`);
164
+
165
+ if (objectType === 'drawing') {
166
+ this._redrawDrawingObject(pixiObject, size);
167
+ } else {
168
+ this._redrawDefaultObject(pixiObject, size);
169
+ }
170
+
171
+ // Устанавливаем pivot в центр
172
+ const pivotX = size.width / 2;
173
+ const pivotY = size.height / 2;
174
+ pixiObject.pivot.set(pivotX, pivotY);
175
+ }
176
+
177
+ /**
178
+ * Перерисовать drawing объект
179
+ * @param {PIXI.Graphics} pixiObject - PIXI Graphics объект
180
+ * @param {Object} size - Размер
181
+ * @private
182
+ */
183
+ _redrawDrawingObject(pixiObject, size) {
184
+ const meta = pixiObject._mb || {};
185
+ const props = meta.properties || {};
186
+ const color = props.strokeColor ?? 0x111827;
187
+ const widthPx = props.strokeWidth ?? 2;
188
+ const alpha = props.mode === 'marker' ? 0.6 : 1;
189
+ const points = Array.isArray(props.points) ? props.points : [];
190
+ const baseW = props.baseWidth || size.width || 1;
191
+ const baseH = props.baseHeight || size.height || 1;
192
+ const scaleX = baseW ? (size.width / baseW) : 1;
193
+ const scaleY = baseH ? (size.height / baseH) : 1;
194
+ const lineWidth = props.mode === 'marker' ? widthPx * 2 : widthPx;
195
+
196
+ pixiObject.lineStyle({
197
+ width: lineWidth,
198
+ color,
199
+ alpha,
200
+ cap: 'round',
201
+ join: 'round',
202
+ miterLimit: 2,
203
+ alignment: 0.5
204
+ });
205
+
206
+ pixiObject.blendMode = props.mode === 'marker' ? PIXI.BLEND_MODES.LIGHTEN : PIXI.BLEND_MODES.NORMAL;
207
+
208
+ if (points.length > 0) {
209
+ this._drawPoints(pixiObject, points, scaleX, scaleY);
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Нарисовать точки для drawing объекта
215
+ * @param {PIXI.Graphics} pixiObject - PIXI Graphics объект
216
+ * @param {Array} points - Массив точек
217
+ * @param {number} scaleX - Масштаб по X
218
+ * @param {number} scaleY - Масштаб по Y
219
+ * @private
220
+ */
221
+ _drawPoints(pixiObject, points, scaleX, scaleY) {
222
+ if (points.length < 3) {
223
+ pixiObject.moveTo(points[0].x * scaleX, points[0].y * scaleY);
224
+ for (let i = 1; i < points.length; i++) {
225
+ pixiObject.lineTo(points[i].x * scaleX, points[i].y * scaleY);
226
+ }
227
+ } else {
228
+ pixiObject.moveTo(points[0].x * scaleX, points[0].y * scaleY);
229
+ for (let i = 1; i < points.length - 1; i++) {
230
+ const cx = points[i].x * scaleX, cy = points[i].y * scaleY;
231
+ const nx = points[i + 1].x * scaleX, ny = points[i + 1].y * scaleY;
232
+ const mx = (cx + nx) / 2, my = (cy + ny) / 2;
233
+ pixiObject.quadraticCurveTo(cx, cy, mx, my);
234
+ }
235
+ const pen = points[points.length - 2];
236
+ const last = points[points.length - 1];
237
+ pixiObject.quadraticCurveTo(pen.x * scaleX, pen.y * scaleY, last.x * scaleX, last.y * scaleY);
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Перерисовать объект по умолчанию
243
+ * @param {PIXI.Graphics} pixiObject - PIXI Graphics объект
244
+ * @param {Object} size - Размер
245
+ * @private
246
+ */
247
+ _redrawDefaultObject(pixiObject, size) {
248
+ const borderWidth = 2;
249
+ pixiObject.lineStyle(borderWidth, 0x333333, 1);
250
+ pixiObject.beginFill(0xFFFFFF, 0.1);
251
+
252
+ const halfBorder = borderWidth / 2;
253
+ pixiObject.drawRect(halfBorder, halfBorder, size.width - borderWidth, size.height - borderWidth);
254
+ pixiObject.endFill();
255
+ }
256
+
257
+ /**
258
+ * Обновить содержимое объекта
259
+ * @param {string} objectId - ID объекта
260
+ * @param {string} content - Новое содержимое
261
+ */
262
+ updateObjectContent(objectId, content) {
263
+ const pixiObject = this.objects.get(objectId);
264
+ if (!pixiObject) return;
265
+
266
+ console.log(`🎨 Обновляем содержимое объекта ${objectId}:`, content);
267
+
268
+ // Делегируем изменение содержимого объекту
269
+ const meta = pixiObject._mb || {};
270
+ if (meta.instance) {
271
+ if (typeof meta.instance.setContent === 'function') {
272
+ meta.instance.setContent(content);
273
+ } else if (typeof meta.instance.setText === 'function') {
274
+ meta.instance.setText(content);
275
+ }
276
+ }
277
+ }
278
+
279
+ /**
280
+ * Скрыть текст объекта (используется во время редактирования)
281
+ * @param {string} objectId - ID объекта
282
+ */
283
+ hideObjectText(objectId) {
284
+ const pixiObject = this.objects.get(objectId);
285
+ if (!pixiObject) return;
286
+
287
+ const meta = pixiObject._mb || {};
288
+ if (meta.instance && typeof meta.instance.hideText === 'function') {
289
+ meta.instance.hideText();
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Показать текст объекта (используется после завершения редактирования)
295
+ * @param {string} objectId - ID объекта
296
+ */
297
+ showObjectText(objectId) {
298
+ const pixiObject = this.objects.get(objectId);
299
+ if (!pixiObject) return;
300
+
301
+ const meta = pixiObject._mb || {};
302
+ if (meta.instance && typeof meta.instance.showText === 'function') {
303
+ meta.instance.showText();
304
+ }
305
+ }
306
+
307
+ /**
308
+ * Обновить угол поворота объекта
309
+ * @param {string} objectId - ID объекта
310
+ * @param {number} angleDegrees - Угол в градусах
311
+ */
312
+ updateObjectRotation(objectId, angleDegrees) {
313
+ const pixiObject = this.objects.get(objectId);
314
+ if (!pixiObject) return;
315
+
316
+ const angleRadians = GeometryUtils.degreesToRadians(angleDegrees);
317
+ pixiObject.rotation = angleRadians;
318
+ }
319
+
320
+ /**
321
+ * Установить цвет заливки для фрейма
322
+ * @param {string} objectId - ID объекта
323
+ * @param {number} width - Ширина
324
+ * @param {number} height - Высота
325
+ * @param {number} fillColor - Цвет заливки
326
+ */
327
+ setFrameFill(objectId, width, height, fillColor = 0xFFFFFF) {
328
+ const pixiObject = this.objects.get(objectId);
329
+ if (!pixiObject || !(pixiObject instanceof PIXI.Graphics)) return;
330
+
331
+ const meta = pixiObject._mb || {};
332
+ if (meta.type !== 'frame') return;
333
+
334
+ if (meta.instance) {
335
+ meta.instance.setFill(fillColor);
336
+ }
337
+ }
338
+
339
+ /**
340
+ * Получить объект по ID
341
+ * @param {string} objectId - ID объекта
342
+ * @returns {PIXI.DisplayObject|null} PIXI объект или null
343
+ */
344
+ getObject(objectId) {
345
+ return this.objects.get(objectId) || null;
346
+ }
347
+
348
+ /**
349
+ * Получить все объекты
350
+ * @returns {Map} Map всех объектов
351
+ */
352
+ getAllObjects() {
353
+ return this.objects;
354
+ }
355
+
356
+ /**
357
+ * Очистить все объекты
358
+ */
359
+ clearAllObjects() {
360
+ // Слой очищается через PixiEngine, здесь только очищаем Map
361
+ this.objects.clear();
362
+ }
363
+ }
@@ -0,0 +1,140 @@
1
+ import * as PIXI from 'pixi.js';
2
+
3
+ /**
4
+ * Основной рендерер PIXI приложения
5
+ * Отвечает за инициализацию PIXI и базовую настройку
6
+ */
7
+ export class PixiRenderer {
8
+ constructor(container, options) {
9
+ this.container = container;
10
+ this.options = options;
11
+ this.app = null;
12
+ }
13
+
14
+ /**
15
+ * Инициализировать PIXI приложение
16
+ * @returns {Promise<void>}
17
+ */
18
+ async init() {
19
+ this.app = new PIXI.Application({
20
+ width: this.options.width,
21
+ height: this.options.height,
22
+ backgroundColor: this.options.backgroundColor,
23
+ antialias: true
24
+ });
25
+
26
+ this.container.appendChild(this.app.view);
27
+ }
28
+
29
+ /**
30
+ * Получить PIXI приложение
31
+ * @returns {PIXI.Application} PIXI приложение
32
+ */
33
+ getApp() {
34
+ return this.app;
35
+ }
36
+
37
+ /**
38
+ * Получить главную сцену
39
+ * @returns {PIXI.Container} Главная сцена
40
+ */
41
+ getStage() {
42
+ return this.app ? this.app.stage : null;
43
+ }
44
+
45
+ /**
46
+ * Получить view (canvas элемент)
47
+ * @returns {HTMLCanvasElement} Canvas элемент
48
+ */
49
+ getView() {
50
+ return this.app ? this.app.view : null;
51
+ }
52
+
53
+ /**
54
+ * Изменить размер приложения
55
+ * @param {number} width - Новая ширина
56
+ * @param {number} height - Новая высота
57
+ */
58
+ resize(width, height) {
59
+ if (this.app) {
60
+ this.app.renderer.resize(width, height);
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Получить текущий размер
66
+ * @returns {{width: number, height: number}} Размер приложения
67
+ */
68
+ getSize() {
69
+ if (this.app) {
70
+ return {
71
+ width: this.app.renderer.width,
72
+ height: this.app.renderer.height
73
+ };
74
+ }
75
+ return { width: 0, height: 0 };
76
+ }
77
+
78
+ /**
79
+ * Установить цвет фона
80
+ * @param {number} color - Цвет фона
81
+ */
82
+ setBackgroundColor(color) {
83
+ if (this.app) {
84
+ this.app.renderer.backgroundColor = color;
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Получить цвет фона
90
+ * @returns {number} Цвет фона
91
+ */
92
+ getBackgroundColor() {
93
+ return this.app ? this.app.renderer.backgroundColor : 0x000000;
94
+ }
95
+
96
+ /**
97
+ * Включить/выключить сглаживание
98
+ * @param {boolean} enabled - Включить сглаживание
99
+ */
100
+ setAntialias(enabled) {
101
+ if (this.app) {
102
+ this.app.renderer.antialias = enabled;
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Получить состояние сглаживания
108
+ * @returns {boolean} Включено ли сглаживание
109
+ */
110
+ getAntialias() {
111
+ return this.app ? this.app.renderer.antialias : false;
112
+ }
113
+
114
+ /**
115
+ * Очистить сцену
116
+ */
117
+ clearStage() {
118
+ if (this.app && this.app.stage) {
119
+ this.app.stage.removeChildren();
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Уничтожить приложение
125
+ */
126
+ destroy() {
127
+ if (this.app) {
128
+ this.app.destroy(true);
129
+ this.app = null;
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Проверить, инициализировано ли приложение
135
+ * @returns {boolean} true если приложение инициализировано
136
+ */
137
+ isInitialized() {
138
+ return this.app !== null;
139
+ }
140
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Экспорт всех классов рендеринга
3
+ */
4
+
5
+ export { PixiRenderer } from './PixiRenderer.js';
6
+ export { LayerManager } from './LayerManager.js';
7
+ export { ObjectRenderer } from './ObjectRenderer.js';
8
+ export { HitTestManager } from './HitTestManager.js';
9
+ export { GeometryUtils } from './GeometryUtils.js';
@@ -0,0 +1,164 @@
1
+ import * as PIXI from 'pixi.js';
2
+
3
+ /**
4
+ * Базовый класс для всех типов сеток
5
+ */
6
+ export class BaseGrid {
7
+ constructor(options = {}) {
8
+ this.type = 'base';
9
+ this.enabled = options.enabled ?? false;
10
+ this.size = options.size || 20;
11
+ this.color = options.color || 0xE0E0E0;
12
+ this.opacity = options.opacity || 0.5;
13
+ this.lineWidth = options.lineWidth || 1;
14
+
15
+ // Размеры области отрисовки
16
+ this.width = options.width || 1920;
17
+ this.height = options.height || 1080;
18
+
19
+ // PIXI графика
20
+ this.graphics = new PIXI.Graphics();
21
+ this.graphics.alpha = this.opacity;
22
+
23
+ // Настройки привязки
24
+ this.snapEnabled = options.snapEnabled ?? true;
25
+ this.snapTolerance = options.snapTolerance || 10;
26
+ }
27
+
28
+ /**
29
+ * Создает визуальное представление сетки
30
+ * Должен быть переопределен в дочерних классах
31
+ */
32
+ createVisual() {
33
+ throw new Error('createVisual() должен быть реализован в дочернем классе');
34
+ }
35
+
36
+ /**
37
+ * Обновляет визуальное представление
38
+ */
39
+ updateVisual() {
40
+ this.graphics.clear();
41
+ if (this.enabled) {
42
+ this.createVisual();
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Привязывает точку к сетке
48
+ * @param {number} x - координата X
49
+ * @param {number} y - координата Y
50
+ * @returns {Object} - новые координаты {x, y}
51
+ */
52
+ snapToGrid(x, y) {
53
+ if (!this.snapEnabled) {
54
+ return { x, y };
55
+ }
56
+
57
+ return this.calculateSnapPoint(x, y);
58
+ }
59
+
60
+ /**
61
+ * Вычисляет точку привязки к сетке
62
+ * Должен быть переопределен в дочерних классах
63
+ */
64
+ calculateSnapPoint(x, y) {
65
+ return { x, y };
66
+ }
67
+
68
+ /**
69
+ * Находит ближайшие линии сетки
70
+ * @param {number} x - координата X
71
+ * @param {number} y - координата Y
72
+ * @returns {Object} - информация о ближайших линиях
73
+ */
74
+ findNearestGridLines(x, y) {
75
+ return {
76
+ vertical: Math.round(x / this.size) * this.size,
77
+ horizontal: Math.round(y / this.size) * this.size,
78
+ distance: {
79
+ x: Math.abs(x % this.size),
80
+ y: Math.abs(y % this.size)
81
+ }
82
+ };
83
+ }
84
+
85
+ /**
86
+ * Проверяет, находится ли точка в зоне привязки
87
+ */
88
+ isInSnapZone(x, y) {
89
+ const nearest = this.findNearestGridLines(x, y);
90
+ return nearest.distance.x <= this.snapTolerance ||
91
+ nearest.distance.y <= this.snapTolerance;
92
+ }
93
+
94
+ /**
95
+ * Включает/выключает сетку
96
+ */
97
+ setEnabled(enabled) {
98
+ this.enabled = enabled;
99
+ this.updateVisual();
100
+ }
101
+
102
+ /**
103
+ * Устанавливает размер сетки
104
+ */
105
+ setSize(size) {
106
+ this.size = Math.max(1, size);
107
+ this.updateVisual();
108
+ }
109
+
110
+ /**
111
+ * Устанавливает цвет сетки
112
+ */
113
+ setColor(color) {
114
+ this.color = color;
115
+ this.updateVisual();
116
+ }
117
+
118
+ /**
119
+ * Устанавливает прозрачность
120
+ */
121
+ setOpacity(opacity) {
122
+ this.opacity = Math.max(0, Math.min(1, opacity));
123
+ this.graphics.alpha = this.opacity;
124
+ }
125
+
126
+ /**
127
+ * Изменяет размеры области отрисовки
128
+ */
129
+ resize(width, height) {
130
+ this.width = width;
131
+ this.height = height;
132
+ this.updateVisual();
133
+ }
134
+
135
+ /**
136
+ * Возвращает PIXI объект для рендеринга
137
+ */
138
+ getPixiObject() {
139
+ return this.graphics;
140
+ }
141
+
142
+ /**
143
+ * Сериализует настройки сетки
144
+ */
145
+ serialize() {
146
+ return {
147
+ type: this.type,
148
+ enabled: this.enabled,
149
+ size: this.size,
150
+ color: this.color,
151
+ opacity: this.opacity,
152
+ lineWidth: this.lineWidth,
153
+ snapEnabled: this.snapEnabled,
154
+ snapTolerance: this.snapTolerance
155
+ };
156
+ }
157
+
158
+ /**
159
+ * Очищает ресурсы
160
+ */
161
+ destroy() {
162
+ this.graphics.destroy();
163
+ }
164
+ }