@pixel-normal-edit/mcp 2.0.10 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/clear-firebase.js +37 -0
  2. package/core/command-bus.js +6 -3
  3. package/core/server.js +98 -3
  4. package/domains/art-tree/README.md +134 -0
  5. package/domains/art-tree/REORGANIZATION_PLAN.md +168 -0
  6. package/domains/art-tree/REORGANIZATION_SUMMARY.md +260 -0
  7. package/domains/art-tree/index.js +58 -58
  8. package/domains/art-tree/lessons/3d/box-properties.js +38 -0
  9. package/domains/art-tree/lessons/3d/cone-properties.js +31 -0
  10. package/domains/art-tree/lessons/3d/cylinder-properties.js +29 -0
  11. package/domains/art-tree/lessons/3d/sphere-properties.js +25 -0
  12. package/domains/art-tree/lessons/advanced/angles.js +30 -0
  13. package/domains/art-tree/lessons/advanced/distances.js +26 -0
  14. package/domains/art-tree/lessons/advanced/shape-ratios.js +25 -0
  15. package/domains/art-tree/lessons/advanced/shape-relationships.js +28 -0
  16. package/domains/art-tree/lessons/advanced/symmetry-and-axis.js +26 -0
  17. package/domains/art-tree/{analysis_lessons.js → lessons/analysis/object-analysis.js} +5 -16
  18. package/domains/art-tree/lessons/basic/circles.js +26 -0
  19. package/domains/art-tree/lessons/basic/composition.js +34 -0
  20. package/domains/art-tree/lessons/basic/curves.js +31 -0
  21. package/domains/art-tree/lessons/basic/ellipses.js +26 -0
  22. package/domains/art-tree/lessons/basic/freehand.js +19 -0
  23. package/domains/art-tree/lessons/basic/index.js +36 -0
  24. package/domains/art-tree/lessons/basic/lines.js +38 -0
  25. package/domains/art-tree/lessons/basic/parallel-and-intersecting.js +22 -0
  26. package/domains/art-tree/lessons/basic/polygons.js +25 -0
  27. package/domains/art-tree/lessons/basic/proportions-and-angles.js +20 -0
  28. package/domains/art-tree/lessons/basic/spiral.js +21 -0
  29. package/domains/art-tree/lessons/basic/squares.js +28 -0
  30. package/domains/art-tree/lessons/basic/strokes.js +23 -0
  31. package/domains/art-tree/lessons/basic/triangles.js +30 -0
  32. package/domains/art-tree/lessons/cross-sections/bottle-sections.js +26 -0
  33. package/domains/art-tree/lessons/cross-sections/glass-sections.js +24 -0
  34. package/domains/art-tree/lessons/cross-sections/head-sections.js +17 -0
  35. package/domains/art-tree/lessons/curves/curve-properties.js +50 -0
  36. package/domains/art-tree/lessons/curves/curve-topology.js +45 -0
  37. package/domains/art-tree/lessons/curves/curve-types.js +44 -0
  38. package/domains/art-tree/lessons/ellipses/coaxial-ellipses.js +29 -0
  39. package/domains/art-tree/lessons/ellipses/ellipse-anatomy.js +26 -0
  40. package/domains/art-tree/lessons/ellipses/ellipse-orientations.js +28 -0
  41. package/domains/art-tree/lessons/ellipses/ellipse-proportions.js +27 -0
  42. package/domains/art-tree/lessons/hidden/hidden-box.js +20 -0
  43. package/domains/art-tree/lessons/hidden/hidden-cup.js +19 -0
  44. package/domains/art-tree/lessons/hidden/hidden-head.js +18 -0
  45. package/domains/art-tree/lessons/index.js +42 -0
  46. package/domains/art-tree/{layer_lessons.js → lessons/layers/layer-step.js} +5 -16
  47. package/domains/art-tree/lessons/lighting/contact-shadow.js +33 -0
  48. package/domains/art-tree/lessons/lighting/light-direction.js +30 -0
  49. package/domains/art-tree/lessons/lighting/light-zones.js +30 -0
  50. package/domains/art-tree/lessons/materials/material-glass.js +21 -0
  51. package/domains/art-tree/lessons/materials/material-shiny.js +26 -0
  52. package/domains/art-tree/lessons/materials/material-texture.js +26 -0
  53. package/domains/art-tree/lessons/perspective/1point-perspective.js +30 -0
  54. package/domains/art-tree/lessons/perspective/2point-perspective.js +30 -0
  55. package/domains/art-tree/lessons/perspective/3point-perspective.js +45 -0
  56. package/domains/art-tree/lessons/perspective/foreshortening.js +38 -0
  57. package/domains/art-tree/lessons/sky/sun-shapes.js +25 -0
  58. package/domains/art-tree/lessons/sky/sunset-colors.js +27 -0
  59. package/domains/art-tree/lessons/sky/sunset-sky.js +32 -0
  60. package/domains/art-tree/{structure_lessons.js → lessons/structure/axis-orientation.js} +3 -53
  61. package/domains/art-tree/lessons/structure/complex-structure.js +21 -0
  62. package/domains/art-tree/lessons/structure/x-yzaxes.js +28 -0
  63. package/domains/art-tree/lessons/surfaces/chair-analysis.js +17 -0
  64. package/domains/art-tree/lessons/surfaces/cup-analysis.js +18 -0
  65. package/domains/art-tree/lessons/surfaces/edge-types.js +40 -0
  66. package/domains/art-tree/lessons/surfaces/surface-types.js +42 -0
  67. package/domains/art-tree/lessons/transforms/bend.js +27 -0
  68. package/domains/art-tree/{transform_lessons_2.js → lessons/transforms/combine.js} +3 -54
  69. package/domains/art-tree/lessons/transforms/cut.js +25 -0
  70. package/domains/art-tree/lessons/transforms/hollow.js +24 -0
  71. package/domains/art-tree/lessons/transforms/rotate.js +16 -0
  72. package/domains/art-tree/lessons/transforms/squash.js +31 -0
  73. package/domains/art-tree/lessons/transforms/stretch.js +31 -0
  74. package/domains/art-tree/lessons/transforms/taper-swell.js +33 -0
  75. package/domains/art-tree/{vocab_lessons.js → lessons/vocab/vocab-house.js} +5 -16
  76. package/domains/art-tree/lib/tool-register.js +104 -0
  77. package/domains/art-tree/lib/validators.js +78 -0
  78. package/domains/art-tree/shapes/grid.js +37 -0
  79. package/domains/art-tree/shapes/polyline.js +26 -0
  80. package/domains/art-tree/{3d_tools.js → tools/3d.js} +2 -2
  81. package/domains/art-tree/{advanced_tools.js → tools/advanced.js} +2 -2
  82. package/domains/art-tree/{analysis_tools.js → tools/analysis.js} +2 -2
  83. package/domains/art-tree/{tools.js → tools/basic.js} +3 -3
  84. package/domains/art-tree/{cross_section_tools.js → tools/cross-sections.js} +2 -2
  85. package/domains/art-tree/{curve_tools.js → tools/curves.js} +2 -2
  86. package/domains/art-tree/{ellipse_tools.js → tools/ellipses.js} +2 -2
  87. package/domains/art-tree/{hidden_tools.js → tools/hidden.js} +2 -2
  88. package/domains/art-tree/tools/index.js +53 -0
  89. package/domains/art-tree/{layer_tools.js → tools/layers.js} +2 -2
  90. package/domains/art-tree/{light_tools.js → tools/lighting.js} +2 -2
  91. package/domains/art-tree/{material_tools.js → tools/materials.js} +2 -2
  92. package/domains/art-tree/{perspective_tools.js → tools/perspective.js} +2 -2
  93. package/domains/art-tree/{sky_tools.js → tools/sky.js} +2 -2
  94. package/domains/art-tree/{structure_tools.js → tools/structure.js} +2 -2
  95. package/domains/art-tree/{surface_tools.js → tools/surfaces.js} +2 -2
  96. package/domains/art-tree/{transform_tools.js → tools/transforms.js} +3 -3
  97. package/domains/art-tree/{vocab_tools.js → tools/vocab.js} +2 -2
  98. package/package.json +1 -1
  99. package/rules/README.md +358 -285
  100. package/rules/index.js +9 -9
  101. package/rules/prerequisites.js +121 -64
  102. package/rules/rules.js +76 -35
  103. package/rules/workflow.js +270 -56
  104. package/tools/canvas-tools.js +2 -2
  105. package/tools/drawing-tools.js +23 -12
  106. package/tools/workspace-tools.js +5 -4
  107. package/domains/art-tree/3d_lessons.js +0 -114
  108. package/domains/art-tree/advanced_lessons.js +0 -124
  109. package/domains/art-tree/cross_section_lessons.js +0 -65
  110. package/domains/art-tree/curve_lessons.js +0 -138
  111. package/domains/art-tree/ellipse_lessons.js +0 -105
  112. package/domains/art-tree/hidden_lessons.js +0 -55
  113. package/domains/art-tree/lessons.js +0 -305
  114. package/domains/art-tree/light_lessons.js +0 -92
  115. package/domains/art-tree/material_lessons.js +0 -71
  116. package/domains/art-tree/perspective_lessons.js +0 -138
  117. package/domains/art-tree/sky_lessons_1.js +0 -82
  118. package/domains/art-tree/surface_lessons.js +0 -112
  119. package/domains/art-tree/transform_lessons_1.js +0 -108
  120. /package/domains/art-tree/{3d_shapes.js → shapes/3d.js} +0 -0
  121. /package/domains/art-tree/{advanced_shapes.js → shapes/advanced.js} +0 -0
  122. /package/domains/art-tree/{analysis_shapes.js → shapes/analysis.js} +0 -0
  123. /package/domains/art-tree/{shapes.js → shapes/basic.js} +0 -0
  124. /package/domains/art-tree/{cross_section_shapes.js → shapes/cross-sections.js} +0 -0
  125. /package/domains/art-tree/{curve_shapes.js → shapes/curves.js} +0 -0
  126. /package/domains/art-tree/{ellipse_shapes.js → shapes/ellipses.js} +0 -0
  127. /package/domains/art-tree/{hidden_shapes.js → shapes/hidden.js} +0 -0
  128. /package/domains/art-tree/{layer_shapes.js → shapes/layers.js} +0 -0
  129. /package/domains/art-tree/{light_shapes.js → shapes/lighting.js} +0 -0
  130. /package/domains/art-tree/{material_shapes.js → shapes/materials.js} +0 -0
  131. /package/domains/art-tree/{perspective_shapes.js → shapes/perspective.js} +0 -0
  132. /package/domains/art-tree/{sky_shapes_1.js → shapes/sky.js} +0 -0
  133. /package/domains/art-tree/{structure_shapes.js → shapes/structure.js} +0 -0
  134. /package/domains/art-tree/{surface_shapes.js → shapes/surfaces.js} +0 -0
  135. /package/domains/art-tree/{transform_shapes.js → shapes/transforms.js} +0 -0
  136. /package/domains/art-tree/{vocab_shapes.js → shapes/vocab.js} +0 -0
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Auto-extracted from structure_lessons.js
3
+ */
4
+ const shapes = require('../../shapes');
5
+
6
+ /**
7
+ * Lesson: Complex Structure Analysis (e.g. A Bottle)
8
+ */
9
+ function lessonComplexStructure(canvasSize = 32, color = '#9c27b0') {
10
+ const commands = [];
11
+ const cx = Math.floor(canvasSize / 2);
12
+ const cy = Math.floor(canvasSize / 2);
13
+ const totalHeight = Math.floor(canvasSize * 0.8);
14
+
15
+ // Draw bottle structure showing cross-sections and central axis
16
+ commands.push(...structureShapes.drawBottleAnatomy(cx, cy, totalHeight, color));
17
+
18
+ return commands;
19
+ }
20
+
21
+ module.exports = { lessonComplexStructure };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Auto-extracted from structure_lessons.js
3
+ */
4
+ const shapes = require('../../shapes');
5
+
6
+ /**
7
+ * Lesson: XYZ Axes (Vertical, Horizontal, Depth)
8
+ */
9
+ function lessonXYZAxes(canvasSize = 32, color = '#2196f3') {
10
+ const commands = [];
11
+ const cx = Math.floor(canvasSize / 2);
12
+ const cy = Math.floor(canvasSize / 2);
13
+ const length = Math.floor(canvasSize * 0.4);
14
+
15
+ // Draw the axes
16
+ commands.push(...structureShapes.drawXYZAxes(cx, cy, length));
17
+
18
+ // Draw a simple box aligned with these axes to show how objects follow them
19
+ const w = Math.floor(length * 0.5);
20
+ const h = Math.floor(length * 0.5);
21
+ const d = Math.floor(length * 0.5);
22
+ // Using a lighter color for the reference box
23
+ commands.push(...shapes3d.drawWireframeBox(cx + Math.floor(w/2), cy - Math.floor(h/2), w, h, d, '#e0e0e0'));
24
+
25
+ return commands;
26
+ }
27
+
28
+ module.exports = { lessonXYZAxes };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Auto-extracted from surface_lessons.js
3
+ */
4
+ const shapes = require('../../shapes');
5
+
6
+ /**
7
+ * Lesson: Chair Analysis (Analyzing surfaces and edges of a chair)
8
+ */
9
+ function lessonChairAnalysis(canvasSize = 32, color = '#795548') {
10
+ const cx = Math.floor(canvasSize / 2);
11
+ const cy = Math.floor(canvasSize / 2);
12
+ const w = Math.floor(canvasSize * 0.3);
13
+
14
+ return surfaceShapes.drawChairAnalysis(cx, cy, w);
15
+ }
16
+
17
+ module.exports = { lessonChairAnalysis };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Auto-extracted from surface_lessons.js
3
+ */
4
+ const shapes = require('../../shapes');
5
+
6
+ /**
7
+ * Lesson: Cup Analysis (Analyzing surfaces of a cup)
8
+ */
9
+ function lessonCupAnalysis(canvasSize = 32, color = '#607d8b') {
10
+ const cx = Math.floor(canvasSize / 2);
11
+ const cy = Math.floor(canvasSize / 2);
12
+ const r = Math.floor(canvasSize * 0.2);
13
+ const h = Math.floor(canvasSize * 0.4);
14
+
15
+ return surfaceShapes.drawCupAnalysis(cx, cy, r, h);
16
+ }
17
+
18
+ module.exports = { lessonCupAnalysis };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Auto-extracted from surface_lessons.js
3
+ */
4
+ const shapes = require('../../shapes');
5
+
6
+ /**
7
+ * Lesson: Edge Types (Hard sharp corners vs Soft rounded edges)
8
+ */
9
+ function lessonEdgeTypes(canvasSize = 32, color = '#ff5722') {
10
+ const commands = [];
11
+ const qX1 = Math.floor(canvasSize * 0.3);
12
+ const qX2 = Math.floor(canvasSize * 0.7);
13
+ const midY = Math.floor(canvasSize / 2);
14
+ const w = Math.floor(canvasSize * 0.25);
15
+
16
+ // 1. Hard Edge (Sharp corners)
17
+ commands.push(shapes.drawRectangle(qX1 - w/2, midY - w/2, w, w, '#e91e63', false));
18
+
19
+ // 2. Soft Edge / Transitional
20
+ // Draw a box with rounded corners (fillet)
21
+ const r = Math.floor(w * 0.2); // Corner radius
22
+ const rx = qX2 - w/2;
23
+ const ry = midY - w/2;
24
+
25
+ // Straight segments
26
+ commands.push(shapes.drawLine(rx + r, ry, rx + w - r, ry, '#00bcd4')); // Top
27
+ commands.push(shapes.drawLine(rx + w, ry + r, rx + w, ry + w - r, '#00bcd4')); // Right
28
+ commands.push(shapes.drawLine(rx + r, ry + w, rx + w - r, ry + w, '#00bcd4')); // Bottom
29
+ commands.push(shapes.drawLine(rx, ry + r, rx, ry + w - r, '#00bcd4')); // Left
30
+
31
+ // Rounded corners (approximated with small circles for visual simplicity)
32
+ commands.push(shapes.drawCircle(rx + r, ry + r, r, '#00bcd4', false)); // TL
33
+ commands.push(shapes.drawCircle(rx + w - r, ry + r, r, '#00bcd4', false)); // TR
34
+ commands.push(shapes.drawCircle(rx + w - r, ry + w - r, r, '#00bcd4', false)); // BR
35
+ commands.push(shapes.drawCircle(rx + r, ry + w - r, r, '#00bcd4', false)); // BL
36
+
37
+ return commands;
38
+ }
39
+
40
+ module.exports = { lessonEdgeTypes };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Auto-extracted from surface_lessons.js
3
+ */
4
+ const shapes = require('../../shapes');
5
+
6
+ /**
7
+ * Lesson: Surface Types (Flat, convex, concave)
8
+ */
9
+ function lessonSurfaceTypes(canvasSize = 32, color = '#2196f3') {
10
+ const commands = [];
11
+ const midY = Math.floor(canvasSize / 2);
12
+ const qX = Math.floor(canvasSize / 4);
13
+ const r = Math.floor(canvasSize * 0.15);
14
+
15
+ // 1. Flat Plane
16
+ const w = Math.floor(canvasSize * 0.2);
17
+ commands.push(shapes.drawRectangle(qX - w/2, midY - w/2, w, w, '#4caf50', false));
18
+ // Add some grid lines to show it's flat
19
+ commands.push(shapes.drawLine(qX, midY - w/2, qX, midY + w/2, '#4caf50'));
20
+ commands.push(shapes.drawLine(qX - w/2, midY, qX + w/2, midY, '#4caf50'));
21
+
22
+ // 2. Convex Surface (e.g. Dome/Sphere)
23
+ // We use an ellipse that curves outwards (downwards)
24
+ commands.push(shapes.drawEllipse(qX * 2, midY, r, r, '#ff9800', false));
25
+ // Cross contour curving DOWN (convex towards viewer)
26
+ commands.push(shapes.drawEllipse(qX * 2, midY + Math.floor(r*0.2), r, Math.floor(r*0.3), '#ff9800', false));
27
+
28
+ // 3. Concave Surface (e.g. Bowl)
29
+ // Draw the top rim of a bowl
30
+ commands.push(shapes.drawEllipse(qX * 3, midY, r, Math.floor(r*0.3), '#9c27b0', false));
31
+ // Draw the bottom curve (the inside of the bowl)
32
+ const bowlPoints = [];
33
+ for(let i=0; i<=18; i++) {
34
+ const a = (i/18) * Math.PI; // 0 to 180 degrees (bottom half)
35
+ bowlPoints.push({ x: Math.round(qX * 3 + r * Math.cos(a)), y: Math.round(midY + r * Math.sin(a)) });
36
+ }
37
+ commands.push(shapes.drawPolyline(bowlPoints, '#9c27b0'));
38
+
39
+ return commands;
40
+ }
41
+
42
+ module.exports = { lessonSurfaceTypes };
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Auto-extracted from transform_lessons_1.js
3
+ */
4
+ const shapes = require('../../shapes');
5
+
6
+ /**
7
+ * Lesson: Bend (Curved bending)
8
+ * Cylinder -> Bent Tube
9
+ */
10
+ function lessonBend(canvasSize = 32, color = '#e91e63') {
11
+ const commands = [];
12
+ const cx = Math.floor(canvasSize / 2);
13
+ const cy = Math.floor(canvasSize / 2);
14
+ const r = Math.floor(canvasSize * 0.1);
15
+ const length = Math.floor(canvasSize * 0.35);
16
+
17
+ // Original straight tube (ghost, pointing right)
18
+ commands.push(shapes.drawLine(cx, cy - r, cx + length * 2, cy - r, '#bdbdbd'));
19
+ commands.push(shapes.drawLine(cx, cy + r, cx + length * 2, cy + r, '#bdbdbd'));
20
+
21
+ // Bent tube
22
+ commands.push(...transformShapes.drawBentTube(cx, cy + length, r, length, color));
23
+
24
+ return commands;
25
+ }
26
+
27
+ module.exports = { lessonBend };
@@ -1,53 +1,7 @@
1
- const shapes = require('./shapes');
2
- const transformShapes = require('./transform_shapes');
3
- const structureLessons = require('./structure_lessons');
4
-
5
- /**
6
- * Lesson: Rotate (Rotation)
7
- * Analyzing how objects change when rotated around X, Y, Z axes.
8
- * We can reuse the Axis Orientation lesson as it perfectly demonstrates rotation.
9
- */
10
- function lessonRotate(canvasSize = 32, color = '#ff5722') {
11
- // Reusing the axis orientation lesson which draws cylinders rotated along X, Y, Z
12
- return structureLessons.lessonAxisOrientation(canvasSize, color);
13
- }
14
-
15
1
  /**
16
- * Lesson: Cut (Slice a form)
17
- * Sphere -> Truncated Sphere (exposing internal cross-section)
2
+ * Auto-extracted from transform_lessons_2.js
18
3
  */
