@pixel-normal-edit/mcp 2.0.6 → 2.0.8

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 (56) hide show
  1. package/domains/art-tree/3d_lessons.js +114 -0
  2. package/domains/art-tree/3d_shapes.js +116 -0
  3. package/domains/art-tree/3d_tools.js +60 -0
  4. package/domains/art-tree/advanced_lessons.js +124 -0
  5. package/domains/art-tree/advanced_shapes.js +73 -0
  6. package/domains/art-tree/advanced_tools.js +69 -0
  7. package/domains/art-tree/analysis_lessons.js +32 -0
  8. package/domains/art-tree/analysis_shapes.js +146 -0
  9. package/domains/art-tree/analysis_tools.js +49 -0
  10. package/domains/art-tree/cross_section_lessons.js +65 -0
  11. package/domains/art-tree/cross_section_shapes.js +84 -0
  12. package/domains/art-tree/cross_section_tools.js +51 -0
  13. package/domains/art-tree/curve_lessons.js +138 -0
  14. package/domains/art-tree/curve_shapes.js +90 -0
  15. package/domains/art-tree/curve_tools.js +51 -0
  16. package/domains/art-tree/ellipse_lessons.js +105 -0
  17. package/domains/art-tree/ellipse_shapes.js +63 -0
  18. package/domains/art-tree/ellipse_tools.js +60 -0
  19. package/domains/art-tree/hidden_lessons.js +55 -0
  20. package/domains/art-tree/hidden_shapes.js +97 -0
  21. package/domains/art-tree/hidden_tools.js +51 -0
  22. package/domains/art-tree/index.js +32 -0
  23. package/domains/art-tree/layer_lessons.js +32 -0
  24. package/domains/art-tree/layer_shapes.js +117 -0
  25. package/domains/art-tree/layer_tools.js +43 -0
  26. package/domains/art-tree/lessons.js +119 -7
  27. package/domains/art-tree/light_lessons.js +92 -0
  28. package/domains/art-tree/light_shapes.js +136 -0
  29. package/domains/art-tree/light_tools.js +49 -0
  30. package/domains/art-tree/material_lessons.js +71 -0
  31. package/domains/art-tree/material_shapes.js +130 -0
  32. package/domains/art-tree/material_tools.js +49 -0
  33. package/domains/art-tree/perspective_lessons.js +138 -0
  34. package/domains/art-tree/perspective_shapes.js +96 -0
  35. package/domains/art-tree/perspective_tools.js +60 -0
  36. package/domains/art-tree/shapes.js +15 -0
  37. package/domains/art-tree/sky_lessons_1.js +82 -0
  38. package/domains/art-tree/sky_shapes_1.js +68 -0
  39. package/domains/art-tree/sky_tools.js +51 -0
  40. package/domains/art-tree/structure_lessons.js +102 -0
  41. package/domains/art-tree/structure_shapes.js +74 -0
  42. package/domains/art-tree/structure_tools.js +51 -0
  43. package/domains/art-tree/surface_lessons.js +112 -0
  44. package/domains/art-tree/surface_shapes.js +87 -0
  45. package/domains/art-tree/surface_tools.js +60 -0
  46. package/domains/art-tree/tools.js +69 -15
  47. package/domains/art-tree/transform_lessons_1.js +108 -0
  48. package/domains/art-tree/transform_lessons_2.js +102 -0
  49. package/domains/art-tree/transform_shapes.js +109 -0
  50. package/domains/art-tree/transform_tools.js +104 -0
  51. package/domains/art-tree/vocab_lessons.js +32 -0
  52. package/domains/art-tree/vocab_shapes.js +116 -0
  53. package/domains/art-tree/vocab_tools.js +41 -0
  54. package/package.json +1 -1
  55. package/tools/index.js +2 -0
  56. package/tools/layer.js +80 -0
