@teachinglab/omd 0.1.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 (144) hide show
  1. package/README.md +138 -0
  2. package/canvas/core/canvasConfig.js +203 -0
  3. package/canvas/core/omdCanvas.js +475 -0
  4. package/canvas/drawing/segment.js +168 -0
  5. package/canvas/drawing/stroke.js +386 -0
  6. package/canvas/events/eventManager.js +435 -0
  7. package/canvas/events/pointerEventHandler.js +263 -0
  8. package/canvas/features/focusFrameManager.js +287 -0
  9. package/canvas/index.js +49 -0
  10. package/canvas/tools/eraserTool.js +322 -0
  11. package/canvas/tools/pencilTool.js +319 -0
  12. package/canvas/tools/selectTool.js +457 -0
  13. package/canvas/tools/tool.js +223 -0
  14. package/canvas/tools/toolManager.js +394 -0
  15. package/canvas/ui/cursor.js +438 -0
  16. package/canvas/ui/toolbar.js +304 -0
  17. package/canvas/utils/boundingBox.js +378 -0
  18. package/canvas/utils/mathUtils.js +259 -0
  19. package/docs/api/configuration-options.md +104 -0
  20. package/docs/api/eventManager.md +68 -0
  21. package/docs/api/focusFrameManager.md +150 -0
  22. package/docs/api/index.md +91 -0
  23. package/docs/api/main.md +58 -0
  24. package/docs/api/omdBinaryExpressionNode.md +227 -0
  25. package/docs/api/omdCanvas.md +142 -0
  26. package/docs/api/omdConfigManager.md +192 -0
  27. package/docs/api/omdConstantNode.md +117 -0
  28. package/docs/api/omdDisplay.md +121 -0
  29. package/docs/api/omdEquationNode.md +161 -0
  30. package/docs/api/omdEquationSequenceNode.md +301 -0
  31. package/docs/api/omdEquationStack.md +139 -0
  32. package/docs/api/omdFunctionNode.md +141 -0
  33. package/docs/api/omdGroupNode.md +182 -0
  34. package/docs/api/omdHelpers.md +96 -0
  35. package/docs/api/omdLeafNode.md +163 -0
  36. package/docs/api/omdNode.md +101 -0
  37. package/docs/api/omdOperationDisplayNode.md +139 -0
  38. package/docs/api/omdOperatorNode.md +127 -0
  39. package/docs/api/omdParenthesisNode.md +122 -0
  40. package/docs/api/omdPopup.md +117 -0
  41. package/docs/api/omdPowerNode.md +127 -0
  42. package/docs/api/omdRationalNode.md +128 -0
  43. package/docs/api/omdSequenceNode.md +128 -0
  44. package/docs/api/omdSimplification.md +110 -0
  45. package/docs/api/omdSqrtNode.md +79 -0
  46. package/docs/api/omdStepVisualizer.md +115 -0
  47. package/docs/api/omdStepVisualizerHighlighting.md +61 -0
  48. package/docs/api/omdStepVisualizerInteractiveSteps.md +129 -0
  49. package/docs/api/omdStepVisualizerLayout.md +60 -0
  50. package/docs/api/omdStepVisualizerNodeUtils.md +140 -0
  51. package/docs/api/omdStepVisualizerTextBoxes.md +68 -0
  52. package/docs/api/omdToolbar.md +102 -0
  53. package/docs/api/omdTranscriptionService.md +76 -0
  54. package/docs/api/omdTreeDiff.md +134 -0
  55. package/docs/api/omdUnaryExpressionNode.md +174 -0
  56. package/docs/api/omdUtilities.md +70 -0
  57. package/docs/api/omdVariableNode.md +148 -0
  58. package/docs/api/selectTool.md +74 -0
  59. package/docs/api/simplificationEngine.md +98 -0
  60. package/docs/api/simplificationRules.md +77 -0
  61. package/docs/api/simplificationUtils.md +64 -0
  62. package/docs/api/transcribe.md +43 -0
  63. package/docs/api-reference.md +85 -0
  64. package/docs/index.html +454 -0
  65. package/docs/user-guide.md +9 -0
  66. package/index.js +67 -0
  67. package/omd/config/omdConfigManager.js +267 -0
  68. package/omd/core/index.js +150 -0
  69. package/omd/core/omdEquationStack.js +347 -0
  70. package/omd/core/omdUtilities.js +115 -0
  71. package/omd/display/omdDisplay.js +443 -0
  72. package/omd/display/omdToolbar.js +502 -0
  73. package/omd/nodes/omdBinaryExpressionNode.js +460 -0
  74. package/omd/nodes/omdConstantNode.js +142 -0
  75. package/omd/nodes/omdEquationNode.js +1223 -0
  76. package/omd/nodes/omdEquationSequenceNode.js +1273 -0
  77. package/omd/nodes/omdFunctionNode.js +352 -0
  78. package/omd/nodes/omdGroupNode.js +68 -0
  79. package/omd/nodes/omdLeafNode.js +77 -0
  80. package/omd/nodes/omdNode.js +557 -0
  81. package/omd/nodes/omdOperationDisplayNode.js +322 -0
  82. package/omd/nodes/omdOperatorNode.js +109 -0
  83. package/omd/nodes/omdParenthesisNode.js +293 -0
  84. package/omd/nodes/omdPowerNode.js +236 -0
  85. package/omd/nodes/omdRationalNode.js +295 -0
  86. package/omd/nodes/omdSqrtNode.js +308 -0
  87. package/omd/nodes/omdUnaryExpressionNode.js +178 -0
  88. package/omd/nodes/omdVariableNode.js +123 -0
  89. package/omd/simplification/omdSimplification.js +171 -0
  90. package/omd/simplification/omdSimplificationEngine.js +886 -0
  91. package/omd/simplification/package.json +6 -0
  92. package/omd/simplification/rules/binaryRules.js +1037 -0
  93. package/omd/simplification/rules/functionRules.js +111 -0
  94. package/omd/simplification/rules/index.js +48 -0
  95. package/omd/simplification/rules/parenthesisRules.js +19 -0
  96. package/omd/simplification/rules/powerRules.js +143 -0
  97. package/omd/simplification/rules/rationalRules.js +475 -0
  98. package/omd/simplification/rules/sqrtRules.js +48 -0
  99. package/omd/simplification/rules/unaryRules.js +37 -0
  100. package/omd/simplification/simplificationRules.js +32 -0
  101. package/omd/simplification/simplificationUtils.js +1056 -0
  102. package/omd/step-visualizer/omdStepVisualizer.js +597 -0
  103. package/omd/step-visualizer/omdStepVisualizerHighlighting.js +206 -0
  104. package/omd/step-visualizer/omdStepVisualizerLayout.js +245 -0
  105. package/omd/step-visualizer/omdStepVisualizerTextBoxes.js +163 -0
  106. package/omd/utils/omdNodeOverlay.js +638 -0
  107. package/omd/utils/omdPopup.js +1084 -0
  108. package/omd/utils/omdStepVisualizerInteractiveSteps.js +491 -0
  109. package/omd/utils/omdStepVisualizerNodeUtils.js +268 -0
  110. package/omd/utils/omdTranscriptionService.js +125 -0
  111. package/omd/utils/omdTreeDiff.js +734 -0
  112. package/package.json +46 -0
  113. package/src/index.js +62 -0
  114. package/src/json-schemas.md +109 -0
  115. package/src/omd-json-samples.js +115 -0
  116. package/src/omd.js +109 -0
  117. package/src/omdApp.js +391 -0
  118. package/src/omdAppCanvas.js +336 -0
  119. package/src/omdBalanceHanger.js +172 -0
  120. package/src/omdColor.js +13 -0
  121. package/src/omdCoordinatePlane.js +467 -0
  122. package/src/omdEquation.js +125 -0
  123. package/src/omdExpression.js +104 -0
  124. package/src/omdFunction.js +113 -0
  125. package/src/omdMetaExpression.js +287 -0
  126. package/src/omdNaturalExpression.js +564 -0
  127. package/src/omdNode.js +384 -0
  128. package/src/omdNumber.js +53 -0
  129. package/src/omdNumberLine.js +107 -0
  130. package/src/omdNumberTile.js +119 -0
  131. package/src/omdOperator.js +73 -0
  132. package/src/omdPowerExpression.js +92 -0
  133. package/src/omdProblem.js +55 -0
  134. package/src/omdRatioChart.js +232 -0
  135. package/src/omdRationalExpression.js +115 -0
  136. package/src/omdSampleData.js +215 -0
  137. package/src/omdShapes.js +476 -0
  138. package/src/omdSpinner.js +148 -0
  139. package/src/omdString.js +39 -0
  140. package/src/omdTable.js +369 -0
  141. package/src/omdTapeDiagram.js +245 -0
  142. package/src/omdTerm.js +92 -0
  143. package/src/omdTileEquation.js +349 -0
  144. package/src/omdVariable.js +51 -0