19
- function lessonCut(canvasSize = 32, color = '#8bc34a') {
20
- const commands = [];
21
- const cx = Math.floor(canvasSize / 2);
22
- const cy = Math.floor(canvasSize / 2);
23
- const r = Math.floor(canvasSize * 0.35);
24
-
25
- // Original sphere (ghost)
26
- commands.push(shapes.drawCircle(cx, cy, r, '#bdbdbd', false));
27
-
28
- // Cut sphere
29
- commands.push(...transformShapes.drawTruncatedSphere(cx, cy, r, color));
30
-
31
- return commands;
32
- }
33
-
34
- /**
35
- * Lesson: Hollow (Carve interior)
36
- * Cylinder -> Pipe/Hole
37
- */
38
- function lessonHollow(canvasSize = 32, color = '#3f51b5') {
39
- const commands = [];
40
- const cx = Math.floor(canvasSize / 2);
41
- const cy = Math.floor(canvasSize / 2);
42
- const outerR = Math.floor(canvasSize * 0.35);
43
- const innerR = Math.floor(canvasSize * 0.25);
44
- const h = Math.floor(canvasSize * 0.6);
45
-
46
- // Hollow cylinder
47
- commands.push(...transformShapes.drawHollowCylinder(cx, cy, outerR, innerR, h, color));
48
-
49
- return commands;
50
- }
4
+ const shapes = require('../../shapes');
51
5
 
