mcp-3d-printer-server 0.1.0 → 0.2.1
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/.env.example +17 -0
- package/README.md +226 -1
- package/dist/index.js +487 -0
- package/dist/stl/stl-manipulator.d.ts +127 -0
- package/dist/stl/stl-manipulator.js +870 -0
- package/dist/test/stl-manipulator-test.d.ts +2 -0
- package/dist/test/stl-manipulator-test.js +91 -0
- package/package.json +8 -5
- package/src/index.ts +585 -0
- package/src/stl/stl-manipulator.ts +1119 -0
- package/src/test/stl-manipulator-test.js +114 -0
- package/src/test/stl-manipulator-test.ts +124 -0
- package/src/test/tsconfig.json +13 -0
- package/test/sample_cube.stl +86 -0
- package/test/sample_cube_extended.stl +172 -0
- package/test/sample_cube_modified.stl +86 -0
- package/test/sample_cube_rotated.stl +86 -0
- package/test/sample_cube_scaled.stl +86 -0
- package/test/sample_cube_translated.stl +86 -0
- package/test/sample_cube_visualization.svg +52 -0
package/dist/index.js
CHANGED
|
@@ -6,7 +6,9 @@ import axios from "axios";
|
|
|
6
6
|
import dotenv from "dotenv";
|
|
7
7
|
import fs from "fs";
|
|
8
8
|
import path from "path";
|
|
9
|
+
import * as THREE from 'three';
|
|
9
10
|
import { PrinterFactory } from "./printers/printer-factory.js";
|
|
11
|
+
import { STLManipulator } from "./stl/stl-manipulator.js";
|
|
10
12
|
// Load environment variables from .env file
|
|
11
13
|
dotenv.config();
|
|
12
14
|
// Default values
|
|
@@ -15,6 +17,10 @@ const DEFAULT_PORT = process.env.PRINTER_PORT || "80";
|
|
|
15
17
|
const DEFAULT_API_KEY = process.env.API_KEY || "";
|
|
16
18
|
const DEFAULT_TYPE = process.env.PRINTER_TYPE || "octoprint"; // Default to OctoPrint
|
|
17
19
|
const TEMP_DIR = process.env.TEMP_DIR || path.join(process.cwd(), "temp");
|
|
20
|
+
// Slicer configuration
|
|
21
|
+
const DEFAULT_SLICER_TYPE = process.env.SLICER_TYPE || "prusaslicer";
|
|
22
|
+
const DEFAULT_SLICER_PATH = process.env.SLICER_PATH || "";
|
|
23
|
+
const DEFAULT_SLICER_PROFILE = process.env.SLICER_PROFILE || "";
|
|
18
24
|
// Bambu-specific default values
|
|
19
25
|
const DEFAULT_BAMBU_SERIAL = process.env.BAMBU_SERIAL || "";
|
|
20
26
|
const DEFAULT_BAMBU_TOKEN = process.env.BAMBU_TOKEN || "";
|
|
@@ -34,6 +40,7 @@ class ThreeDPrinterMCPServer {
|
|
|
34
40
|
}
|
|
35
41
|
});
|
|
36
42
|
this.printerFactory = new PrinterFactory();
|
|
43
|
+
this.stlManipulator = new STLManipulator(TEMP_DIR);
|
|
37
44
|
this.setupHandlers();
|
|
38
45
|
this.setupErrorHandling();
|
|
39
46
|
}
|
|
@@ -168,6 +175,294 @@ class ThreeDPrinterMCPServer {
|
|
|
168
175
|
}
|
|
169
176
|
}
|
|
170
177
|
},
|
|
178
|
+
// New STL manipulation tools
|
|
179
|
+
{
|
|
180
|
+
name: "extend_stl_base",
|
|
181
|
+
description: "Extend the base of an STL file by a specified amount",
|
|
182
|
+
inputSchema: {
|
|
183
|
+
type: "object",
|
|
184
|
+
properties: {
|
|
185
|
+
stl_path: {
|
|
186
|
+
type: "string",
|
|
187
|
+
description: "Path to the STL file to modify"
|
|
188
|
+
},
|
|
189
|
+
extension_inches: {
|
|
190
|
+
type: "number",
|
|
191
|
+
description: "Amount to extend the base in inches"
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
required: ["stl_path", "extension_inches"]
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
name: "slice_stl",
|
|
199
|
+
description: "Slice an STL file to generate G-code",
|
|
200
|
+
inputSchema: {
|
|
201
|
+
type: "object",
|
|
202
|
+
properties: {
|
|
203
|
+
stl_path: {
|
|
204
|
+
type: "string",
|
|
205
|
+
description: "Path to the STL file to slice"
|
|
206
|
+
},
|
|
207
|
+
slicer_type: {
|
|
208
|
+
type: "string",
|
|
209
|
+
description: "Type of slicer to use (prusaslicer, cura, slic3r) (default: value from env)"
|
|
210
|
+
},
|
|
211
|
+
slicer_path: {
|
|
212
|
+
type: "string",
|
|
213
|
+
description: "Path to the slicer executable (default: value from env)"
|
|
214
|
+
},
|
|
215
|
+
slicer_profile: {
|
|
216
|
+
type: "string",
|
|
217
|
+
description: "Profile to use for slicing (default: value from env)"
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
required: ["stl_path"]
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
name: "confirm_temperatures",
|
|
225
|
+
description: "Confirm temperature settings in a G-code file",
|
|
226
|
+
inputSchema: {
|
|
227
|
+
type: "object",
|
|
228
|
+
properties: {
|
|
229
|
+
gcode_path: {
|
|
230
|
+
type: "string",
|
|
231
|
+
description: "Path to the G-code file"
|
|
232
|
+
},
|
|
233
|
+
extruder_temp: {
|
|
234
|
+
type: "number",
|
|
235
|
+
description: "Expected extruder temperature"
|
|
236
|
+
},
|
|
237
|
+
bed_temp: {
|
|
238
|
+
type: "number",
|
|
239
|
+
description: "Expected bed temperature"
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
required: ["gcode_path"]
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
name: "process_and_print_stl",
|
|
247
|
+
description: "Process an STL file (extend base), slice it, confirm temperatures, and start printing",
|
|
248
|
+
inputSchema: {
|
|
249
|
+
type: "object",
|
|
250
|
+
properties: {
|
|
251
|
+
stl_path: {
|
|
252
|
+
type: "string",
|
|
253
|
+
description: "Path to the STL file to process"
|
|
254
|
+
},
|
|
255
|
+
extension_inches: {
|
|
256
|
+
type: "number",
|
|
257
|
+
description: "Amount to extend the base in inches"
|
|
258
|
+
},
|
|
259
|
+
extruder_temp: {
|
|
260
|
+
type: "number",
|
|
261
|
+
description: "Expected extruder temperature"
|
|
262
|
+
},
|
|
263
|
+
bed_temp: {
|
|
264
|
+
type: "number",
|
|
265
|
+
description: "Expected bed temperature"
|
|
266
|
+
},
|
|
267
|
+
host: {
|
|
268
|
+
type: "string",
|
|
269
|
+
description: "Hostname or IP address of the printer (default: value from env)"
|
|
270
|
+
},
|
|
271
|
+
port: {
|
|
272
|
+
type: "string",
|
|
273
|
+
description: "Port of the printer API (default: value from env)"
|
|
274
|
+
},
|
|
275
|
+
type: {
|
|
276
|
+
type: "string",
|
|
277
|
+
description: "Type of printer management system (default: value from env)"
|
|
278
|
+
},
|
|
279
|
+
api_key: {
|
|
280
|
+
type: "string",
|
|
281
|
+
description: "API key for authentication (default: value from env)"
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
required: ["stl_path", "extension_inches"]
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
// New STL manipulation tools
|
|
288
|
+
{
|
|
289
|
+
name: "get_stl_info",
|
|
290
|
+
description: "Get detailed information about an STL file",
|
|
291
|
+
inputSchema: {
|
|
292
|
+
type: "object",
|
|
293
|
+
properties: {
|
|
294
|
+
stl_path: {
|
|
295
|
+
type: "string",
|
|
296
|
+
description: "Path to the STL file"
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
required: ["stl_path"]
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
name: "scale_stl",
|
|
304
|
+
description: "Scale an STL model uniformly or along specific axes",
|
|
305
|
+
inputSchema: {
|
|
306
|
+
type: "object",
|
|
307
|
+
properties: {
|
|
308
|
+
stl_path: {
|
|
309
|
+
type: "string",
|
|
310
|
+
description: "Path to the STL file"
|
|
311
|
+
},
|
|
312
|
+
scale_factor: {
|
|
313
|
+
type: "number",
|
|
314
|
+
description: "Uniform scaling factor to apply"
|
|
315
|
+
},
|
|
316
|
+
scale_x: {
|
|
317
|
+
type: "number",
|
|
318
|
+
description: "X-axis scaling factor (overrides scale_factor for X axis)"
|
|
319
|
+
},
|
|
320
|
+
scale_y: {
|
|
321
|
+
type: "number",
|
|
322
|
+
description: "Y-axis scaling factor (overrides scale_factor for Y axis)"
|
|
323
|
+
},
|
|
324
|
+
scale_z: {
|
|
325
|
+
type: "number",
|
|
326
|
+
description: "Z-axis scaling factor (overrides scale_factor for Z axis)"
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
required: ["stl_path"]
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
name: "rotate_stl",
|
|
334
|
+
description: "Rotate an STL model around specific axes",
|
|
335
|
+
inputSchema: {
|
|
336
|
+
type: "object",
|
|
337
|
+
properties: {
|
|
338
|
+
stl_path: {
|
|
339
|
+
type: "string",
|
|
340
|
+
description: "Path to the STL file"
|
|
341
|
+
},
|
|
342
|
+
rotate_x: {
|
|
343
|
+
type: "number",
|
|
344
|
+
description: "Rotation around X-axis in degrees"
|
|
345
|
+
},
|
|
346
|
+
rotate_y: {
|
|
347
|
+
type: "number",
|
|
348
|
+
description: "Rotation around Y-axis in degrees"
|
|
349
|
+
},
|
|
350
|
+
rotate_z: {
|
|
351
|
+
type: "number",
|
|
352
|
+
description: "Rotation around Z-axis in degrees"
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
required: ["stl_path"]
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
name: "translate_stl",
|
|
360
|
+
description: "Move an STL model along specific axes",
|
|
361
|
+
inputSchema: {
|
|
362
|
+
type: "object",
|
|
363
|
+
properties: {
|
|
364
|
+
stl_path: {
|
|
365
|
+
type: "string",
|
|
366
|
+
description: "Path to the STL file"
|
|
367
|
+
},
|
|
368
|
+
translate_x: {
|
|
369
|
+
type: "number",
|
|
370
|
+
description: "Translation along X-axis in millimeters"
|
|
371
|
+
},
|
|
372
|
+
translate_y: {
|
|
373
|
+
type: "number",
|
|
374
|
+
description: "Translation along Y-axis in millimeters"
|
|
375
|
+
},
|
|
376
|
+
translate_z: {
|
|
377
|
+
type: "number",
|
|
378
|
+
description: "Translation along Z-axis in millimeters"
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
required: ["stl_path"]
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
name: "modify_stl_section",
|
|
386
|
+
description: "Apply a specific transformation to a selected section of an STL file",
|
|
387
|
+
inputSchema: {
|
|
388
|
+
type: "object",
|
|
389
|
+
properties: {
|
|
390
|
+
stl_path: {
|
|
391
|
+
type: "string",
|
|
392
|
+
description: "Path to the STL file"
|
|
393
|
+
},
|
|
394
|
+
section: {
|
|
395
|
+
type: "string",
|
|
396
|
+
description: "Section to modify: 'top', 'bottom', 'center', or custom bounds",
|
|
397
|
+
enum: ["top", "bottom", "center", "custom"]
|
|
398
|
+
},
|
|
399
|
+
transformation_type: {
|
|
400
|
+
type: "string",
|
|
401
|
+
description: "Type of transformation to apply",
|
|
402
|
+
enum: ["scale", "rotate", "translate"]
|
|
403
|
+
},
|
|
404
|
+
value_x: {
|
|
405
|
+
type: "number",
|
|
406
|
+
description: "Transformation value for X axis"
|
|
407
|
+
},
|
|
408
|
+
value_y: {
|
|
409
|
+
type: "number",
|
|
410
|
+
description: "Transformation value for Y axis"
|
|
411
|
+
},
|
|
412
|
+
value_z: {
|
|
413
|
+
type: "number",
|
|
414
|
+
description: "Transformation value for Z axis"
|
|
415
|
+
},
|
|
416
|
+
custom_min_x: {
|
|
417
|
+
type: "number",
|
|
418
|
+
description: "Minimum X for custom section bounds"
|
|
419
|
+
},
|
|
420
|
+
custom_min_y: {
|
|
421
|
+
type: "number",
|
|
422
|
+
description: "Minimum Y for custom section bounds"
|
|
423
|
+
},
|
|
424
|
+
custom_min_z: {
|
|
425
|
+
type: "number",
|
|
426
|
+
description: "Minimum Z for custom section bounds"
|
|
427
|
+
},
|
|
428
|
+
custom_max_x: {
|
|
429
|
+
type: "number",
|
|
430
|
+
description: "Maximum X for custom section bounds"
|
|
431
|
+
},
|
|
432
|
+
custom_max_y: {
|
|
433
|
+
type: "number",
|
|
434
|
+
description: "Maximum Y for custom section bounds"
|
|
435
|
+
},
|
|
436
|
+
custom_max_z: {
|
|
437
|
+
type: "number",
|
|
438
|
+
description: "Maximum Z for custom section bounds"
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
required: ["stl_path", "section", "transformation_type"]
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
name: "generate_stl_visualization",
|
|
446
|
+
description: "Generate an SVG visualization of an STL file from multiple angles",
|
|
447
|
+
inputSchema: {
|
|
448
|
+
type: "object",
|
|
449
|
+
properties: {
|
|
450
|
+
stl_path: {
|
|
451
|
+
type: "string",
|
|
452
|
+
description: "Path to the STL file"
|
|
453
|
+
},
|
|
454
|
+
width: {
|
|
455
|
+
type: "number",
|
|
456
|
+
description: "Width of each view in pixels (default: 300)"
|
|
457
|
+
},
|
|
458
|
+
height: {
|
|
459
|
+
type: "number",
|
|
460
|
+
description: "Height of each view in pixels (default: 300)"
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
required: ["stl_path"]
|
|
464
|
+
}
|
|
465
|
+
},
|
|
171
466
|
// ...other tools with similar structure
|
|
172
467
|
// Abbreviated for clarity, would include all other tools
|
|
173
468
|
]
|
|
@@ -183,6 +478,9 @@ class ThreeDPrinterMCPServer {
|
|
|
183
478
|
const apiKey = String(args?.api_key || DEFAULT_API_KEY);
|
|
184
479
|
const bambuSerial = String(args?.bambu_serial || DEFAULT_BAMBU_SERIAL);
|
|
185
480
|
const bambuToken = String(args?.bambu_token || DEFAULT_BAMBU_TOKEN);
|
|
481
|
+
const slicerType = String(args?.slicer_type || DEFAULT_SLICER_TYPE);
|
|
482
|
+
const slicerPath = String(args?.slicer_path || DEFAULT_SLICER_PATH);
|
|
483
|
+
const slicerProfile = String(args?.slicer_profile || DEFAULT_SLICER_PROFILE);
|
|
186
484
|
try {
|
|
187
485
|
let result;
|
|
188
486
|
switch (name) {
|
|
@@ -213,6 +511,195 @@ class ThreeDPrinterMCPServer {
|
|
|
213
511
|
}
|
|
214
512
|
result = await this.setPrinterTemperature(host, port, type, apiKey, bambuSerial, bambuToken, String(args.component), Number(args.temperature));
|
|
215
513
|
break;
|
|
514
|
+
// New STL manipulation tools
|
|
515
|
+
case "extend_stl_base":
|
|
516
|
+
if (!args?.stl_path || args?.extension_inches === undefined) {
|
|
517
|
+
throw new Error("Missing required parameters: stl_path and extension_inches");
|
|
518
|
+
}
|
|
519
|
+
result = await this.stlManipulator.extendBase(String(args.stl_path), Number(args.extension_inches));
|
|
520
|
+
break;
|
|
521
|
+
case "slice_stl":
|
|
522
|
+
if (!args?.stl_path) {
|
|
523
|
+
throw new Error("Missing required parameter: stl_path");
|
|
524
|
+
}
|
|
525
|
+
result = await this.stlManipulator.sliceSTL(String(args.stl_path), slicerType, slicerPath, slicerProfile || undefined);
|
|
526
|
+
break;
|
|
527
|
+
case "confirm_temperatures":
|
|
528
|
+
if (!args?.gcode_path) {
|
|
529
|
+
throw new Error("Missing required parameter: gcode_path");
|
|
530
|
+
}
|
|
531
|
+
result = await this.stlManipulator.confirmTemperatures(String(args.gcode_path), {
|
|
532
|
+
extruder: args.extruder_temp !== undefined ? Number(args.extruder_temp) : undefined,
|
|
533
|
+
bed: args.bed_temp !== undefined ? Number(args.bed_temp) : undefined
|
|
534
|
+
});
|
|
535
|
+
break;
|
|
536
|
+
case "process_and_print_stl":
|
|
537
|
+
if (!args?.stl_path || args?.extension_inches === undefined) {
|
|
538
|
+
throw new Error("Missing required parameters: stl_path and extension_inches");
|
|
539
|
+
}
|
|
540
|
+
// Define progress callback for UI updates
|
|
541
|
+
const processProgressCallback = (progress, message) => {
|
|
542
|
+
console.log(`Process progress: ${progress}% - ${message || ''}`);
|
|
543
|
+
};
|
|
544
|
+
// 1. Extend the base of the STL file
|
|
545
|
+
const extendedStlPath = await this.stlManipulator.extendBase(String(args.stl_path), Number(args.extension_inches), processProgressCallback);
|
|
546
|
+
// 2. Slice the extended STL file
|
|
547
|
+
const gcodePath = await this.stlManipulator.sliceSTL(extendedStlPath, slicerType, slicerPath, slicerProfile || undefined, processProgressCallback);
|
|
548
|
+
// 3. Confirm temperatures if specified
|
|
549
|
+
if (args.extruder_temp !== undefined || args.bed_temp !== undefined) {
|
|
550
|
+
const tempConfirmation = await this.stlManipulator.confirmTemperatures(gcodePath, {
|
|
551
|
+
extruder: args.extruder_temp !== undefined ? Number(args.extruder_temp) : undefined,
|
|
552
|
+
bed: args.bed_temp !== undefined ? Number(args.bed_temp) : undefined
|
|
553
|
+
}, processProgressCallback);
|
|
554
|
+
if (!tempConfirmation.match) {
|
|
555
|
+
console.warn("Temperature mismatch:", tempConfirmation);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
// 4. Upload the G-code file to the printer
|
|
559
|
+
const gcodeContent = await fs.promises.readFile(gcodePath, 'utf8');
|
|
560
|
+
const filename = path.basename(gcodePath);
|
|
561
|
+
await this.uploadGcode(host, port, type, apiKey, bambuSerial, bambuToken, filename, gcodeContent, true // Start printing immediately
|
|
562
|
+
);
|
|
563
|
+
result = {
|
|
564
|
+
extended_stl_path: extendedStlPath,
|
|
565
|
+
gcode_path: gcodePath,
|
|
566
|
+
filename,
|
|
567
|
+
status: "Print job started"
|
|
568
|
+
};
|
|
569
|
+
break;
|
|
570
|
+
// New STL manipulation tool handlers
|
|
571
|
+
case "get_stl_info":
|
|
572
|
+
if (!args?.stl_path) {
|
|
573
|
+
throw new Error("Missing required parameter: stl_path");
|
|
574
|
+
}
|
|
575
|
+
result = await this.stlManipulator.getSTLInfo(String(args.stl_path));
|
|
576
|
+
break;
|
|
577
|
+
case "scale_stl":
|
|
578
|
+
if (!args?.stl_path) {
|
|
579
|
+
throw new Error("Missing required parameter: stl_path");
|
|
580
|
+
}
|
|
581
|
+
// Define progress callback for UI updates
|
|
582
|
+
const scaleProgressCallback = (progress, message) => {
|
|
583
|
+
console.log(`Scale progress: ${progress}% - ${message || ''}`);
|
|
584
|
+
};
|
|
585
|
+
let scaleFactors;
|
|
586
|
+
// Check if we have individual axis scaling factors
|
|
587
|
+
if (args.scale_x !== undefined || args.scale_y !== undefined || args.scale_z !== undefined) {
|
|
588
|
+
// Use individual axis scaling
|
|
589
|
+
scaleFactors = [
|
|
590
|
+
Number(args.scale_x ?? 1.0),
|
|
591
|
+
Number(args.scale_y ?? 1.0),
|
|
592
|
+
Number(args.scale_z ?? 1.0)
|
|
593
|
+
];
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
// Use uniform scaling
|
|
597
|
+
scaleFactors = Number(args.scale_factor ?? 1.0);
|
|
598
|
+
}
|
|
599
|
+
result = await this.stlManipulator.scaleSTL(String(args.stl_path), scaleFactors, scaleProgressCallback);
|
|
600
|
+
break;
|
|
601
|
+
case "rotate_stl":
|
|
602
|
+
if (!args?.stl_path) {
|
|
603
|
+
throw new Error("Missing required parameter: stl_path");
|
|
604
|
+
}
|
|
605
|
+
// Define progress callback for UI updates
|
|
606
|
+
const rotateProgressCallback = (progress, message) => {
|
|
607
|
+
console.log(`Rotate progress: ${progress}% - ${message || ''}`);
|
|
608
|
+
};
|
|
609
|
+
// Get rotation angles, defaulting to 0 for any undefined axis
|
|
610
|
+
const rotationAngles = [
|
|
611
|
+
Number(args.rotate_x ?? 0),
|
|
612
|
+
Number(args.rotate_y ?? 0),
|
|
613
|
+
Number(args.rotate_z ?? 0)
|
|
614
|
+
];
|
|
615
|
+
result = await this.stlManipulator.rotateSTL(String(args.stl_path), rotationAngles, rotateProgressCallback);
|
|
616
|
+
break;
|
|
617
|
+
case "translate_stl":
|
|
618
|
+
if (!args?.stl_path) {
|
|
619
|
+
throw new Error("Missing required parameter: stl_path");
|
|
620
|
+
}
|
|
621
|
+
// Define progress callback for UI updates
|
|
622
|
+
const translateProgressCallback = (progress, message) => {
|
|
623
|
+
console.log(`Translate progress: ${progress}% - ${message || ''}`);
|
|
624
|
+
};
|
|
625
|
+
// Get translation values, defaulting to 0 for any undefined axis
|
|
626
|
+
const translationValues = [
|
|
627
|
+
Number(args.translate_x ?? 0),
|
|
628
|
+
Number(args.translate_y ?? 0),
|
|
629
|
+
Number(args.translate_z ?? 0)
|
|
630
|
+
];
|
|
631
|
+
result = await this.stlManipulator.translateSTL(String(args.stl_path), translationValues, translateProgressCallback);
|
|
632
|
+
break;
|
|
633
|
+
case "modify_stl_section":
|
|
634
|
+
if (!args?.stl_path || !args?.section || !args?.transformation_type) {
|
|
635
|
+
throw new Error("Missing required parameters: stl_path, section, and transformation_type");
|
|
636
|
+
}
|
|
637
|
+
// Define progress callback for UI updates
|
|
638
|
+
const modifySectionProgressCallback = (progress, message) => {
|
|
639
|
+
console.log(`Modify section progress: ${progress}% - ${message || ''}`);
|
|
640
|
+
};
|
|
641
|
+
// Determine the section to modify
|
|
642
|
+
let sectionBox;
|
|
643
|
+
if (args.section === 'custom') {
|
|
644
|
+
// Create a custom bounding box from the provided bounds
|
|
645
|
+
if (args.custom_min_x === undefined || args.custom_min_y === undefined ||
|
|
646
|
+
args.custom_min_z === undefined || args.custom_max_x === undefined ||
|
|
647
|
+
args.custom_max_y === undefined || args.custom_max_z === undefined) {
|
|
648
|
+
throw new Error("Custom section requires all min/max bounds to be specified");
|
|
649
|
+
}
|
|
650
|
+
sectionBox = new THREE.Box3(new THREE.Vector3(Number(args.custom_min_x), Number(args.custom_min_y), Number(args.custom_min_z)), new THREE.Vector3(Number(args.custom_max_x), Number(args.custom_max_y), Number(args.custom_max_z)));
|
|
651
|
+
}
|
|
652
|
+
else {
|
|
653
|
+
// Use a predefined section
|
|
654
|
+
sectionBox = String(args.section);
|
|
655
|
+
}
|
|
656
|
+
// Determine the transformation to apply
|
|
657
|
+
const transformationType = String(args.transformation_type);
|
|
658
|
+
let transformationValue;
|
|
659
|
+
if (transformationType === 'scale') {
|
|
660
|
+
if (args.value_x !== undefined || args.value_y !== undefined || args.value_z !== undefined) {
|
|
661
|
+
transformationValue = [
|
|
662
|
+
Number(args.value_x ?? 1.0),
|
|
663
|
+
Number(args.value_y ?? 1.0),
|
|
664
|
+
Number(args.value_z ?? 1.0)
|
|
665
|
+
];
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
transformationValue = 1.0; // Default scale factor
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
else if (transformationType === 'rotate') {
|
|
672
|
+
transformationValue = [
|
|
673
|
+
Number(args.value_x ?? 0),
|
|
674
|
+
Number(args.value_y ?? 0),
|
|
675
|
+
Number(args.value_z ?? 0)
|
|
676
|
+
];
|
|
677
|
+
}
|
|
678
|
+
else { // translate
|
|
679
|
+
transformationValue = [
|
|
680
|
+
Number(args.value_x ?? 0),
|
|
681
|
+
Number(args.value_y ?? 0),
|
|
682
|
+
Number(args.value_z ?? 0)
|
|
683
|
+
];
|
|
684
|
+
}
|
|
685
|
+
result = await this.stlManipulator.modifySection(String(args.stl_path), sectionBox, {
|
|
686
|
+
type: transformationType,
|
|
687
|
+
value: transformationValue
|
|
688
|
+
}, modifySectionProgressCallback);
|
|
689
|
+
break;
|
|
690
|
+
case "generate_stl_visualization":
|
|
691
|
+
if (!args?.stl_path) {
|
|
692
|
+
throw new Error("Missing required parameter: stl_path");
|
|
693
|
+
}
|
|
694
|
+
// Define progress callback for UI updates
|
|
695
|
+
const visualizationProgressCallback = (progress, message) => {
|
|
696
|
+
console.log(`Visualization progress: ${progress}% - ${message || ''}`);
|
|
697
|
+
};
|
|
698
|
+
// Get width and height parameters, with defaults
|
|
699
|
+
const width = args.width !== undefined ? Number(args.width) : 300;
|
|
700
|
+
const height = args.height !== undefined ? Number(args.height) : 300;
|
|
701
|
+
result = await this.stlManipulator.generateVisualization(String(args.stl_path), width, height, visualizationProgressCallback);
|
|
702
|
+
break;
|
|
216
703
|
default:
|
|
217
704
|
throw new Error(`Unknown tool: ${name}`);
|
|
218
705
|
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
export type ProgressCallback = (progress: number, message?: string) => void;
|
|
4
|
+
export type OperationResult = {
|
|
5
|
+
success: boolean;
|
|
6
|
+
filePath?: string;
|
|
7
|
+
error?: string;
|
|
8
|
+
operationId: string;
|
|
9
|
+
};
|
|
10
|
+
export type TransformationType = 'scale' | 'rotate' | 'translate' | 'extendBase' | 'customModify';
|
|
11
|
+
export type TransformationAxis = 'x' | 'y' | 'z' | 'all';
|
|
12
|
+
export type BoundingBox = {
|
|
13
|
+
min: THREE.Vector3;
|
|
14
|
+
max: THREE.Vector3;
|
|
15
|
+
center: THREE.Vector3;
|
|
16
|
+
dimensions: THREE.Vector3;
|
|
17
|
+
};
|
|
18
|
+
export type TransformationParams = {
|
|
19
|
+
type: TransformationType;
|
|
20
|
+
axis?: TransformationAxis;
|
|
21
|
+
value: number | number[];
|
|
22
|
+
relative?: boolean;
|
|
23
|
+
selectionBounds?: THREE.Box3;
|
|
24
|
+
};
|
|
25
|
+
export declare class STLManipulator extends EventEmitter {
|
|
26
|
+
private tempDir;
|
|
27
|
+
private activeOperations;
|
|
28
|
+
constructor(tempDir?: string);
|
|
29
|
+
/**
|
|
30
|
+
* Generate a unique operation ID
|
|
31
|
+
*/
|
|
32
|
+
private generateOperationId;
|
|
33
|
+
/**
|
|
34
|
+
* Load STL file and return geometry and bounding box
|
|
35
|
+
*/
|
|
36
|
+
private loadSTL;
|
|
37
|
+
/**
|
|
38
|
+
* Save a geometry to STL file
|
|
39
|
+
*/
|
|
40
|
+
private saveSTL;
|
|
41
|
+
/**
|
|
42
|
+
* Get comprehensive information about an STL file
|
|
43
|
+
*/
|
|
44
|
+
getSTLInfo(stlFilePath: string): Promise<{
|
|
45
|
+
filePath: string;
|
|
46
|
+
fileName: string;
|
|
47
|
+
fileSize: number;
|
|
48
|
+
boundingBox: BoundingBox;
|
|
49
|
+
vertexCount: number;
|
|
50
|
+
faceCount: number;
|
|
51
|
+
}>;
|
|
52
|
+
/**
|
|
53
|
+
* Scale an STL model uniformly or along specific axes
|
|
54
|
+
*/
|
|
55
|
+
scaleSTL(stlFilePath: string, scaleFactors: number | [number, number, number], progressCallback?: ProgressCallback): Promise<string>;
|
|
56
|
+
/**
|
|
57
|
+
* Rotate an STL model around specific axes
|
|
58
|
+
*/
|
|
59
|
+
rotateSTL(stlFilePath: string, rotationAngles: [number, number, number], // [x, y, z] in degrees
|
|
60
|
+
progressCallback?: ProgressCallback): Promise<string>;
|
|
61
|
+
/**
|
|
62
|
+
* Translate (move) an STL model along specific axes
|
|
63
|
+
*/
|
|
64
|
+
translateSTL(stlFilePath: string, translationValues: [number, number, number], // [x, y, z] in mm
|
|
65
|
+
progressCallback?: ProgressCallback): Promise<string>;
|
|
66
|
+
/**
|
|
67
|
+
* Cancel an ongoing operation
|
|
68
|
+
*/
|
|
69
|
+
cancelOperation(operationId: string): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Generate an SVG visualization of an STL file from multiple angles
|
|
72
|
+
* @param stlFilePath Path to the STL file
|
|
73
|
+
* @param width Width of each view in pixels
|
|
74
|
+
* @param height Height of each view in pixels
|
|
75
|
+
* @param progressCallback Optional callback for progress updates
|
|
76
|
+
* @returns Path to the generated SVG file
|
|
77
|
+
*/
|
|
78
|
+
generateVisualization(stlFilePath: string, width?: number, height?: number, progressCallback?: ProgressCallback): Promise<string>;
|
|
79
|
+
/**
|
|
80
|
+
* Apply a specific transformation to a selected section of an STL file
|
|
81
|
+
* This allows for targeted modifications of specific parts of a model
|
|
82
|
+
*/
|
|
83
|
+
modifySection(stlFilePath: string, selection: THREE.Box3 | 'top' | 'bottom' | 'center', transformation: TransformationParams, progressCallback?: ProgressCallback): Promise<string>;
|
|
84
|
+
/**
|
|
85
|
+
* Enhanced version of extendBase with progress reporting
|
|
86
|
+
* @param stlFilePath Path to the input STL file
|
|
87
|
+
* @param extensionInches Amount to extend base in inches
|
|
88
|
+
* @param progressCallback Optional callback for progress updates
|
|
89
|
+
* @returns Path to the modified STL file
|
|
90
|
+
*/
|
|
91
|
+
extendBase(stlFilePath: string, extensionInches: number, progressCallback?: ProgressCallback): Promise<string>;
|
|
92
|
+
/**
|
|
93
|
+
* Enhanced version of sliceSTL with progress reporting and error handling
|
|
94
|
+
* @param stlFilePath Path to the STL file
|
|
95
|
+
* @param slicerType Type of slicer to use ('prusaslicer', 'cura', 'slic3r')
|
|
96
|
+
* @param slicerPath Path to the slicer executable
|
|
97
|
+
* @param slicerProfile Profile to use for slicing
|
|
98
|
+
* @param progressCallback Optional callback for progress updates
|
|
99
|
+
* @returns Path to the generated G-code file
|
|
100
|
+
*/
|
|
101
|
+
sliceSTL(stlFilePath: string, slicerType: 'prusaslicer' | 'cura' | 'slic3r', slicerPath: string, slicerProfile?: string, progressCallback?: ProgressCallback): Promise<string>;
|
|
102
|
+
/**
|
|
103
|
+
* Enhanced version of confirmTemperatures with better error handling
|
|
104
|
+
* @param gcodePath Path to the G-code file
|
|
105
|
+
* @param expected Expected temperature settings
|
|
106
|
+
* @param progressCallback Optional callback for progress updates
|
|
107
|
+
* @returns Object with comparison results
|
|
108
|
+
*/
|
|
109
|
+
confirmTemperatures(gcodePath: string, expected: {
|
|
110
|
+
extruder?: number;
|
|
111
|
+
bed?: number;
|
|
112
|
+
}, progressCallback?: ProgressCallback): Promise<{
|
|
113
|
+
match: boolean;
|
|
114
|
+
actual: {
|
|
115
|
+
extruder?: number;
|
|
116
|
+
bed?: number;
|
|
117
|
+
};
|
|
118
|
+
expected: {
|
|
119
|
+
extruder?: number;
|
|
120
|
+
bed?: number;
|
|
121
|
+
};
|
|
122
|
+
allTemperatures: {
|
|
123
|
+
extruder: number[];
|
|
124
|
+
bed: number[];
|
|
125
|
+
};
|
|
126
|
+
}>;
|
|
127
|
+
}
|