@teachinglab/omd 0.7.17 → 0.7.19
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.
|
@@ -48,7 +48,26 @@ export class CanvasConfig {
|
|
|
48
48
|
danger: '#dc3545',
|
|
49
49
|
...options.theme
|
|
50
50
|
};
|
|
51
|
-
|
|
51
|
+
|
|
52
|
+
// Selection / resize-handle styling
|
|
53
|
+
this.selection = {
|
|
54
|
+
// Selection border
|
|
55
|
+
border: {
|
|
56
|
+
color: options.selection?.border?.color ?? '#007bff',
|
|
57
|
+
width: options.selection?.border?.width ?? 2,
|
|
58
|
+
dasharray: options.selection?.border?.dasharray ?? '4,2',
|
|
59
|
+
cornerRadius: options.selection?.border?.cornerRadius ?? undefined,
|
|
60
|
+
},
|
|
61
|
+
// Resize handles
|
|
62
|
+
handle: {
|
|
63
|
+
size: options.selection?.handle?.size ?? 8,
|
|
64
|
+
color: options.selection?.handle?.color ?? '#007bff',
|
|
65
|
+
strokeColor: options.selection?.handle?.strokeColor ?? '#ffffff',
|
|
66
|
+
strokeWidth: options.selection?.handle?.strokeWidth ?? 1,
|
|
67
|
+
cornerRadius: options.selection?.handle?.cornerRadius ?? 1,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
52
71
|
// Validate configuration
|
|
53
72
|
this._validate();
|
|
54
73
|
}
|
|
@@ -156,7 +175,8 @@ export class CanvasConfig {
|
|
|
156
175
|
enableKeyboardShortcuts: this.enableKeyboardShortcuts,
|
|
157
176
|
enableMultiTouch: this.enableMultiTouch,
|
|
158
177
|
tools: JSON.parse(JSON.stringify(this.tools)),
|
|
159
|
-
theme: { ...this.theme }
|
|
178
|
+
theme: { ...this.theme },
|
|
179
|
+
selection: JSON.parse(JSON.stringify(this.selection))
|
|
160
180
|
});
|
|
161
181
|
}
|
|
162
182
|
|
|
@@ -178,7 +198,8 @@ export class CanvasConfig {
|
|
|
178
198
|
enableKeyboardShortcuts: this.enableKeyboardShortcuts,
|
|
179
199
|
enableMultiTouch: this.enableMultiTouch,
|
|
180
200
|
tools: this.tools,
|
|
181
|
-
theme: this.theme
|
|
201
|
+
theme: this.theme,
|
|
202
|
+
selection: this.selection
|
|
182
203
|
};
|
|
183
204
|
}
|
|
184
205
|
|
package/canvas/core/omdCanvas.js
CHANGED
|
@@ -199,6 +199,13 @@ export class omdCanvas {
|
|
|
199
199
|
if (this.config.enableFocusFrames) {
|
|
200
200
|
this.focusFrameManager = new FocusFrameManager(this);
|
|
201
201
|
}
|
|
202
|
+
|
|
203
|
+
// Apply any selection styles defined in config to the ResizeHandleManager
|
|
204
|
+
// (PointerTool registers the manager on canvas.resizeHandleManager during its constructor)
|
|
205
|
+
if (this.resizeHandleManager && this.config.selection) {
|
|
206
|
+
this.resizeHandleManager.setSelectionStyle(this.config.selection.border);
|
|
207
|
+
this.resizeHandleManager.setHandleStyle(this.config.selection.handle);
|
|
208
|
+
}
|
|
202
209
|
}
|
|
203
210
|
|
|
204
211
|
/**
|
|
@@ -547,4 +554,42 @@ export class omdCanvas {
|
|
|
547
554
|
this.isDestroyed = true;
|
|
548
555
|
this.emit('destroyed');
|
|
549
556
|
}
|
|
550
|
-
|
|
557
|
+
/**
|
|
558
|
+
* Style the selection border shown when an OMD visual is selected.
|
|
559
|
+
* Can be called at any time; changes take effect immediately.
|
|
560
|
+
* @param {Object} style
|
|
561
|
+
* @param {string} [style.color] - Border stroke colour (e.g. '#007bff')
|
|
562
|
+
* @param {number} [style.width] - Border stroke width in px
|
|
563
|
+
* @param {string} [style.dasharray] - SVG stroke-dasharray (e.g. '4,2' or 'none')
|
|
564
|
+
* @param {number} [style.cornerRadius] - Border corner radius
|
|
565
|
+
*/
|
|
566
|
+
setSelectionStyle(style = {}) {
|
|
567
|
+
if (this.resizeHandleManager) {
|
|
568
|
+
this.resizeHandleManager.setSelectionStyle(style);
|
|
569
|
+
}
|
|
570
|
+
// Persist into config so clones / serialisation reflect the change
|
|
571
|
+
if (this.config.selection?.border) {
|
|
572
|
+
Object.assign(this.config.selection.border, style);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Style the resize handles shown when an OMD visual is selected.
|
|
578
|
+
* Can be called at any time; changes take effect immediately.
|
|
579
|
+
* @param {Object} style
|
|
580
|
+
* @param {number} [style.size] - Handle size in px
|
|
581
|
+
* @param {string} [style.color] - Handle fill colour
|
|
582
|
+
* @param {string} [style.strokeColor] - Handle border colour
|
|
583
|
+
* @param {number} [style.strokeWidth] - Handle border width in px
|
|
584
|
+
* @param {number} [style.cornerRadius] - Handle corner radius (0 = square, size/2 = circle)
|
|
585
|
+
*/
|
|
586
|
+
setHandleStyle(style = {}) {
|
|
587
|
+
if (this.resizeHandleManager) {
|
|
588
|
+
this.resizeHandleManager.setHandleStyle(style);
|
|
589
|
+
}
|
|
590
|
+
// Persist into config
|
|
591
|
+
if (this.config.selection?.handle) {
|
|
592
|
+
Object.assign(this.config.selection.handle, style);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
@@ -24,7 +24,7 @@ export class ResizeHandleManager {
|
|
|
24
24
|
// Resize constraints
|
|
25
25
|
this.minSize = 20;
|
|
26
26
|
this.maxSize = 800;
|
|
27
|
-
this.maintainAspectRatio =
|
|
27
|
+
this.maintainAspectRatio = true; // Always maintain aspect ratio by default
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/**
|
|
@@ -195,25 +195,23 @@ export class ResizeHandleManager {
|
|
|
195
195
|
break;
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
-
// Maintain aspect ratio
|
|
198
|
+
// Maintain aspect ratio — use the larger delta to drive the resize
|
|
199
199
|
if (this.maintainAspectRatio) {
|
|
200
200
|
const aspectRatio = startWidth / startHeight;
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
201
|
+
const widthChange = Math.abs(newWidth - startWidth);
|
|
202
|
+
const heightChange = Math.abs(newHeight - startHeight);
|
|
203
|
+
|
|
204
|
+
if (widthChange >= heightChange) {
|
|
204
205
|
newHeight = newWidth / aspectRatio;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
// Correct offsetY for top-anchored corners
|
|
207
|
+
if (handle.type.includes('n')) {
|
|
208
|
+
offsetY = startHeight - newHeight;
|
|
209
|
+
}
|
|
208
210
|
} else {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if (widthChange > heightChange) {
|
|
214
|
-
newHeight = newWidth / aspectRatio;
|
|
215
|
-
} else {
|
|
216
|
-
newWidth = newHeight * aspectRatio;
|
|
211
|
+
newWidth = newHeight * aspectRatio;
|
|
212
|
+
// Correct offsetX for left-anchored corners
|
|
213
|
+
if (handle.type.includes('w')) {
|
|
214
|
+
offsetX = startWidth - newWidth;
|
|
217
215
|
}
|
|
218
216
|
}
|
|
219
217
|
}
|
|
@@ -264,6 +262,68 @@ export class ResizeHandleManager {
|
|
|
264
262
|
this.resizeData = null;
|
|
265
263
|
}
|
|
266
264
|
|
|
265
|
+
/**
|
|
266
|
+
* Style the selection border.
|
|
267
|
+
* @param {Object} style
|
|
268
|
+
* @param {string} [style.color] - Stroke colour of the border (e.g. '#007bff')
|
|
269
|
+
* @param {number} [style.width] - Stroke width in px
|
|
270
|
+
* @param {string} [style.dasharray] - SVG stroke-dasharray value (e.g. '4,2' or 'none')
|
|
271
|
+
* @param {number} [style.cornerRadius]- rx/ry corner radius of the border rect
|
|
272
|
+
*/
|
|
273
|
+
setSelectionStyle({ color, width, dasharray, cornerRadius } = {}) {
|
|
274
|
+
if (color !== undefined) this.selectionBorderColor = color;
|
|
275
|
+
if (width !== undefined) this.selectionBorderWidth = width;
|
|
276
|
+
if (dasharray !== undefined) this.selectionBorderDasharray = dasharray;
|
|
277
|
+
if (cornerRadius !== undefined) this.selectionBorderCornerRadius = cornerRadius;
|
|
278
|
+
|
|
279
|
+
// Re-apply to live border if one exists
|
|
280
|
+
if (this.selectionBorder) {
|
|
281
|
+
this.selectionBorder.setAttribute('stroke', this.selectionBorderColor);
|
|
282
|
+
this.selectionBorder.setAttribute('stroke-width', this.selectionBorderWidth);
|
|
283
|
+
this.selectionBorder.setAttribute('stroke-dasharray',
|
|
284
|
+
this.selectionBorderDasharray !== undefined ? this.selectionBorderDasharray : '4,2');
|
|
285
|
+
if (this.selectionBorderCornerRadius !== undefined) {
|
|
286
|
+
this.selectionBorder.setAttribute('rx', this.selectionBorderCornerRadius);
|
|
287
|
+
this.selectionBorder.setAttribute('ry', this.selectionBorderCornerRadius);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Style the resize handles.
|
|
294
|
+
* @param {Object} style
|
|
295
|
+
* @param {number} [style.size] - Handle square size in px
|
|
296
|
+
* @param {string} [style.color] - Fill colour
|
|
297
|
+
* @param {string} [style.strokeColor] - Border colour
|
|
298
|
+
* @param {number} [style.strokeWidth] - Border width
|
|
299
|
+
* @param {number} [style.cornerRadius] - rx/ry corner radius (0 = square, size/2 = circle)
|
|
300
|
+
*/
|
|
301
|
+
setHandleStyle({ size, color, strokeColor, strokeWidth, cornerRadius } = {}) {
|
|
302
|
+
if (size !== undefined) this.handleSize = size;
|
|
303
|
+
if (color !== undefined) this.handleColor = color;
|
|
304
|
+
if (strokeColor !== undefined) this.handleStrokeColor = strokeColor;
|
|
305
|
+
if (strokeWidth !== undefined) this.handleStrokeWidth = strokeWidth;
|
|
306
|
+
if (cornerRadius !== undefined) this.handleCornerRadius = cornerRadius;
|
|
307
|
+
|
|
308
|
+
// Re-apply to any live handles
|
|
309
|
+
const radius = this.handleCornerRadius !== undefined
|
|
310
|
+
? this.handleCornerRadius
|
|
311
|
+
: 1;
|
|
312
|
+
this.handles.forEach(h => {
|
|
313
|
+
h.element.setAttribute('width', this.handleSize);
|
|
314
|
+
h.element.setAttribute('height', this.handleSize);
|
|
315
|
+
h.element.setAttribute('fill', this.handleColor);
|
|
316
|
+
h.element.setAttribute('stroke', this.handleStrokeColor);
|
|
317
|
+
h.element.setAttribute('stroke-width', this.handleStrokeWidth);
|
|
318
|
+
h.element.setAttribute('rx', radius);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// Reposition so centres stay correct after a size change
|
|
322
|
+
if (this.handles.length > 0) {
|
|
323
|
+
this._updateHandlePositions();
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
267
327
|
/**
|
|
268
328
|
* Update handle positions for currently selected element (called externally)
|
|
269
329
|
*/
|
|
@@ -304,7 +364,12 @@ export class ResizeHandleManager {
|
|
|
304
364
|
this.selectionBorder.setAttribute('fill', 'none');
|
|
305
365
|
this.selectionBorder.setAttribute('stroke', this.selectionBorderColor);
|
|
306
366
|
this.selectionBorder.setAttribute('stroke-width', this.selectionBorderWidth);
|
|
307
|
-
this.selectionBorder.setAttribute('stroke-dasharray',
|
|
367
|
+
this.selectionBorder.setAttribute('stroke-dasharray',
|
|
368
|
+
this.selectionBorderDasharray !== undefined ? this.selectionBorderDasharray : '4,2');
|
|
369
|
+
if (this.selectionBorderCornerRadius !== undefined) {
|
|
370
|
+
this.selectionBorder.setAttribute('rx', this.selectionBorderCornerRadius);
|
|
371
|
+
this.selectionBorder.setAttribute('ry', this.selectionBorderCornerRadius);
|
|
372
|
+
}
|
|
308
373
|
this.selectionBorder.style.pointerEvents = 'none';
|
|
309
374
|
this.selectionBorder.classList.add('omd-selection-border');
|
|
310
375
|
|
|
@@ -346,15 +411,12 @@ export class ResizeHandleManager {
|
|
|
346
411
|
_createResizeHandles() {
|
|
347
412
|
if (!this.selectedElement) return;
|
|
348
413
|
|
|
414
|
+
// Only corner handles — mid-edge handles would break aspect ratio
|
|
349
415
|
const handleTypes = [
|
|
350
416
|
{ type: 'nw', pos: 'top-left' },
|
|
351
|
-
{ type: 'n', pos: 'top-center' },
|
|
352
417
|
{ type: 'ne', pos: 'top-right' },
|
|
353
|
-
{ type: 'e', pos: 'middle-right' },
|
|
354
418
|
{ type: 'se', pos: 'bottom-right' },
|
|
355
|
-
{ type: '
|
|
356
|
-
{ type: 'sw', pos: 'bottom-left' },
|
|
357
|
-
{ type: 'w', pos: 'middle-left' }
|
|
419
|
+
{ type: 'sw', pos: 'bottom-left' }
|
|
358
420
|
];
|
|
359
421
|
|
|
360
422
|
handleTypes.forEach(handleDef => {
|
|
@@ -371,12 +433,13 @@ export class ResizeHandleManager {
|
|
|
371
433
|
_createHandle(type, position) {
|
|
372
434
|
const handle = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
373
435
|
|
|
436
|
+
const cornerRadius = this.handleCornerRadius !== undefined ? this.handleCornerRadius : 1;
|
|
374
437
|
handle.setAttribute('width', this.handleSize);
|
|
375
438
|
handle.setAttribute('height', this.handleSize);
|
|
376
439
|
handle.setAttribute('fill', this.handleColor);
|
|
377
440
|
handle.setAttribute('stroke', this.handleStrokeColor);
|
|
378
441
|
handle.setAttribute('stroke-width', this.handleStrokeWidth);
|
|
379
|
-
handle.setAttribute('rx',
|
|
442
|
+
handle.setAttribute('rx', cornerRadius);
|
|
380
443
|
handle.style.cursor = this.getCursorForHandle(type);
|
|
381
444
|
handle.classList.add('resize-handle', `resize-handle-${type}`);
|
|
382
445
|
|
|
@@ -972,6 +972,15 @@ export class PointerTool extends Tool {
|
|
|
972
972
|
selectionLayer.removeChild(selectionLayer.firstChild);
|
|
973
973
|
}
|
|
974
974
|
|
|
975
|
+
// If a single OMD element was directly clicked, ResizeHandleManager already draws
|
|
976
|
+
// its own selection border + handles — don't draw a second box on top of it.
|
|
977
|
+
// For drag-box selections (no active resizeHandleManager element), always draw the box.
|
|
978
|
+
if (this.selectedSegments.size === 0 &&
|
|
979
|
+
this.selectedOMDElements.size > 0 &&
|
|
980
|
+
this.resizeHandleManager?.selectedElement !== null) {
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
|
|
975
984
|
const bounds = this._getSelectionBounds();
|
|
976
985
|
if (!bounds) return;
|
|
977
986
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teachinglab/omd",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.19",
|
|
4
4
|
"description": "omd",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"omd/",
|
|
17
17
|
"docs/",
|
|
18
18
|
"canvas/",
|
|
19
|
-
"jsvg/"
|
|
19
|
+
"jsvg/",
|
|
20
20
|
"npm-docs/",
|
|
21
21
|
"README.md"
|
|
22
22
|
],
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@teachinglab/jsvg": "^0.1.1",
|
|
36
36
|
"mathjs": "^14.5.2",
|
|
37
|
-
"openai": "6.6.0"
|
|
37
|
+
"openai": "6.6.0"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"dev": "npm run build:docs && vite",
|
package/src/json-schemas.md
CHANGED
|
@@ -216,7 +216,12 @@ This document provides schemas and examples for the `loadFromJSON` method used i
|
|
|
216
216
|
"backgroundCornerRadius": "number",
|
|
217
217
|
"backgroundOpacity": "number",
|
|
218
218
|
"showBackground": "boolean",
|
|
219
|
-
"alternatingRowColors": ["array"]
|
|
219
|
+
"alternatingRowColors": ["array - e.g. ['#EEEEEE', '#FFFFFF']; index 0 used for header row"],
|
|
220
|
+
"headerBackgroundColor": "string (used when alternatingRowColors is not set)",
|
|
221
|
+
"cellBackgroundColor": "string (used when alternatingRowColors is not set)",
|
|
222
|
+
"evenRowColor": "string (legacy)",
|
|
223
|
+
"oddRowColor": "string (legacy)",
|
|
224
|
+
"alternatingRowOpacity": "number (legacy)"
|
|
220
225
|
}
|
|
221
226
|
```
|
|
222
227
|
|
|
@@ -243,7 +248,7 @@ This document provides schemas and examples for the `loadFromJSON` method used i
|
|
|
243
248
|
"backgroundCornerRadius": 15,
|
|
244
249
|
"backgroundOpacity": 1.0,
|
|
245
250
|
"showBackground": true,
|
|
246
|
-
"alternatingRowColors": ["#
|
|
251
|
+
"alternatingRowColors": ["#EEEEEE", "#FFFFFF"]
|
|
247
252
|
}
|
|
248
253
|
```
|
|
249
254
|
|
|
@@ -295,23 +300,149 @@ This document provides schemas and examples for the `loadFromJSON` method used i
|
|
|
295
300
|
|
|
296
301
|
## 8. `omdShapes`
|
|
297
302
|
|
|
298
|
-
`omdShapes`
|
|
303
|
+
`omdShapes` is a module containing several individual geometric shape classes. Each is used by `omdCoordinatePlane`'s `shapeSet` array (via the `omdType` field) and can also be instantiated independently.
|
|
299
304
|
|
|
300
|
-
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
### 8a. `omdRightTriangle`
|
|
308
|
+
|
|
309
|
+
#### Schema
|
|
301
310
|
```json
|
|
302
311
|
{
|
|
303
|
-
"
|
|
304
|
-
"
|
|
305
|
-
"
|
|
312
|
+
"horizontalLeg": "number (default: 5)",
|
|
313
|
+
"verticalLeg": "number (default: 10)",
|
|
314
|
+
"angleA": "number (optional) - angle in degrees; used with hypotenuse to compute legs",
|
|
315
|
+
"hypotenuse": "number (optional)",
|
|
316
|
+
"unitScale": "number (default: 10) - pixels per unit",
|
|
317
|
+
"showLabels": "boolean (default: false)"
|
|
306
318
|
}
|
|
307
319
|
```
|
|
308
320
|
|
|
309
|
-
|
|
321
|
+
#### Example
|
|
310
322
|
```json
|
|
311
323
|
{
|
|
312
|
-
"
|
|
313
|
-
"
|
|
314
|
-
"
|
|
324
|
+
"omdType": "rightTriangle",
|
|
325
|
+
"horizontalLeg": 3,
|
|
326
|
+
"verticalLeg": 4,
|
|
327
|
+
"unitScale": 20,
|
|
328
|
+
"showLabels": true
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
### 8b. `omdIsoscelesTriangle`
|
|
335
|
+
|
|
336
|
+
#### Schema
|
|
337
|
+
```json
|
|
338
|
+
{
|
|
339
|
+
"base": "number (default: 5)",
|
|
340
|
+
"height": "number (default: 10)",
|
|
341
|
+
"unitScale": "number (default: 10)",
|
|
342
|
+
"showLabels": "boolean (default: false)"
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
#### Example
|
|
347
|
+
```json
|
|
348
|
+
{
|
|
349
|
+
"omdType": "isoscelesTriangle",
|
|
350
|
+
"base": 6,
|
|
351
|
+
"height": 8,
|
|
352
|
+
"unitScale": 15
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
### 8c. `omdRectangle`
|
|
359
|
+
|
|
360
|
+
#### Schema
|
|
361
|
+
```json
|
|
362
|
+
{
|
|
363
|
+
"width": "number (default: 10)",
|
|
364
|
+
"height": "number (default: 10)",
|
|
365
|
+
"unitScale": "number (default: 10)",
|
|
366
|
+
"showLabels": "boolean (default: false)"
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
#### Example
|
|
371
|
+
```json
|
|
372
|
+
{
|
|
373
|
+
"omdType": "rectangle",
|
|
374
|
+
"width": 5,
|
|
375
|
+
"height": 3,
|
|
376
|
+
"unitScale": 20,
|
|
377
|
+
"showLabels": true
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
### 8d. `omdEllipse`
|
|
384
|
+
|
|
385
|
+
#### Schema
|
|
386
|
+
```json
|
|
387
|
+
{
|
|
388
|
+
"width": "number (default: 10)",
|
|
389
|
+
"height": "number (default: 5)",
|
|
390
|
+
"unitScale": "number (default: 10)"
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
#### Example
|
|
395
|
+
```json
|
|
396
|
+
{
|
|
397
|
+
"omdType": "ellipse",
|
|
398
|
+
"width": 8,
|
|
399
|
+
"height": 4,
|
|
400
|
+
"unitScale": 12
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
### 8e. `omdCircle`
|
|
407
|
+
|
|
408
|
+
#### Schema
|
|
409
|
+
```json
|
|
410
|
+
{
|
|
411
|
+
"radius": "number (default: 5)",
|
|
412
|
+
"unitScale": "number (default: 10)"
|
|
413
|
+
}
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
#### Example
|
|
417
|
+
```json
|
|
418
|
+
{
|
|
419
|
+
"omdType": "circle",
|
|
420
|
+
"radius": 4,
|
|
421
|
+
"unitScale": 15
|
|
422
|
+
}
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
### 8f. `omdRegularPolygon`
|
|
428
|
+
|
|
429
|
+
#### Schema
|
|
430
|
+
```json
|
|
431
|
+
{
|
|
432
|
+
"radius": "number (default: 5)",
|
|
433
|
+
"numberOfSides": "number (default: 5)",
|
|
434
|
+
"unitScale": "number (default: 10)",
|
|
435
|
+
"showLabels": "boolean (default: false)"
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
#### Example
|
|
440
|
+
```json
|
|
441
|
+
{
|
|
442
|
+
"omdType": "regularPolygon",
|
|
443
|
+
"radius": 5,
|
|
444
|
+
"numberOfSides": 6,
|
|
445
|
+
"unitScale": 18
|
|
315
446
|
}
|
|
316
447
|
```
|
|
317
448
|
|
|
@@ -346,14 +477,16 @@ This document provides schemas and examples for the `loadFromJSON` method used i
|
|
|
346
477
|
### Schema
|
|
347
478
|
```json
|
|
348
479
|
{
|
|
349
|
-
"valueA": "number",
|
|
350
|
-
"valueB": "number",
|
|
351
|
-
"
|
|
352
|
-
"
|
|
480
|
+
"valueA": "number - filled/first portion (alias: numerator)",
|
|
481
|
+
"valueB": "number - unfilled/second portion",
|
|
482
|
+
"numerator": "number (optional alias for valueA)",
|
|
483
|
+
"denominator": "number (optional) - when provided with numerator, sets valueA=numerator and valueB=denominator-numerator",
|
|
484
|
+
"renderType": "string - 'pie' | 'dots' | 'dot' | 'tile' | 'bar' (default: 'pie')",
|
|
485
|
+
"size": "string - 'small' | 'medium' | 'large' (default: 'large')"
|
|
353
486
|
}
|
|
354
487
|
```
|
|
355
488
|
|
|
356
|
-
### Example
|
|
489
|
+
### Example (basic)
|
|
357
490
|
```json
|
|
358
491
|
{
|
|
359
492
|
"valueA": 3,
|
|
@@ -363,6 +496,16 @@ This document provides schemas and examples for the `loadFromJSON` method used i
|
|
|
363
496
|
}
|
|
364
497
|
```
|
|
365
498
|
|
|
499
|
+
### Example (fraction form)
|
|
500
|
+
```json
|
|
501
|
+
{
|
|
502
|
+
"numerator": 2,
|
|
503
|
+
"denominator": 5,
|
|
504
|
+
"renderType": "dots",
|
|
505
|
+
"size": "medium"
|
|
506
|
+
}
|
|
507
|
+
```
|
|
508
|
+
|
|
366
509
|
---
|
|
367
510
|
|
|
368
511
|
## 11. `omdProblem`
|
|
@@ -465,6 +608,7 @@ This document provides schemas and examples for the `loadFromJSON` method used i
|
|
|
465
608
|
```json
|
|
466
609
|
{
|
|
467
610
|
"title": "string (optional)",
|
|
611
|
+
"label": "string (optional) - secondary label text",
|
|
468
612
|
"min": "number (required)",
|
|
469
613
|
"max": "number (required)",
|
|
470
614
|
"increment": "number (optional, default: 1)",
|
|
@@ -618,10 +762,14 @@ This document provides schemas and examples for the `loadFromJSON` method used i
|
|
|
618
762
|
### Schema
|
|
619
763
|
```json
|
|
620
764
|
{
|
|
621
|
-
"graphEquations": [
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
"
|
|
765
|
+
"graphEquations": [
|
|
766
|
+
"array of objects: { equation, color, strokeWidth, domain: { min, max }, label, labelAtX, labelPosition: 'above'|'below'|'left'|'right' }"
|
|
767
|
+
],
|
|
768
|
+
"lineSegments": [
|
|
769
|
+
"array of objects: { point1: [x, y], point2: [x, y], color, strokeWidth }"
|
|
770
|
+
],
|
|
771
|
+
"dotValues": ["array of [x, y] or [x, y, color] tuples"],
|
|
772
|
+
"shapeSet": ["array of shape objects with omdType (see omdShapes)"],
|
|
625
773
|
"xMin": "number",
|
|
626
774
|
"xMax": "number",
|
|
627
775
|
"yMin": "number",
|
|
@@ -629,7 +777,7 @@ This document provides schemas and examples for the `loadFromJSON` method used i
|
|
|
629
777
|
"xLabel": "string",
|
|
630
778
|
"yLabel": "string",
|
|
631
779
|
"axisLabelOffsetPx": "number",
|
|
632
|
-
"size": "string",
|
|
780
|
+
"size": "string - 'small' | 'medium' | 'large'",
|
|
633
781
|
"tickInterval": "number",
|
|
634
782
|
"forceAllTickLabels": "boolean",
|
|
635
783
|
"tickLabelOffsetPx": "number",
|
|
@@ -645,14 +793,22 @@ This document provides schemas and examples for the `loadFromJSON` method used i
|
|
|
645
793
|
```json
|
|
646
794
|
{
|
|
647
795
|
"graphEquations": [
|
|
648
|
-
{
|
|
796
|
+
{
|
|
797
|
+
"equation": "y = x^2",
|
|
798
|
+
"color": "blue",
|
|
799
|
+
"strokeWidth": 2,
|
|
800
|
+
"domain": { "min": -5, "max": 5 },
|
|
801
|
+
"label": "f(x) = x²",
|
|
802
|
+
"labelAtX": 2,
|
|
803
|
+
"labelPosition": "above"
|
|
804
|
+
}
|
|
649
805
|
],
|
|
650
806
|
"lineSegments": [
|
|
651
807
|
{ "point1": [0, 0], "point2": [1, 1], "color": "red", "strokeWidth": 2 }
|
|
652
808
|
],
|
|
653
809
|
"dotValues": [[0, 0, "green"], [1, 1, "blue"]],
|
|
654
810
|
"shapeSet": [
|
|
655
|
-
{ "omdType": "circle", "radius":
|
|
811
|
+
{ "omdType": "circle", "radius": 1, "unitScale": 15 }
|
|
656
812
|
],
|
|
657
813
|
"xMin": -5,
|
|
658
814
|
"xMax": 5,
|
|
@@ -682,7 +838,8 @@ This document provides schemas and examples for the `loadFromJSON` method used i
|
|
|
682
838
|
### Preferred schema (string form)
|
|
683
839
|
```json
|
|
684
840
|
{
|
|
685
|
-
"equation": "sin(x) + 2 = 3"
|
|
841
|
+
"equation": "sin(x) + 2 = 3",
|
|
842
|
+
"fontSize": "number (optional) - font size for the rendered equation"
|
|
686
843
|
}
|
|
687
844
|
```
|
|
688
845
|
|
|
@@ -700,5 +857,48 @@ This document provides schemas and examples for the `loadFromJSON` method used i
|
|
|
700
857
|
{ "equation": "sin(x) + 2 = 3" }
|
|
701
858
|
```
|
|
702
859
|
```json
|
|
703
|
-
{ "equation": "(x^2 + 3x - 4)/(2x) = 5" }
|
|
860
|
+
{ "equation": "(x^2 + 3x - 4)/(2x) = 5", "fontSize": 20 }
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
---
|
|
864
|
+
|
|
865
|
+
## 21. `omdBalanceHanger`
|
|
866
|
+
|
|
867
|
+
`omdBalanceHanger` is a visual balance/scale with values stacked on each arm. Variable values (strings) render as ellipse pills; numeric values render as rounded rectangles. The `tilt` property visually tips the beam left or right.
|
|
868
|
+
|
|
869
|
+
### Schema
|
|
870
|
+
```json
|
|
871
|
+
{
|
|
872
|
+
"leftValues": ["array - strings or numbers to stack on the left arm (default: [])"],
|
|
873
|
+
"rightValues": ["array - strings or numbers to stack on the right arm (default: [])"],
|
|
874
|
+
"tilt": "string - 'none' | 'left' | 'right' (default: 'none')",
|
|
875
|
+
"fontFamily": "string (default: 'Albert Sans')",
|
|
876
|
+
"fontSize": "number (default: 18)",
|
|
877
|
+
"backgroundColor": "string (default: lightGray)",
|
|
878
|
+
"backgroundCornerRadius": "number (default: 5)",
|
|
879
|
+
"backgroundOpacity": "number (default: 1.0)",
|
|
880
|
+
"showBackground": "boolean (default: true)"
|
|
881
|
+
}
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
### Example
|
|
885
|
+
```json
|
|
886
|
+
{
|
|
887
|
+
"leftValues": ["x", "x", "3"],
|
|
888
|
+
"rightValues": ["9"],
|
|
889
|
+
"tilt": "none",
|
|
890
|
+
"fontFamily": "Albert Sans",
|
|
891
|
+
"fontSize": 18
|
|
892
|
+
}
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
### Example (tilted)
|
|
896
|
+
```json
|
|
897
|
+
{
|
|
898
|
+
"leftValues": [5, 3],
|
|
899
|
+
"rightValues": [4],
|
|
900
|
+
"tilt": "left",
|
|
901
|
+
"backgroundColor": "#E0F0FF",
|
|
902
|
+
"backgroundCornerRadius": 8
|
|
903
|
+
}
|
|
704
904
|
```
|
|
@@ -12,6 +12,18 @@ import {
|
|
|
12
12
|
export class omdCoordinatePlane extends jsvgGroup {
|
|
13
13
|
constructor() {
|
|
14
14
|
super();
|
|
15
|
+
|
|
16
|
+
// Replace the default <g> element with an <svg> element so that
|
|
17
|
+
// overflow:hidden is respected and getBBox() returns the correct
|
|
18
|
+
// clipped dimensions (fixing the oversized selection box bug).
|
|
19
|
+
const svgNS = "http://www.w3.org/2000/svg";
|
|
20
|
+
const svgEl = document.createElementNS(svgNS, "svg");
|
|
21
|
+
if (this.svgObject && this.svgObject.parentNode) {
|
|
22
|
+
this.svgObject.parentNode.replaceChild(svgEl, this.svgObject);
|
|
23
|
+
}
|
|
24
|
+
this.svgObject = svgEl;
|
|
25
|
+
this.svgObject.setAttribute("overflow", "hidden");
|
|
26
|
+
|
|
15
27
|
this.graphEquations = [];
|
|
16
28
|
this.lineSegments = [];
|
|
17
29
|
this.dotValues = [];
|