52
6
  /**
53
7
  * Lesson: Combine (Merge forms)
@@ -94,9 +48,4 @@ function lessonCombine(canvasSize = 32, color = '#607d8b') {
94
48
  return commands;
95
49
  }
96
50
 
97
- module.exports = {
98
- lessonRotate,
99
- lessonCut,
100
- lessonHollow,
101
- lessonCombine
102
- };
51
+ module.exports = { lessonCombine };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Auto-extracted from transform_lessons_2.js
3
+ */
4
+ const shapes = require('../../shapes');
5
+
6
+ /**
7
+ * Lesson: Cut (Slice a form)
8
+ * Sphere -> Truncated Sphere (exposing internal cross-section)
9
+ */
10
+ function lessonCut(canvasSize = 32, color = '#8bc34a') {
11
+ const commands = [];
12
+ const cx = Math.floor(canvasSize / 2);
13
+ const cy = Math.floor(canvasSize / 2);
14
+ const r = Math.floor(canvasSize * 0.35);
15
+
16
+ // Original sphere (ghost)
17
+ commands.push(shapes.drawCircle(cx, cy, r, '#bdbdbd', false));
18
+
19
+ // Cut sphere
20
+ commands.push(...transformShapes.drawTruncatedSphere(cx, cy, r, color));
21
+
22
+ return commands;
23
+ }
24
+
25
+ module.exports = { lessonCut };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Auto-extracted from transform_lessons_2.js
3
+ */
4
+ const shapes = require('../../shapes');
5
+
6
+ /**
7
+ * Lesson: Hollow (Carve interior)
8
+ * Cylinder -> Pipe/Hole
9
+ */
10
+ function lessonHollow(canvasSize = 32, color = '#3f51b5') {
11
+ const commands = [];
12
+ const cx = Math.floor(canvasSize / 2);
13
+ const cy = Math.floor(canvasSize / 2);
14
+ const outerR = Math.floor(canvasSize * 0.35);
15
+ const innerR = Math.floor(canvasSize * 0.25);
16
+ const h = Math.floor(canvasSize * 0.6);
17
+
18
+ // Hollow cylinder
19
+ commands.push(...transformShapes.drawHollowCylinder(cx, cy, outerR, innerR, h, color));
20
+
21
+ return commands;
22
+ }
23
+
24
+ module.exports = { lessonHollow };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Auto-extracted from transform_lessons_2.js
3
+ */
4
+ const shapes = require('../../shapes');
5
+
6
+ /**
7
+ * Lesson: Rotate (Rotation)
8
+ * Analyzing how objects change when rotated around X, Y, Z axes.
9
+ * We can reuse the Axis Orientation lesson as it perfectly demonstrates rotation.
10
+ */
11
+ function lessonRotate(canvasSize = 32, color = '#ff5722') {
12
+ // Reusing the axis orientation lesson which draws cylinders rotated along X, Y, Z
13
+ return structureLessons.lessonAxisOrientation(canvasSize, color);
14
+ }
15
+
16
+ module.exports = { lessonRotate };
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Auto-extracted from transform_lessons_1.js
3
+ */
4
+ const shapes = require('../../shapes');
5
+
6
+ /**
7
+ * Lesson: Squash (Compress)
8
+ * Sphere -> Disk, Box -> Plate
9
+ */
10
+ function lessonSquash(canvasSize = 32, color = '#ff9800') {
11
+ const commands = [];
12
+ const qX1 = Math.floor(canvasSize * 0.25);
13
+ const qX2 = Math.floor(canvasSize * 0.75);
14
+ const cy = Math.floor(canvasSize / 2);
15
+ const r = Math.floor(canvasSize * 0.15);
16
+
17
+ // 1. Squash: Sphere -> Disk
18
+ commands.push(shapes.drawCircle(qX1, cy, r, '#bdbdbd', false)); // Original
19
+ // Squashed into a flat ellipse/disk
20
+ commands.push(shapes.drawEllipse(qX1, cy, r * 1.2, Math.floor(r * 0.3), color, false));
21
+
22
+ // 2. Squash: Box -> Plate
23
+ const w = Math.floor(canvasSize * 0.2);
24
+ commands.push(shapes.drawRectangle(qX2 - w/2, cy - w/2, w, w, '#bdbdbd', false)); // Original
25
+ // Squashed into plate
26
+ commands.push(shapes.drawRectangle(qX2 - w, cy - Math.floor(w*0.2), w * 2, Math.floor(w*0.4), '#4caf50', false));
27
+
28
+ return commands;
29
+ }
30
+
31
+ module.exports = { lessonSquash };
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Auto-extracted from transform_lessons_1.js
3
+ */
4
+ const shapes = require('../../shapes');
5
+
6
+ /**
7
+ * Lesson: Stretch (Elongate)
8
+ * Sphere -> Egg, Box -> Bar
9
+ */
10
+ function lessonStretch(canvasSize = 32, color = '#2196f3') {
11
+ const commands = [];
12
+ const qX1 = Math.floor(canvasSize * 0.25);
13
+ const qX2 = Math.floor(canvasSize * 0.75);
14
+ const cy = Math.floor(canvasSize / 2);
15
+ const r = Math.floor(canvasSize * 0.15);
16
+
17
+ // 1. Stretch: Sphere -> Egg
18
+ commands.push(shapes.drawCircle(qX1, cy, r, '#bdbdbd', false)); // Original sphere (ghost)
19
+ commands.push(...transformShapes.drawEgg(qX1, cy, r, color)); // Stretched into egg
20
+
21
+ // 2. Stretch: Box -> Bar (Tall)
22
+ const w = Math.floor(canvasSize * 0.15);
23
+ // Original box (ghost)
24
+ commands.push(shapes.drawRectangle(qX2 - w/2, cy - w/2, w, w, '#bdbdbd', false));
25
+ // Stretched bar
26
+ commands.push(shapes.drawRectangle(qX2 - w/2, cy - w * 1.5, w, w * 3, '#9c27b0', false));
27
+
28
+ return commands;
29
+ }
30
+
31
+ module.exports = { lessonStretch };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Auto-extracted from transform_lessons_1.js
3
+ */
4
+ const shapes = require('../../shapes');
5
+
6
+ /**
7
+ * Lesson: Taper & Swell (Narrow or bulge)
8
+ * Cylinder -> Bottle
9
+ */
10
+ function lessonTaperSwell(canvasSize = 32, color = '#00bcd4') {
11
+ const commands = [];
12
+ const cx = Math.floor(canvasSize / 2);
13
+ const totalH = Math.floor(canvasSize * 0.8);
14
+ const topY = Math.floor(canvasSize * 0.1);
15
+ const bottomY = topY + totalH;
16
+ const r = Math.floor(canvasSize * 0.25);
17
+
18
+ // Original Cylinder (ghost)
19
+ commands.push(shapes.drawLine(cx - r, topY, cx - r, bottomY, '#bdbdbd'));
20
+ commands.push(shapes.drawLine(cx + r, topY, cx + r, bottomY, '#bdbdbd'));
21
+
22
+ // Tapered and Swelled form (Bottle)
23
+ const sections = [
24
+ { y: topY, r: Math.floor(r * 0.3) }, // Neck (Tapered)
25
+ { y: topY + Math.floor(totalH * 0.4), r: r },// Body (Swelled back to original radius)
26
+ { y: bottomY, r: Math.floor(r * 0.8) } // Base (Slightly tapered)
27
+ ];
28
+ commands.push(...csShapes.drawLoftedForm(cx, topY, bottomY, sections, color, true));
29
+
30
+ return commands;
31
+ }
32
+
33
+ module.exports = { lessonTaperSwell };
@@ -1,4 +1,7 @@
1
- const vocabShapes = require('./vocab_shapes');
1
+ /**
2
+ * Auto-extracted from vocab_lessons.js
3
+ */
4
+ const shapes = require('../../shapes');
2
5
 
