@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,114 @@
1
+ const shapes3d = require('./3d_shapes');
2
+ const advShapes = require('./advanced_shapes');
3
+
4
+ /**
5
+ * Lesson: Box Properties (Width, Height, Depth, Planes, Edges)
6
+ */
7
+ function lessonBoxProperties(canvasSize = 32, color = '#2196f3') {
8
+ const commands = [];
9
+ const cx = Math.floor(canvasSize / 2);
10
+ const cy = Math.floor(canvasSize / 2);
11
+ const w = Math.floor(canvasSize * 0.4);
12
+ const h = Math.floor(canvasSize * 0.4);
13
+ const d = Math.floor(canvasSize * 0.3);
14
+
15
+ // Draw wireframe box
16
+ commands.push(...shapes3d.drawWireframeBox(cx, cy, w, h, d, color));
17
+
18
+ // Highlight dimensions
19
+ const fX = cx - Math.floor(w/2);
20
+ const fY = cy - Math.floor(h/2);
21
+
22
+ // Width
23
+ commands.push(...advShapes.drawDistance(fX, fY + h + 2, fX + w, fY + h + 2, '#4caf50'));
24
+ // Height
25
+ commands.push(...advShapes.drawDistance(fX - 2, fY, fX - 2, fY + h, '#ff9800'));
26
+
27
+ // Depth (approximate along the oblique edge)
28
+ const dx = Math.floor(d * 0.7);
29
+ const dy = Math.floor(d * 0.5);
30
+ commands.push(...advShapes.drawDistance(fX + w + 2, fY + h, fX + w + 2 + dx, fY + h - dy, '#e91e63'));
31
+
32
+ return commands;
33
+ }
34
+
35
+ /**
36
+ * Lesson: Sphere Properties (Center, Radius, Cross contours, Axis)
37
+ */
38
+ function lessonSphereProperties(canvasSize = 32, color = '#9c27b0') {
39
+ const commands = [];
40
+ const cx = Math.floor(canvasSize / 2);
41
+ const cy = Math.floor(canvasSize / 2);
42
+ const r = Math.floor(canvasSize * 0.35);
43
+
44
+ // Draw sphere with volume lines
45
+ commands.push(...shapes3d.drawSphereVolume(cx, cy, r, color));
46
+
47
+ // Highlight radius
48
+ commands.push(...advShapes.drawDistance(cx, cy, cx + r, cy, '#ff9800'));
49
+
50
+ return commands;
51
+ }
52
+
53
+ /**
54
+ * Lesson: Cylinder Properties (Central axis, Bases, Perspective change)
55
+ */
56
+ function lessonCylinderProperties(canvasSize = 32, color = '#00bcd4') {
57
+ const commands = [];
58
+ const cx = Math.floor(canvasSize / 2);
59
+ const cy = Math.floor(canvasSize / 2);
60
+ const r = Math.floor(canvasSize * 0.3);
61
+ const h = Math.floor(canvasSize * 0.6);
62
+
63
+ // Draw cylinder volume
64
+ commands.push(...shapes3d.drawCylinderVolume(cx, cy, r, h, color));
65
+
66
+ // Highlight Height
67
+ commands.push(...advShapes.drawDistance(cx + r + 4, cy - Math.floor(h/2), cx + r + 4, cy + Math.floor(h/2), '#4caf50'));
68
+
69
+ // Highlight Radius (top base)
70
+ commands.push(...advShapes.drawDistance(cx, cy - Math.floor(h/2), cx + r, cy - Math.floor(h/2), '#ff9800'));
71
+
72
+ return commands;
73
+ }
74
+
75
+ /**
76
+ * Lesson: Cone Properties (Apex, Base, Axis)
77
+ */
78
+ function lessonConeProperties(canvasSize = 32, color = '#ff5722') {
79
+ const commands = [];
80
+ const cx = Math.floor(canvasSize / 2);
81
+ const cy = Math.floor(canvasSize / 2);
82
+ const r = Math.floor(canvasSize * 0.35);
83
+ const h = Math.floor(canvasSize * 0.6);
84
+
85
+ // Draw cone volume
86
+ commands.push(...shapes3d.drawConeVolume(cx, cy, r, h, color));
87
+
88
+ // Highlight Height
89
+ const apexY = cy - Math.floor(h/2);
90
+ const baseY = cy + Math.floor(h/2);
91
+ commands.push(...advShapes.drawDistance(cx + r + 4, apexY, cx + r + 4, baseY, '#4caf50'));
92
+
93
+ // Highlight Radius (base)
94
+ commands.push(...advShapes.drawDistance(cx, baseY, cx + r, baseY, '#ff9800'));
95
+
96
+ return commands;
97
+ }
98
+
99
+ function get3DLessonCatalog() {
100
+ return [
101
+ { id: '3d_box', name: '3D Box Properties', description: 'Width, height, depth, planes, inner structure', fn: lessonBoxProperties },
102
+ { id: '3d_sphere', name: '3D Sphere Properties', description: 'Center, radius, axis, cross contours', fn: lessonSphereProperties },
103
+ { id: '3d_cylinder', name: '3D Cylinder Properties', description: 'Axis, bases, height, perspective changes', fn: lessonCylinderProperties },
104
+ { id: '3d_cone', name: '3D Cone Properties', description: 'Apex, base, axis, radius', fn: lessonConeProperties },
105
+ ];
106
+ }
107
+
108
+ module.exports = {
109
+ lessonBoxProperties,
110
+ lessonSphereProperties,
111
+ lessonCylinderProperties,
112
+ lessonConeProperties,
113
+ get3DLessonCatalog
114
+ };
@@ -0,0 +1,116 @@
1
+ const shapes = require('./shapes');
2
+ const ellipseShapes = require('./ellipse_shapes');
3
+ const advShapes = require('./advanced_shapes');
4
+
5
+ /**
6
+ * Draw a wireframe box (oblique projection)
7
+ */
8
+ function drawWireframeBox(cx, cy, width, height, depth, color) {
9
+ const commands = [];
10
+ const dx = Math.floor(depth * 0.7);
11
+ const dy = Math.floor(depth * 0.5);
12
+
13
+ // Front face
14
+ const fX = cx - Math.floor(width/2);
15
+ const fY = cy - Math.floor(height/2);
16
+ commands.push(shapes.drawRectangle(fX, fY, width, height, color, false));
17
+
18
+ // Back face (shifted by dx, -dy)
19
+ const bX = fX + dx;
20
+ const bY = fY - dy;
21
+ const backColor = '#9e9e9e'; // Lighter color for inner/back structure
22
+ commands.push(shapes.drawRectangle(bX, bY, width, height, backColor, false));
23
+
24
+ // Connect corners (edges)
25
+ commands.push(shapes.drawLine(fX, fY, bX, bY, color)); // Top-left
26
+ commands.push(shapes.drawLine(fX + width, fY, bX + width, bY, color)); // Top-right
27
+ commands.push(shapes.drawLine(fX, fY + height, bX, bY + height, backColor)); // Bottom-left (hidden)
28
+ commands.push(shapes.drawLine(fX + width, fY + height, bX + width, bY + height, color)); // Bottom-right
29
+
30
+ return commands;
31
+ }
32
+
33
+ /**
34
+ * Draw a sphere showing volume with cross contours
35
+ */
36
+ function drawSphereVolume(cx, cy, r, color) {
37
+ const commands = [];
38
+
39
+ // Silhouette
40
+ commands.push(shapes.drawCircle(cx, cy, r, color, false));
41
+
42
+ // Horizontal cross contour (latitude)
43
+ commands.push(shapes.drawEllipse(cx, cy, r, Math.floor(r * 0.3), '#03a9f4', false));
44
+
45
+ // Vertical cross contour (longitude)
46
+ commands.push(shapes.drawEllipse(cx, cy, Math.floor(r * 0.3), r, '#03a9f4', false));
47
+
48
+ // Center
49
+ commands.push(shapes.drawCircle(cx, cy, 1, '#ff0000', true));
50
+
51
+ // Axis
52
+ commands.push(advShapes.drawAxis(cx, cy - r - 5, cx, cy + r + 5));
53
+
54
+ return commands;
55
+ }
56
+
57
+ /**
58
+ * Draw a cylinder showing volume and perspective
59
+ */
60
+ function drawCylinderVolume(cx, cy, r, h, color) {
61
+ const commands = [];
62
+
63
+ const topRy = Math.floor(r * 0.2); // Top ellipse is narrower
64
+ const botRy = Math.floor(r * 0.3); // Bottom ellipse is wider (perspective)
65
+
66
+ const topY = cy - Math.floor(h/2);
67
+ const botY = cy + Math.floor(h/2);
68
+
69
+ // Central axis
70
+ commands.push(advShapes.drawAxis(cx, topY - 10, cx, botY + 10));
71
+
72
+ // Back curve of bottom ellipse (lighter)
73
+ // Our drawEllipse draws the whole thing, we'll just draw the whole ellipse
74
+ commands.push(shapes.drawEllipse(cx, botY, r, botRy, color, false));
75
+
76
+ // Vertical edges
77
+ commands.push(shapes.drawLine(cx - r, topY, cx - r, botY, color));
78
+ commands.push(shapes.drawLine(cx + r, topY, cx + r, botY, color));
79
+
80
+ // Top ellipse
81
+ commands.push(shapes.drawEllipse(cx, topY, r, topRy, color, false));
82
+
83
+ return commands;
84
+ }
85
+
86
+ /**
87
+ * Draw a cone showing volume
88
+ */
89
+ function drawConeVolume(cx, cy, r, h, color) {
90
+ const commands = [];
91
+ const baseRy = Math.floor(r * 0.3);
92
+ const apexY = cy - Math.floor(h/2);
93
+ const baseY = cy + Math.floor(h/2);
94
+
95
+ // Central axis
96
+ commands.push(advShapes.drawAxis(cx, apexY - 10, cx, baseY + 10));
97
+
98
+ // Base ellipse
99
+ commands.push(shapes.drawEllipse(cx, baseY, r, baseRy, color, false));
100
+
101
+ // Tangent edges (approximate to the edges of the ellipse rx)
102
+ commands.push(shapes.drawLine(cx, apexY, cx - r, baseY, color));
103
+ commands.push(shapes.drawLine(cx, apexY, cx + r, baseY, color));
104
+
105
+ // Apex
106
+ commands.push(shapes.drawCircle(cx, apexY, 2, '#ff0000', true));
107
+
108
+ return commands;
109
+ }
110
+
111
+ module.exports = {
112
+ drawWireframeBox,
113
+ drawSphereVolume,
114
+ drawCylinderVolume,
115
+ drawConeVolume
116
+ };
@@ -0,0 +1,60 @@
1
+ const { z } = require('zod');
2
+ const { registerTool, sendCommand } = require('../../core/server');
3
+ const lessons3d = require('./3d_lessons');
4
+
5
+ const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g. #ff0000');
6
+
7
+ /**
8
+ * Register all 3D tools on the MCP server.
9
+ * @param {McpServer} server
10
+ */
11
+ function register(server) {
12
+ registerTool(server, 'art_tree_lesson_3d_box',
13
+ 'Lesson: 3D Forms - Box Properties (Width, Height, Depth, Planes)',
14
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#2196f3') },
15
+ async (p) => {
16
+ const commands = lessons3d.lessonBoxProperties(p.canvasSize, p.color);
17
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
18
+ return { content: [{ type: 'text', text: `✓ 3D Box Properties lesson completed` }] };
19
+ });
20
+
21
+ registerTool(server, 'art_tree_lesson_3d_sphere',
22
+ 'Lesson: 3D Forms - Sphere Properties (Center, Radius, Cross contours)',
23
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#9c27b0') },
24
+ async (p) => {
25
+ const commands = lessons3d.lessonSphereProperties(p.canvasSize, p.color);
26
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
27
+ return { content: [{ type: 'text', text: `✓ 3D Sphere Properties lesson completed` }] };
28
+ });
29
+
30
+ registerTool(server, 'art_tree_lesson_3d_cylinder',
31
+ 'Lesson: 3D Forms - Cylinder Properties (Axis, Bases, Height)',
32
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#00bcd4') },
33
+ async (p) => {
34
+ const commands = lessons3d.lessonCylinderProperties(p.canvasSize, p.color);
35
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
36
+ return { content: [{ type: 'text', text: `✓ 3D Cylinder Properties lesson completed` }] };
37
+ });
38
+
39
+ registerTool(server, 'art_tree_lesson_3d_cone',
40
+ 'Lesson: 3D Forms - Cone Properties (Apex, Base, Axis)',
41
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#ff5722') },
42
+ async (p) => {
43
+ const commands = lessons3d.lessonConeProperties(p.canvasSize, p.color);
44
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
45
+ return { content: [{ type: 'text', text: `✓ 3D Cone Properties lesson completed` }] };
46
+ });
47
+
48
+ registerTool(server, 'art_tree_lesson_3d_catalog',
49
+ 'Get the catalog of all available 3D Form lessons',
50
+ {},
51
+ () => {
52
+ const catalog = lessons3d.get3DLessonCatalog();
53
+ const text = catalog.map(l =>
54
+ ` • ${l.id}: ${l.name} — ${l.description}`
55
+ ).join('\n');
56
+ return { content: [{ type: 'text', text: `📚 3D Form Lessons:\n\n${text}` }] };
57
+ });
58
+ }
59
+
60
+ module.exports = { register };
@@ -0,0 +1,124 @@
1
+ const shapes = require('./shapes');
2
+ const advShapes = require('./advanced_shapes');
3
+
4
+ /**
5
+ * Lesson: Shape Ratios
6
+ */
7
+ function lessonShapeRatios(canvasSize = 32, color = '#2196f3') {
8
+ const margin = Math.floor(canvasSize * 0.1);
9
+ const w1 = Math.floor(canvasSize * 0.2);
10
+ const h1 = w1; // 1:1
11
+ const w2 = Math.floor(canvasSize * 0.2);
12
+ const h2 = Math.floor(canvasSize * 0.4); // 1:2
13
+
14
+ return [
15
+ shapes.drawRectangle(margin, margin, w1, h1, color, false),
16
+ shapes.drawRectangle(canvasSize - margin - w2, margin, w2, h2, '#4caf50', false),
17
+ ...advShapes.drawDistance(margin, margin + h1 + 2, margin + w1, margin + h1 + 2, '#888'),
18
+ ...advShapes.drawDistance(canvasSize - margin - w2, margin + h2 + 2, canvasSize - margin, margin + h2 + 2, '#888')
19
+ ];
20
+ }
21
+
22
+ /**
23
+ * Lesson: Symmetry & Axes
24
+ */
25
+ function lessonSymmetryAndAxis(canvasSize = 32, color = '#9c27b0') {
26
+ const mid = Math.floor(canvasSize / 2);
27
+ const w = Math.floor(canvasSize * 0.5);
28
+ const h = Math.floor(canvasSize * 0.5);
29
+ const commands = [];
30
+
31
+ // Draw Rhombus
32
+ commands.push(advShapes.drawRhombus(mid, mid, w, h, color, false));
33
+ // Vertical axis
34
+ commands.push(advShapes.drawAxis(mid, Math.floor(canvasSize * 0.1), mid, canvasSize - Math.floor(canvasSize * 0.1)));
35
+ // Horizontal axis
36
+ commands.push(advShapes.drawAxis(Math.floor(canvasSize * 0.1), mid, canvasSize - Math.floor(canvasSize * 0.1), mid));
37
+
38
+ return commands;
39
+ }
40
+
41
+ /**
42
+ * Lesson: Angles
43
+ */
44
+ function lessonAngles(canvasSize = 32, color = '#ff5722') {
45
+ const mid = Math.floor(canvasSize / 2);
46
+ const w = Math.floor(canvasSize * 0.6);
47
+ const h = Math.floor(canvasSize * 0.3);
48
+ const margin = Math.floor(canvasSize * 0.2);
49
+
50
+ const commands = [];
51
+ // Trapezoid (shows acute and obtuse angles)
52
+ commands.push(advShapes.drawTrapezoid(margin, margin, Math.floor(w/2), w, h, color, false));
53
+
54
+ // Right Triangle (shows 90 degree angle)
55
+ commands.push(shapes.drawTriangle([
56
+ { x: margin, y: canvasSize - margin },
57
+ { x: margin, y: mid + margin },
58
+ { x: margin + w, y: canvasSize - margin }
59
+ ], '#3f51b5', false));
60
+
61
+ return commands;
62
+ }
63
+
64
+ /**
65
+ * Lesson: Distances
66
+ */
67
+ function lessonDistances(canvasSize = 32, color = '#00bcd4') {
68
+ const margin = Math.floor(canvasSize * 0.2);
69
+ const commands = [];
70
+
71
+ // Points
72
+ commands.push(shapes.drawCircle(margin, margin, 1, color, true));
73
+ commands.push(shapes.drawCircle(canvasSize - margin, margin, 1, color, true));
74
+ commands.push(shapes.drawCircle(margin, canvasSize - margin, 1, color, true));
75
+
76
+ // Distances between them
77
+ commands.push(...advShapes.drawDistance(margin, margin, canvasSize - margin, margin));
78
+ commands.push(...advShapes.drawDistance(margin, margin, margin, canvasSize - margin));
79
+
80
+ return commands;
81
+ }
82
+
83
+ /**
84
+ * Lesson: Shape Relationships
85
+ */
86
+ function lessonShapeRelationships(canvasSize = 32, color = '#607d8b') {
87
+ const mid = Math.floor(canvasSize / 2);
88
+ const r = Math.floor(canvasSize * 0.2);
89
+ const commands = [];
90
+
91
+ // Overlapping circles
92
+ commands.push(shapes.drawCircle(mid - Math.floor(r/2), mid, r, color, false));
93
+ commands.push(shapes.drawCircle(mid + Math.floor(r/2), mid, r, '#e91e63', false));
94
+
95
+ // Nested squares (top left)
96
+ const margin = Math.floor(canvasSize * 0.1);
97
+ const size = Math.floor(canvasSize * 0.25);
98
+ commands.push(shapes.drawSquare(margin, margin, size, '#4caf50', false));
99
+ commands.push(shapes.drawSquare(margin + 2, margin + 2, size - 4, '#8bc34a', false));
100
+
101
+ return commands;
102
+ }
103
+
104
+ /**
105
+ * Get catalog of advanced lessons
106
+ */
107
+ function getAdvancedLessonCatalog() {
108
+ return [
109
+ { id: 'adv_ratios', name: 'Shape Ratios', description: 'Width-to-height ratios of shapes', fn: lessonShapeRatios },
110
+ { id: 'adv_symmetry', name: 'Symmetry & Axes', description: 'Symmetry and central axes', fn: lessonSymmetryAndAxis },
111
+ { id: 'adv_angles', name: 'Angles', description: 'Acute, right, and obtuse angles', fn: lessonAngles },
112
+ { id: 'adv_distances', name: 'Distances', description: 'Distances between points', fn: lessonDistances },
113
+ { id: 'adv_relationships', name: 'Shape Relationships', description: 'Overlap and nesting of shapes', fn: lessonShapeRelationships },
114
+ ];
115
+ }
116
+
117
+ module.exports = {
118
+ lessonShapeRatios,
119
+ lessonSymmetryAndAxis,
120
+ lessonAngles,
121
+ lessonDistances,
122
+ lessonShapeRelationships,
123
+ getAdvancedLessonCatalog
124
+ };
@@ -0,0 +1,73 @@
1
+ const shapes = require('./shapes');
2
+
3
+ /**
4
+ * Draw a trapezoid (isosceles usually) centered horizontally.
5
+ * @param {number} x - Top-left X of the bounding box
6
+ * @param {number} y - Top Y
7
+ * @param {number} topWidth - Width of the top edge
8
+ * @param {number} bottomWidth - Width of the bottom edge
9
+ * @param {number} height - Height of the trapezoid
10
+ * @param {string} color - Hex color
11
+ * @param {boolean} filled
12
+ */
13
+ function drawTrapezoid(x, y, topWidth, bottomWidth, height, color, filled = false) {
14
+ const midX = x + Math.max(topWidth, bottomWidth) / 2;
15
+ const topX1 = Math.round(midX - topWidth / 2);
16
+ const topX2 = Math.round(midX + topWidth / 2);
17
+ const botX1 = Math.round(midX - bottomWidth / 2);
18
+ const botX2 = Math.round(midX + bottomWidth / 2);
19
+
20
+ const points = [
21
+ { x: topX1, y },
22
+ { x: topX2, y },
23
+ { x: botX2, y: y + height },
24
+ { x: botX1, y: y + height }
25
+ ];
26
+ return shapes.drawPolygon(points, color, filled);
27
+ }
28
+
29
+ /**
30
+ * Draw a rhombus given center and its two diagonals (width, height).
31
+ */
32
+ function drawRhombus(cx, cy, width, height, color, filled = false) {
33
+ const points = [
34
+ { x: cx, y: cy - Math.floor(height / 2) },
35
+ { x: cx + Math.floor(width / 2), y: cy },
36
+ { x: cx, y: cy + Math.floor(height / 2) },
37
+ { x: cx - Math.floor(width / 2), y: cy }
38
+ ];
39
+ return shapes.drawPolygon(points, color, filled);
40
+ }
41
+
42
+ /**
43
+ * Draw a symmetry axis line (using a distinct color, e.g., light grey/red).
44
+ */
45
+ function drawAxis(x0, y0, x1, y1) {
46
+ return shapes.drawLine(x0, y0, x1, y1, '#ffaaaa'); // Light red for axis
47
+ }
48
+
49
+ /**
50
+ * Draw a dimension/distance line with end ticks.
51
+ */
52
+ function drawDistance(x0, y0, x1, y1, color = '#888888') {
53
+ const commands = [];
54
+ commands.push(shapes.drawLine(x0, y0, x1, y1, color));
55
+
56
+ // Draw small ticks at ends (perpendicular)
57
+ const isHorizontal = Math.abs(x1 - x0) > Math.abs(y1 - y0);
58
+ if (isHorizontal) {
59
+ commands.push(shapes.drawLine(x0, y0 - 2, x0, y0 + 2, color));
60
+ commands.push(shapes.drawLine(x1, y1 - 2, x1, y1 + 2, color));
61
+ } else {
62
+ commands.push(shapes.drawLine(x0 - 2, y0, x0 + 2, y0, color));
63
+ commands.push(shapes.drawLine(x1 - 2, y1, x1 + 2, y1, color));
64
+ }
65
+ return commands;
66
+ }
67
+
68
+ module.exports = {
69
+ drawTrapezoid,
70
+ drawRhombus,
71
+ drawAxis,
72
+ drawDistance
73
+ };
@@ -0,0 +1,69 @@
1
+ const { z } = require('zod');
2
+ const { registerTool, sendCommand } = require('../../core/server');
3
+ const advLessons = require('./advanced_lessons');
4
+
5
+ const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g. #ff0000');
6
+
7
+ /**
8
+ * Register all advanced art-tree tools on the MCP server.
9
+ * @param {McpServer} server
10
+ */
11
+ function register(server) {
12
+ registerTool(server, 'art_tree_lesson_ratios',
13
+ 'Lesson: Properties - Shape Ratios',
14
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#2196f3') },
15
+ async (p) => {
16
+ const commands = advLessons.lessonShapeRatios(p.canvasSize, p.color);
17
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
18
+ return { content: [{ type: 'text', text: `✓ Shape Ratios lesson completed` }] };
19
+ });
20
+
21
+ registerTool(server, 'art_tree_lesson_symmetry',
22
+ 'Lesson: Properties - Symmetry & Axes',
23
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#9c27b0') },
24
+ async (p) => {
25
+ const commands = advLessons.lessonSymmetryAndAxis(p.canvasSize, p.color);
26
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
27
+ return { content: [{ type: 'text', text: `✓ Symmetry & Axes lesson completed` }] };
28
+ });
29
+
30
+ registerTool(server, 'art_tree_lesson_angles',
31
+ 'Lesson: Properties - Angles',
32
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#ff5722') },
33
+ async (p) => {
34
+ const commands = advLessons.lessonAngles(p.canvasSize, p.color);
35
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
36
+ return { content: [{ type: 'text', text: `✓ Angles lesson completed` }] };
37
+ });
38
+
39
+ registerTool(server, 'art_tree_lesson_distances',
40
+ 'Lesson: Properties - Distances',
41
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#00bcd4') },
42
+ async (p) => {
43
+ const commands = advLessons.lessonDistances(p.canvasSize, p.color);
44
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
45
+ return { content: [{ type: 'text', text: `✓ Distances lesson completed` }] };
46
+ });
47
+
48
+ registerTool(server, 'art_tree_lesson_relationships',
49
+ 'Lesson: Properties - Shape Relationships',
50
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#607d8b') },
51
+ async (p) => {
52
+ const commands = advLessons.lessonShapeRelationships(p.canvasSize, p.color);
53
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
54
+ return { content: [{ type: 'text', text: `✓ Shape Relationships lesson completed` }] };
55
+ });
56
+
57
+ registerTool(server, 'art_tree_lesson_adv_catalog',
58
+ 'Get the catalog of all available Advanced Art Tree lessons',
59
+ {},
60
+ () => {
61
+ const catalog = advLessons.getAdvancedLessonCatalog();
62
+ const text = catalog.map(l =>
63
+ ` • ${l.id}: ${l.name} — ${l.description}`
64
+ ).join('\n');
65
+ return { content: [{ type: 'text', text: `📚 Advanced Art Tree Lesson Catalog:\n\n${text}` }] };
66
+ });
67
+ }
68
+
69
+ module.exports = { register };
@@ -0,0 +1,32 @@
1
+ const analysisShapes = require('./analysis_shapes');
2
+
3
+ /**
4
+ * Lesson: Object Analysis (Capstone)
5
+ * A 10-step breakdown of a Teapot.
6
+ * Users can supply the step number (1 to 10) to see a specific layer of analysis.
7
+ */
8
+ function lessonObjectAnalysis(canvasSize = 32, step = 1, color = '#2196f3') {
9
+ const cx = Math.floor(canvasSize / 2);
10
+ const cy = Math.floor(canvasSize / 2);
11
+
12
+ // Ensure step is between 1 and 10
13
+ const validStep = Math.max(1, Math.min(10, step));
14
+
15
+ return analysisShapes.drawTeapotAnalysis(cx, cy, canvasSize, validStep, color);
16
+ }
17
+
18
+ function getAnalysisLessonCatalog() {
19
+ return [
20
+ {
21
+ id: 'analysis_step',
22
+ name: '10-Step Object Analysis',
23
+ description: 'Break down a Teapot from Silhouette (1) to Lighting (10). Supply step=1..10',
24
+ fn: lessonObjectAnalysis
25
+ }
26
+ ];
27
+ }
28
+
29
+ module.exports = {
30
+ lessonObjectAnalysis,
31
+ getAnalysisLessonCatalog
32
+ };