@pixel-normal-edit/mcp 2.0.7 → 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 (32) hide show
  1. package/domains/art-tree/advanced_lessons.js +11 -11
  2. package/domains/art-tree/advanced_tools.js +12 -12
  3. package/domains/art-tree/analysis_lessons.js +32 -0
  4. package/domains/art-tree/analysis_shapes.js +146 -0
  5. package/domains/art-tree/analysis_tools.js +49 -0
  6. package/domains/art-tree/hidden_lessons.js +3 -3
  7. package/domains/art-tree/index.js +10 -0
  8. package/domains/art-tree/layer_lessons.js +32 -0
  9. package/domains/art-tree/layer_shapes.js +117 -0
  10. package/domains/art-tree/layer_tools.js +43 -0
  11. package/domains/art-tree/lessons.js +19 -19
  12. package/domains/art-tree/light_lessons.js +3 -3
  13. package/domains/art-tree/light_shapes.js +5 -5
  14. package/domains/art-tree/material_lessons.js +71 -0
  15. package/domains/art-tree/material_shapes.js +130 -0
  16. package/domains/art-tree/material_tools.js +49 -0
  17. package/domains/art-tree/perspective_lessons.js +1 -1
  18. package/domains/art-tree/perspective_tools.js +1 -1
  19. package/domains/art-tree/sky_lessons_1.js +82 -0
  20. package/domains/art-tree/sky_shapes_1.js +68 -0
  21. package/domains/art-tree/sky_tools.js +51 -0
  22. package/domains/art-tree/surface_lessons.js +7 -7
  23. package/domains/art-tree/tools.js +27 -27
  24. package/domains/art-tree/transform_lessons_1.js +2 -2
  25. package/domains/art-tree/transform_lessons_2.js +4 -4
  26. package/domains/art-tree/transform_tools.js +8 -8
  27. package/domains/art-tree/vocab_lessons.js +32 -0
  28. package/domains/art-tree/vocab_shapes.js +116 -0
  29. package/domains/art-tree/vocab_tools.js +41 -0
  30. package/package.json +1 -1
  31. package/tools/index.js +2 -0
  32. package/tools/layer.js +80 -0
@@ -2,7 +2,7 @@ const shapes = require('./shapes');
2
2
  const advShapes = require('./advanced_shapes');
3
3
 
4
4
  /**
5
- * Lesson: Tỉ lệ (Ratios)
5
+ * Lesson: Shape Ratios
6
6
  */