@@ -0,0 +1,74 @@
1
+ const shapes = require('./shapes');
2
+ const ellipseShapes = require('./ellipse_shapes');
3
+
4
+ /**
5
+ * Draw a 3D coordinate system (X, Y, Z axes) originating from a center point
6
+ */
7
+ function drawXYZAxes(cx, cy, length) {
8
+ const commands = [];
9
+
10
+ // Y-axis (Vertical) - Green
11
+ commands.push(shapes.drawLine(cx, cy, cx, cy - length, '#4caf50'));
12
+
13
+ // X-axis (Horizontal) - Red
14
+ commands.push(shapes.drawLine(cx, cy, cx + length, cy, '#f44336'));
15
+
16
+ // Z-axis (Depth - Oblique projection) - Blue
17
+ const dx = Math.floor(length * 0.7 * Math.cos(Math.PI / 6));
18
+ const dy = Math.floor(length * 0.7 * Math.sin(Math.PI / 6));
19
+ commands.push(shapes.drawLine(cx, cy, cx - dx, cy + dy, '#2196f3'));
20
+
21
+ // Center point
22
+ commands.push(shapes.drawCircle(cx, cy, 2, '#000000', true));
23
+
24
+ return commands;
25
+ }
26
+
27
+ /**
28
+ * Draw a complex bottle structure using cross-sections and central axis
29
+ */
30
+ function drawBottleAnatomy(cx, cy, totalHeight, color) {
31
+ const commands = [];
32
+
33
+ const bottomY = cy + Math.floor(totalHeight / 2);
34
+ const topY = cy - Math.floor(totalHeight / 2);
35
+
36
+ // Central vertical axis
37
+ commands.push(shapes.drawLine(cx, topY - 10, cx, bottomY + 10, '#bdbdbd'));
38
+
39
+ // Define cross sections (y position from bottom, radius)
40
+ const sections = [
41
+ { y: bottomY, r: Math.floor(totalHeight * 0.2) }, // Base
42
+ { y: bottomY - Math.floor(totalHeight * 0.4), r: Math.floor(totalHeight * 0.2) }, // Body
43
+ { y: bottomY - Math.floor(totalHeight * 0.7), r: Math.floor(totalHeight * 0.08) }, // Neck start
44
+ { y: topY, r: Math.floor(totalHeight * 0.08) } // Mouth
45
+ ];
46
+
47
+ // Draw cross section ellipses
48
+ for (const sec of sections) {
49
+ // ry is based on perspective (lower = wider perspective, but keep it simple here)
50
+ const ry = Math.floor(sec.r * 0.3);
51
+ commands.push(shapes.drawEllipse(cx, sec.y, sec.r, ry, color, false));
52
+
53
+ // Cross point on the axis
54
+ commands.push(shapes.drawCircle(cx, sec.y, 1, '#ff0000', true));
55
+ }
56
+
57
+ // Draw outer contour lines
58
+ for (let i = 0; i < sections.length - 1; i++) {
59
+ const s1 = sections[i];
60
+ const s2 = sections[i+1];
61
+
62
+ // Left contour
63
+ commands.push(shapes.drawLine(cx - s1.r, s1.y, cx - s2.r, s2.y, color));
64
+ // Right contour
65
+ commands.push(shapes.drawLine(cx + s1.r, s1.y, cx + s2.r, s2.y, color));
66
+ }
67
+
68
+ return commands;
69
+ }
70
+
71
+ module.exports = {
72
+ drawXYZAxes,
73
+ drawBottleAnatomy
74
+ };
@@ -0,0 +1,51 @@
1
+ const { z } = require('zod');
2
+ const { registerTool, sendCommand } = require('../../core/server');
3
+ const structureLessons = require('./structure_lessons');
4
+
5
+ const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g. #ff0000');
6
+
7
+ /**
8
+ * Register all structure and axis tools on the MCP server.
9
+ * @param {McpServer} server
10
+ */
11
+ function register(server) {
12
+ registerTool(server, 'art_tree_lesson_structure_xyz',
13
+ 'Lesson: Structure - XYZ Axes (Vertical, Horizontal, Depth directions)',
14
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#2196f3') },
15
+ async (p) => {
16
+ const commands = structureLessons.lessonXYZAxes(p.canvasSize, p.color);
17
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
18
+ return { content: [{ type: 'text', text: `✓ XYZ Axes lesson completed` }] };
19
+ });
20
+
21
+ registerTool(server, 'art_tree_lesson_structure_bottle',
22
+ 'Lesson: Structure - Complex Analysis (Bottle with cross-sections)',
23
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#9c27b0') },
24
+ async (p) => {
25
+ const commands = structureLessons.lessonComplexStructure(p.canvasSize, p.color);
26
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
27
+ return { content: [{ type: 'text', text: `✓ Complex Structure (Bottle) lesson completed` }] };
28
+ });
29
+
30
+ registerTool(server, 'art_tree_lesson_structure_orientation',
31
+ 'Lesson: Structure - Axis Orientation (Which axis is it oriented along?)',
32
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#00bcd4') },
33
+ async (p) => {
34
+ const commands = structureLessons.lessonAxisOrientation(p.canvasSize, p.color);
35
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
36
+ return { content: [{ type: 'text', text: `✓ Axis Orientation lesson completed` }] };
37
+ });
38
+
39
+ registerTool(server, 'art_tree_lesson_structure_catalog',
40
+ 'Get the catalog of all available Structure lessons',
41
+ {},
42
+ () => {
43
+ const catalog = structureLessons.getStructureLessonCatalog();
44
+ const text = catalog.map(l =>
45
+ ` • ${l.id}: ${l.name} — ${l.description}`
46
+ ).join('\n');
47
+ return { content: [{ type: 'text', text: `📚 Structure Lessons:\n\n${text}` }] };
48
+ });
49
+ }
50
+
51
+ module.exports = { register };
@@ -0,0 +1,112 @@
1
+ const shapes = require('./shapes');
2
+ const surfaceShapes = require('./surface_shapes');
3
+
4
+ /**
5
+ * Lesson: Surface Types (Flat, convex, concave)
6
+ */
7
+ function lessonSurfaceTypes(canvasSize = 32, color = '#2196f3') {
8
+ const commands = [];
9
+ const midY = Math.floor(canvasSize / 2);
10
+ const qX = Math.floor(canvasSize / 4);
11
+ const r = Math.floor(canvasSize * 0.15);
12
+
13
+ // 1. Flat Plane
14
+ const w = Math.floor(canvasSize * 0.2);
15
+ commands.push(shapes.drawRectangle(qX - w/2, midY - w/2, w, w, '#4caf50', false));
16
+ // Add some grid lines to show it's flat
17
+ commands.push(shapes.drawLine(qX, midY - w/2, qX, midY + w/2, '#4caf50'));
18
+ commands.push(shapes.drawLine(qX - w/2, midY, qX + w/2, midY, '#4caf50'));
19
+
20
+ // 2. Convex Surface (e.g. Dome/Sphere)
21
+ // We use an ellipse that curves outwards (downwards)
22
+ commands.push(shapes.drawEllipse(qX * 2, midY, r, r, '#ff9800', false));
23
+ // Cross contour curving DOWN (convex towards viewer)
24
+ commands.push(shapes.drawEllipse(qX * 2, midY + Math.floor(r*0.2), r, Math.floor(r*0.3), '#ff9800', false));
25
+
26
+ // 3. Concave Surface (e.g. Bowl)
27
+ // Draw the top rim of a bowl
28
+ commands.push(shapes.drawEllipse(qX * 3, midY, r, Math.floor(r*0.3), '#9c27b0', false));
29
+ // Draw the bottom curve (the inside of the bowl)
30
+ const bowlPoints = [];
31
+ for(let i=0; i<=18; i++) {
32
+ const a = (i/18) * Math.PI; // 0 to 180 degrees (bottom half)
33
+ bowlPoints.push({ x: Math.round(qX * 3 + r * Math.cos(a)), y: Math.round(midY + r * Math.sin(a)) });
34
+ }
35
+ commands.push(shapes.drawPolyline(bowlPoints, '#9c27b0'));
36
+
37
+ return commands;
38
+ }
39
+
40
+ /**
41
+ * Lesson: Edge Types (Hard sharp corners vs Soft rounded edges)
42
+ */
43
+ function lessonEdgeTypes(canvasSize = 32, color = '#ff5722') {
44
+ const commands = [];
45
+ const qX1 = Math.floor(canvasSize * 0.3);
46
+ const qX2 = Math.floor(canvasSize * 0.7);
47
+ const midY = Math.floor(canvasSize / 2);
48
+ const w = Math.floor(canvasSize * 0.25);
49
+
50
+ // 1. Hard Edge (Sharp corners)
51
+ commands.push(shapes.drawRectangle(qX1 - w/2, midY - w/2, w, w, '#e91e63', false));
52
+
53
+ // 2. Soft Edge / Transitional
54
+ // Draw a box with rounded corners (fillet)
55
+ const r = Math.floor(w * 0.2); // Corner radius
56
+ const rx = qX2 - w/2;
57
+ const ry = midY - w/2;
58
+
59
+ // Straight segments
60
+ commands.push(shapes.drawLine(rx + r, ry, rx + w - r, ry, '#00bcd4')); // Top
61
+ commands.push(shapes.drawLine(rx + w, ry + r, rx + w, ry + w - r, '#00bcd4')); // Right
62
+ commands.push(shapes.drawLine(rx + r, ry + w, rx + w - r, ry + w, '#00bcd4')); // Bottom
63
+ commands.push(shapes.drawLine(rx, ry + r, rx, ry + w - r, '#00bcd4')); // Left
64
+
65
+ // Rounded corners (approximated with small circles for visual simplicity)
66
+ commands.push(shapes.drawCircle(rx + r, ry + r, r, '#00bcd4', false)); // TL
67
+ commands.push(shapes.drawCircle(rx + w - r, ry + r, r, '#00bcd4', false)); // TR
68
+ commands.push(shapes.drawCircle(rx + w - r, ry + w - r, r, '#00bcd4', false)); // BR
69
+ commands.push(shapes.drawCircle(rx + r, ry + w - r, r, '#00bcd4', false)); // BL
70
+
71
+ return commands;
72
+ }
73
+
74
+ /**
75
+ * Lesson: Cup Analysis (Analyzing surfaces of a cup)
76
+ */
77
+ function lessonCupAnalysis(canvasSize = 32, color = '#607d8b') {
78
+ const cx = Math.floor(canvasSize / 2);
79
+ const cy = Math.floor(canvasSize / 2);
80
+ const r = Math.floor(canvasSize * 0.2);
81
+ const h = Math.floor(canvasSize * 0.4);
82
+
83
+ return surfaceShapes.drawCupAnalysis(cx, cy, r, h);
84
+ }
85
+
86
+ /**
87
+ * Lesson: Chair Analysis (Analyzing surfaces and edges of a chair)
88
+ */
89
+ function lessonChairAnalysis(canvasSize = 32, color = '#795548') {
90
+ const cx = Math.floor(canvasSize / 2);
91
+ const cy = Math.floor(canvasSize / 2);
92
+ const w = Math.floor(canvasSize * 0.3);
93
+
94
+ return surfaceShapes.drawChairAnalysis(cx, cy, w);
95
+ }
96
+
97
+ function getSurfaceLessonCatalog() {
98
+ return [
99
+ { id: 'surface_types', name: 'Surface Types', description: 'Flat, Convex, Concave planes', fn: lessonSurfaceTypes },
100
+ { id: 'edge_types', name: 'Edge Types', description: 'Hard vs Soft (Transitional) edges', fn: lessonEdgeTypes },
101
+ { id: 'surface_cup', name: 'Cup Analysis', description: 'Real object: Convex/Concave walls, Rim, Tube handle', fn: lessonCupAnalysis },
102
+ { id: 'surface_chair', name: 'Chair Analysis', description: 'Real object: Flat planes, Hard edges, Cylindrical legs', fn: lessonChairAnalysis },
103
+ ];
104
+ }
105
+
106
+ module.exports = {
107
+ lessonSurfaceTypes,
108
+ lessonEdgeTypes,
109
+ lessonCupAnalysis,
110
+ lessonChairAnalysis,
111
+ getSurfaceLessonCatalog
112
+ };
@@ -0,0 +1,87 @@
1
+ const shapes = require('./shapes');
2
+ const shapes3d = require('./3d_shapes');
3
+
4
+ function drawCupAnalysis(cx, cy, r, h) {
5
+ const commands = [];
6
+ const topY = cy - Math.floor(h/2);
7
+ const botY = cy + Math.floor(h/2);
8
+ const topRy = Math.floor(r * 0.3);
9
+ const botRy = Math.floor(r * 0.4);
10
+
11
+ // Outer Wall (Convex) - Blue
12
+ commands.push(shapes.drawLine(cx - r, topY, cx - Math.floor(r*0.8), botY, '#2196f3'));
13
+ commands.push(shapes.drawLine(cx + r, topY, cx + Math.floor(r*0.8), botY, '#2196f3'));
14
+ // Base (Flat/Curved)
15
+ commands.push(shapes.drawEllipse(cx, botY, Math.floor(r*0.8), botRy, '#4caf50', false));
16
+
17
+ // Inner Wall (Concave) - Purple
18
+ // Draw the back half of the inner ellipse (the visible inside of the cup)
19
+ const innerR = Math.floor(r * 0.9);
20
+ const innerRy = Math.floor(topRy * 0.9);
21
+ // Just draw the full inner ellipse but highlighted differently
22
+ commands.push(shapes.drawEllipse(cx, topY, innerR, innerRy, '#9c27b0', false));
23
+
24
+ // Lip / Rim (Transitional edge) - Orange
25
+ // We represent the lip as the gap between the inner and outer ellipses at the top
26
+ commands.push(shapes.drawEllipse(cx, topY, r, topRy, '#ff9800', false));
27
+
28
+ // Handle (Tube surface) - Red
29
+ // Draw a curved handle on the right side
30
+ const hx = cx + r;
31
+ const hy = cy;
32
+ const hr = Math.floor(h * 0.3);
33
+ // Two nested semi-circles (arcs) for the handle thickness
34
+ const handleOuter = [];
35
+ const handleInner = [];
36
+ for(let i=0; i<=18; i++) {
37
+ const angle = (i/18) * Math.PI - Math.PI/2; // -90 to 90 degrees
38
+ handleOuter.push({ x: Math.round(hx + hr * Math.cos(angle)), y: Math.round(hy + hr * Math.sin(angle)) });
39
+ handleInner.push({ x: Math.round(hx + (hr-5) * Math.cos(angle)), y: Math.round(hy + (hr-5) * Math.sin(angle)) });
40
+ }
41
+ commands.push(shapes.drawPolyline(handleOuter, '#f44336'));
42
+ commands.push(shapes.drawPolyline(handleInner, '#f44336'));
43
+
44
+ return commands;
45
+ }
46
+
47
+ function drawChairAnalysis(cx, cy, w) {
48
+ const commands = [];
49
+ const h = Math.floor(w * 1.5);
50
+ const d = Math.floor(w * 0.8);
51
+ const seatY = cy;
52
+ const dx = Math.floor(d * 0.7);
53
+ const dy = Math.floor(d * 0.5);
54
+
55
+ // Seat (Flat plane) - Green
56
+ const fX = cx - Math.floor(w/2);
57
+ const bX = fX + dx;
58
+ const bY = seatY - dy;
59
+ // Seat wireframe
60
+ commands.push(shapes.drawRectangle(fX, seatY, w, 10, '#4caf50', false)); // Front thickness
61
+ commands.push(shapes.drawLine(fX, seatY, bX, bY, '#4caf50'));
62
+ commands.push(shapes.drawLine(fX+w, seatY, bX+w, bY, '#4caf50'));
63
+ commands.push(shapes.drawLine(bX, bY, bX+w, bY, '#4caf50'));
64
+
65
+ // Backrest (Flat plane + Hard edges) - Blue
66
+ commands.push(shapes.drawRectangle(bX, bY - Math.floor(h*0.5), w, Math.floor(h*0.5), '#2196f3', false));
67
+ // Inner rungs (Hard edges)
68
+ commands.push(shapes.drawRectangle(bX + 10, bY - Math.floor(h*0.4), 5, Math.floor(h*0.4), '#ff9800', false));
69
+ commands.push(shapes.drawRectangle(bX + w - 15, bY - Math.floor(h*0.4), 5, Math.floor(h*0.4), '#ff9800', false));
70
+
71
+ // Legs (Cylindrical surfaces) - Red
72
+ const legH = Math.floor(h*0.4);
73
+ const legR = 3;
74
+ // Front-left
75
+ commands.push(shapes.drawLine(fX+legR, seatY+10, fX+legR, seatY+10+legH, '#f44336'));
76
+ commands.push(shapes.drawLine(fX+10-legR, seatY+10, fX+10-legR, seatY+10+legH, '#f44336'));
77
+ // Front-right
78
+ commands.push(shapes.drawLine(fX+w-10+legR, seatY+10, fX+w-10+legR, seatY+10+legH, '#f44336'));
79
+ commands.push(shapes.drawLine(fX+w-legR, seatY+10, fX+w-legR, seatY+10+legH, '#f44336'));
80
+
81
+ return commands;
82
+ }
83
+
84
+ module.exports = {
85
+ drawCupAnalysis,
86
+ drawChairAnalysis
87
+ };
@@ -0,0 +1,60 @@
1
+ const { z } = require('zod');
2
+ const { registerTool, sendCommand } = require('../../core/server');
3
+ const surfaceLessons = require('./surface_lessons');
4
+
5
+ const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g. #ff0000');
6
+
7
+ /**
8
+ * Register all surface analysis tools on the MCP server.
9
+ * @param {McpServer} server
10
+ */
11
+ function register(server) {
12
+ registerTool(server, 'art_tree_lesson_surface_types',
13
+ 'Lesson: Surfaces - Analyze Flat, Convex, and Concave planes',
14
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#2196f3') },
15
+ async (p) => {
16
+ const commands = surfaceLessons.lessonSurfaceTypes(p.canvasSize, p.color);
17
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
18
+ return { content: [{ type: 'text', text: `✓ Surface Types lesson completed` }] };
19
+ });
20
+
21
+ registerTool(server, 'art_tree_lesson_edge_types',
22
+ 'Lesson: Surfaces - Analyze Hard vs Soft (Transitional) edges',
23
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#ff5722') },
24
+ async (p) => {
25
+ const commands = surfaceLessons.lessonEdgeTypes(p.canvasSize, p.color);
26
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
27
+ return { content: [{ type: 'text', text: `✓ Edge Types lesson completed` }] };
28
+ });
29
+
30
+ registerTool(server, 'art_tree_lesson_surface_cup',
31
+ 'Lesson: Surfaces - Cup Analysis (Real object breakdown)',
32
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#607d8b') },
33
+ async (p) => {
34
+ const commands = surfaceLessons.lessonCupAnalysis(p.canvasSize, p.color);
35
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
36
+ return { content: [{ type: 'text', text: `✓ Cup Surface Analysis lesson completed` }] };
37
+ });
38
+
39
+ registerTool(server, 'art_tree_lesson_surface_chair',
40
+ 'Lesson: Surfaces - Chair Analysis (Real object breakdown)',
41
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#795548') },
42
+ async (p) => {
43
+ const commands = surfaceLessons.lessonChairAnalysis(p.canvasSize, p.color);
44
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
45
+ return { content: [{ type: 'text', text: `✓ Chair Surface Analysis lesson completed` }] };
46
+ });
47
+
48
+ registerTool(server, 'art_tree_lesson_surface_catalog',
49
+ 'Get the catalog of all available Surface lessons',
50
+ {},
51
+ () => {
52
+ const catalog = surfaceLessons.getSurfaceLessonCatalog();
53
+ const text = catalog.map(l =>
54
+ ` • ${l.id}: ${l.name} — ${l.description}`
55
+ ).join('\n');
56
+ return { content: [{ type: 'text', text: `📚 Surface Lessons:\n\n${text}` }] };
57
+ });
58
+ }
59
+
60
+ module.exports = { register };
@@ -96,72 +96,126 @@ function register(server) {
96
96
  // ── Lesson Tools ──────────────────────────────────────────────────────
97
97
 
98
98
  registerTool(server, 'art_tree_lesson_lines',
99
- 'Lesson: Vẽ đường thẳng — Introduction to drawing straight lines',
99
+ 'Lesson: Lines — Introduction to drawing straight lines',
100
100
  { canvasSize: z.number().int().min(16).max(64).default(32),
101
101
  color: hexColor.default('#ff0000') },
102
102
  async (p) => {
103
103
  const commands = lessons.lessonLines(p.canvasSize, p.color);
104
104
  await Promise.all(commands.map(cmd => sendCommand(cmd)));
105
- return { content: [{ type: 'text', text: `✓ Bài học đường thẳng hoàn thành (${commands.length} nét vẽ)` }] };
105
+ return { content: [{ type: 'text', text: `✓ Lines lesson completed (${commands.length} strokes)` }] };
106
106
  });
107
107
 
108
108
  registerTool(server, 'art_tree_lesson_squares',
109
- 'Lesson: Hình vuông & Hình chữ nhật — Understanding right angles and proportions',
109
+ 'Lesson: Squares & Rectangles — Understanding right angles and proportions',
110
110
  { canvasSize: z.number().int().min(16).max(64).default(32),
111
111
  color: hexColor.default('#1565c0') },
112
112
  async (p) => {
113
113
  const commands = lessons.lessonSquares(p.canvasSize, p.color);
114
114
  await Promise.all(commands.map(cmd => sendCommand(cmd)));
115
- return { content: [{ type: 'text', text: `✓ Bài học hình vuông & chữ nhật hoàn thành (${commands.length} nét vẽ)` }] };
115
+ return { content: [{ type: 'text', text: `✓ Squares & Rectangles lesson completed (${commands.length} strokes)` }] };
116
116
  });
117
117
 
118
118
  registerTool(server, 'art_tree_lesson_circles',
119
- 'Lesson: Hình tròn — Learning curves and symmetry',
119
+ 'Lesson: Circles — Learning curves and symmetry',
120
120
  { canvasSize: z.number().int().min(16).max(64).default(32),
121
121
  color: hexColor.default('#e91e63') },
122
122
  async (p) => {
123
123
  const commands = lessons.lessonCircles(p.canvasSize, p.color);
124
124
  await Promise.all(commands.map(cmd => sendCommand(cmd)));
125
- return { content: [{ type: 'text', text: `✓ Bài học hình tròn hoàn thành (${commands.length} nét vẽ)` }] };
125
+ return { content: [{ type: 'text', text: `✓ Circles lesson completed (${commands.length} strokes)` }] };
126
126
  });
127
127
 
128
128
  registerTool(server, 'art_tree_lesson_ellipses',
129
- 'Lesson: Hình elip — Understanding oval shapes and proportions',
129
+ 'Lesson: Ellipses — Understanding oval shapes and proportions',
130
130
  { canvasSize: z.number().int().min(16).max(64).default(32),
131
131
  color: hexColor.default('#9c27b0') },
132
132
  async (p) => {
133
133
  const commands = lessons.lessonEllipses(p.canvasSize, p.color);
134
134
  await Promise.all(commands.map(cmd => sendCommand(cmd)));
135
- return { content: [{ type: 'text', text: `✓ Bài học hình elip hoàn thành (${commands.length} nét vẽ)` }] };
135
+ return { content: [{ type: 'text', text: `✓ Ellipses lesson completed (${commands.length} strokes)` }] };
136
136
  });
137
137
 
138
138
  registerTool(server, 'art_tree_lesson_triangles',
139
- 'Lesson: Hình tam giác — Three-point shapes and stability',
139
+ 'Lesson: Triangles — Three-point shapes and stability',
140
140
  { canvasSize: z.number().int().min(16).max(64).default(32),
141
141
  color: hexColor.default('#ff5722') },
142
142
  async (p) => {
143
143
  const commands = lessons.lessonTriangles(p.canvasSize, p.color);
144
144
  await Promise.all(commands.map(cmd => sendCommand(cmd)));
145
- return { content: [{ type: 'text', text: `✓ Bài học hình tam giác hoàn thành (${commands.length} nét vẽ)` }] };
145
+ return { content: [{ type: 'text', text: `✓ Triangles lesson completed (${commands.length} strokes)` }] };
146
146
  });
147
147
 
148
148
  registerTool(server, 'art_tree_lesson_polygons',
149
- 'Lesson: Đa giác — Multi-sided shapes',
149
+ 'Lesson: Polygons — Multi-sided shapes',
150
150
  { canvasSize: z.number().int().min(16).max(64).default(32),
151
151
  color: hexColor.default('#607d8b') },
152
152
  async (p) => {
153
153
  const commands = lessons.lessonPolygons(p.canvasSize, p.color);
154
154
  await Promise.all(commands.map(cmd => sendCommand(cmd)));
155
- return { content: [{ type: 'text', text: `✓ Bài học đa giác hoàn thành (${commands.length} nét vẽ)` }] };
155
+ return { content: [{ type: 'text', text: `✓ Polygons lesson completed (${commands.length} strokes)` }] };
156
156
  });
157
157
 
158
158
  registerTool(server, 'art_tree_lesson_composition',
159
- 'Lesson: Tổng hợp — Combining basic shapes to create simple objects (house, sun, tree)',
159
+ 'Lesson: Composition — Combining basic shapes to create simple objects (house, sun, tree)',
160
160
  { canvasSize: z.number().int().min(16).max(64).default(32) },
161
161
  async (p) => {
162
162
  const commands = lessons.lessonComposition(p.canvasSize);
163
163
  await Promise.all(commands.map(cmd => sendCommand(cmd)));
164
- return { content: [{ type: 'text', text: `✓ Bài học tổng hợp hoàn thành (${commands.length} nét vẽ)` }] };
164
+ return { content: [{ type: 'text', text: `✓ Composition lesson completed (${commands.length} strokes)` }] };
165
+ });
166
+
167
+ registerTool(server, 'art_tree_lesson_freehand',
168
+ 'Lesson: Freehand Drawing — Practice freehand drawing without rulers',
169
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#795548') },
170
+ async (p) => {
171
+ const commands = lessons.lessonFreehand(p.canvasSize, p.color);
172
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
173
+ return { content: [{ type: 'text', text: `✓ Freehand drawing lesson completed (${commands.length} small segments)` }] };
174
+ });
175
+
176
+ registerTool(server, 'art_tree_lesson_proportions',
177
+ 'Lesson: Proportions and Angles — Practice maintaining proportions and angles',
178
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#3f51b5') },
179
+ async (p) => {
180
+ const commands = lessons.lessonProportionsAndAngles(p.canvasSize, p.color);
181
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
182
+ return { content: [{ type: 'text', text: `✓ Proportions and Angles lesson completed` }] };
183
+ });
184
+
185
+ registerTool(server, 'art_tree_lesson_curves',
186
+ 'Lesson: Curves — C-curves and S-curves',
187
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#9c27b0') },
188
+ async (p) => {
189
+ const commands = lessons.lessonCurves(p.canvasSize, p.color);
190
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
191
+ return { content: [{ type: 'text', text: `✓ Curves (C, S) lesson completed (${commands.length} strokes)` }] };
192
+ });
193
+
194
+ registerTool(server, 'art_tree_lesson_spiral',
195
+ 'Lesson: Spiral — Drawing spiral curves',
196
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#ff9800') },
197
+ async (p) => {
198
+ const commands = lessons.lessonSpiral(p.canvasSize, p.color);
199
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
200
+ return { content: [{ type: 'text', text: `✓ Spiral lesson completed (${commands.length} strokes)` }] };
201
+ });
202
+
203
+ registerTool(server, 'art_tree_lesson_strokes',
204
+ 'Lesson: Stroke Types — Long strokes, short strokes, thick and thin lines',
205
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#2196f3') },
206
+ async (p) => {
207
+ const commands = lessons.lessonStrokes(p.canvasSize, p.color);
208
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
209
+ return { content: [{ type: 'text', text: `✓ Strokes lesson completed` }] };
210
+ });
211
+
212
+ registerTool(server, 'art_tree_lesson_parallel_intersecting',
213
+ 'Lesson: Parallel & Intersecting Lines — Drawing parallel and intersecting lines',
214
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#00bcd4') },
215
+ async (p) => {
216
+ const commands = lessons.lessonParallelAndIntersecting(p.canvasSize, p.color);
217
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
218
+ return { content: [{ type: 'text', text: `✓ Parallel & Intersecting Lines lesson completed` }] };
165
219
  });
166
220
 
167
221
  registerTool(server, 'art_tree_lesson_catalog',
@@ -172,7 +226,7 @@ function register(server) {
172
226
  const text = catalog.map(l =>
173
227
  ` • ${l.id}: ${l.name} — ${l.description}`
174
228
  ).join('\n');
175
- return { content: [{ type: 'text', text: `📚 Danh sách bài học Art Tree:\n\n${text}` }] };
229
+ return { content: [{ type: 'text', text: `📚 Art Tree Lesson Catalog:\n\n${text}` }] };
176
230
  });
177
231
  }