@@ -0,0 +1,319 @@
1
+ import { Tool } from './tool.js';
2
+ import { Stroke } from '../drawing/stroke.js';
3
+
4
+ /**
5
+ * Pencil tool for freehand drawing
6
+ */
7
+ export class PencilTool extends Tool {
8
+ constructor(canvas, options = {}) {
9
+ super(canvas, {
10
+ strokeWidth: 5,
11
+ strokeColor: '#000000',
12
+ strokeOpacity: 1,
13
+ smoothing: 0.5,
14
+ pressureSensitive: true,
15
+ ...options
16
+ });
17
+
18
+ this.displayName = 'Pencil';
19
+ this.description = 'Draw freehand strokes';
20
+ this.icon = 'pencil';
21
+ this.shortcut = 'P';
22
+ this.category = 'drawing';
23
+
24
+ // Drawing state
25
+ this.points = [];
26
+ this.lastPoint = null;
27
+ this.minDistance = 2.0; // Minimum distance between points (increased for better performance)
28
+ }
29
+
30
+ /**
31
+ * Start drawing a new stroke
32
+ */
33
+ onPointerDown(event) {
34
+ if (!this.canUse()) return;
35
+
36
+ this.isDrawing = true;
37
+ this.points = [];
38
+ this.lastPoint = { x: event.x, y: event.y };
39
+
40
+ // Create new stroke
41
+ this.currentStroke = new Stroke({
42
+ x: event.x,
43
+ y: event.y,
44
+ strokeWidth: this.calculateStrokeWidth(event.pressure),
45
+ strokeColor: this.config.strokeColor,
46
+ strokeOpacity: this.config.strokeOpacity,
47
+ tool: this.name
48
+ });
49
+
50
+ // Add first point
51
+ this.addPoint(event.x, event.y, event.pressure);
52
+
53
+ // Add stroke to canvas
54
+ this.canvas.addStroke(this.currentStroke);
55
+
56
+ this.canvas.emit('strokeStarted', {
57
+ stroke: this.currentStroke,
58
+ tool: this.name,
59
+ point: { x: event.x, y: event.y, pressure: event.pressure }
60
+ });
61
+ }
62
+
63
+ /**
64
+ * Continue drawing the stroke
65
+ */
66
+ onPointerMove(event) {
67
+ if (!this.isDrawing || !this.currentStroke) return;
68
+
69
+ // Process coalesced events for higher precision if available
70
+ if (event.coalescedEvents && event.coalescedEvents.length > 0) {
71
+ // Limit the number of coalesced events processed to prevent performance issues
72
+ const maxCoalescedEvents = 5;
73
+ const eventsToProcess = event.coalescedEvents.slice(0, maxCoalescedEvents);
74
+
75
+ for (const coalescedEvent of eventsToProcess) {
76
+ this._addPointIfNeeded(coalescedEvent.x, coalescedEvent.y, coalescedEvent.pressure);
77
+ }
78
+ } else {
79
+ // Fallback to regular event
80
+ this._addPointIfNeeded(event.x, event.y, event.pressure);
81
+ }
82
+
83
+ this.canvas.emit('strokeContinued', {
84
+ stroke: this.currentStroke,
85
+ tool: this.name,
86
+ point: { x: event.x, y: event.y, pressure: event.pressure }
87
+ });
88
+ }
89
+
90
+ /**
91
+ * Finish drawing the stroke
92
+ */
93
+ onPointerUp(event) {
94
+ if (!this.isDrawing || !this.currentStroke) return;
95
+
96
+ // Add final point
97
+ this.addPoint(event.x, event.y, event.pressure);
98
+
99
+ // Finish the stroke
100
+ this.currentStroke.finish();
101
+
102
+ this.canvas.emit('strokeCompleted', {
103
+ stroke: this.currentStroke,
104
+ tool: this.name,
105
+ totalPoints: this.points.length
106
+ });
107
+
108
+ // Reset drawing state
109
+ this.isDrawing = false;
110
+ this.currentStroke = null;
111
+ this.points = [];
112
+ this.lastPoint = null;
113
+ }
114
+
115
+ /**
116
+ * Cancel current stroke
117
+ */
118
+ onCancel() {
119
+ if (this.isDrawing && this.currentStroke) {
120
+ // Remove incomplete stroke
121
+ this.canvas.removeStroke(this.currentStroke.id);
122
+
123
+ this.canvas.emit('strokeCancelled', {
124
+ stroke: this.currentStroke,
125
+ tool: this.name
126
+ });
127
+ }
128
+
129
+ super.onCancel();
130
+
131
+ // Reset state
132
+ this.points = [];
133
+ this.lastPoint = null;
134
+ }
135
+
136
+ /**
137
+ * Add a point to the current stroke
138
+ * @private
139
+ */
140
+ addPoint(x, y, pressure = 0.5) {
141
+ const point = {
142
+ x,
143
+ y,
144
+ pressure,
145
+ width: this.calculateStrokeWidth(pressure),
146
+ timestamp: Date.now()
147
+ };
148
+
149
+ this.points.push(point);
150
+
151
+ if (this.currentStroke) {
152
+ this.currentStroke.addPoint(point);
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Add a point only if it meets distance requirements
158
+ * @private
159
+ */
160
+ _addPointIfNeeded(x, y, pressure = 0.5) {
161
+ if (!this.lastPoint) {
162
+ this.addPoint(x, y, pressure);
163
+ this.lastPoint = { x, y };
164
+ return;
165
+ }
166
+
167
+ const distance = this.getDistance(this.lastPoint, { x, y });
168
+
169
+ // Add point if moved enough distance
170
+ if (distance >= this.minDistance) {
171
+ // Only interpolate for very large gaps to prevent performance issues
172
+ if (distance > 8) {
173
+ this._interpolatePoints(this.lastPoint, { x, y, pressure });
174
+ } else {
175
+ this.addPoint(x, y, pressure);
176
+ }
177
+ this.lastPoint = { x, y };
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Add interpolated points between two points for smoother strokes
183
+ * @private
184
+ */
185
+ _interpolatePoints(fromPoint, toPoint) {
186
+ const distance = this.getDistance(fromPoint, toPoint);
187
+ // Increase spacing to reduce point density - point every 3 pixels instead of 1.5
188
+ const steps = Math.ceil(distance / 3);
189
+
190
+ // Limit maximum interpolation steps to prevent performance issues
191
+ const maxSteps = 10;
192
+ const actualSteps = Math.min(steps, maxSteps);
193
+
194
+ for (let i = 1; i <= actualSteps; i++) {
195
+ const t = i / actualSteps;
196
+ const x = fromPoint.x + (toPoint.x - fromPoint.x) * t;
197
+ const y = fromPoint.y + (toPoint.y - fromPoint.y) * t;
198
+ const pressure = fromPoint.pressure ?
199
+ fromPoint.pressure + (toPoint.pressure - fromPoint.pressure) * t :
200
+ toPoint.pressure;
201
+
202
+ this.addPoint(x, y, pressure);
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Calculate distance between two points
208
+ * @private
209
+ */
210
+ getDistance(p1, p2) {
211
+ const dx = p2.x - p1.x;
212
+ const dy = p2.y - p1.y;
213
+ return Math.sqrt(dx * dx + dy * dy);
214
+ }
215
+
216
+ /**
217
+ * Update configuration and apply smoothing
218
+ */
219
+ onConfigUpdate() {
220
+ // Update minimum distance based on stroke width
221
+ this.minDistance = Math.max(1, this.config.strokeWidth * 0.2);
222
+
223
+ // Update current stroke if drawing
224
+ if (this.isDrawing && this.currentStroke) {
225
+ this.currentStroke.updateConfig({
226
+ strokeColor: this.config.strokeColor,
227
+ strokeOpacity: this.config.strokeOpacity
228
+ });
229
+ }
230
+
231
+ // Update cursor size
232
+ if (this.canvas.cursor) {
233
+ this.canvas.cursor.updateFromToolConfig(this.config);
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Calculate stroke width with pressure sensitivity
239
+ */
240
+ calculateStrokeWidth(pressure = 0.5) {
241
+ if (!this.config.pressureSensitive) {
242
+ return this.config.strokeWidth;
243
+ }
244
+
245
+ return super.calculateStrokeWidth(pressure);
246
+ }
247
+
248
+ /**
249
+ * Get smoothed points using interpolation
250
+ * @param {Array} points - Array of points to smooth
251
+ * @returns {Array} Smoothed points
252
+ */
253
+ getSmoothPath(points) {
254
+ if (points.length < 2) return points;
255
+
256
+ const smoothed = [];
257
+ const smoothing = this.config.smoothing;
258
+
259
+ // First point
260
+ smoothed.push(points[0]);
261
+
262
+ // Smooth intermediate points
263
+ for (let i = 1; i < points.length - 1; i++) {
264
+ const prev = points[i - 1];
265
+ const curr = points[i];
266
+ const next = points[i + 1];
267
+
268
+ const smoothedPoint = {
269
+ x: curr.x + smoothing * ((prev.x + next.x) / 2 - curr.x),
270
+ y: curr.y + smoothing * ((prev.y + next.y) / 2 - curr.y),
271
+ pressure: curr.pressure,
272
+ width: curr.width,
273
+ timestamp: curr.timestamp
274
+ };
275
+
276
+ smoothed.push(smoothedPoint);
277
+ }
278
+
279
+ // Last point
280
+ if (points.length > 1) {
281
+ smoothed.push(points[points.length - 1]);
282
+ }
283
+
284
+ return smoothed;
285
+ }
286
+
287
+ /**
288
+ * Handle keyboard shortcuts specific to pencil tool
289
+ */
290
+ onKeyboardShortcut(key, event) {
291
+ switch (key) {
292
+ case '[':
293
+ // Decrease brush size
294
+ this.updateConfig({
295
+ strokeWidth: Math.max(1, this.config.strokeWidth - 1)
296
+ });
297
+ // Notify tool manager of config change
298
+ this.canvas.toolManager.updateToolConfig(this.name, this.config);
299
+ return true;
300
+ case ']':
301
+ // Increase brush size
302
+ this.updateConfig({
303
+ strokeWidth: Math.min(50, this.config.strokeWidth + 1)
304
+ });
305
+ // Notify tool manager of config change
306
+ this.canvas.toolManager.updateToolConfig(this.name, this.config);
307
+ return true;
308
+ default:
309
+ return super.onKeyboardShortcut(key, event);
310
+ }
311
+ }
312
+
313
+ /**
314
+ * Get help text for pencil tool
315
+ */
316
+ getHelpText() {
317
+ return `${super.getHelpText()}\nShortcuts: [ ] to adjust brush size`;
318
+ }
319
+ }