@teachinglab/omd 0.6.1 → 0.6.3
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/README.md +257 -251
- package/README.old.md +137 -137
- package/canvas/core/canvasConfig.js +202 -202
- package/canvas/drawing/segment.js +167 -167
- package/canvas/drawing/stroke.js +385 -385
- package/canvas/events/eventManager.js +444 -444
- package/canvas/events/pointerEventHandler.js +262 -262
- package/canvas/index.js +48 -48
- package/canvas/tools/PointerTool.js +71 -71
- package/canvas/tools/tool.js +222 -222
- package/canvas/utils/boundingBox.js +377 -377
- package/canvas/utils/mathUtils.js +258 -258
- package/docs/api/configuration-options.md +198 -198
- package/docs/api/eventManager.md +82 -82
- package/docs/api/focusFrameManager.md +144 -144
- package/docs/api/index.md +105 -105
- package/docs/api/main.md +62 -62
- package/docs/api/omdBinaryExpressionNode.md +86 -86
- package/docs/api/omdCanvas.md +83 -83
- package/docs/api/omdConfigManager.md +112 -112
- package/docs/api/omdConstantNode.md +52 -52
- package/docs/api/omdDisplay.md +87 -87
- package/docs/api/omdEquationNode.md +174 -174
- package/docs/api/omdEquationSequenceNode.md +258 -258
- package/docs/api/omdEquationStack.md +192 -192
- package/docs/api/omdFunctionNode.md +82 -82
- package/docs/api/omdGroupNode.md +78 -78
- package/docs/api/omdHelpers.md +87 -87
- package/docs/api/omdLeafNode.md +85 -85
- package/docs/api/omdNode.md +201 -201
- package/docs/api/omdOperationDisplayNode.md +117 -117
- package/docs/api/omdOperatorNode.md +91 -91
- package/docs/api/omdParenthesisNode.md +133 -133
- package/docs/api/omdPopup.md +191 -191
- package/docs/api/omdPowerNode.md +131 -131
- package/docs/api/omdRationalNode.md +144 -144
- package/docs/api/omdSequenceNode.md +128 -128
- package/docs/api/omdSimplification.md +78 -78
- package/docs/api/omdSqrtNode.md +144 -144
- package/docs/api/omdStepVisualizer.md +146 -146
- package/docs/api/omdStepVisualizerHighlighting.md +65 -65
- package/docs/api/omdStepVisualizerInteractiveSteps.md +108 -108
- package/docs/api/omdStepVisualizerLayout.md +70 -70
- package/docs/api/omdStepVisualizerNodeUtils.md +140 -140
- package/docs/api/omdStepVisualizerTextBoxes.md +76 -76
- package/docs/api/omdToolbar.md +130 -130
- package/docs/api/omdTranscriptionService.md +95 -95
- package/docs/api/omdTreeDiff.md +169 -169
- package/docs/api/omdUnaryExpressionNode.md +137 -137
- package/docs/api/omdUtilities.md +82 -82
- package/docs/api/omdVariableNode.md +123 -123
- package/docs/api/selectTool.md +74 -74
- package/docs/api/simplificationEngine.md +97 -97
- package/docs/api/simplificationRules.md +76 -76
- package/docs/api/simplificationUtils.md +64 -64
- package/docs/api/transcribe.md +43 -43
- package/docs/api-reference.md +85 -85
- package/docs/index.html +453 -453
- package/docs/index.md +38 -38
- package/docs/omd-objects.md +258 -258
- package/index.js +79 -79
- package/jsvg/index.js +3 -0
- package/jsvg/jsvg.js +898 -898
- package/jsvg/jsvgComponents.js +357 -358
- package/npm-docs/DOCUMENTATION_SUMMARY.md +220 -220
- package/npm-docs/README.md +251 -251
- package/npm-docs/api/api-reference.md +85 -85
- package/npm-docs/api/configuration-options.md +198 -198
- package/npm-docs/api/eventManager.md +82 -82
- package/npm-docs/api/expression-nodes.md +561 -561
- package/npm-docs/api/focusFrameManager.md +144 -144
- package/npm-docs/api/index.md +105 -105
- package/npm-docs/api/main.md +62 -62
- package/npm-docs/api/omdBinaryExpressionNode.md +86 -86
- package/npm-docs/api/omdCanvas.md +83 -83
- package/npm-docs/api/omdConfigManager.md +112 -112
- package/npm-docs/api/omdConstantNode.md +52 -52
- package/npm-docs/api/omdDisplay.md +87 -87
- package/npm-docs/api/omdEquationNode.md +174 -174
- package/npm-docs/api/omdEquationSequenceNode.md +258 -258
- package/npm-docs/api/omdEquationStack.md +192 -192
- package/npm-docs/api/omdFunctionNode.md +82 -82
- package/npm-docs/api/omdGroupNode.md +78 -78
- package/npm-docs/api/omdHelpers.md +87 -87
- package/npm-docs/api/omdLeafNode.md +85 -85
- package/npm-docs/api/omdNode.md +201 -201
- package/npm-docs/api/omdOperationDisplayNode.md +117 -117
- package/npm-docs/api/omdOperatorNode.md +91 -91
- package/npm-docs/api/omdParenthesisNode.md +133 -133
- package/npm-docs/api/omdPopup.md +191 -191
- package/npm-docs/api/omdPowerNode.md +131 -131
- package/npm-docs/api/omdRationalNode.md +144 -144
- package/npm-docs/api/omdSequenceNode.md +128 -128
- package/npm-docs/api/omdSimplification.md +78 -78
- package/npm-docs/api/omdSqrtNode.md +144 -144
- package/npm-docs/api/omdStepVisualizer.md +146 -146
- package/npm-docs/api/omdStepVisualizerHighlighting.md +65 -65
- package/npm-docs/api/omdStepVisualizerInteractiveSteps.md +108 -108
- package/npm-docs/api/omdStepVisualizerLayout.md +70 -70
- package/npm-docs/api/omdStepVisualizerNodeUtils.md +140 -140
- package/npm-docs/api/omdStepVisualizerTextBoxes.md +76 -76
- package/npm-docs/api/omdToolbar.md +130 -130
- package/npm-docs/api/omdTranscriptionService.md +95 -95
- package/npm-docs/api/omdTreeDiff.md +169 -169
- package/npm-docs/api/omdUnaryExpressionNode.md +137 -137
- package/npm-docs/api/omdUtilities.md +82 -82
- package/npm-docs/api/omdVariableNode.md +123 -123
- package/npm-docs/api/selectTool.md +74 -74
- package/npm-docs/api/simplificationEngine.md +97 -97
- package/npm-docs/api/simplificationRules.md +76 -76
- package/npm-docs/api/simplificationUtils.md +64 -64
- package/npm-docs/api/transcribe.md +43 -43
- package/npm-docs/guides/equations.md +854 -854
- package/npm-docs/guides/factory-functions.md +354 -354
- package/npm-docs/guides/getting-started.md +318 -318
- package/npm-docs/guides/quick-examples.md +525 -525
- package/npm-docs/guides/visualizations.md +682 -682
- package/npm-docs/index.html +12 -0
- package/npm-docs/json-schemas.md +826 -826
- package/omd/config/omdConfigManager.js +279 -267
- package/omd/core/index.js +158 -158
- package/omd/core/omdEquationStack.js +606 -547
- package/omd/core/omdUtilities.js +113 -113
- package/omd/display/omdDisplay.js +1045 -963
- package/omd/display/omdToolbar.js +501 -501
- package/omd/nodes/omdBinaryExpressionNode.js +459 -459
- package/omd/nodes/omdConstantNode.js +141 -141
- package/omd/nodes/omdEquationNode.js +1327 -1327
- package/omd/nodes/omdFunctionNode.js +351 -351
- package/omd/nodes/omdGroupNode.js +67 -67
- package/omd/nodes/omdLeafNode.js +76 -76
- package/omd/nodes/omdNode.js +556 -556
- package/omd/nodes/omdOperationDisplayNode.js +321 -321
- package/omd/nodes/omdOperatorNode.js +108 -108
- package/omd/nodes/omdParenthesisNode.js +292 -292
- package/omd/nodes/omdPowerNode.js +235 -235
- package/omd/nodes/omdRationalNode.js +295 -295
- package/omd/nodes/omdSqrtNode.js +307 -307
- package/omd/nodes/omdUnaryExpressionNode.js +227 -227
- package/omd/nodes/omdVariableNode.js +122 -122
- package/omd/simplification/omdSimplification.js +140 -140
- package/omd/simplification/omdSimplificationEngine.js +887 -887
- package/omd/simplification/package.json +5 -5
- package/omd/simplification/rules/binaryRules.js +1037 -1037
- package/omd/simplification/rules/functionRules.js +111 -111
- package/omd/simplification/rules/index.js +48 -48
- package/omd/simplification/rules/parenthesisRules.js +19 -19
- package/omd/simplification/rules/powerRules.js +143 -143
- package/omd/simplification/rules/rationalRules.js +725 -725
- package/omd/simplification/rules/sqrtRules.js +48 -48
- package/omd/simplification/rules/unaryRules.js +37 -37
- package/omd/simplification/simplificationRules.js +31 -31
- package/omd/simplification/simplificationUtils.js +1055 -1055
- package/omd/step-visualizer/omdStepVisualizer.js +947 -947
- package/omd/step-visualizer/omdStepVisualizerHighlighting.js +246 -246
- package/omd/step-visualizer/omdStepVisualizerLayout.js +892 -892
- package/omd/step-visualizer/omdStepVisualizerTextBoxes.js +200 -200
- package/omd/utils/aiNextEquationStep.js +106 -106
- package/omd/utils/omdNodeOverlay.js +638 -638
- package/omd/utils/omdPopup.js +1203 -1203
- package/omd/utils/omdStepVisualizerInteractiveSteps.js +684 -684
- package/omd/utils/omdStepVisualizerNodeUtils.js +267 -267
- package/omd/utils/omdTranscriptionService.js +123 -123
- package/omd/utils/omdTreeDiff.js +733 -733
- package/package.json +59 -57
- package/readme.html +184 -120
- package/src/index.js +74 -74
- package/src/json-schemas.md +576 -576
- package/src/omd-json-samples.js +147 -147
- package/src/omdApp.js +391 -391
- package/src/omdAppCanvas.js +335 -335
- package/src/omdBalanceHanger.js +199 -199
- package/src/omdColor.js +13 -13
- package/src/omdCoordinatePlane.js +541 -541
- package/src/omdExpression.js +115 -115
- package/src/omdFactory.js +150 -150
- package/src/omdFunction.js +114 -114
- package/src/omdMetaExpression.js +290 -290
- package/src/omdNaturalExpression.js +563 -563
- package/src/omdNode.js +383 -383
- package/src/omdNumber.js +52 -52
- package/src/omdNumberLine.js +114 -112
- package/src/omdNumberTile.js +118 -118
- package/src/omdOperator.js +72 -72
- package/src/omdPowerExpression.js +91 -91
- package/src/omdProblem.js +259 -259
- package/src/omdRatioChart.js +251 -251
- package/src/omdRationalExpression.js +114 -114
- package/src/omdSampleData.js +215 -215
- package/src/omdShapes.js +512 -512
- package/src/omdSpinner.js +151 -151
- package/src/omdString.js +49 -49
- package/src/omdTable.js +498 -498
- package/src/omdTapeDiagram.js +244 -244
- package/src/omdTerm.js +91 -91
- package/src/omdTileEquation.js +349 -349
- package/src/omdUtils.js +84 -84
- package/src/omdVariable.js +51 -51
|
@@ -1,263 +1,263 @@
|
|
|
1
|
-
export class pointerEventHandler {
|
|
2
|
-
/**
|
|
3
|
-
* @param {OMDCanvas} canvas - Canvas instance
|
|
4
|
-
*/
|
|
5
|
-
constructor(canvas) {
|
|
6
|
-
this.canvas = canvas;
|
|
7
|
-
|
|
8
|
-
// State tracking
|
|
9
|
-
this.lastPointerPosition = { x: 0, y: 0 };
|
|
10
|
-
this.lastPointerTime = 0;
|
|
11
|
-
this.velocity = { x: 0, y: 0 };
|
|
12
|
-
|
|
13
|
-
// Multi-touch state
|
|
14
|
-
this.multiTouchState = {
|
|
15
|
-
isActive: false,
|
|
16
|
-
initialDistance: 0,
|
|
17
|
-
initialAngle: 0,
|
|
18
|
-
lastScale: 1,
|
|
19
|
-
lastRotation: 0
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
// Gesture thresholds
|
|
23
|
-
this.gestureThresholds = {
|
|
24
|
-
minPinchDistance: 20,
|
|
25
|
-
minRotationAngle: 0.1
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Handle pointer down event
|
|
31
|
-
* @param {PointerEvent} event - Original pointer event
|
|
32
|
-
* @param {Object} normalizedEvent - Normalized event data
|
|
33
|
-
*/
|
|
34
|
-
handlePointerDown(event, normalizedEvent) {
|
|
35
|
-
this.lastPointerPosition = { x: normalizedEvent.x, y: normalizedEvent.y };
|
|
36
|
-
this.lastPointerTime = normalizedEvent.timestamp;
|
|
37
|
-
this.velocity = { x: 0, y: 0 };
|
|
38
|
-
|
|
39
|
-
// Update velocity and pressure for active tool
|
|
40
|
-
normalizedEvent.velocity = this.velocity;
|
|
41
|
-
normalizedEvent.normalizedPressure = this._normalizePressure(normalizedEvent.pressure);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Handle pointer move event
|
|
46
|
-
* @param {PointerEvent} event - Original pointer event
|
|
47
|
-
* @param {Object} normalizedEvent - Normalized event data
|
|
48
|
-
*/
|
|
49
|
-
handlePointerMove(event, normalizedEvent) {
|
|
50
|
-
// Calculate velocity
|
|
51
|
-
this._calculateVelocity(normalizedEvent);
|
|
52
|
-
|
|
53
|
-
// Handle coalesced events for smoother drawing
|
|
54
|
-
if (event.getCoalescedEvents) {
|
|
55
|
-
const coalescedEvents = event.getCoalescedEvents();
|
|
56
|
-
normalizedEvent.coalescedEvents = coalescedEvents.map(coalescedEvent => {
|
|
57
|
-
const coalescedCoords = this.canvas.clientToSVG(coalescedEvent.clientX, coalescedEvent.clientY);
|
|
58
|
-
return {
|
|
59
|
-
x: coalescedCoords.x,
|
|
60
|
-
y: coalescedCoords.y,
|
|
61
|
-
pressure: this._normalizePressure(coalescedEvent.pressure),
|
|
62
|
-
timestamp: coalescedEvent.timeStamp || Date.now()
|
|
63
|
-
};
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Add velocity and pressure data
|
|
68
|
-
normalizedEvent.velocity = this.velocity;
|
|
69
|
-
normalizedEvent.normalizedPressure = this._normalizePressure(normalizedEvent.pressure);
|
|
70
|
-
|
|
71
|
-
// Update last position and time
|
|
72
|
-
this.lastPointerPosition = { x: normalizedEvent.x, y: normalizedEvent.y };
|
|
73
|
-
this.lastPointerTime = normalizedEvent.timestamp;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Handle pointer up event
|
|
78
|
-
* @param {PointerEvent} event - Original pointer event
|
|
79
|
-
* @param {Object} normalizedEvent - Normalized event data
|
|
80
|
-
*/
|
|
81
|
-
handlePointerUp(event, normalizedEvent) {
|
|
82
|
-
// Final velocity calculation
|
|
83
|
-
this._calculateVelocity(normalizedEvent);
|
|
84
|
-
normalizedEvent.velocity = this.velocity;
|
|
85
|
-
normalizedEvent.normalizedPressure = this._normalizePressure(normalizedEvent.pressure);
|
|
86
|
-
|
|
87
|
-
// Reset velocity
|
|
88
|
-
this.velocity = { x: 0, y: 0 };
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Handle multi-touch start
|
|
93
|
-
* @param {Map} activePointers - Map of active pointers
|
|
94
|
-
*/
|
|
95
|
-
handleMultiTouchStart(activePointers) {
|
|
96
|
-
if (activePointers.size === 2) {
|
|
97
|
-
const pointers = Array.from(activePointers.values());
|
|
98
|
-
const pointer1 = pointers[0];
|
|
99
|
-
const pointer2 = pointers[1];
|
|
100
|
-
|
|
101
|
-
this.multiTouchState.isActive = true;
|
|
102
|
-
this.multiTouchState.initialDistance = this._calculateDistance(pointer1, pointer2);
|
|
103
|
-
this.multiTouchState.initialAngle = this._calculateAngle(pointer1, pointer2);
|
|
104
|
-
this.multiTouchState.lastScale = 1;
|
|
105
|
-
this.multiTouchState.lastRotation = 0;
|
|
106
|
-
|
|
107
|
-
this.canvas.emit('multiTouchStart', {
|
|
108
|
-
pointers: pointers,
|
|
109
|
-
distance: this.multiTouchState.initialDistance,
|
|
110
|
-
angle: this.multiTouchState.initialAngle
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Handle multi-touch move
|
|
117
|
-
* @param {Map} activePointers - Map of active pointers
|
|
118
|
-
*/
|
|
119
|
-
handleMultiTouchMove(activePointers) {
|
|
120
|
-
if (activePointers.size === 2 && this.multiTouchState.isActive) {
|
|
121
|
-
const pointers = Array.from(activePointers.values());
|
|
122
|
-
const pointer1 = pointers[0];
|
|
123
|
-
const pointer2 = pointers[1];
|
|
124
|
-
|
|
125
|
-
const currentDistance = this._calculateDistance(pointer1, pointer2);
|
|
126
|
-
const currentAngle = this._calculateAngle(pointer1, pointer2);
|
|
127
|
-
|
|
128
|
-
const scale = currentDistance / this.multiTouchState.initialDistance;
|
|
129
|
-
const rotation = currentAngle - this.multiTouchState.initialAngle;
|
|
130
|
-
|
|
131
|
-
// Detect pinch gesture
|
|
132
|
-
if (Math.abs(scale - 1) > 0.1) {
|
|
133
|
-
this.canvas.emit('pinch', {
|
|
134
|
-
scale: scale,
|
|
135
|
-
deltaScale: scale - this.multiTouchState.lastScale,
|
|
136
|
-
center: this._calculateCenter(pointer1, pointer2)
|
|
137
|
-
});
|
|
138
|
-
this.multiTouchState.lastScale = scale;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Detect rotation gesture
|
|
142
|
-
if (Math.abs(rotation) > this.gestureThresholds.minRotationAngle) {
|
|
143
|
-
this.canvas.emit('rotate', {
|
|
144
|
-
rotation: rotation,
|
|
145
|
-
deltaRotation: rotation - this.multiTouchState.lastRotation,
|
|
146
|
-
center: this._calculateCenter(pointer1, pointer2)
|
|
147
|
-
});
|
|
148
|
-
this.multiTouchState.lastRotation = rotation;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Handle multi-touch end
|
|
155
|
-
* @param {Map} activePointers - Map of active pointers
|
|
156
|
-
*/
|
|
157
|
-
handleMultiTouchEnd(activePointers) {
|
|
158
|
-
if (activePointers.size < 2) {
|
|
159
|
-
this.multiTouchState.isActive = false;
|
|
160
|
-
this.canvas.emit('multiTouchEnd');
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Calculate velocity between pointer events
|
|
166
|
-
* @private
|
|
167
|
-
*/
|
|
168
|
-
_calculateVelocity(normalizedEvent) {
|
|
169
|
-
const deltaTime = normalizedEvent.timestamp - this.lastPointerTime;
|
|
170
|
-
|
|
171
|
-
if (deltaTime > 0) {
|
|
172
|
-
const deltaX = normalizedEvent.x - this.lastPointerPosition.x;
|
|
173
|
-
const deltaY = normalizedEvent.y - this.lastPointerPosition.y;
|
|
174
|
-
|
|
175
|
-
this.velocity.x = deltaX / deltaTime * 1000; // pixels per second
|
|
176
|
-
this.velocity.y = deltaY / deltaTime * 1000;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Normalize pressure value with device-specific adjustments
|
|
182
|
-
* @param {number} pressure - Raw pressure value
|
|
183
|
-
* @returns {number} Normalized pressure (0-1)
|
|
184
|
-
* @private
|
|
185
|
-
*/
|
|
186
|
-
_normalizePressure(pressure = 0.5) {
|
|
187
|
-
if (pressure === undefined || pressure === null) {
|
|
188
|
-
return 0.5;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Device-specific pressure normalization
|
|
192
|
-
// Some devices report pressure differently
|
|
193
|
-
let normalizedPressure = pressure;
|
|
194
|
-
|
|
195
|
-
// Apple Pencil typically reports good pressure values
|
|
196
|
-
// Surface Pen and other styluses might need adjustment
|
|
197
|
-
if (normalizedPressure < 0.1) {
|
|
198
|
-
normalizedPressure = 0.1; // Minimum pressure to ensure visibility
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Apply slight curve to make pressure feel more natural
|
|
202
|
-
normalizedPressure = Math.pow(normalizedPressure, 0.8);
|
|
203
|
-
|
|
204
|
-
return Math.max(0, Math.min(1, normalizedPressure));
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Calculate distance between two pointers
|
|
209
|
-
* @private
|
|
210
|
-
*/
|
|
211
|
-
_calculateDistance(pointer1, pointer2) {
|
|
212
|
-
const dx = pointer2.clientX - pointer1.clientX;
|
|
213
|
-
const dy = pointer2.clientY - pointer1.clientY;
|
|
214
|
-
return Math.sqrt(dx * dx + dy * dy);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Calculate angle between two pointers
|
|
219
|
-
* @private
|
|
220
|
-
*/
|
|
221
|
-
_calculateAngle(pointer1, pointer2) {
|
|
222
|
-
return Math.atan2(
|
|
223
|
-
pointer2.clientY - pointer1.clientY,
|
|
224
|
-
pointer2.clientX - pointer1.clientX
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Calculate center point between two pointers
|
|
230
|
-
* @private
|
|
231
|
-
*/
|
|
232
|
-
_calculateCenter(pointer1, pointer2) {
|
|
233
|
-
const centerX = (pointer1.clientX + pointer2.clientX) / 2;
|
|
234
|
-
const centerY = (pointer1.clientY + pointer2.clientY) / 2;
|
|
235
|
-
|
|
236
|
-
return this.canvas.clientToSVG(centerX, centerY);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Get velocity information
|
|
241
|
-
* @returns {Object} Current velocity data
|
|
242
|
-
*/
|
|
243
|
-
getVelocity() {
|
|
244
|
-
return {
|
|
245
|
-
x: this.velocity.x,
|
|
246
|
-
y: this.velocity.y,
|
|
247
|
-
magnitude: Math.sqrt(this.velocity.x * this.velocity.x + this.velocity.y * this.velocity.y),
|
|
248
|
-
angle: Math.atan2(this.velocity.y, this.velocity.x)
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Get multi-touch state
|
|
254
|
-
* @returns {Object} Current multi-touch state
|
|
255
|
-
*/
|
|
256
|
-
getMultiTouchState() {
|
|
257
|
-
return {
|
|
258
|
-
isActive: this.multiTouchState.isActive,
|
|
259
|
-
scale: this.multiTouchState.lastScale,
|
|
260
|
-
rotation: this.multiTouchState.lastRotation
|
|
261
|
-
};
|
|
262
|
-
}
|
|
1
|
+
export class pointerEventHandler {
|
|
2
|
+
/**
|
|
3
|
+
* @param {OMDCanvas} canvas - Canvas instance
|
|
4
|
+
*/
|
|
5
|
+
constructor(canvas) {
|
|
6
|
+
this.canvas = canvas;
|
|
7
|
+
|
|
8
|
+
// State tracking
|
|
9
|
+
this.lastPointerPosition = { x: 0, y: 0 };
|
|
10
|
+
this.lastPointerTime = 0;
|
|
11
|
+
this.velocity = { x: 0, y: 0 };
|
|
12
|
+
|
|
13
|
+
// Multi-touch state
|
|
14
|
+
this.multiTouchState = {
|
|
15
|
+
isActive: false,
|
|
16
|
+
initialDistance: 0,
|
|
17
|
+
initialAngle: 0,
|
|
18
|
+
lastScale: 1,
|
|
19
|
+
lastRotation: 0
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Gesture thresholds
|
|
23
|
+
this.gestureThresholds = {
|
|
24
|
+
minPinchDistance: 20,
|
|
25
|
+
minRotationAngle: 0.1
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Handle pointer down event
|
|
31
|
+
* @param {PointerEvent} event - Original pointer event
|
|
32
|
+
* @param {Object} normalizedEvent - Normalized event data
|
|
33
|
+
*/
|
|
34
|
+
handlePointerDown(event, normalizedEvent) {
|
|
35
|
+
this.lastPointerPosition = { x: normalizedEvent.x, y: normalizedEvent.y };
|
|
36
|
+
this.lastPointerTime = normalizedEvent.timestamp;
|
|
37
|
+
this.velocity = { x: 0, y: 0 };
|
|
38
|
+
|
|
39
|
+
// Update velocity and pressure for active tool
|
|
40
|
+
normalizedEvent.velocity = this.velocity;
|
|
41
|
+
normalizedEvent.normalizedPressure = this._normalizePressure(normalizedEvent.pressure);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Handle pointer move event
|
|
46
|
+
* @param {PointerEvent} event - Original pointer event
|
|
47
|
+
* @param {Object} normalizedEvent - Normalized event data
|
|
48
|
+
*/
|
|
49
|
+
handlePointerMove(event, normalizedEvent) {
|
|
50
|
+
// Calculate velocity
|
|
51
|
+
this._calculateVelocity(normalizedEvent);
|
|
52
|
+
|
|
53
|
+
// Handle coalesced events for smoother drawing
|
|
54
|
+
if (event.getCoalescedEvents) {
|
|
55
|
+
const coalescedEvents = event.getCoalescedEvents();
|
|
56
|
+
normalizedEvent.coalescedEvents = coalescedEvents.map(coalescedEvent => {
|
|
57
|
+
const coalescedCoords = this.canvas.clientToSVG(coalescedEvent.clientX, coalescedEvent.clientY);
|
|
58
|
+
return {
|
|
59
|
+
x: coalescedCoords.x,
|
|
60
|
+
y: coalescedCoords.y,
|
|
61
|
+
pressure: this._normalizePressure(coalescedEvent.pressure),
|
|
62
|
+
timestamp: coalescedEvent.timeStamp || Date.now()
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Add velocity and pressure data
|
|
68
|
+
normalizedEvent.velocity = this.velocity;
|
|
69
|
+
normalizedEvent.normalizedPressure = this._normalizePressure(normalizedEvent.pressure);
|
|
70
|
+
|
|
71
|
+
// Update last position and time
|
|
72
|
+
this.lastPointerPosition = { x: normalizedEvent.x, y: normalizedEvent.y };
|
|
73
|
+
this.lastPointerTime = normalizedEvent.timestamp;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Handle pointer up event
|
|
78
|
+
* @param {PointerEvent} event - Original pointer event
|
|
79
|
+
* @param {Object} normalizedEvent - Normalized event data
|
|
80
|
+
*/
|
|
81
|
+
handlePointerUp(event, normalizedEvent) {
|
|
82
|
+
// Final velocity calculation
|
|
83
|
+
this._calculateVelocity(normalizedEvent);
|
|
84
|
+
normalizedEvent.velocity = this.velocity;
|
|
85
|
+
normalizedEvent.normalizedPressure = this._normalizePressure(normalizedEvent.pressure);
|
|
86
|
+
|
|
87
|
+
// Reset velocity
|
|
88
|
+
this.velocity = { x: 0, y: 0 };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Handle multi-touch start
|
|
93
|
+
* @param {Map} activePointers - Map of active pointers
|
|
94
|
+
*/
|
|
95
|
+
handleMultiTouchStart(activePointers) {
|
|
96
|
+
if (activePointers.size === 2) {
|
|
97
|
+
const pointers = Array.from(activePointers.values());
|
|
98
|
+
const pointer1 = pointers[0];
|
|
99
|
+
const pointer2 = pointers[1];
|
|
100
|
+
|
|
101
|
+
this.multiTouchState.isActive = true;
|
|
102
|
+
this.multiTouchState.initialDistance = this._calculateDistance(pointer1, pointer2);
|
|
103
|
+
this.multiTouchState.initialAngle = this._calculateAngle(pointer1, pointer2);
|
|
104
|
+
this.multiTouchState.lastScale = 1;
|
|
105
|
+
this.multiTouchState.lastRotation = 0;
|
|
106
|
+
|
|
107
|
+
this.canvas.emit('multiTouchStart', {
|
|
108
|
+
pointers: pointers,
|
|
109
|
+
distance: this.multiTouchState.initialDistance,
|
|
110
|
+
angle: this.multiTouchState.initialAngle
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Handle multi-touch move
|
|
117
|
+
* @param {Map} activePointers - Map of active pointers
|
|
118
|
+
*/
|
|
119
|
+
handleMultiTouchMove(activePointers) {
|
|
120
|
+
if (activePointers.size === 2 && this.multiTouchState.isActive) {
|
|
121
|
+
const pointers = Array.from(activePointers.values());
|
|
122
|
+
const pointer1 = pointers[0];
|
|
123
|
+
const pointer2 = pointers[1];
|
|
124
|
+
|
|
125
|
+
const currentDistance = this._calculateDistance(pointer1, pointer2);
|
|
126
|
+
const currentAngle = this._calculateAngle(pointer1, pointer2);
|
|
127
|
+
|
|
128
|
+
const scale = currentDistance / this.multiTouchState.initialDistance;
|
|
129
|
+
const rotation = currentAngle - this.multiTouchState.initialAngle;
|
|
130
|
+
|
|
131
|
+
// Detect pinch gesture
|
|
132
|
+
if (Math.abs(scale - 1) > 0.1) {
|
|
133
|
+
this.canvas.emit('pinch', {
|
|
134
|
+
scale: scale,
|
|
135
|
+
deltaScale: scale - this.multiTouchState.lastScale,
|
|
136
|
+
center: this._calculateCenter(pointer1, pointer2)
|
|
137
|
+
});
|
|
138
|
+
this.multiTouchState.lastScale = scale;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Detect rotation gesture
|
|
142
|
+
if (Math.abs(rotation) > this.gestureThresholds.minRotationAngle) {
|
|
143
|
+
this.canvas.emit('rotate', {
|
|
144
|
+
rotation: rotation,
|
|
145
|
+
deltaRotation: rotation - this.multiTouchState.lastRotation,
|
|
146
|
+
center: this._calculateCenter(pointer1, pointer2)
|
|
147
|
+
});
|
|
148
|
+
this.multiTouchState.lastRotation = rotation;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Handle multi-touch end
|
|
155
|
+
* @param {Map} activePointers - Map of active pointers
|
|
156
|
+
*/
|
|
157
|
+
handleMultiTouchEnd(activePointers) {
|
|
158
|
+
if (activePointers.size < 2) {
|
|
159
|
+
this.multiTouchState.isActive = false;
|
|
160
|
+
this.canvas.emit('multiTouchEnd');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Calculate velocity between pointer events
|
|
166
|
+
* @private
|
|
167
|
+
*/
|
|
168
|
+
_calculateVelocity(normalizedEvent) {
|
|
169
|
+
const deltaTime = normalizedEvent.timestamp - this.lastPointerTime;
|
|
170
|
+
|
|
171
|
+
if (deltaTime > 0) {
|
|
172
|
+
const deltaX = normalizedEvent.x - this.lastPointerPosition.x;
|
|
173
|
+
const deltaY = normalizedEvent.y - this.lastPointerPosition.y;
|
|
174
|
+
|
|
175
|
+
this.velocity.x = deltaX / deltaTime * 1000; // pixels per second
|
|
176
|
+
this.velocity.y = deltaY / deltaTime * 1000;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Normalize pressure value with device-specific adjustments
|
|
182
|
+
* @param {number} pressure - Raw pressure value
|
|
183
|
+
* @returns {number} Normalized pressure (0-1)
|
|
184
|
+
* @private
|
|
185
|
+
*/
|
|
186
|
+
_normalizePressure(pressure = 0.5) {
|
|
187
|
+
if (pressure === undefined || pressure === null) {
|
|
188
|
+
return 0.5;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Device-specific pressure normalization
|
|
192
|
+
// Some devices report pressure differently
|
|
193
|
+
let normalizedPressure = pressure;
|
|
194
|
+
|
|
195
|
+
// Apple Pencil typically reports good pressure values
|
|
196
|
+
// Surface Pen and other styluses might need adjustment
|
|
197
|
+
if (normalizedPressure < 0.1) {
|
|
198
|
+
normalizedPressure = 0.1; // Minimum pressure to ensure visibility
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Apply slight curve to make pressure feel more natural
|
|
202
|
+
normalizedPressure = Math.pow(normalizedPressure, 0.8);
|
|
203
|
+
|
|
204
|
+
return Math.max(0, Math.min(1, normalizedPressure));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Calculate distance between two pointers
|
|
209
|
+
* @private
|
|
210
|
+
*/
|
|
211
|
+
_calculateDistance(pointer1, pointer2) {
|
|
212
|
+
const dx = pointer2.clientX - pointer1.clientX;
|
|
213
|
+
const dy = pointer2.clientY - pointer1.clientY;
|
|
214
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Calculate angle between two pointers
|
|
219
|
+
* @private
|
|
220
|
+
*/
|
|
221
|
+
_calculateAngle(pointer1, pointer2) {
|
|
222
|
+
return Math.atan2(
|
|
223
|
+
pointer2.clientY - pointer1.clientY,
|
|
224
|
+
pointer2.clientX - pointer1.clientX
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Calculate center point between two pointers
|
|
230
|
+
* @private
|
|
231
|
+
*/
|
|
232
|
+
_calculateCenter(pointer1, pointer2) {
|
|
233
|
+
const centerX = (pointer1.clientX + pointer2.clientX) / 2;
|
|
234
|
+
const centerY = (pointer1.clientY + pointer2.clientY) / 2;
|
|
235
|
+
|
|
236
|
+
return this.canvas.clientToSVG(centerX, centerY);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Get velocity information
|
|
241
|
+
* @returns {Object} Current velocity data
|
|
242
|
+
*/
|
|
243
|
+
getVelocity() {
|
|
244
|
+
return {
|
|
245
|
+
x: this.velocity.x,
|
|
246
|
+
y: this.velocity.y,
|
|
247
|
+
magnitude: Math.sqrt(this.velocity.x * this.velocity.x + this.velocity.y * this.velocity.y),
|
|
248
|
+
angle: Math.atan2(this.velocity.y, this.velocity.x)
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Get multi-touch state
|
|
254
|
+
* @returns {Object} Current multi-touch state
|
|
255
|
+
*/
|
|
256
|
+
getMultiTouchState() {
|
|
257
|
+
return {
|
|
258
|
+
isActive: this.multiTouchState.isActive,
|
|
259
|
+
scale: this.multiTouchState.lastScale,
|
|
260
|
+
rotation: this.multiTouchState.lastRotation
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
263
|
}
|
package/canvas/index.js
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
// Core canvas system
|
|
2
|
-
import { omdCanvas } from './core/omdCanvas.js';
|
|
3
|
-
export { omdCanvas } from './core/omdCanvas.js';
|
|
4
|
-
export { CanvasConfig as canvasConfig } from './core/canvasConfig.js';
|
|
5
|
-
|
|
6
|
-
// Event handling
|
|
7
|
-
export { EventManager } from './events/eventManager.js';
|
|
8
|
-
export { pointerEventHandler } from './events/pointerEventHandler.js';
|
|
9
|
-
|
|
10
|
-
// Tool system
|
|
11
|
-
export { ToolManager } from './tools/toolManager.js';
|
|
12
|
-
export { Tool } from './tools/tool.js';
|
|
13
|
-
export { PencilTool } from './tools/PencilTool.js';
|
|
14
|
-
export { EraserTool } from './tools/EraserTool.js';
|
|
15
|
-
export { SelectTool } from './tools/SelectTool.js';
|
|
16
|
-
|
|
17
|
-
// UI components
|
|
18
|
-
export { Toolbar } from './ui/toolbar.js';
|
|
19
|
-
export { Cursor } from './ui/cursor.js';
|
|
20
|
-
|
|
21
|
-
// Drawing objects
|
|
22
|
-
export { Stroke } from './drawing/stroke.js';
|
|
23
|
-
export { segment } from './drawing/segment.js';
|
|
24
|
-
|
|
25
|
-
// Utilities
|
|
26
|
-
export { BoundingBox } from './utils/boundingBox.js';
|
|
27
|
-
export { mathUtils } from './utils/mathUtils.js';
|
|
28
|
-
|
|
29
|
-
// Focus frame system
|
|
30
|
-
export { FocusFrameManager } from './features/focusFrameManager.js';
|
|
31
|
-
/**
|
|
32
|
-
* Quick setup function for common use cases
|
|
33
|
-
* @param {HTMLElement|string} container - Container element or selector
|
|
34
|
-
* @param {Object} options - Configuration options
|
|
35
|
-
* @returns {omdCanvas} Configured canvas instance
|
|
36
|
-
*/
|
|
37
|
-
export function createCanvas(container, options = {}) {
|
|
38
|
-
const canvas = new omdCanvas(container, options);
|
|
39
|
-
return canvas;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Create multiple canvas instances for comparison or multi-canvas apps
|
|
44
|
-
* @param {Array} configs - Array of {container, options} objects
|
|
45
|
-
* @returns {Array<omdCanvas>} Array of canvas instances
|
|
46
|
-
*/
|
|
47
|
-
export function createMultipleCanvases(configs) {
|
|
48
|
-
return configs.map(config => createCanvas(config.container, config.options));
|
|
1
|
+
// Core canvas system
|
|
2
|
+
import { omdCanvas } from './core/omdCanvas.js';
|
|
3
|
+
export { omdCanvas } from './core/omdCanvas.js';
|
|
4
|
+
export { CanvasConfig as canvasConfig } from './core/canvasConfig.js';
|
|
5
|
+
|
|
6
|
+
// Event handling
|
|
7
|
+
export { EventManager } from './events/eventManager.js';
|
|
8
|
+
export { pointerEventHandler } from './events/pointerEventHandler.js';
|
|
9
|
+
|
|
10
|
+
// Tool system
|
|
11
|
+
export { ToolManager } from './tools/toolManager.js';
|
|
12
|
+
export { Tool } from './tools/tool.js';
|
|
13
|
+
export { PencilTool } from './tools/PencilTool.js';
|
|
14
|
+
export { EraserTool } from './tools/EraserTool.js';
|
|
15
|
+
export { SelectTool } from './tools/SelectTool.js';
|
|
16
|
+
|
|
17
|
+
// UI components
|
|
18
|
+
export { Toolbar } from './ui/toolbar.js';
|
|
19
|
+
export { Cursor } from './ui/cursor.js';
|
|
20
|
+
|
|
21
|
+
// Drawing objects
|
|
22
|
+
export { Stroke } from './drawing/stroke.js';
|
|
23
|
+
export { segment } from './drawing/segment.js';
|
|
24
|
+
|
|
25
|
+
// Utilities
|
|
26
|
+
export { BoundingBox } from './utils/boundingBox.js';
|
|
27
|
+
export { mathUtils } from './utils/mathUtils.js';
|
|
28
|
+
|
|
29
|
+
// Focus frame system
|
|
30
|
+
export { FocusFrameManager } from './features/focusFrameManager.js';
|
|
31
|
+
/**
|
|
32
|
+
* Quick setup function for common use cases
|
|
33
|
+
* @param {HTMLElement|string} container - Container element or selector
|
|
34
|
+
* @param {Object} options - Configuration options
|
|
35
|
+
* @returns {omdCanvas} Configured canvas instance
|
|
36
|
+
*/
|
|
37
|
+
export function createCanvas(container, options = {}) {
|
|
38
|
+
const canvas = new omdCanvas(container, options);
|
|
39
|
+
return canvas;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Create multiple canvas instances for comparison or multi-canvas apps
|
|
44
|
+
* @param {Array} configs - Array of {container, options} objects
|
|
45
|
+
* @returns {Array<omdCanvas>} Array of canvas instances
|
|
46
|
+
*/
|
|
47
|
+
export function createMultipleCanvases(configs) {
|
|
48
|
+
return configs.map(config => createCanvas(config.container, config.options));
|
|
49
49
|
}
|