3
6
  /**
4
7
  * Lesson: Visual Vocabulary - House Breakdown
@@ -15,18 +18,4 @@ function lessonVocabHouse(canvasSize = 64, step = 1) {
15
18
  return vocabShapes.drawHouseBreakdown(cx, cy, canvasSize, validStep);
16
19
  }
17
20
 
18
- function getVocabLessonCatalog() {
19
- return [
20
- {
21
- id: 'vocab_house',
22
- name: 'House Breakdown (5 Steps)',
23
- description: 'Break down a house: Masses -> Structural -> Functional -> Identity -> Materials. Supply step=1..5',
24
- fn: lessonVocabHouse
25
- }
26
- ];
27
- }
28
-
29
- module.exports = {
30
- lessonVocabHouse,
31
- getVocabLessonCatalog
32
- };
21
+ module.exports = { lessonVocabHouse };
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * tool-register.js — Art Tree: Tool Registration Helpers
4
+ *
5
+ * Provides helper functions to reduce boilerplate when registering
6
+ * Art Tree MCP tools. Common patterns are abstracted here.
7
+ */
8
+
9
+ const { z } = require('zod');
10
+ const { registerTool, sendCommand } = require('../../core/server');
11
+ const { hexColor, canvasSize } = require('./validators');
12
+
13
+ /**
14
+ * Register a lesson tool with standard pattern
15
+ * @param {McpServer} server - MCP server instance
16
+ * @param {string} name - Tool name
17
+ * @param {string} description - Tool description
18
+ * @param {Function} lessonFn - Function that returns array of commands
19
+ * @param {Object} [options] - Optional configuration
20
+ * @param {number} [options.canvasSizeMax=64] - Max canvas size
21
+ * @param {string} [options.defaultColor] - Default color (uses hexColor default if not provided)
22
+ */
23
+ function registerLessonTool(server, name, description, lessonFn, options = {}) {
24
+ const { canvasSizeMax = 64, defaultColor } = options;
25
+
26
+ const schema = {
27
+ canvasSize: canvasSize.max(canvasSizeMax),
28
+ };
29
+
30
+ if (defaultColor) {
31
+ schema.color = hexColor.default(defaultColor);
32
+ }
33
+
34
+ registerTool(server, name, description, schema, async (params) => {
35
+ const commands = lessonFn(params.canvasSize, params.color || defaultColor);
36
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
37
+ return {
38
+ content: [{
39
+ type: 'text',
40
+ text: `✓ ${description.split(' — ')[0]} completed (${commands.length} strokes)`
41
+ }]
42
+ };
43
+ });
44
+ }
45
+
46
+ /**
47
+ * Register a catalog tool that returns a list of available lessons
48
+ * @param {McpServer} server - MCP server instance
49
+ * @param {string} name - Tool name
50
+ * @param {string} description - Tool description
51
+ * @param {Function} getCatalogFn - Function that returns catalog array
52
+ * @param {string} catalogTitle - Title for the catalog output
53
+ */
54
+ function registerCatalogTool(server, name, description, getCatalogFn, catalogTitle) {
55
+ registerTool(server, name, description, {}, () => {
56
+ const catalog = getCatalogFn();
57
+ const text = catalog.map(l =>
58
+ ` • ${l.id}: ${l.name} — ${l.description}`
59
+ ).join('\n');
60
+ return { content: [{ type: 'text', text: `📚 ${catalogTitle}:\n\n${text}` }] };
61
+ });
62
+ }
63
+
64
+ /**
65
+ * Register a tool that executes a specific lesson step
66
+ * @param {McpServer} server - MCP server instance
67
+ * @param {string} name - Tool name
68
+ * @param {string} description - Tool description
69
+ * @param {Function} lessonStepFn - Function that takes step number and returns commands
70
+ * @param {Object} [options] - Optional configuration
71
+ * @param {number} [options.minStep=1] - Minimum step number
72
+ * @param {number} [options.maxStep=10] - Maximum step number
73
+ * @param {number} [options.canvasSizeMax=64] - Max canvas size
74
+ * @param {string} [options.defaultColor] - Default color
75
+ */
76
+ function registerStepTool(server, name, description, lessonStepFn, options = {}) {
77
+ const { minStep = 1, maxStep = 10, canvasSizeMax = 64, defaultColor } = options;
78
+
79
+ const schema = {
80
+ canvasSize: canvasSize.max(canvasSizeMax),
81
+ step: z.number().int().min(minStep).max(maxStep).describe('Step number'),
82
+ };
83
+
84
+ if (defaultColor) {
85
+ schema.color = hexColor.default(defaultColor);
86
+ }
87
+
88
+ registerTool(server, name, description, schema, async (params) => {
89
+ const commands = lessonStepFn(params.canvasSize, params.step, params.color || defaultColor);
90
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
91
+ return {
92
+ content: [{
93
+ type: 'text',
94
+ text: `✓ ${description.split(' — ')[0]} - Step ${params.step} completed`
95
+ }]
96
+ };
97
+ });
98
+ }
99
+
100
+ module.exports = {
101
+ registerLessonTool,
102
+ registerCatalogTool,
103
+ registerStepTool,
104
+ };
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * validators.js — Art Tree: Shared Zod Validators
4
+ *
5
+ * Common validation schemas used across all Art Tree tools.
6
+ * Centralizes repeated patterns to reduce code duplication.
7
+ */
8
+
9
+ const { z } = require('zod');
10
+
11
+ /**
12
+ * Hex color validator - ensures valid 6-character hex color codes
13
+ */
14
+ const hexColor = z.string()
15
+ .regex(/^#[0-9a-fA-F]{6}$/)
16
+ .describe('Hex color e.g. #ff0000');
17
+
18
+ /**
19
+ * Canvas size validator - ensures reasonable canvas dimensions
20
+ */
21
+ const canvasSize = z.number()
22
+ .int()
23
+ .min(16)
24
+ .max(128)
25
+ .default(32)
26
+ .describe('Canvas dimension (16-128, default 32)');
27
+
28
+ /**
29
+ * Coordinate validator - ensures integer coordinates
30
+ */
31
+ const coordinate = z.number()
32
+ .int()
33
+ .describe('Integer coordinate');
34
+
35
+ /**
36
+ * Positive integer validator - for sizes, counts, etc.
37
+ */
38
+ const positiveInt = z.number()
39
+ .int()
40
+ .min(1)
41
+ .describe('Positive integer (min 1)');
42
+
43
+ /**
44
+ * Radius validator - ensures positive radius
45
+ */
46
+ const radius = z.number()
47
+ .int()
48
+ .min(1)
49
+ .describe('Radius (min 1)');
50
+
51
+ /**
52
+ * Boolean with default false
53
+ */
54
+ const booleanFalse = z.boolean()
55
+ .default(false)
56
+ .describe('Boolean flag (default false)');
57
+
58
+ /**
59
+ * Common tool parameter schemas
60
+ */
61
+ const schemas = {
62
+ hexColor,
63
+ canvasSize,
64
+ coordinate,
65
+ positiveInt,
66
+ radius,
67
+ booleanFalse,
68
+ };
69
+
70
+ module.exports = {
71
+ hexColor,
72
+ canvasSize,
73
+ coordinate,
74
+ positiveInt,
75
+ radius,
76
+ booleanFalse,
77
+ schemas,
78
+ };
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * shapes/grid.js — Art Tree: Grid Shape Primitives
4
+ *
5
+ * Grid-related shape drawing functions.
6
+ */
7
+
8
+ const { drawLine } = require('./basic');
9
+
10
+ /**
11
+ * Draw a grid of squares (useful for pixel art foundation lessons).
12
+ * @param {number} startX - Top-left X
13
+ * @param {number} startY - Top-left Y
14
+ * @param {number} cols - Number of columns
15
+ * @param {number} rows - Number of rows
16
+ * @param {number} cellSize - Size of each cell
17
+ * @param {string} color - Grid line color
18
+ * @returns {Array<Object>} Array of command payloads
19
+ */
20
+ function drawGrid(startX, startY, cols, rows, cellSize, color) {
21
+ const commands = [];
22
+ // Horizontal lines
23
+ for (let r = 0; r <= rows; r++) {
24
+ const y = startY + r * cellSize;
25
+ commands.push(drawLine(startX, y, startX + cols * cellSize, y, color));
26
+ }
27
+ // Vertical lines
28
+ for (let c = 0; c <= cols; c++) {
29
+ const x = startX + c * cellSize;
30
+ commands.push(drawLine(x, startY, x, startY + rows * cellSize, color));
31
+ }
32
+ return commands;
33
+ }
34
+
35
+ module.exports = {
36
+ drawGrid,
37
+ };