@lydt/geogebra-studio 0.0.0-stage → 0.4.0-rc.2
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/.agents/plugins/marketplace.json +21 -0
- package/.codex-plugin/plugin.json +21 -0
- package/.mcp.json +18 -0
- package/LICENSE +339 -0
- package/LICENSE-MIT +21 -0
- package/README.md +108 -2
- package/README.zh-CN.md +89 -0
- package/THIRD_PARTY_NOTICES.md +35 -0
- package/dist/cli.d.ts +27 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +234 -0
- package/dist/cli.js.map +1 -0
- package/dist/export/files.d.ts +4 -0
- package/dist/export/files.d.ts.map +1 -0
- package/dist/export/files.js +44 -0
- package/dist/export/files.js.map +1 -0
- package/dist/export/gikz.d.ts +11 -0
- package/dist/export/gikz.d.ts.map +1 -0
- package/dist/export/gikz.js +28 -0
- package/dist/export/gikz.js.map +1 -0
- package/dist/export/html.d.ts +14 -0
- package/dist/export/html.d.ts.map +1 -0
- package/dist/export/html.js +97 -0
- package/dist/export/html.js.map +1 -0
- package/dist/export/semantic.d.ts +43 -0
- package/dist/export/semantic.d.ts.map +1 -0
- package/dist/export/semantic.js +212 -0
- package/dist/export/semantic.js.map +1 -0
- package/dist/export/viewer-themes.d.ts +76 -0
- package/dist/export/viewer-themes.d.ts.map +1 -0
- package/dist/export/viewer-themes.js +169 -0
- package/dist/export/viewer-themes.js.map +1 -0
- package/dist/export/workspace.d.ts +4 -0
- package/dist/export/workspace.d.ts.map +1 -0
- package/dist/export/workspace.js +25 -0
- package/dist/export/workspace.js.map +1 -0
- package/dist/gikz-browser.js +770 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +84 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +35 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +187 -0
- package/dist/server.js.map +1 -0
- package/dist/studio/artifact.d.ts +37 -0
- package/dist/studio/artifact.d.ts.map +1 -0
- package/dist/studio/artifact.js +61 -0
- package/dist/studio/artifact.js.map +1 -0
- package/dist/studio/browser.d.ts +7 -0
- package/dist/studio/browser.d.ts.map +1 -0
- package/dist/studio/browser.js +67 -0
- package/dist/studio/browser.js.map +1 -0
- package/dist/studio/install.d.ts +4 -0
- package/dist/studio/install.d.ts.map +1 -0
- package/dist/studio/install.js +60 -0
- package/dist/studio/install.js.map +1 -0
- package/dist/tools/apps-api-tools.d.ts +4 -0
- package/dist/tools/apps-api-tools.d.ts.map +1 -0
- package/dist/tools/apps-api-tools.js +96 -0
- package/dist/tools/apps-api-tools.js.map +1 -0
- package/dist/tools/educational-templates.d.ts +33 -0
- package/dist/tools/educational-templates.d.ts.map +1 -0
- package/dist/tools/educational-templates.js +261 -0
- package/dist/tools/educational-templates.js.map +1 -0
- package/dist/tools/export-tools.d.ts +3 -0
- package/dist/tools/export-tools.d.ts.map +1 -0
- package/dist/tools/export-tools.js +118 -0
- package/dist/tools/export-tools.js.map +1 -0
- package/dist/tools/geogebra-tools.d.ts +15 -0
- package/dist/tools/geogebra-tools.d.ts.map +1 -0
- package/dist/tools/geogebra-tools.js +3442 -0
- package/dist/tools/geogebra-tools.js.map +1 -0
- package/dist/tools/index.d.ts +43 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +183 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/performance-tools.d.ts +7 -0
- package/dist/tools/performance-tools.d.ts.map +1 -0
- package/dist/tools/performance-tools.js +312 -0
- package/dist/tools/performance-tools.js.map +1 -0
- package/dist/tools/templates/algebra-templates.d.ts +7 -0
- package/dist/tools/templates/algebra-templates.d.ts.map +1 -0
- package/dist/tools/templates/algebra-templates.js +568 -0
- package/dist/tools/templates/algebra-templates.js.map +1 -0
- package/dist/tools/templates/geometry-templates.d.ts +7 -0
- package/dist/tools/templates/geometry-templates.d.ts.map +1 -0
- package/dist/tools/templates/geometry-templates.js +492 -0
- package/dist/tools/templates/geometry-templates.js.map +1 -0
- package/dist/types/geogebra.d.ts +103 -0
- package/dist/types/geogebra.d.ts.map +1 -0
- package/dist/types/geogebra.js +34 -0
- package/dist/types/geogebra.js.map +1 -0
- package/dist/types/mcp.d.ts +125 -0
- package/dist/types/mcp.d.ts.map +1 -0
- package/dist/types/mcp.js +19 -0
- package/dist/types/mcp.js.map +1 -0
- package/dist/utils/app-info.d.ts +12 -0
- package/dist/utils/app-info.d.ts.map +1 -0
- package/dist/utils/app-info.js +30 -0
- package/dist/utils/app-info.js.map +1 -0
- package/dist/utils/errors.d.ts +37 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +72 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/gemini-compatibility.d.ts +39 -0
- package/dist/utils/gemini-compatibility.d.ts.map +1 -0
- package/dist/utils/gemini-compatibility.js +154 -0
- package/dist/utils/gemini-compatibility.js.map +1 -0
- package/dist/utils/geogebra-instance.d.ts +252 -0
- package/dist/utils/geogebra-instance.d.ts.map +1 -0
- package/dist/utils/geogebra-instance.js +1560 -0
- package/dist/utils/geogebra-instance.js.map +1 -0
- package/dist/utils/geogebra-mock.d.ts +172 -0
- package/dist/utils/geogebra-mock.d.ts.map +1 -0
- package/dist/utils/geogebra-mock.js +732 -0
- package/dist/utils/geogebra-mock.js.map +1 -0
- package/dist/utils/logger.d.ts +4 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +46 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/performance/index.d.ts +76 -0
- package/dist/utils/performance/index.d.ts.map +1 -0
- package/dist/utils/performance/index.js +163 -0
- package/dist/utils/performance/index.js.map +1 -0
- package/dist/utils/performance/instance-pool.d.ts +78 -0
- package/dist/utils/performance/instance-pool.d.ts.map +1 -0
- package/dist/utils/performance/instance-pool.js +268 -0
- package/dist/utils/performance/instance-pool.js.map +1 -0
- package/dist/utils/validation.d.ts +92 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +557 -0
- package/dist/utils/validation.js.map +1 -0
- package/dist/widget.html +110 -0
- package/docs/APPS-API-BRIDGE.md +28 -0
- package/docs/MODERNIZATION.md +15 -0
- package/docs/TANGENT-REGRESSION.md +34 -0
- package/docs/VALIDATION.md +26 -0
- package/docs/VISUAL-STANDARD.md +26 -0
- package/docs/images/native-construction.png +0 -0
- package/examples/tangent-construction.json +26 -0
- package/gikz/LICENSE +339 -0
- package/gikz/PROVENANCE.md +8 -0
- package/gikz/browser.cjs +2 -0
- package/gikz/cli.cjs +195 -0
- package/gikz/core.cjs +806 -0
- package/mcp.json +20 -0
- package/package.json +107 -4
- package/plugin.json +35 -0
- package/runtime/package.json +4 -0
- package/scripts/build-plugin.cjs +15 -0
- package/scripts/build-widget.cjs +7 -0
- package/skills/geogebra-construct/SKILL.md +40 -0
- package/skills/geogebra-setup/SKILL.md +37 -0
- package/skills/geometry-export/SKILL.md +31 -0
- package/src/cli.ts +212 -0
- package/src/export/files.ts +26 -0
- package/src/export/gikz.ts +22 -0
- package/src/export/html.ts +108 -0
- package/src/export/semantic.ts +180 -0
- package/src/export/viewer-themes.ts +165 -0
- package/src/export/workspace.ts +15 -0
- package/src/index.ts +71 -0
- package/src/server.ts +168 -0
- package/src/studio/artifact.ts +50 -0
- package/src/studio/browser.ts +38 -0
- package/src/studio/install.ts +43 -0
- package/src/tools/apps-api-tools.ts +96 -0
- package/src/tools/educational-templates.ts +299 -0
- package/src/tools/export-tools.ts +110 -0
- package/src/tools/geogebra-tools.ts +3836 -0
- package/src/tools/index.ts +192 -0
- package/src/tools/performance-tools.ts +322 -0
- package/src/tools/templates/algebra-templates.ts +619 -0
- package/src/tools/templates/geometry-templates.ts +550 -0
- package/src/types/geogebra.ts +140 -0
- package/src/types/global.d.ts +31 -0
- package/src/types/mcp.ts +141 -0
- package/src/utils/app-info.ts +35 -0
- package/src/utils/errors.ts +97 -0
- package/src/utils/gemini-compatibility.ts +193 -0
- package/src/utils/geogebra-instance.ts +1712 -0
- package/src/utils/geogebra-mock.ts +867 -0
- package/src/utils/logger.ts +60 -0
- package/src/utils/performance/index.ts +211 -0
- package/src/utils/performance/instance-pool.ts +323 -0
- package/src/utils/validation.ts +647 -0
- package/tsconfig.json +35 -0
- package/widget/main.ts +12 -0
|
@@ -0,0 +1,867 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GeoGebraConfig,
|
|
3
|
+
GeoGebraAPI,
|
|
4
|
+
GeoGebraCommandResult,
|
|
5
|
+
GeoGebraObject,
|
|
6
|
+
GeoGebraConnectionError,
|
|
7
|
+
GeoGebraCommandError
|
|
8
|
+
} from '../types/geogebra';
|
|
9
|
+
import logger from './logger';
|
|
10
|
+
import { randomUUID as uuidv4 } from 'crypto';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Mock GeoGebra Instance for demonstration and testing
|
|
14
|
+
* This provides the same interface as the full implementation
|
|
15
|
+
* but uses in-memory simulation instead of a real browser instance
|
|
16
|
+
*/
|
|
17
|
+
export class MockGeoGebraInstance implements GeoGebraAPI {
|
|
18
|
+
private isInitialized: boolean = false;
|
|
19
|
+
private lastActivity: Date = new Date();
|
|
20
|
+
private objects: Map<string, GeoGebraObject> = new Map();
|
|
21
|
+
private animatingObjects: Set<string> = new Set();
|
|
22
|
+
private animationSpeeds: Map<string, number> = new Map();
|
|
23
|
+
private tracedObjects: Set<string> = new Set();
|
|
24
|
+
private isAnimating: boolean = false;
|
|
25
|
+
|
|
26
|
+
public readonly id: string;
|
|
27
|
+
public readonly config: GeoGebraConfig;
|
|
28
|
+
|
|
29
|
+
constructor(config: GeoGebraConfig = {}) {
|
|
30
|
+
this.id = uuidv4();
|
|
31
|
+
this.config = {
|
|
32
|
+
appName: 'graphing',
|
|
33
|
+
width: 800,
|
|
34
|
+
height: 600,
|
|
35
|
+
showMenuBar: false,
|
|
36
|
+
showToolBar: false,
|
|
37
|
+
showAlgebraInput: false,
|
|
38
|
+
showResetIcon: false,
|
|
39
|
+
enableRightClick: true,
|
|
40
|
+
language: 'en',
|
|
41
|
+
...config
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
logger.info(`Created Mock GeoGebra instance ${this.id}`, { config: this.config });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Initialize the mock instance
|
|
49
|
+
*/
|
|
50
|
+
async initialize(): Promise<void> {
|
|
51
|
+
try {
|
|
52
|
+
logger.info(`Initializing Mock GeoGebra instance ${this.id}`);
|
|
53
|
+
|
|
54
|
+
// Simulate initialization delay
|
|
55
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
56
|
+
|
|
57
|
+
this.isInitialized = true;
|
|
58
|
+
this.updateActivity();
|
|
59
|
+
|
|
60
|
+
logger.info(`Mock GeoGebra instance ${this.id} initialized successfully`);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
logger.error(`Failed to initialize Mock GeoGebra instance ${this.id}`, error);
|
|
63
|
+
throw new GeoGebraConnectionError(
|
|
64
|
+
`Failed to initialize GeoGebra: ${error instanceof Error ? error.message : String(error)}`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Execute a GeoGebra command (mock implementation)
|
|
71
|
+
*/
|
|
72
|
+
async evalCommand(command: string): Promise<GeoGebraCommandResult> {
|
|
73
|
+
this.ensureInitialized();
|
|
74
|
+
this.updateActivity();
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
logger.debug(`Executing mock command on instance ${this.id}: ${command}`);
|
|
78
|
+
|
|
79
|
+
// Parse basic commands
|
|
80
|
+
const result = this.parseCommand(command);
|
|
81
|
+
|
|
82
|
+
logger.debug(`Mock command executed successfully on instance ${this.id}: ${command}`);
|
|
83
|
+
return {
|
|
84
|
+
success: true,
|
|
85
|
+
result: result
|
|
86
|
+
};
|
|
87
|
+
} catch (error) {
|
|
88
|
+
logger.error(`Mock command execution failed on instance ${this.id}`, { command, error });
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
error: error instanceof Error ? error.message : String(error)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Parse and execute basic GeoGebra commands
|
|
98
|
+
*/
|
|
99
|
+
private parseCommand(command: string): string {
|
|
100
|
+
const trimmed = command.trim();
|
|
101
|
+
|
|
102
|
+
// Point creation: A = (x, y)
|
|
103
|
+
const pointMatch = trimmed.match(/^([A-Za-z]\w*)\s*=\s*\(\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*\)$/);
|
|
104
|
+
if (pointMatch && pointMatch[1] && pointMatch[2] && pointMatch[3]) {
|
|
105
|
+
const [, name, x, y] = pointMatch;
|
|
106
|
+
this.objects.set(name, {
|
|
107
|
+
name,
|
|
108
|
+
type: 'point',
|
|
109
|
+
visible: true,
|
|
110
|
+
defined: true,
|
|
111
|
+
x: parseFloat(x),
|
|
112
|
+
y: parseFloat(y),
|
|
113
|
+
z: 0,
|
|
114
|
+
color: '#0000FF'
|
|
115
|
+
});
|
|
116
|
+
return `Point ${name} created at (${x}, ${y})`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Slider creation: name = Slider(min, max, increment, speed, width, isAngle, horizontal, animating, random)
|
|
120
|
+
const sliderMatch = trimmed.match(/^([A-Za-z]\w*)\s*=\s*Slider\s*\(\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*(?:\s*,\s*(\d+(?:\.\d+)?))?\s*.*?\)$/);
|
|
121
|
+
if (sliderMatch && sliderMatch[1] && sliderMatch[2] && sliderMatch[3]) {
|
|
122
|
+
const [, name, min, max, _increment] = sliderMatch;
|
|
123
|
+
const defaultValue = parseFloat(min);
|
|
124
|
+
this.objects.set(name, {
|
|
125
|
+
name,
|
|
126
|
+
type: 'slider',
|
|
127
|
+
visible: true,
|
|
128
|
+
defined: true,
|
|
129
|
+
value: defaultValue,
|
|
130
|
+
color: '#0073E6'
|
|
131
|
+
});
|
|
132
|
+
return `Slider ${name} created with range [${min}, ${max}], current value: ${defaultValue}`;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Line creation: l = Line(A, B)
|
|
136
|
+
const lineMatch = trimmed.match(/^([A-Za-z]\w*)\s*=\s*Line\s*\(\s*([A-Za-z]\w*)\s*,\s*([A-Za-z]\w*)\s*\)$/);
|
|
137
|
+
if (lineMatch && lineMatch[1] && lineMatch[2] && lineMatch[3]) {
|
|
138
|
+
const [, name, point1, point2] = lineMatch;
|
|
139
|
+
|
|
140
|
+
if (!this.objects.has(point1) || !this.objects.has(point2)) {
|
|
141
|
+
throw new Error(`Points ${point1} or ${point2} do not exist`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
this.objects.set(name, {
|
|
145
|
+
name,
|
|
146
|
+
type: 'line',
|
|
147
|
+
visible: true,
|
|
148
|
+
defined: true,
|
|
149
|
+
color: '#000000'
|
|
150
|
+
});
|
|
151
|
+
return `Line ${name} created through points ${point1} and ${point2}`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Function creation: f(x) = expression
|
|
155
|
+
const functionMatch = trimmed.match(/^([A-Za-z]\w*)\s*\(\s*x\s*\)\s*=\s*(.+)$/);
|
|
156
|
+
if (functionMatch && functionMatch[1] && functionMatch[2]) {
|
|
157
|
+
const [, name, expression] = functionMatch;
|
|
158
|
+
this.objects.set(name, {
|
|
159
|
+
name,
|
|
160
|
+
type: 'function',
|
|
161
|
+
visible: true,
|
|
162
|
+
defined: true,
|
|
163
|
+
value: expression,
|
|
164
|
+
color: '#FF0000'
|
|
165
|
+
});
|
|
166
|
+
return `Function ${name}(x) = ${expression} created`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Parametric curve creation: name = Curve(x_expr, y_expr, param, start, end)
|
|
170
|
+
const parametricMatch = trimmed.match(/^([A-Za-z]\w*)\s*=\s*Curve\s*\(\s*(.+?)\s*,\s*(.+?)\s*,\s*([A-Za-z]\w*)\s*,\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*\)$/);
|
|
171
|
+
if (parametricMatch && parametricMatch[1] && parametricMatch[2] && parametricMatch[3] && parametricMatch[4] && parametricMatch[5] && parametricMatch[6]) {
|
|
172
|
+
const [, name, xExpr, yExpr, param, tMin, tMax] = parametricMatch;
|
|
173
|
+
this.objects.set(name, {
|
|
174
|
+
name,
|
|
175
|
+
type: 'curve',
|
|
176
|
+
visible: true,
|
|
177
|
+
defined: true,
|
|
178
|
+
value: `Parametric: x(${param}) = ${xExpr}, y(${param}) = ${yExpr}, ${param} ∈ [${tMin}, ${tMax}]`,
|
|
179
|
+
color: '#00AA00'
|
|
180
|
+
});
|
|
181
|
+
return `Parametric curve ${name} created: x(${param}) = ${xExpr}, y(${param}) = ${yExpr}, ${param} ∈ [${tMin}, ${tMax}]`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Implicit curve creation: name = ImplicitCurve(expression)
|
|
185
|
+
const implicitMatch = trimmed.match(/^([A-Za-z]\w*)\s*=\s*ImplicitCurve\s*\(\s*(.+)\s*\)$/);
|
|
186
|
+
if (implicitMatch && implicitMatch[1] && implicitMatch[2]) {
|
|
187
|
+
const [, name, expression] = implicitMatch;
|
|
188
|
+
this.objects.set(name, {
|
|
189
|
+
name,
|
|
190
|
+
type: 'implicitcurve',
|
|
191
|
+
visible: true,
|
|
192
|
+
defined: true,
|
|
193
|
+
value: `Implicit: ${expression} = 0`,
|
|
194
|
+
color: '#AA0000'
|
|
195
|
+
});
|
|
196
|
+
return `Implicit curve ${name} created: ${expression} = 0`;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Styling commands: SetColor, SetLineThickness, SetLineStyle
|
|
200
|
+
const setColorMatch = trimmed.match(/^SetColor\s*\(\s*([A-Za-z]\w*)\s*,\s*"([^"]+)"\s*\)$/);
|
|
201
|
+
if (setColorMatch && setColorMatch[1] && setColorMatch[2]) {
|
|
202
|
+
const [, name, color] = setColorMatch;
|
|
203
|
+
const obj = this.objects.get(name);
|
|
204
|
+
if (obj) {
|
|
205
|
+
obj.color = color;
|
|
206
|
+
return `Color of ${name} set to ${color}`;
|
|
207
|
+
}
|
|
208
|
+
return `Object ${name} not found for color setting`;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const setThicknessMatch = trimmed.match(/^SetLineThickness\s*\(\s*([A-Za-z]\w*)\s*,\s*(\d+)\s*\)$/);
|
|
212
|
+
if (setThicknessMatch && setThicknessMatch[1] && setThicknessMatch[2]) {
|
|
213
|
+
const [, name, thickness] = setThicknessMatch;
|
|
214
|
+
const obj = this.objects.get(name);
|
|
215
|
+
if (obj) {
|
|
216
|
+
// Store thickness in a custom property (mock doesn't have thickness in type)
|
|
217
|
+
(obj as any).thickness = parseInt(thickness);
|
|
218
|
+
return `Line thickness of ${name} set to ${thickness}`;
|
|
219
|
+
}
|
|
220
|
+
return `Object ${name} not found for thickness setting`;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const setStyleMatch = trimmed.match(/^SetLineStyle\s*\(\s*([A-Za-z]\w*)\s*,\s*(\d+)\s*\)$/);
|
|
224
|
+
if (setStyleMatch && setStyleMatch[1] && setStyleMatch[2]) {
|
|
225
|
+
const [, name, lineType] = setStyleMatch;
|
|
226
|
+
const obj = this.objects.get(name);
|
|
227
|
+
if (obj) {
|
|
228
|
+
const style = lineType === '10' ? 'dashed' : lineType === '20' ? 'dotted' : 'solid';
|
|
229
|
+
(obj as any).style = style;
|
|
230
|
+
return `Line style of ${name} set to ${style}`;
|
|
231
|
+
}
|
|
232
|
+
return `Object ${name} not found for style setting`;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Circle creation: Circle((x, y), r)
|
|
236
|
+
const circleMatch = trimmed.match(/^Circle\s*\(\s*\(\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*\)\s*,\s*(\d+(?:\.\d+)?)\s*\)$/);
|
|
237
|
+
if (circleMatch && circleMatch[1] && circleMatch[2] && circleMatch[3]) {
|
|
238
|
+
const [, x, y, r] = circleMatch;
|
|
239
|
+
const name = `circle_${Date.now()}`;
|
|
240
|
+
this.objects.set(name, {
|
|
241
|
+
name,
|
|
242
|
+
type: 'circle',
|
|
243
|
+
visible: true,
|
|
244
|
+
defined: true,
|
|
245
|
+
x: parseFloat(x),
|
|
246
|
+
y: parseFloat(y),
|
|
247
|
+
value: parseFloat(r),
|
|
248
|
+
color: '#00FF00'
|
|
249
|
+
});
|
|
250
|
+
return `Circle created at (${x}, ${y}) with radius ${r}`;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// CAS Operations
|
|
254
|
+
|
|
255
|
+
// Solve equation: Solve(equation)
|
|
256
|
+
const solveMatch = trimmed.match(/^Solve\s*\(\s*(.+)\s*\)$/);
|
|
257
|
+
if (solveMatch && solveMatch[1]) {
|
|
258
|
+
const [, equation] = solveMatch;
|
|
259
|
+
return this.mockSolveEquation(equation);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Solve equation for variable: Solve(equation, variable)
|
|
263
|
+
const solveVarMatch = trimmed.match(/^Solve\s*\(\s*(.+?)\s*,\s*([a-zA-Z]\w*)\s*\)$/);
|
|
264
|
+
if (solveVarMatch && solveVarMatch[1] && solveVarMatch[2]) {
|
|
265
|
+
const [, equation, variable] = solveVarMatch;
|
|
266
|
+
return this.mockSolveEquation(equation, variable);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Differentiate with respect to variable: Derivative(expression, variable)
|
|
270
|
+
const derivativeVarMatch = trimmed.match(/^Derivative\s*\(\s*(.+?)\s*,\s*([a-zA-Z]\w*)\s*\)$/);
|
|
271
|
+
if (derivativeVarMatch && derivativeVarMatch[1] && derivativeVarMatch[2]) {
|
|
272
|
+
const [, expression, variable] = derivativeVarMatch;
|
|
273
|
+
return this.mockDifferentiate(expression, variable);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Differentiate: Derivative(expression)
|
|
277
|
+
const derivativeMatch = trimmed.match(/^Derivative\s*\(\s*(.+)\s*\)$/);
|
|
278
|
+
if (derivativeMatch && derivativeMatch[1]) {
|
|
279
|
+
const [, expression] = derivativeMatch;
|
|
280
|
+
return this.mockDifferentiate(expression);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Integrate with respect to variable: Integral(expression, variable)
|
|
284
|
+
const integralVarMatch = trimmed.match(/^Integral\s*\(\s*(.+?)\s*,\s*([a-zA-Z]\w*)\s*\)$/);
|
|
285
|
+
if (integralVarMatch && integralVarMatch[1] && integralVarMatch[2]) {
|
|
286
|
+
const [, expression, variable] = integralVarMatch;
|
|
287
|
+
return this.mockIntegrate(expression, variable);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Integrate: Integral(expression)
|
|
291
|
+
const integralMatch = trimmed.match(/^Integral\s*\(\s*(.+)\s*\)$/);
|
|
292
|
+
if (integralMatch && integralMatch[1]) {
|
|
293
|
+
const [, expression] = integralMatch;
|
|
294
|
+
return this.mockIntegrate(expression);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Simplify: Simplify(expression)
|
|
298
|
+
const simplifyMatch = trimmed.match(/^Simplify\s*\(\s*(.+)\s*\)$/);
|
|
299
|
+
if (simplifyMatch && simplifyMatch[1]) {
|
|
300
|
+
const [, expression] = simplifyMatch;
|
|
301
|
+
return this.mockSimplify(expression);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Generic command (just acknowledge)
|
|
305
|
+
return `Command executed: ${command}`;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Execute command and get labels of created objects
|
|
310
|
+
*/
|
|
311
|
+
async evalCommandGetLabels(command: string): Promise<string[]> {
|
|
312
|
+
this.ensureInitialized();
|
|
313
|
+
this.updateActivity();
|
|
314
|
+
|
|
315
|
+
try {
|
|
316
|
+
const beforeObjects = new Set(this.objects.keys());
|
|
317
|
+
await this.evalCommand(command);
|
|
318
|
+
const afterObjects = new Set(this.objects.keys());
|
|
319
|
+
|
|
320
|
+
const newLabels = Array.from(afterObjects).filter(label => !beforeObjects.has(label));
|
|
321
|
+
|
|
322
|
+
logger.debug(`Mock command executed on instance ${this.id}, labels: ${newLabels.join(', ')}`);
|
|
323
|
+
return newLabels;
|
|
324
|
+
} catch (error) {
|
|
325
|
+
logger.error(`Failed to execute mock command with labels on instance ${this.id}`, { command, error });
|
|
326
|
+
throw new GeoGebraCommandError(
|
|
327
|
+
`Failed to execute command: ${error instanceof Error ? error.message : String(error)}`,
|
|
328
|
+
command
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Delete an object
|
|
335
|
+
*/
|
|
336
|
+
async deleteObject(objName: string): Promise<boolean> {
|
|
337
|
+
this.ensureInitialized();
|
|
338
|
+
this.updateActivity();
|
|
339
|
+
|
|
340
|
+
try {
|
|
341
|
+
const deleted = this.objects.delete(objName);
|
|
342
|
+
if (deleted) {
|
|
343
|
+
logger.debug(`Deleted object ${objName} from mock instance ${this.id}`);
|
|
344
|
+
}
|
|
345
|
+
return deleted;
|
|
346
|
+
} catch (error) {
|
|
347
|
+
logger.error(`Failed to delete object ${objName} on mock instance ${this.id}`, error);
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Check if object exists
|
|
354
|
+
*/
|
|
355
|
+
async exists(objName: string): Promise<boolean> {
|
|
356
|
+
this.ensureInitialized();
|
|
357
|
+
this.updateActivity();
|
|
358
|
+
return this.objects.has(objName);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Check if object is defined
|
|
363
|
+
*/
|
|
364
|
+
async isDefined(objName: string): Promise<boolean> {
|
|
365
|
+
this.ensureInitialized();
|
|
366
|
+
this.updateActivity();
|
|
367
|
+
const obj = this.objects.get(objName);
|
|
368
|
+
return obj ? obj.defined : false;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Get all object names
|
|
373
|
+
*/
|
|
374
|
+
async getAllObjectNames(type?: string): Promise<string[]> {
|
|
375
|
+
this.ensureInitialized();
|
|
376
|
+
this.updateActivity();
|
|
377
|
+
|
|
378
|
+
if (type) {
|
|
379
|
+
return Array.from(this.objects.values())
|
|
380
|
+
.filter(obj => obj.type === type)
|
|
381
|
+
.map(obj => obj.name);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return Array.from(this.objects.keys());
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Get object information
|
|
389
|
+
*/
|
|
390
|
+
async getObjectInfo(objName: string): Promise<GeoGebraObject | null> {
|
|
391
|
+
this.ensureInitialized();
|
|
392
|
+
this.updateActivity();
|
|
393
|
+
return this.objects.get(objName) || null;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Get X coordinate of object
|
|
398
|
+
*/
|
|
399
|
+
async getXcoord(objName: string): Promise<number> {
|
|
400
|
+
this.ensureInitialized();
|
|
401
|
+
this.updateActivity();
|
|
402
|
+
const obj = this.objects.get(objName);
|
|
403
|
+
return obj?.x || 0;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Get Y coordinate of object
|
|
408
|
+
*/
|
|
409
|
+
async getYcoord(objName: string): Promise<number> {
|
|
410
|
+
this.ensureInitialized();
|
|
411
|
+
this.updateActivity();
|
|
412
|
+
const obj = this.objects.get(objName);
|
|
413
|
+
return obj?.y || 0;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Get Z coordinate of object
|
|
418
|
+
*/
|
|
419
|
+
async getZcoord(objName: string): Promise<number> {
|
|
420
|
+
this.ensureInitialized();
|
|
421
|
+
this.updateActivity();
|
|
422
|
+
const obj = this.objects.get(objName);
|
|
423
|
+
return obj?.z || 0;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Get value of object
|
|
428
|
+
*/
|
|
429
|
+
async getValue(objName: string): Promise<number> {
|
|
430
|
+
this.ensureInitialized();
|
|
431
|
+
this.updateActivity();
|
|
432
|
+
const obj = this.objects.get(objName);
|
|
433
|
+
return typeof obj?.value === 'number' ? obj.value : 0;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Get value as string
|
|
438
|
+
*/
|
|
439
|
+
async getValueString(objName: string): Promise<string> {
|
|
440
|
+
this.ensureInitialized();
|
|
441
|
+
this.updateActivity();
|
|
442
|
+
const obj = this.objects.get(objName);
|
|
443
|
+
return obj?.value?.toString() || '';
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Create new construction
|
|
448
|
+
*/
|
|
449
|
+
async newConstruction(): Promise<void> {
|
|
450
|
+
this.ensureInitialized();
|
|
451
|
+
this.updateActivity();
|
|
452
|
+
this.objects.clear();
|
|
453
|
+
logger.debug(`New construction created on mock instance ${this.id}`);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Reset to initial state
|
|
458
|
+
*/
|
|
459
|
+
async reset(): Promise<void> {
|
|
460
|
+
this.ensureInitialized();
|
|
461
|
+
this.updateActivity();
|
|
462
|
+
this.objects.clear();
|
|
463
|
+
logger.debug(`Mock instance ${this.id} reset to initial state`);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Refresh all views
|
|
468
|
+
*/
|
|
469
|
+
async refreshViews(): Promise<void> {
|
|
470
|
+
this.ensureInitialized();
|
|
471
|
+
this.updateActivity();
|
|
472
|
+
logger.debug(`Views refreshed on mock instance ${this.id}`);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Set coordinate system bounds (mock implementation)
|
|
477
|
+
*/
|
|
478
|
+
async setCoordSystem(xmin: number, xmax: number, ymin: number, ymax: number): Promise<void> {
|
|
479
|
+
this.ensureInitialized();
|
|
480
|
+
this.updateActivity();
|
|
481
|
+
logger.debug(`Mock coordinate system set on instance ${this.id}: x[${xmin}, ${xmax}], y[${ymin}, ${ymax}]`);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Set axes visibility (mock implementation)
|
|
486
|
+
*/
|
|
487
|
+
async setAxesVisible(xAxis: boolean, yAxis: boolean): Promise<void> {
|
|
488
|
+
this.ensureInitialized();
|
|
489
|
+
this.updateActivity();
|
|
490
|
+
logger.debug(`Mock axes visibility set on instance ${this.id}: x=${xAxis}, y=${yAxis}`);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Set grid visibility (mock implementation)
|
|
495
|
+
*/
|
|
496
|
+
async setGridVisible(visible: boolean): Promise<void> {
|
|
497
|
+
this.ensureInitialized();
|
|
498
|
+
this.updateActivity();
|
|
499
|
+
logger.debug(`Mock instance ${this.id}: Set grid visible: ${visible}`);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Check if instance is ready
|
|
504
|
+
*/
|
|
505
|
+
async isReady(): Promise<boolean> {
|
|
506
|
+
return this.isInitialized;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Export construction as PNG (base64) - Mock implementation with enhanced parameters
|
|
511
|
+
*/
|
|
512
|
+
async exportPNG(scale: number = 1, transparent: boolean = false, dpi: number = 72, width?: number, height?: number): Promise<string> {
|
|
513
|
+
this.ensureInitialized();
|
|
514
|
+
this.updateActivity();
|
|
515
|
+
|
|
516
|
+
// Return a simple base64 encoded placeholder PNG
|
|
517
|
+
const placeholderPNG = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==';
|
|
518
|
+
logger.debug(`Mock PNG exported from instance ${this.id} with scale ${scale}, transparent ${transparent}, dpi ${dpi}, dimensions ${width}x${height}`);
|
|
519
|
+
return placeholderPNG;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Export construction as SVG - Mock implementation
|
|
524
|
+
*/
|
|
525
|
+
async exportSVG(): Promise<string> {
|
|
526
|
+
this.ensureInitialized();
|
|
527
|
+
this.updateActivity();
|
|
528
|
+
|
|
529
|
+
// Return a simple SVG with mock construction
|
|
530
|
+
const mockSVG = `<svg width="800" height="600" xmlns="http://www.w3.org/2000/svg">
|
|
531
|
+
<rect width="800" height="600" fill="white"/>
|
|
532
|
+
<text x="400" y="300" text-anchor="middle" font-family="Arial" font-size="20">Mock GeoGebra Construction</text>
|
|
533
|
+
${Array.from(this.objects.values()).map(obj => {
|
|
534
|
+
if (obj.type === 'point') {
|
|
535
|
+
return `<circle cx="${(obj.x || 0) * 50 + 400}" cy="${(obj.y || 0) * -50 + 300}" r="3" fill="${obj.color || '#0000FF'}"/>`;
|
|
536
|
+
}
|
|
537
|
+
return '';
|
|
538
|
+
}).join('\n ')}
|
|
539
|
+
</svg>`;
|
|
540
|
+
|
|
541
|
+
logger.debug(`Mock SVG exported from instance ${this.id}`);
|
|
542
|
+
return mockSVG.trim();
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Export construction as PDF (base64) - Mock implementation
|
|
547
|
+
*/
|
|
548
|
+
async exportPDF(): Promise<string> {
|
|
549
|
+
this.ensureInitialized();
|
|
550
|
+
this.updateActivity();
|
|
551
|
+
|
|
552
|
+
logger.debug(`Mock PDF export from instance ${this.id}`);
|
|
553
|
+
|
|
554
|
+
// Return mock PDF data (base64 encoded)
|
|
555
|
+
const mockPdfData = 'JVBERi0xLjQKJcOkw7zDtsOgCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp4nDPQM1Qo5ypUMFAw0DMwULBVqOZSUDBQykvMTbVSUDC2UshIzStRyMnPS7VVqAUAYQsKZAplbmRzdHJlYW0KZW5kb2JqCgozIDAgb2JqCjI4CmVuZG9iagoKMSAwIG9iago8PC9UeXBlL0NhdGFsb2cvUGFnZXMgNCAwIFI+PgplbmRvYmoKCjQgMCBvYmoKPDwvVHlwZS9QYWdlcy9LaWRzWzUgMCBSXS9Db3VudCAxPj4KZW5kb2JqCgo1IDAgb2JqCjw8L1R5cGUvUGFnZS9QYXJlbnQgNCAwIFIvTWVkaWFCb3hbMCAwIDYxMiA3OTJdL0NvbnRlbnRzIDIgMCBSPj4KZW5kb2JqCgp4cmVmCjAgNgowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAyOTQgMDAwMDAgbiAKMDAwMDAwMDAwOSAwMDAwMCBuIAowMDAwMDAwMjc0IDAwMDAwIG4gCjAwMDAwMDAzNDMgMDAwMDAgbiAKMDAwMDAwMDQwMCAwMDAwMCBuIAp0cmFpbGVyCjw8L1NpemUgNi9Sb290IDEgMCBSPj4Kc3RhcnR4cmVmCjUwMwolJUVPRg==';
|
|
556
|
+
|
|
557
|
+
return mockPdfData;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Export animation as frames (GEB-17 enhancement)
|
|
562
|
+
*/
|
|
563
|
+
async exportAnimation(options: {
|
|
564
|
+
duration?: number;
|
|
565
|
+
frameRate?: number;
|
|
566
|
+
format?: 'gif' | 'frames';
|
|
567
|
+
width?: number;
|
|
568
|
+
height?: number;
|
|
569
|
+
} = {}): Promise<string | string[]> {
|
|
570
|
+
this.ensureInitialized();
|
|
571
|
+
this.updateActivity();
|
|
572
|
+
|
|
573
|
+
const {
|
|
574
|
+
duration = 5000, // 5 seconds
|
|
575
|
+
frameRate = 10, // 10 fps
|
|
576
|
+
format = 'frames',
|
|
577
|
+
width = 800,
|
|
578
|
+
height = 600
|
|
579
|
+
} = options;
|
|
580
|
+
|
|
581
|
+
logger.debug(`Mock animation export from instance ${this.id}`, { duration, frameRate, format, width, height });
|
|
582
|
+
|
|
583
|
+
// Calculate number of frames
|
|
584
|
+
const totalFrames = Math.ceil((duration / 1000) * frameRate);
|
|
585
|
+
|
|
586
|
+
// Generate mock frame data
|
|
587
|
+
const frames: string[] = [];
|
|
588
|
+
for (let i = 0; i < totalFrames; i++) {
|
|
589
|
+
// Generate a simple mock PNG frame (base64 encoded)
|
|
590
|
+
const mockFrameData = `iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==_frame_${i}`;
|
|
591
|
+
frames.push(mockFrameData);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
logger.debug(`Mock animation export completed: ${frames.length} frames generated`);
|
|
595
|
+
|
|
596
|
+
if (format === 'frames') {
|
|
597
|
+
return frames;
|
|
598
|
+
} else {
|
|
599
|
+
// For GIF format, return a mock GIF data string
|
|
600
|
+
return 'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7_mock_gif';
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Cleanup resources
|
|
606
|
+
*/
|
|
607
|
+
async cleanup(): Promise<void> {
|
|
608
|
+
logger.info(`Cleaning up Mock GeoGebra instance ${this.id}`);
|
|
609
|
+
this.objects.clear();
|
|
610
|
+
this.isInitialized = false;
|
|
611
|
+
logger.info(`Mock GeoGebra instance ${this.id} cleaned up successfully`);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Get instance state
|
|
616
|
+
*/
|
|
617
|
+
getState() {
|
|
618
|
+
return {
|
|
619
|
+
id: this.id,
|
|
620
|
+
isReady: this.isInitialized,
|
|
621
|
+
lastActivity: this.lastActivity,
|
|
622
|
+
config: this.config
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* Update last activity timestamp
|
|
628
|
+
*/
|
|
629
|
+
private updateActivity(): void {
|
|
630
|
+
this.lastActivity = new Date();
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Ensure instance is initialized
|
|
635
|
+
*/
|
|
636
|
+
private ensureInitialized(): void {
|
|
637
|
+
if (!this.isInitialized) {
|
|
638
|
+
throw new GeoGebraConnectionError('Mock GeoGebra instance not initialized');
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Mock implementation of equation solving
|
|
644
|
+
*/
|
|
645
|
+
private mockSolveEquation(equation: string, variable?: string): string {
|
|
646
|
+
logger.debug(`Mock solving equation: ${equation}${variable ? ` for ${variable}` : ''}`);
|
|
647
|
+
|
|
648
|
+
// Simple mock responses for common cases
|
|
649
|
+
if (equation.includes('x^2')) {
|
|
650
|
+
return variable ?
|
|
651
|
+
`{${variable} = -√(solution), ${variable} = √(solution)}` :
|
|
652
|
+
`Solutions: x = -√(solution), x = √(solution)`;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
if (equation.includes('x =') || equation.includes('= x')) {
|
|
656
|
+
const match = equation.match(/(-?\d+(?:\.\d+)?)/);
|
|
657
|
+
const value = match ? match[1] : '0';
|
|
658
|
+
return variable ?
|
|
659
|
+
`{${variable} = ${value}}` :
|
|
660
|
+
`Solution: x = ${value}`;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// Linear equation pattern
|
|
664
|
+
if (equation.match(/\d*x\s*[+-]/)) {
|
|
665
|
+
return variable ?
|
|
666
|
+
`{${variable} = solution_value}` :
|
|
667
|
+
`Solution: x = solution_value`;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
return `Mock solution for equation: ${equation}`;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Mock implementation of differentiation
|
|
675
|
+
*/
|
|
676
|
+
private mockDifferentiate(expression: string, variable: string = 'x'): string {
|
|
677
|
+
logger.debug(`Mock differentiating: ${expression} with respect to ${variable}`);
|
|
678
|
+
|
|
679
|
+
// Simple mock responses for common cases
|
|
680
|
+
if (expression === variable) {
|
|
681
|
+
return '1';
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
if (expression.includes(`${variable}^2`)) {
|
|
685
|
+
return `2*${variable}`;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
if (expression.includes(`${variable}^3`)) {
|
|
689
|
+
return `3*${variable}^2`;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
if (expression.includes(`sin(${variable})`)) {
|
|
693
|
+
return `cos(${variable})`;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
if (expression.includes(`cos(${variable})`)) {
|
|
697
|
+
return `-sin(${variable})`;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
if (expression.includes(`e^${variable}`)) {
|
|
701
|
+
return `e^${variable}`;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
if (!expression.includes(variable)) {
|
|
705
|
+
return '0';
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
return `d/d${variable}(${expression})`;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Mock implementation of integration
|
|
713
|
+
*/
|
|
714
|
+
private mockIntegrate(expression: string, variable: string = 'x'): string {
|
|
715
|
+
logger.debug(`Mock integrating: ${expression} with respect to ${variable}`);
|
|
716
|
+
|
|
717
|
+
// Simple mock responses for common cases
|
|
718
|
+
if (expression === '1') {
|
|
719
|
+
return variable;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
if (expression === variable) {
|
|
723
|
+
return `${variable}^2/2`;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
if (expression.includes(`${variable}^2`)) {
|
|
727
|
+
return `${variable}^3/3`;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
if (expression.includes(`${variable}^3`)) {
|
|
731
|
+
return `${variable}^4/4`;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if (expression.includes(`sin(${variable})`)) {
|
|
735
|
+
return `-cos(${variable})`;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
if (expression.includes(`cos(${variable})`)) {
|
|
739
|
+
return `sin(${variable})`;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
if (expression.includes(`e^${variable}`)) {
|
|
743
|
+
return `e^${variable}`;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
return `∫(${expression}, ${variable})dx`;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Mock implementation of expression simplification
|
|
751
|
+
*/
|
|
752
|
+
private mockSimplify(expression: string): string {
|
|
753
|
+
logger.debug(`Mock simplifying: ${expression}`);
|
|
754
|
+
|
|
755
|
+
// Simple mock simplifications
|
|
756
|
+
let simplified = expression;
|
|
757
|
+
|
|
758
|
+
// Remove unnecessary parentheses and spaces
|
|
759
|
+
simplified = simplified.replace(/\s+/g, '');
|
|
760
|
+
simplified = simplified.replace(/\(([^()]+)\)/g, '$1');
|
|
761
|
+
|
|
762
|
+
// Combine like terms (very basic)
|
|
763
|
+
simplified = simplified.replace(/(\d+)\*x\+(\d+)\*x/g, (_match, a, b) => {
|
|
764
|
+
return `${parseInt(a) + parseInt(b)}*x`;
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
// Basic arithmetic
|
|
768
|
+
simplified = simplified.replace(/(\d+)\+(\d+)/g, (_match, a, b) => {
|
|
769
|
+
return `${parseInt(a) + parseInt(b)}`;
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
simplified = simplified.replace(/(\d+)\*1/g, '$1');
|
|
773
|
+
simplified = simplified.replace(/1\*(\d+)/g, '$1');
|
|
774
|
+
simplified = simplified.replace(/\+0/g, '');
|
|
775
|
+
simplified = simplified.replace(/0\+/g, '');
|
|
776
|
+
|
|
777
|
+
return simplified === expression ? expression : simplified;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Animation Methods
|
|
782
|
+
*/
|
|
783
|
+
async setAnimating(objName: string, animate: boolean): Promise<void> {
|
|
784
|
+
this.ensureInitialized();
|
|
785
|
+
this.updateActivity();
|
|
786
|
+
|
|
787
|
+
if (!this.objects.has(objName)) {
|
|
788
|
+
throw new Error(`Object ${objName} does not exist`);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
if (animate) {
|
|
792
|
+
this.animatingObjects.add(objName);
|
|
793
|
+
if (!this.animationSpeeds.has(objName)) {
|
|
794
|
+
this.animationSpeeds.set(objName, 1); // Default speed
|
|
795
|
+
}
|
|
796
|
+
} else {
|
|
797
|
+
this.animatingObjects.delete(objName);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
logger.debug(`Mock instance ${this.id}: Set animating for ${objName}: ${animate}`);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
async setAnimationSpeed(objName: string, speed: number): Promise<void> {
|
|
804
|
+
this.ensureInitialized();
|
|
805
|
+
this.updateActivity();
|
|
806
|
+
|
|
807
|
+
if (!this.objects.has(objName)) {
|
|
808
|
+
throw new Error(`Object ${objName} does not exist`);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
this.animationSpeeds.set(objName, speed);
|
|
812
|
+
logger.debug(`Mock instance ${this.id}: Set animation speed for ${objName}: ${speed}`);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
async startAnimation(): Promise<void> {
|
|
816
|
+
this.ensureInitialized();
|
|
817
|
+
this.updateActivity();
|
|
818
|
+
|
|
819
|
+
this.isAnimating = true;
|
|
820
|
+
logger.debug(`Mock instance ${this.id}: Started animation for ${this.animatingObjects.size} objects`);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
async stopAnimation(): Promise<void> {
|
|
824
|
+
this.ensureInitialized();
|
|
825
|
+
this.updateActivity();
|
|
826
|
+
|
|
827
|
+
this.isAnimating = false;
|
|
828
|
+
logger.debug(`Mock instance ${this.id}: Stopped animation`);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
async isAnimationRunning(): Promise<boolean> {
|
|
832
|
+
this.ensureInitialized();
|
|
833
|
+
this.updateActivity();
|
|
834
|
+
|
|
835
|
+
return this.isAnimating && this.animatingObjects.size > 0;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
async setTrace(objName: string, flag: boolean): Promise<void> {
|
|
839
|
+
this.ensureInitialized();
|
|
840
|
+
this.updateActivity();
|
|
841
|
+
|
|
842
|
+
if (!this.objects.has(objName)) {
|
|
843
|
+
throw new Error(`Object ${objName} does not exist`);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
if (flag) {
|
|
847
|
+
this.tracedObjects.add(objName);
|
|
848
|
+
} else {
|
|
849
|
+
this.tracedObjects.delete(objName);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
logger.debug(`Mock instance ${this.id}: Set trace for ${objName}: ${flag}`);
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
async setValue(objName: string, value: number): Promise<void> {
|
|
856
|
+
this.ensureInitialized();
|
|
857
|
+
this.updateActivity();
|
|
858
|
+
|
|
859
|
+
const obj = this.objects.get(objName);
|
|
860
|
+
if (!obj) {
|
|
861
|
+
throw new Error(`Object ${objName} does not exist`);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
obj.value = value;
|
|
865
|
+
logger.debug(`Mock instance ${this.id}: Set value for ${objName}: ${value}`);
|
|
866
|
+
}
|
|
867
|
+
}
|