7
7
  function lessonShapeRatios(canvasSize = 32, color = '#2196f3') {
8
8
  const margin = Math.floor(canvasSize * 0.1);
@@ -20,7 +20,7 @@ function lessonShapeRatios(canvasSize = 32, color = '#2196f3') {
20
20
  }
21
21
 
22
22
  /**
23
- * Lesson: Đối xứng & Trục giữa (Symmetry & Axes)
23
+ * Lesson: Symmetry & Axes
24
24
  */
25
25
  function lessonSymmetryAndAxis(canvasSize = 32, color = '#9c27b0') {
26
26
  const mid = Math.floor(canvasSize / 2);
@@ -39,7 +39,7 @@ function lessonSymmetryAndAxis(canvasSize = 32, color = '#9c27b0') {
39
39
  }
40
40
 
41
41
  /**
42
- * Lesson: Góc (Angles)
42
+ * Lesson: Angles
43
43
  */
44
44
  function lessonAngles(canvasSize = 32, color = '#ff5722') {
45
45
  const mid = Math.floor(canvasSize / 2);
@@ -62,7 +62,7 @@ function lessonAngles(canvasSize = 32, color = '#ff5722') {
62
62
  }
63
63
 
64
64
  /**
65
- * Lesson: Khoảng cách (Distances)
65
+ * Lesson: Distances
66
66
  */
67
67
  function lessonDistances(canvasSize = 32, color = '#00bcd4') {
68
68
  const margin = Math.floor(canvasSize * 0.2);
@@ -81,7 +81,7 @@ function lessonDistances(canvasSize = 32, color = '#00bcd4') {
81
81
  }
82
82
 
83
83
  /**
84
- * Lesson: Quan hệ hình khối (Relationships)
84
+ * Lesson: Shape Relationships
85
85
  */
86
86
  function lessonShapeRelationships(canvasSize = 32, color = '#607d8b') {
87
87
  const mid = Math.floor(canvasSize / 2);
@@ -106,11 +106,11 @@ function lessonShapeRelationships(canvasSize = 32, color = '#607d8b') {
106
106
  */
107
107
  function getAdvancedLessonCatalog() {
108
108
  return [
109
- { id: 'adv_ratios', name: 'Tỉ lệ hình', description: 'Tỉ lệ chiều rộng và chiều cao', fn: lessonShapeRatios },
110
- { id: 'adv_symmetry', name: 'Đối xứng & Trục', description: 'Sự đối xứng và trục giữa', fn: lessonSymmetryAndAxis },
111
- { id: 'adv_angles', name: 'Góc', description: 'Góc nhọn, góc vuông, góc tù', fn: lessonAngles },
112
- { id: 'adv_distances', name: 'Khoảng cách', description: 'Khoảng cách giữa các điểm', fn: lessonDistances },
113
- { id: 'adv_relationships', name: 'Quan hệ hình', description: 'Sự giao nhau lồng nhau của các hình', fn: lessonShapeRelationships },
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
114
  ];
115
115
  }
116
116
 
@@ -121,4 +121,4 @@ module.exports = {
121
121
  lessonDistances,
122
122
  lessonShapeRelationships,
123
123
  getAdvancedLessonCatalog
124
- };
124
+ };
@@ -10,48 +10,48 @@ const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g.
10
10
  */
11
11
  function register(server) {
12
12
  registerTool(server, 'art_tree_lesson_ratios',
13
- 'Lesson: Đặc tính - Tỉ lệ hình (Ratios)',
13
+ 'Lesson: Properties - Shape Ratios',
14
14
  { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#2196f3') },
15
15
  async (p) => {
16
16
  const commands = advLessons.lessonShapeRatios(p.canvasSize, p.color);
17
17
  await Promise.all(commands.map(cmd => sendCommand(cmd)));
18
- return { content: [{ type: 'text', text: `✓ Bài học tỉ lệ hình hoàn thành` }] };
18
+ return { content: [{ type: 'text', text: `✓ Shape Ratios lesson completed` }] };
19
19
  });
20
20
 
21
21
  registerTool(server, 'art_tree_lesson_symmetry',
22
- 'Lesson: Đặc tính - Đối xứng & Trục (Symmetry and Axes)',
22
+ 'Lesson: Properties - Symmetry & Axes',
23
23
  { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#9c27b0') },
24
24
  async (p) => {
25
25
  const commands = advLessons.lessonSymmetryAndAxis(p.canvasSize, p.color);
26
26
  await Promise.all(commands.map(cmd => sendCommand(cmd)));
27
- return { content: [{ type: 'text', text: `✓ Bài học đối xứng & trục hoàn thành` }] };
27
+ return { content: [{ type: 'text', text: `✓ Symmetry & Axes lesson completed` }] };
28
28
  });
29
29
 
30
30
  registerTool(server, 'art_tree_lesson_angles',
31
- 'Lesson: Đặc tính - Góc (Angles)',
31
+ 'Lesson: Properties - Angles',
32
32
  { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#ff5722') },
33
33
  async (p) => {
34
34
  const commands = advLessons.lessonAngles(p.canvasSize, p.color);
35
35
  await Promise.all(commands.map(cmd => sendCommand(cmd)));
36
- return { content: [{ type: 'text', text: `✓ Bài học góc hoàn thành` }] };
36
+ return { content: [{ type: 'text', text: `✓ Angles lesson completed` }] };
37
37
  });
38
38
 
39
39
  registerTool(server, 'art_tree_lesson_distances',
40
- 'Lesson: Đặc tính - Khoảng cách (Distances)',
40
+ 'Lesson: Properties - Distances',
41
41
  { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#00bcd4') },
42
42
  async (p) => {
43
43
  const commands = advLessons.lessonDistances(p.canvasSize, p.color);
44
44
  await Promise.all(commands.map(cmd => sendCommand(cmd)));
45
- return { content: [{ type: 'text', text: `✓ Bài học khoảng cách hoàn thành` }] };
45
+ return { content: [{ type: 'text', text: `✓ Distances lesson completed` }] };
46
46
  });
47
47
 
48
48
  registerTool(server, 'art_tree_lesson_relationships',
49
- 'Lesson: Đặc tính - Quan hệ hình (Relationships)',
49
+ 'Lesson: Properties - Shape Relationships',
50
50
  { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#607d8b') },
51
51
  async (p) => {
52
52
  const commands = advLessons.lessonShapeRelationships(p.canvasSize, p.color);
53
53
  await Promise.all(commands.map(cmd => sendCommand(cmd)));
54
- return { content: [{ type: 'text', text: `✓ Bài học quan hệ hình hoàn thành` }] };
54
+ return { content: [{ type: 'text', text: `✓ Shape Relationships lesson completed` }] };
55
55
  });
56
56
 
57
57
  registerTool(server, 'art_tree_lesson_adv_catalog',
@@ -62,8 +62,8 @@ function register(server) {
62
62
  const text = catalog.map(l =>
63
63
  ` • ${l.id}: ${l.name} — ${l.description}`
64
64
  ).join('\n');
65
- return { content: [{ type: 'text', text: `📚 Danh sách bài học Art Tree (Đặc tính):\n\n${text}` }] };
65
+ return { content: [{ type: 'text', text: `📚 Advanced Art Tree Lesson Catalog:\n\n${text}` }] };
66
66
  });
67
67
  }
68
68
 
69
- module.exports = { register };
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
+ };
@@ -0,0 +1,146 @@
1
+ const shapes = require('./shapes');
2
+ const lightShapes = require('./light_shapes');
3
+
4
+ // Master function to draw the Teapot at a specific analytical step
5
+ function drawTeapotAnalysis(cx, cy, scale, step, color = '#2196f3') {
6
+ const commands = [];
7
+ const r = Math.floor(scale * 0.3); // Main body radius
8
+
9
+ // Base outlines for the Teapot used across multiple steps
10
+ const drawBaseOutline = (c) => {
11
+ // Body (Sphere)
12
+ commands.push(shapes.drawCircle(cx, cy, r, c, false));
13
+ // Base/Foot
14
+ commands.push(shapes.drawLine(cx - Math.floor(r*0.6), cy + r, cx + Math.floor(r*0.6), cy + r, c));
15
+ commands.push(shapes.drawLine(cx - Math.floor(r*0.6), cy + r, cx - Math.floor(r*0.7), cy + r + 5, c));
16
+ commands.push(shapes.drawLine(cx + Math.floor(r*0.6), cy + r, cx + Math.floor(r*0.7), cy + r + 5, c));
17
+ commands.push(shapes.drawLine(cx - Math.floor(r*0.7), cy + r + 5, cx + Math.floor(r*0.7), cy + r + 5, c));
18
+ // Lid
19
+ commands.push(shapes.drawLine(cx - Math.floor(r*0.6), cy - r, cx + Math.floor(r*0.6), cy - r, c));
20
+ commands.push(shapes.drawEllipse(cx, cy - r - 5, Math.floor(r*0.6), Math.floor(r*0.2), c, false));
21
+ commands.push(shapes.drawCircle(cx, cy - r - 15, Math.floor(r*0.15), c, false)); // Knob
22
+ // Spout (Left)
23
+ commands.push(shapes.drawLine(cx - r, cy, cx - Math.floor(r*1.8), cy - Math.floor(r*0.8), c));
24
+ commands.push(shapes.drawLine(cx - Math.floor(r*0.8), cy + Math.floor(r*0.4), cx - Math.floor(r*1.5), cy - Math.floor(r*0.6), c));
25
+ commands.push(shapes.drawLine(cx - Math.floor(r*1.8), cy - Math.floor(r*0.8), cx - Math.floor(r*1.5), cy - Math.floor(r*0.6), c));
26
+ // Handle (Right)
27
+ const handlePts1 = [], handlePts2 = [];
28
+ for(let i=0; i<=10; i++) {
29
+ const a = -Math.PI/2 + (i/10)*Math.PI; // -90 to 90
30
+ handlePts1.push({x: Math.round(cx + r + Math.floor(r*0.8)*Math.cos(a)), y: Math.round(cy - Math.floor(r*0.2) + Math.floor(r*0.6)*Math.sin(a))});
31
+ handlePts2.push({x: Math.round(cx + r + Math.floor(r*0.5)*Math.cos(a)), y: Math.round(cy - Math.floor(r*0.2) + Math.floor(r*0.6)*Math.sin(a))});
32
+ }
33
+ commands.push(shapes.drawPolyline(handlePts1, c));
34
+ commands.push(shapes.drawPolyline(handlePts2, c));
35
+ };
36
+
37
+ switch (step) {
38
+ case 1: // Silhouette (Flat black)
39
+ // Since we can't easily union-fill all paths, we simulate it by drawing the base thick
40
+ // For this, we just draw the outline in solid black with no inner details
41
+ drawBaseOutline('#000000');
42
+ break;
43
+
44
+ case 2: // Proportions (Bounding box)
45
+ drawBaseOutline('#e0e0e0');
46
+ // Bounding box
47
+ const minX = cx - Math.floor(r*1.8) - 10;
48
+ const maxX = cx + Math.floor(r*1.8) + 10;
49
+ const minY = cy - r - 30;
50
+ const maxY = cy + r + 15;
51
+ commands.push(shapes.drawRectangle(minX, minY, maxX - minX, maxY - minY, color, false));
52
+ // Center lines of bounding box
53
+ commands.push(shapes.drawLine(cx, minY, cx, maxY, '#ff9800'));
54
+ commands.push(shapes.drawLine(minX, cy, maxX, cy, '#ff9800'));
55
+ break;
56
+
57
+ case 3: // Axis (X, Y, Z structural lines)
58
+ drawBaseOutline('#e0e0e0');
59
+ // Y axis
60
+ commands.push(shapes.drawLine(cx, cy - r - 40, cx, cy + r + 40, '#f44336'));
61
+ // X axis
62
+ commands.push(shapes.drawLine(cx - r - 40, cy, cx + r + 40, cy, '#4caf50'));
63
+ // Z axis (depth, diagonal)
64
+ commands.push(shapes.drawLine(cx - Math.floor(r*0.5), cy + Math.floor(r*0.5), cx + Math.floor(r*0.5), cy - Math.floor(r*0.5), '#2196f3'));
65
+ break;
66
+
67
+ case 4: // Main Masses (Primitives)
68
+ drawBaseOutline('#e0e0e0');
69
+ // Highlight the sphere
70
+ commands.push(shapes.drawCircle(cx, cy, r, '#e91e63', false));
71
+ // Highlight the spout cylinder/cone
72
+ commands.push(shapes.drawLine(cx - r, cy, cx - Math.floor(r*1.8), cy - Math.floor(r*0.8), '#00bcd4'));
73
+ commands.push(shapes.drawLine(cx - Math.floor(r*0.8), cy + Math.floor(r*0.4), cx - Math.floor(r*1.5), cy - Math.floor(r*0.6), '#00bcd4'));
74
+ break;
75
+
76
+ case 5: // Cross-sections
77
+ drawBaseOutline('#e0e0e0');
78
+ // Slicing ellipses on the body
79
+ commands.push(shapes.drawEllipse(cx, cy, r, Math.floor(r*0.3), '#9c27b0', false));
80
+ commands.push(shapes.drawEllipse(cx, cy - Math.floor(r*0.5), Math.floor(r*0.86), Math.floor(r*0.25), '#9c27b0', false));
81
+ commands.push(shapes.drawEllipse(cx, cy + Math.floor(r*0.5), Math.floor(r*0.86), Math.floor(r*0.25), '#9c27b0', false));
82
+ break;
83
+
84
+ case 6: // Transformations (Bend, Taper)
85
+ drawBaseOutline('#e0e0e0');
86
+ // Highlight bending spout
87
+ commands.push(shapes.drawEllipse(cx - Math.floor(r*1.4), cy - Math.floor(r*0.4), Math.floor(r*0.4), Math.floor(r*0.2), '#ff9800', false));
88
+ // Tapering lid
89
+ commands.push(shapes.drawLine(cx - Math.floor(r*0.6), cy - r, cx, cy - r - 15, '#ff9800'));
90
+ commands.push(shapes.drawLine(cx + Math.floor(r*0.6), cy - r, cx, cy - r - 15, '#ff9800'));
91
+ break;
92
+
93
+ case 7: // Secondary parts
94
+ drawBaseOutline('#e0e0e0');
95
+ // Boldly highlight spout and handle
96
+ commands.push(shapes.drawLine(cx - r, cy, cx - Math.floor(r*1.8), cy - Math.floor(r*0.8), '#ff5722'));
97
+ commands.push(shapes.drawLine(cx - Math.floor(r*0.8), cy + Math.floor(r*0.4), cx - Math.floor(r*1.5), cy - Math.floor(r*0.6), '#ff5722'));
98
+ const hPts = [];
99
+ for(let i=0; i<=10; i++) {
100
+ const a = -Math.PI/2 + (i/10)*Math.PI;
101
+ hPts.push({x: Math.round(cx + r + Math.floor(r*0.8)*Math.cos(a)), y: Math.round(cy - Math.floor(r*0.2) + Math.floor(r*0.6)*Math.sin(a))});
102
+ }
103
+ commands.push(shapes.drawPolyline(hPts, '#3f51b5'));
104
+ break;
105
+
106
+ case 8: // Surfaces (Convex, Concave)
107
+ drawBaseOutline('#e0e0e0');
108
+ // Convex body
109
+ commands.push(shapes.drawCircle(cx, cy, r, '#8bc34a', false));
110
+ // Concave inner handle space
111
+ commands.push(shapes.drawEllipse(cx + r + Math.floor(r*0.2), cy - Math.floor(r*0.2), Math.floor(r*0.3), Math.floor(r*0.5), '#00bcd4', false));
112
+ break;
113
+
114
+ case 9: // Perspective
115
+ drawBaseOutline(color);
116
+ // Horizon line above
117
+ const horizonY = cy - r - 50;
118
+ const vpX = cx + Math.floor(r*2);
119
+ commands.push(shapes.drawLine(0, horizonY, scale*2, horizonY, '#9e9e9e'));
120
+ commands.push(shapes.drawCircle(vpX, horizonY, 3, '#ff0000', true));
121
+ // Orthogonals from top and bottom to VP
122
+ commands.push(shapes.drawLine(cx, cy - r, vpX, horizonY, '#bdbdbd'));
123
+ commands.push(shapes.drawLine(cx, cy + r, vpX, horizonY, '#bdbdbd'));
124
+ break;
125
+
126
+ case 10: // Lighting
127
+ // Re-use shaded sphere for the main body
128
+ commands.push(...lightShapes.drawShadedSphere(cx, cy, r, -Math.PI*0.75));
129
+ // We skip the detailed spout/handle shading to stay within line limits,
130
+ // but draw them solidly over the sphere to complete the form
131
+ const c = '#546e7a';
132
+ commands.push(shapes.drawLine(cx - r, cy, cx - Math.floor(r*1.8), cy - Math.floor(r*0.8), c));
133
+ commands.push(shapes.drawLine(cx - Math.floor(r*0.8), cy + Math.floor(r*0.4), cx - Math.floor(r*1.5), cy - Math.floor(r*0.6), c));
134
+ break;
135
+
136
+ default:
137
+ drawBaseOutline(color);
138
+ break;
139
+ }
140
+
141
+ return commands;
142
+ }
143
+
144
+ module.exports = {
145
+ drawTeapotAnalysis
146
+ };
@@ -0,0 +1,49 @@
1
+ const { z } = require('zod');
2
+ const { registerTool, sendCommand } = require('../../core/server');
3
+ const analysisLessons = require('./analysis_lessons');
4
+
5
+ const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g. #ff0000');
6
+
7
+ /**
8
+ * Register all analysis tools on the MCP server.
9
+ * @param {McpServer} server
10
+ */
11
+ function register(server) {
12
+ registerTool(server, 'art_tree_lesson_analysis_step',
13
+ 'Lesson: Capstone - 10-Step Object Analysis of a Teapot. Pass step (1 to 10).',
14
+ {
15
+ canvasSize: z.number().int().min(16).max(64).default(32),
16
+ step: z.number().int().min(1).max(10).describe('The analysis step to view (1=Silhouette, 2=Proportions, ..., 10=Lighting)'),
17
+ color: hexColor.default('#2196f3')
18
+ },
19
+ async (p) => {
20
+ const commands = analysisLessons.lessonObjectAnalysis(p.canvasSize, p.step, p.color);
21
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
22
+ const steps = [
23
+ "1. Silhouette (Hình bao ngoài)",
24
+ "2. Proportions (Tỉ lệ)",
25
+ "3. Axis (Trục)",
26
+ "4. Main masses (Khối lớn)",
27
+ "5. Cross-sections (Mặt cắt)",
28
+ "6. Transformations (Biến dạng)",
29
+ "7. Secondary parts (Các bộ phận phụ)",
30
+ "8. Surfaces (Bề mặt)",
31
+ "9. Perspective (Phối cảnh)",
32
+ "10. Lighting (Ánh sáng)"
33
+ ];
34
+ return { content: [{ type: 'text', text: `✓ Step ${p.step} Analysis completed: ${steps[p.step - 1]}` }] };
35
+ });
36
+
37
+ registerTool(server, 'art_tree_lesson_analysis_catalog',
38
+ 'Get the catalog of all available Analysis lessons',
39
+ {},
40
+ () => {
41
+ const catalog = analysisLessons.getAnalysisLessonCatalog();
42
+ const text = catalog.map(l =>
43
+ ` • ${l.id}: ${l.name} — ${l.description}`
44
+ ).join('\n');
45
+ return { content: [{ type: 'text', text: `📚 Analysis Lessons:\n\n${text}` }] };
46
+ });
47
+ }
48
+
49
+ module.exports = { register };
@@ -1,7 +1,7 @@
1
1
  const hiddenShapes = require('./hidden_shapes');
2
2
 
3
3
  /**
4
- * Lesson: Hidden Box (Khối hộp 6 mặt)
4
+ * Lesson: Hidden Box (6-sided box)
5
5
  * You see 3 faces, but you must understand all 6.
6
6
  */
7
7
  function lessonHiddenBox(canvasSize = 32, visibleColor = '#2196f3', hiddenColor = '#e0e0e0') {
@@ -15,7 +15,7 @@ function lessonHiddenBox(canvasSize = 32, visibleColor = '#2196f3', hiddenColor
15
15
  }
16
16
 
17
17
  /**
18
- * Lesson: Hidden Head (Đầu người hộp sọ)
18
+ * Lesson: Hidden Head (Human head and skull)
19
19
  * You don't see the whole skull, but must understand the structure behind it.
20
20
  */
21
21
  function lessonHiddenHead(canvasSize = 32, visibleColor = '#ff5722', hiddenColor = '#e0e0e0') {
@@ -27,7 +27,7 @@ function lessonHiddenHead(canvasSize = 32, visibleColor = '#ff5722', hiddenColor
27
27
  }
28
28
 
29
29
  /**
30
- * Lesson: Hidden Cup (Cái cốc đáy bên trong)
30
+ * Lesson: Hidden Cup (Cup and inner bottom)
31
31
  * You don't see the full inside bottom, but must understand how the wall goes down.
32
32
  */
33
33
  function lessonHiddenCup(canvasSize = 32, visibleColor = '#4caf50', hiddenColor = '#e0e0e0') {
@@ -21,6 +21,11 @@ const surfaceTools = require('./surface_tools');
21
21
  const perspTools = require('./perspective_tools');
22
22
  const hiddenTools = require('./hidden_tools');
23
23
  const lightTools = require('./light_tools');
24
+ const materialTools = require('./material_tools');
25
+ const analysisTools = require('./analysis_tools');
26
+ const layerTools = require('./layer_tools');
27
+ const skyTools = require('./sky_tools');
28
+ const vocabTools = require('./vocab_tools');
24
29
 
25
30
  /**
26
31
  * Register all Art Tree MCP tools on the server
@@ -39,6 +44,11 @@ function registerAll(server) {
39
44
  perspTools.register(server);
40
45
  hiddenTools.register(server);
41
46
  lightTools.register(server);
47
+ materialTools.register(server);
48
+ analysisTools.register(server);
49
+ layerTools.register(server);
50
+ skyTools.register(server);
51
+ vocabTools.register(server);
42
52
  }
43
53
 
44
54
  module.exports = {
@@ -0,0 +1,32 @@
1
+ const layerShapes = require('./layer_shapes');
2
+
3
+ /**
4
+ * Lesson: Layer Structure (Tư duy Layer)
5
+ * A 7-step progression showing what belongs on each layer.
6
+ * Users can supply the layer index (1 to 7) to see a specific layer's content.
7
+ */
8
+ function lessonLayerStep(canvasSize = 32, layerIndex = 1) {
9
+ const cx = Math.floor(canvasSize / 2);
10
+ const cy = Math.floor(canvasSize / 2);
11
+
12
+ // Ensure layerIndex is between 1 and 7
13
+ const validLayer = Math.max(1, Math.min(7, layerIndex));
14
+
15
+ return layerShapes.drawLayeredCup(cx, cy, canvasSize, validLayer);
16
+ }
17
+
18
+ function getLayerLessonCatalog() {
19
+ return [
20
+ {
21
+ id: 'layer_step',
22
+ name: '7-Layer Painting Structure',
23
+ description: 'View the contents of a specific layer of a Cup (1=Ref, 2=Sketch, 3=Construction, 4=Lineart, 5=Color, 6=Shadow, 7=Light)',
24
+ fn: lessonLayerStep
25
+ }
26
+ ];
27
+ }
28
+
29
+ module.exports = {
30
+ lessonLayerStep,
31
+ getLayerLessonCatalog
32
+ };
@@ -0,0 +1,117 @@
1
+ const shapes = require('./shapes');
2
+
3
+ // Helper to draw a specific layer of a Cup
4
+ function drawLayeredCup(cx, cy, scale, layerIndex) {
5
+ const commands = [];
6
+ const w = Math.floor(scale * 0.4);
7
+ const h = Math.floor(scale * 0.6);
8
+ const r = Math.floor(w / 2);
9
+ const topRy = Math.floor(r * 0.3);
10
+ const botRy = Math.floor(r * 0.25);
11
+ const topY = cy - Math.floor(h/2);
12
+ const botY = cy + Math.floor(h/2);
13
+
14
+ switch (layerIndex) {
15
+ case 1: // Reference
16
+ // Draw a "photo frame" indicating where a reference image goes
17
+ const refX = cx - Math.floor(scale*0.4);
18
+ const refY = cy - Math.floor(scale*0.4);
19
+ const refW = Math.floor(scale*0.3);
20
+ const refH = Math.floor(scale*0.3);
21
+ commands.push(shapes.drawRectangle(refX, refY, refW, refH, '#e0e0e0', true));
22
+ commands.push(shapes.drawRectangle(refX, refY, refW, refH, '#9e9e9e', false));
23
+ commands.push(shapes.drawLine(refX, refY, refX+refW, refY+refH, '#9e9e9e'));
24
+ commands.push(shapes.drawLine(refX+refW, refY, refX, refY+refH, '#9e9e9e'));
25
+ break;
26
+
27
+ case 2: // Sketch
28
+ // Loose, messy lines (Gesture, proportion bounds)
29
+ const sketchC = '#90caf9'; // Light blue sketch pencil
30
+ commands.push(shapes.drawRectangle(cx - r - 5, topY - topRy - 5, w + 10, h + botRy + 10, sketchC, false));
31
+ // Messy circles/ovals for top and bottom
32
+ commands.push(shapes.drawEllipse(cx, topY, Math.floor(r*1.1), Math.floor(topRy*1.2), sketchC, false));
33
+ commands.push(shapes.drawEllipse(cx+2, topY-2, Math.floor(r*0.9), Math.floor(topRy*0.8), sketchC, false));
34
+ // Messy walls
35
+ commands.push(shapes.drawLine(cx - r - 2, topY, cx - r + 3, botY, sketchC));
36
+ commands.push(shapes.drawLine(cx + r + 2, topY, cx + r - 3, botY, sketchC));
37
+ break;
38
+
39
+ case 3: // Construction
40
+ // Precise geometric primitives and axes
41
+ const axisC = '#f44336';
42
+ const constC = '#4caf50';
43
+ // Axis
44
+ commands.push(shapes.drawLine(cx, cy - h, cx, cy + h, axisC));
45
+ // Top ellipse
46
+ commands.push(shapes.drawEllipse(cx, topY, r, topRy, constC, false));
47
+ // Bottom ellipse (full, showing through)
48
+ commands.push(shapes.drawEllipse(cx, botY, r, botRy, constC, false));
49
+ // Straight walls connecting tangents
50
+ commands.push(shapes.drawLine(cx - r, topY, cx - r, botY, constC));
51
+ commands.push(shapes.drawLine(cx + r, topY, cx + r, botY, constC));
52
+ break;
53
+
54
+ case 4: // Lineart
55
+ // Clean, solid outer and inner contours
56
+ const lineC = '#000000';
57
+ // Walls
58
+ commands.push(shapes.drawLine(cx - r, topY, cx - r, botY, lineC));
59
+ commands.push(shapes.drawLine(cx + r, topY, cx + r, botY, lineC));
60
+ // Top rim
61
+ commands.push(shapes.drawEllipse(cx, topY, r, topRy, lineC, false));
62
+ // Inner lip
63
+ commands.push(shapes.drawEllipse(cx, topY, Math.floor(r*0.85), Math.floor(topRy*0.85), lineC, false));
64
+ // Front bottom curve (simulate by drawing half an ellipse manually, but we don't have arc tool)
65
+ // We use polyline for the front bottom curve to not show the hidden back
66
+ const frontCurve = [];
67
+ for (let i = 0; i <= 10; i++) {
68
+ const a = (i / 10) * Math.PI;
69
+ frontCurve.push({ x: Math.round(cx + r * Math.cos(a)), y: Math.round(botY + botRy * Math.sin(a)) });
70
+ }
71
+ commands.push(shapes.drawPolyline(frontCurve, lineC));
72
+ break;
73
+
74
+ case 5: // Base Color
75
+ // Flat solid colors for the cup body and interior
76
+ const bodyC = '#ff9800'; // Orange cup
77
+ const innerC = '#795548'; // Coffee inside
78
+ // Body fill (Using wireframe/thick lines to fake fill if needed, or assume SVG renders it)
79
+ // Actually, we can use drawEllipse and rectangle
80
+ commands.push(shapes.drawRectangle(cx - r, topY, w, h, bodyC, true));
81
+ commands.push(shapes.drawEllipse(cx, botY, r, botRy, bodyC, true));
82
+ // Fill the top area
83
+ commands.push(shapes.drawEllipse(cx, topY, r, topRy, bodyC, true));
84
+ // Coffee inside
85
+ commands.push(shapes.drawEllipse(cx, topY, Math.floor(r*0.85), Math.floor(topRy*0.85), innerC, true));
86
+ break;
87
+
88
+ case 6: // Shadow
89
+ // Core shadow and Cast shadow (Usually drawn with Multiply blend mode, but here we just draw dark shapes)
90
+ const shadowC = '#424242';
91
+ // Cast shadow on floor
92
+ commands.push(shapes.drawEllipse(cx - Math.floor(w*0.8), botY + Math.floor(botRy*0.5), Math.floor(w*1.2), Math.floor(botRy*1.2), shadowC, true));
93
+ // Core shadow on the left side of the cup body
94
+ commands.push(shapes.drawRectangle(cx - r, topY, Math.floor(r*0.6), h, '#3e2723', true));
95
+ break;
96
+
97
+ case 7: // Light
98
+ // Highlights and Rim light (Usually drawn with Screen/Add blend mode, here just bright white/yellow)
99
+ const lightC = '#ffffff';
100
+ // Rim light on the right edge
101
+ commands.push(shapes.drawLine(cx + r - 2, topY, cx + r - 2, botY, lightC));
102
+ // Strong sharp highlight on the body
103
+ commands.push(shapes.drawEllipse(cx + Math.floor(r*0.5), cy, Math.floor(r*0.1), Math.floor(h*0.3), lightC, true));
104
+ // Highlight on the rim
105
+ commands.push(shapes.drawEllipse(cx + Math.floor(r*0.7), topY - Math.floor(topRy*0.5), Math.floor(r*0.2), 2, lightC, true));
106
+ break;
107
+
108
+ default:
109
+ break;
110
+ }
111
+
112
+ return commands;
113
+ }
114
+
115
+ module.exports = {
116
+ drawLayeredCup
117
+ };
@@ -0,0 +1,43 @@
1
+ const { z } = require('zod');
2
+ const { registerTool, sendCommand } = require('../../core/server');
3
+ const layerLessons = require('./layer_lessons');
4
+
5
+ /**
6
+ * Register all layer tools on the MCP server.
7
+ * @param {McpServer} server
8
+ */
9
+ function register(server) {
10
+ registerTool(server, 'art_tree_lesson_layer_step',
11
+ 'Lesson: Layer Structure - See exactly what belongs on each layer of a Cup (1 to 7).',
12
+ {
13
+ canvasSize: z.number().int().min(16).max(64).default(32),
14
+ layer: z.number().int().min(1).max(7).describe('The layer index (1=Ref, 2=Sketch, 3=Construction, 4=Lineart, 5=Color, 6=Shadow, 7=Light)')
15
+ },
16
+ async (p) => {
17
+ const commands = layerLessons.lessonLayerStep(p.canvasSize, p.layer);
18
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
19
+ const layers = [
20
+ "1. Reference (Ảnh tham khảo)",
21
+ "2. Sketch (Phác thảo)",
22
+ "3. Construction (Dựng hình)",
23
+ "4. Lineart (Nét chính)",
24
+ "5. Base Color (Màu cơ bản)",
25
+ "6. Shadow (Bóng tối)",
26
+ "7. Light (Ánh sáng)"
27
+ ];
28
+ return { content: [{ type: 'text', text: `✓ Layer ${p.layer} visualization completed: ${layers[p.layer - 1]}` }] };
29
+ });
30
+
31
+ registerTool(server, 'art_tree_lesson_layer_catalog',
32
+ 'Get the catalog of all available Layer Structure lessons',
33
+ {},
34
+ () => {
35
+ const catalog = layerLessons.getLayerLessonCatalog();
36
+ const text = catalog.map(l =>
37
+ ` • ${l.id}: ${l.name} — ${l.description}`
38
+ ).join('\n');
39
+ return { content: [{ type: 'text', text: `📚 Layer Lessons:\n\n${text}` }] };
40
+ });
41
+ }
42
+
43
+ module.exports = { register };