178
232
 
@@ -0,0 +1,108 @@
1
+ const shapes = require('./shapes');
2
+ const transformShapes = require('./transform_shapes');
3
+ const csShapes = require('./cross_section_shapes');
4
+
5
+ /**
6
+ * Lesson: Stretch (Elongate)
7
+ * Sphere -> Egg, Box -> Bar
8
+ */
9
+ function lessonStretch(canvasSize = 32, color = '#2196f3') {
10
+ const commands = [];
11
+ const qX1 = Math.floor(canvasSize * 0.25);
12
+ const qX2 = Math.floor(canvasSize * 0.75);
13
+ const cy = Math.floor(canvasSize / 2);
14
+ const r = Math.floor(canvasSize * 0.15);
15
+
16
+ // 1. Stretch: Sphere -> Egg
17
+ commands.push(shapes.drawCircle(qX1, cy, r, '#bdbdbd', false)); // Original sphere (ghost)
18
+ commands.push(...transformShapes.drawEgg(qX1, cy, r, color)); // Stretched into egg
19
+
20
+ // 2. Stretch: Box -> Bar (Tall)
21
+ const w = Math.floor(canvasSize * 0.15);
22
+ // Original box (ghost)
23
+ commands.push(shapes.drawRectangle(qX2 - w/2, cy - w/2, w, w, '#bdbdbd', false));
24
+ // Stretched bar
25
+ commands.push(shapes.drawRectangle(qX2 - w/2, cy - w * 1.5, w, w * 3, '#9c27b0', false));
26
+
27
+ return commands;
28
+ }
29
+
30
+ /**
31
+ * Lesson: Squash (Compress)
32
+ * Sphere -> Disk, Box -> Plate
33
+ */
34
+ function lessonSquash(canvasSize = 32, color = '#ff9800') {
35
+ const commands = [];
36
+ const qX1 = Math.floor(canvasSize * 0.25);
37
+ const qX2 = Math.floor(canvasSize * 0.75);
38
+ const cy = Math.floor(canvasSize / 2);
39
+ const r = Math.floor(canvasSize * 0.15);
40
+
41
+ // 1. Squash: Sphere -> Disk
42
+ commands.push(shapes.drawCircle(qX1, cy, r, '#bdbdbd', false)); // Original
43
+ // Squashed into a flat ellipse/disk
44
+ commands.push(shapes.drawEllipse(qX1, cy, r * 1.2, Math.floor(r * 0.3), color, false));
45
+
46
+ // 2. Squash: Box -> Plate
47
+ const w = Math.floor(canvasSize * 0.2);
48
+ commands.push(shapes.drawRectangle(qX2 - w/2, cy - w/2, w, w, '#bdbdbd', false)); // Original
49
+ // Squashed into plate
50
+ commands.push(shapes.drawRectangle(qX2 - w, cy - Math.floor(w*0.2), w * 2, Math.floor(w*0.4), '#4caf50', false));
51
+
52
+ return commands;
53
+ }
54
+
55
+ /**
56
+ * Lesson: Taper & Swell (Narrow or bulge)
57
+ * Cylinder -> Bottle
58
+ */
59
+ function lessonTaperSwell(canvasSize = 32, color = '#00bcd4') {
60
+ const commands = [];
61
+ const cx = Math.floor(canvasSize / 2);
62
+ const totalH = Math.floor(canvasSize * 0.8);
63
+ const topY = Math.floor(canvasSize * 0.1);
64
+ const bottomY = topY + totalH;
65
+ const r = Math.floor(canvasSize * 0.25);
66
+
67
+ // Original Cylinder (ghost)
68
+ commands.push(shapes.drawLine(cx - r, topY, cx - r, bottomY, '#bdbdbd'));
69
+ commands.push(shapes.drawLine(cx + r, topY, cx + r, bottomY, '#bdbdbd'));
70
+
71
+ // Tapered and Swelled form (Bottle)
72
+ const sections = [
73
+ { y: topY, r: Math.floor(r * 0.3) }, // Neck (Tapered)
74
+ { y: topY + Math.floor(totalH * 0.4), r: r },// Body (Swelled back to original radius)
75
+ { y: bottomY, r: Math.floor(r * 0.8) } // Base (Slightly tapered)
76
+ ];
77
+ commands.push(...csShapes.drawLoftedForm(cx, topY, bottomY, sections, color, true));
78
+
79
+ return commands;
80
+ }
81
+
82
+ /**
83
+ * Lesson: Bend (Curved bending)
84
+ * Cylinder -> Bent Tube
85
+ */
86
+ function lessonBend(canvasSize = 32, color = '#e91e63') {
87
+ const commands = [];
88
+ const cx = Math.floor(canvasSize / 2);
89
+ const cy = Math.floor(canvasSize / 2);
90
+ const r = Math.floor(canvasSize * 0.1);
91
+ const length = Math.floor(canvasSize * 0.35);
92
+
93
+ // Original straight tube (ghost, pointing right)
94
+ commands.push(shapes.drawLine(cx, cy - r, cx + length * 2, cy - r, '#bdbdbd'));
95
+ commands.push(shapes.drawLine(cx, cy + r, cx + length * 2, cy + r, '#bdbdbd'));
96
+
97
+ // Bent tube
98
+ commands.push(...transformShapes.drawBentTube(cx, cy + length, r, length, color));
99
+
100
+ return commands;
101
+ }
102
+
103
+ module.exports = {
104
+ lessonStretch,
105
+ lessonSquash,
106
+ lessonTaperSwell,
107
+ lessonBend
108
+ };