@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
@@ -166,7 +166,7 @@ function lessonComposition(canvasSize = 32) {
166
166
  }
167
167
 
168
168
  /**
169
- * Lesson 8: Freehand — Luyện vẽ bằng tay, phỏng nét rung.
169
+ * Lesson 8: Freehand — Practice freehand drawing with simulated hand tremor.
170
170
  */
171
171
  function lessonFreehand(canvasSize = 32, color = '#795548') {
172
172
  const margin = Math.floor(canvasSize * 0.1);
@@ -179,7 +179,7 @@ function lessonFreehand(canvasSize = 32, color = '#795548') {
179
179
  }
180
180
 
181
181
  /**
182
- * Lesson 9: Proportions and Angles — Tập giữ tỉ lệ và góc.
182
+ * Lesson 9: Proportions and Angles — Practice maintaining proportions and angles.
183
183
  */
184
184
  function lessonProportionsAndAngles(canvasSize = 32, color = '#3f51b5') {
185
185
  const mid = Math.floor(canvasSize / 2);
@@ -193,7 +193,7 @@ function lessonProportionsAndAngles(canvasSize = 32, color = '#3f51b5') {
193
193
  }
194
194
 
195
195
  /**
196
- * Lesson 10: Curves — Đường cong C, S.
196
+ * Lesson 10: Curves — C-curves and S-curves.
197
197
  */
198
198
  function lessonCurves(canvasSize = 32, color = '#9c27b0') {
199
199
  const commands = [];
@@ -218,7 +218,7 @@ function lessonCurves(canvasSize = 32, color = '#9c27b0') {
218
218
  }
219
219
 
220
220
  /**
221
- * Lesson 11: Spiral — Đường xoắn ốc.
221
+ * Lesson 11: Spiral — Drawing spiral curves.
222
222
  */
223
223
  function lessonSpiral(canvasSize = 32, color = '#ff9800') {
224
224
  const mid = Math.floor(canvasSize / 2);
@@ -233,7 +233,7 @@ function lessonSpiral(canvasSize = 32, color = '#ff9800') {
233
233
  }
234
234
 
235
235
  /**
236
- * Lesson 12: Strokes — Nét dài, nét ngắn, nét đậm–nhạt.
236
+ * Lesson 12: Strokes — Long strokes, short strokes, thick and thin lines.
237
237
  */
238
238
  function lessonStrokes(canvasSize = 32, color = '#2196f3') {
239
239
  const margin = Math.floor(canvasSize * 0.1);
@@ -250,7 +250,7 @@ function lessonStrokes(canvasSize = 32, color = '#2196f3') {
250
250
  }
251
251
 
252
252
  /**
253
- * Lesson 13: Parallel & Intersecting — Các đường song song và giao nhau.
253
+ * Lesson 13: Parallel & Intersecting Lines Drawing parallel and intersecting lines.
254
254
  */
255
255
  function lessonParallelAndIntersecting(canvasSize = 32, color = '#00bcd4') {
256
256
  const margin = Math.floor(canvasSize * 0.1);
@@ -271,19 +271,19 @@ function lessonParallelAndIntersecting(canvasSize = 32, color = '#00bcd4') {
271
271
  */
272
272
  function getLessonCatalog() {
273
273
  return [
274
- { id: 'lines', name: 'Đường thẳng', description: 'Vẽ đường thẳng ngang, dọc và chéo', fn: lessonLines },
275
- { id: 'squares', name: 'Hình vuông & Hình chữ nhật', description: 'Hiểu về góc vuông và tỷ lệ', fn: lessonSquares },
276
- { id: 'circles', name: 'Hình tròn', description: 'Làm quen với đường cong và đối xứng', fn: lessonCircles },
277
- { id: 'ellipses', name: 'Hình elip', description: 'Hiểu về hình bầu dục và tỷ lệ', fn: lessonEllipses },
278
- { id: 'triangles', name: 'Hình tam giác', description: 'Hình ba cạnh và sự ổn định', fn: lessonTriangles },
279
- { id: 'polygons', name: 'Đa giác', description: 'Hình nhiều cạnh', fn: lessonPolygons },
280
- { id: 'composition', name: 'Tổng hợp', description: 'Kết hợp các hình bản tạo vật thể đơn giản', fn: lessonComposition },
281
- { id: 'freehand', name: 'Vẽ bằng tay', description: 'Luyện vẽ bằng tay, hạn chế dùng thước', fn: lessonFreehand },
282
- { id: 'proportions', name: 'Tỉ lệ và góc', description: 'Tập giữ tỉ lệ và góc', fn: lessonProportionsAndAngles },
283
- { id: 'curves', name: 'Đường cong', description: 'Đường cong và nét: Đường cong C, S', fn: lessonCurves },
284
- { id: 'spiral', name: 'Xoắn ốc', description: 'Đường xoắn ốc', fn: lessonSpiral },
285
- { id: 'strokes', name: 'Các loại nét', description: 'Nét dài, nét ngắn, nét đậm–nhạt', fn: lessonStrokes },
286
- { id: 'parallel_intersecting', name: 'Song song & giao nhau', description: 'Vẽ các đường song song và đường giao nhau', fn: lessonParallelAndIntersecting },
274
+ { id: 'lines', name: 'Lines', description: 'Draw horizontal, vertical and diagonal lines', fn: lessonLines },
275
+ { id: 'squares', name: 'Squares & Rectangles', description: 'Understand right angles and proportions', fn: lessonSquares },
276
+ { id: 'circles', name: 'Circles', description: 'Learn curves and symmetry', fn: lessonCircles },
277
+ { id: 'ellipses', name: 'Ellipses', description: 'Understand oval shapes and proportions', fn: lessonEllipses },
278
+ { id: 'triangles', name: 'Triangles', description: 'Three-point shapes and stability', fn: lessonTriangles },
279
+ { id: 'polygons', name: 'Polygons', description: 'Multi-sided shapes', fn: lessonPolygons },
280
+ { id: 'composition', name: 'Composition', description: 'Combine basic shapes to create simple objects', fn: lessonComposition },
281
+ { id: 'freehand', name: 'Freehand Drawing', description: 'Practice freehand drawing without rulers', fn: lessonFreehand },
282
+ { id: 'proportions', name: 'Proportions & Angles', description: 'Practice maintaining proportions and angles', fn: lessonProportionsAndAngles },
283
+ { id: 'curves', name: 'Curves', description: 'C-curves and S-curves', fn: lessonCurves },
284
+ { id: 'spiral', name: 'Spiral', description: 'Drawing spiral curves', fn: lessonSpiral },
285
+ { id: 'strokes', name: 'Stroke Types', description: 'Long strokes, short strokes, thick and thin lines', fn: lessonStrokes },
286
+ { id: 'parallel_intersecting', name: 'Parallel & Intersecting', description: 'Draw parallel and intersecting lines', fn: lessonParallelAndIntersecting },
287
287
  ];
288
288
  }
289
289
 
@@ -2,7 +2,7 @@ const shapes = require('./shapes');
2
2
  const lightShapes = require('./light_shapes');
3
3
 
4
4
  /**
5
- * Lesson: 6 Zones of Light (Khối cầu)
5
+ * Lesson: 6 Zones of Light (Sphere)
6
6
  * Highlight, Light, Halftone, Core shadow, Reflected light, Cast shadow.
7
7
  */
8
8
  function lessonLightZones(canvasSize = 32) {
@@ -26,7 +26,7 @@ function lessonLightZones(canvasSize = 32) {
26
26
  }
27
27
 
28
28
  /**
29
- * Lesson: Light Direction & Intensity (Hướng ánh sáng mặt phẳng)
29
+ * Lesson: Light Direction & Intensity (Light direction and plane values)
30
30
  * Shows a box with planes facing/turning away from light.
31
31
  */
32
32
  function lessonLightDirection(canvasSize = 32) {
@@ -50,7 +50,7 @@ function lessonLightDirection(canvasSize = 32) {
50
50
  }
51
51
 
52
52
  /**
53
- * Lesson: Contact Shadow (Bóng tiếp xúc)
53
+ * Lesson: Contact Shadow (Point of contact shadow)
54
54
  * The darkest area where an object touches the ground.
55
55
  */
56
56
  function lessonContactShadow(canvasSize = 32) {
@@ -28,7 +28,7 @@ function drawShadedSphere(cx, cy, r, lightAngle) {
28
28
  const offX = Math.cos(lightAngle);
29
29
  const offY = Math.sin(lightAngle);
30
30
 
31
- // 1. Cast Shadow (Bóng đổ) on the ground
31
+ // 1. Cast Shadow on the ground
32
32
  // Drawn first so it's behind the sphere
33
33
  // Shift cast shadow away from light
34
34
  const castR = Math.floor(r * 1.2);
@@ -41,26 +41,26 @@ function drawShadedSphere(cx, cy, r, lightAngle) {
41
41
  // The very edge away from light gets some bounce light
42
42
  commands.push(shapes.drawCircle(cx, cy, r, '#78909c', true));
43
43
 
44
- // 3. Core Shadow (Vùng tối chính)
44
+ // 3. Core Shadow (main shadow zone)
45
45
  // Offset slightly away from light
46
46
  const coreR = Math.floor(r * 0.9);
47
47
  const coreX = cx - Math.floor(offX * r * 0.1);
48
48
  const coreY = cy - Math.floor(offY * r * 0.1);
49
49
  commands.push(shapes.drawCircle(coreX, coreY, coreR, '#263238', true));
50
50
 
51
- // 4. Halftone (Vùng trung gian)
51
+ // 4. Halftone (mid-tone zone)
52
52
  const halfR = Math.floor(r * 0.75);
53
53
  const halfX = cx + Math.floor(offX * r * 0.1);
54
54
  const halfY = cy + Math.floor(offY * r * 0.1);
55
55
  commands.push(shapes.drawCircle(halfX, halfY, halfR, '#546e7a', true));
56
56
 
57
- // 5. Light (Vùng sáng)
57
+ // 5. Light zone
58
58
  const lightR = Math.floor(r * 0.5);
59
59
  const lightX = cx + Math.floor(offX * r * 0.3);
60
60
  const lightY = cy + Math.floor(offY * r * 0.3);
61
61
  commands.push(shapes.drawCircle(lightX, lightY, lightR, '#90a4ae', true));
62
62
 
63
- // 6. Highlight (Điểm sáng)
63
+ // 6. Highlight (bright spot)
64
64
  const highR = Math.floor(r * 0.15);
65
65
  const highX = cx + Math.floor(offX * r * 0.5);
66
66
  const highY = cy + Math.floor(offY * r * 0.5);
@@ -0,0 +1,71 @@
1
+ const materialShapes = require('./material_shapes');
2
+
3
+ /**
4
+ * Lesson: Shiny vs Matte (Metal vs Plastic/Rubber)
5
+ * Compares high contrast sharp highlights vs low contrast diffused highlights.
6
+ */
7
+ function lessonMaterialShiny(canvasSize = 32) {
8
+ const commands = [];
9
+ const cx1 = Math.floor(canvasSize * 0.3);
10
+ const cx2 = Math.floor(canvasSize * 0.7);
11
+ const cy = Math.floor(canvasSize / 2);
12
+ const r = Math.floor(canvasSize * 0.2);
13
+
14
+ // 1. Metal Sphere (Left)
15
+ commands.push(...materialShapes.drawMetalSphere(cx1, cy, r));
16
+
17
+ // 2. Matte/Plastic Sphere (Right)
18
+ commands.push(...materialShapes.drawMatteSphere(cx2, cy, r, '#e65100'));
19
+
20
+ return commands;
21
+ }
22
+
23
+ /**
24
+ * Lesson: Transparent (Glass)
25
+ * Demonstrates Fresnel effect, sharp reflections, and light transmission.
26
+ */
27
+ function lessonMaterialGlass(canvasSize = 32) {
28
+ const commands = [];
29
+ const cx = Math.floor(canvasSize / 2);
30
+ const cy = Math.floor(canvasSize / 2);
31
+ const r = Math.floor(canvasSize * 0.35);
32
+
33
+ commands.push(...materialShapes.drawGlassSphere(cx, cy, r));
34
+
35
+ return commands;
36
+ }
37
+
38
+ /**
39
+ * Lesson: Texture (Wood vs Stone)
40
+ * Compares organic lines vs rough cracks.
41
+ */
42
+ function lessonMaterialTexture(canvasSize = 32) {
43
+ const commands = [];
44
+ const cx1 = Math.floor(canvasSize * 0.3);
45
+ const cx2 = Math.floor(canvasSize * 0.7);
46
+ const cy = Math.floor(canvasSize / 2);
47
+ const r = Math.floor(canvasSize * 0.2);
48
+
49
+ // 1. Wood Sphere (Left)
50
+ commands.push(...materialShapes.drawTexturedSphere(cx1, cy, r, 'wood'));
51
+
52
+ // 2. Stone Sphere (Right)
53
+ commands.push(...materialShapes.drawTexturedSphere(cx2, cy, r, 'stone'));
54
+
55
+ return commands;
56
+ }
57
+
58
+ function getMaterialLessonCatalog() {
59
+ return [
60
+ { id: 'material_shiny', name: 'Metal vs Matte', description: 'Sharp highlights (Metal) vs Soft highlights (Plastic)', fn: lessonMaterialShiny },
61
+ { id: 'material_glass', name: 'Glass & Transparency', description: 'Fresnel effect and transmitted light', fn: lessonMaterialGlass },
62
+ { id: 'material_texture', name: 'Texture: Wood vs Stone', description: 'Grain lines vs cracks on a 3D form', fn: lessonMaterialTexture },
63
+ ];
64
+ }
65
+
66
+ module.exports = {
67
+ lessonMaterialShiny,
68
+ lessonMaterialGlass,
69
+ lessonMaterialTexture,
70
+ getMaterialLessonCatalog
71
+ };
@@ -0,0 +1,130 @@
1
+ const shapes = require('./shapes');
2
+
3
+ function drawMetalSphere(cx, cy, r) {
4
+ const commands = [];
5
+
6
+ // Base sphere (darkest shadow/environment reflection)
7
+ commands.push(shapes.drawCircle(cx, cy, r, '#263238', true));
8
+
9
+ // High contrast core shadow
10
+ const coreR = Math.floor(r * 0.9);
11
+ commands.push(shapes.drawCircle(cx, cy + Math.floor(r*0.1), coreR, '#1a1a1a', true));
12
+
13
+ // Environment reflection (Ground bounce)
14
+ const envR = Math.floor(r * 0.7);
15
+ commands.push(shapes.drawCircle(cx, cy + Math.floor(r*0.3), envR, '#546e7a', true));
16
+
17
+ // Sharp light zone
18
+ const lightR = Math.floor(r * 0.6);
19
+ commands.push(shapes.drawCircle(cx - Math.floor(r*0.2), cy - Math.floor(r*0.2), lightR, '#cfd8dc', true));
20
+
21
+ // VERY sharp highlight (Tiny and pure white)
22
+ const highR = Math.floor(r * 0.1);
23
+ commands.push(shapes.drawCircle(cx - Math.floor(r*0.4), cy - Math.floor(r*0.4), highR, '#ffffff', true));
24
+
25
+ return commands;
26
+ }
27
+
28
+ function drawMatteSphere(cx, cy, r, colorBase) {
29
+ const commands = [];
30
+
31
+ // Base sphere (shadow)
32
+ commands.push(shapes.drawCircle(cx, cy, r, '#795548', true)); // Dark brownish shadow
33
+
34
+ // Smooth gradation (Halftone)
35
+ const halfR = Math.floor(r * 0.85);
36
+ commands.push(shapes.drawCircle(cx - Math.floor(r*0.05), cy - Math.floor(r*0.05), halfR, colorBase, true));
37
+
38
+ // Light area
39
+ const lightR = Math.floor(r * 0.6);
40
+ commands.push(shapes.drawCircle(cx - Math.floor(r*0.15), cy - Math.floor(r*0.15), lightR, '#ffcc80', true)); // Lighter tone
41
+
42
+ // Soft highlight (Large and diffused)
43
+ const highR = Math.floor(r * 0.3);
44
+ commands.push(shapes.drawCircle(cx - Math.floor(r*0.25), cy - Math.floor(r*0.25), highR, '#ffe0b2', true));
45
+
46
+ return commands;
47
+ }
48
+
49
+ function drawGlassSphere(cx, cy, r) {
50
+ const commands = [];
51
+
52
+ // Fresnel effect: Edges are brighter/more reflective than the center
53
+ // Base (Outline & Rim light)
54
+ commands.push(shapes.drawCircle(cx, cy, r, '#80deea', true));
55
+
56
+ // Darker inner area (light passes through)
57
+ const innerR = Math.floor(r * 0.9);
58
+ commands.push(shapes.drawCircle(cx, cy, innerR, '#006064', true));
59
+
60
+ // Transmitted light hitting the back bottom
61
+ const transR = Math.floor(r * 0.7);
62
+ commands.push(shapes.drawCircle(cx + Math.floor(r*0.1), cy + Math.floor(r*0.2), transR, '#4dd0e1', true));
63
+
64
+ // Very center is darkest (least reflection)
65
+ const centerR = Math.floor(r * 0.5);
66
+ commands.push(shapes.drawCircle(cx, cy, centerR, '#00363a', true));
67
+
68
+ // Sharp Highlights (Primary and secondary reflections)
69
+ const highR1 = Math.floor(r * 0.15);
70
+ commands.push(shapes.drawCircle(cx - Math.floor(r*0.4), cy - Math.floor(r*0.4), highR1, '#ffffff', true));
71
+
72
+ const highR2 = Math.floor(r * 0.08);
73
+ commands.push(shapes.drawCircle(cx + Math.floor(r*0.3), cy + Math.floor(r*0.4), highR2, '#e0f7fa', true));
74
+
75
+ return commands;
76
+ }
77
+
78
+ function drawTexturedSphere(cx, cy, r, type) {
79
+ const commands = [];
80
+
81
+ // Base sphere
82
+ const baseColor = type === 'wood' ? '#8d6e63' : '#9e9e9e';
83
+ commands.push(shapes.drawCircle(cx, cy, r, baseColor, true));
84
+
85
+ // Shadow
86
+ const shadowColor = type === 'wood' ? '#4e342e' : '#616161';
87
+ const shadowR = Math.floor(r * 0.85);
88
+ commands.push(shapes.drawCircle(cx + Math.floor(r*0.1), cy + Math.floor(r*0.1), shadowR, shadowColor, true));
89
+
90
+ // Texture application
91
+ if (type === 'wood') {
92
+ // Wood grain lines curving around the sphere
93
+ const grainColor = '#3e2723';
94
+ for (let i = -2; i <= 2; i++) {
95
+ const rx = r * 0.8;
96
+ const ry = r * 0.3 * Math.abs(i) + r * 0.2;
97
+ commands.push(shapes.drawEllipse(cx, cy + (i * Math.floor(r*0.3)), Math.floor(rx), Math.floor(ry), grainColor, false));
98
+ }
99
+ } else if (type === 'stone') {
100
+ // Stone cracks and rough patches
101
+ const crackColor = '#212121';
102
+ // Simplified cracks using polylines
103
+ commands.push(shapes.drawPolyline([
104
+ {x: cx - Math.floor(r*0.5), y: cy - Math.floor(r*0.5)},
105
+ {x: cx - Math.floor(r*0.2), y: cy - Math.floor(r*0.1)},
106
+ {x: cx + Math.floor(r*0.3), y: cy - Math.floor(r*0.2)},
107
+ {x: cx + Math.floor(r*0.6), y: cy + Math.floor(r*0.1)}
108
+ ], crackColor));
109
+
110
+ commands.push(shapes.drawPolyline([
111
+ {x: cx - Math.floor(r*0.2), y: cy - Math.floor(r*0.1)},
112
+ {x: cx, y: cy + Math.floor(r*0.4)},
113
+ {x: cx - Math.floor(r*0.4), y: cy + Math.floor(r*0.7)}
114
+ ], crackColor));
115
+ }
116
+
117
+ // Soft Highlight over texture
118
+ const highColor = type === 'wood' ? '#d7ccc8' : '#e0e0e0';
119
+ const highR = Math.floor(r * 0.4);
120
+ commands.push(shapes.drawCircle(cx - Math.floor(r*0.3), cy - Math.floor(r*0.3), highR, highColor, true));
121
+
122
+ return commands;
123
+ }
124
+
125
+ module.exports = {
126
+ drawMetalSphere,
127
+ drawMatteSphere,
128
+ drawGlassSphere,
129
+ drawTexturedSphere
130
+ };
@@ -0,0 +1,49 @@
1
+ const { z } = require('zod');
2
+ const { registerTool, sendCommand } = require('../../core/server');
3
+ const materialLessons = require('./material_lessons');
4
+
5
+ /**
6
+ * Register all material & surface rendering tools on the MCP server.
7
+ * @param {McpServer} server
8
+ */
9
+ function register(server) {
10
+ registerTool(server, 'art_tree_lesson_material_shiny',
11
+ 'Lesson: Materials - Metal vs Matte (High contrast vs diffused highlights)',
12
+ { canvasSize: z.number().int().min(16).max(64).default(32) },
13
+ async (p) => {
14
+ const commands = materialLessons.lessonMaterialShiny(p.canvasSize);
15
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
16
+ return { content: [{ type: 'text', text: `✓ Metal vs Matte material lesson completed` }] };
17
+ });
18
+
19
+ registerTool(server, 'art_tree_lesson_material_glass',
20
+ 'Lesson: Materials - Glass (Transparency, Fresnel effect)',
21
+ { canvasSize: z.number().int().min(16).max(64).default(32) },
22
+ async (p) => {
23
+ const commands = materialLessons.lessonMaterialGlass(p.canvasSize);
24
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
25
+ return { content: [{ type: 'text', text: `✓ Glass material lesson completed` }] };
26
+ });
27
+
28
+ registerTool(server, 'art_tree_lesson_material_texture',
29
+ 'Lesson: Materials - Texture (Wood grain vs Stone cracks)',
30
+ { canvasSize: z.number().int().min(16).max(64).default(32) },
31
+ async (p) => {
32
+ const commands = materialLessons.lessonMaterialTexture(p.canvasSize);
33
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
34
+ return { content: [{ type: 'text', text: `✓ Wood vs Stone texture lesson completed` }] };
35
+ });
36
+
37
+ registerTool(server, 'art_tree_lesson_material_catalog',
38
+ 'Get the catalog of all available Material lessons',
39
+ {},
40
+ () => {
41
+ const catalog = materialLessons.getMaterialLessonCatalog();
42
+ const text = catalog.map(l =>
43
+ ` • ${l.id}: ${l.name} — ${l.description}`
44
+ ).join('\n');
45
+ return { content: [{ type: 'text', text: `📚 Material Lessons:\n\n${text}` }] };
46
+ });
47
+ }
48
+
49
+ module.exports = { register };
@@ -89,7 +89,7 @@ function lesson3PointPerspective(canvasSize = 32, color = '#ff9800') {
89
89
  }
90
90
 
91
91
  /**
92
- * Lesson: Foreshortening (Sự rút ngắn theo chiều sâu)
92
+ * Lesson: Foreshortening (Depth foreshortening)
93
93
  * Drawing a cylinder pointing directly at the viewer
94
94
  */
95
95
  function lessonForeshortening(canvasSize = 32, color = '#9c27b0') {
@@ -37,7 +37,7 @@ function register(server) {
37
37
  });
38
38
 
39
39
  registerTool(server, 'art_tree_lesson_persp_foreshorten',
40
- 'Lesson: Perspective - Foreshortening (Rút ngắn theo chiều sâu)',
40
+ 'Lesson: Perspective - Foreshortening (Depth foreshortening)',
41
41
  { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#9c27b0') },
42
42
  async (p) => {
43
43
  const commands = perspLessons.lessonForeshortening(p.canvasSize, p.color);
@@ -0,0 +1,82 @@
1
+ const skyShapes1 = require('./sky_shapes_1');
2
+
3
+ /**
4
+ * Lesson 1: Sun Shapes (Hình dạng mặt trời)
5
+ * Compares Basic, Soft, and Rays
6
+ */
7
+ function lessonSunShapes(canvasSize = 32) {
8
+ const commands = [];
9
+ const y = Math.floor(canvasSize / 2);
10
+ const r = Math.floor(canvasSize * 0.15);
11
+
12
+ // Left: Basic
13
+ commands.push(...skyShapes1.drawSunBasic(Math.floor(canvasSize * 0.2), y, r));
14
+ // Center: Soft
15
+ commands.push(...skyShapes1.drawSunSoft(Math.floor(canvasSize * 0.5), y, r));
16
+ // Right: Rays
17
+ commands.push(...skyShapes1.drawSunRays(Math.floor(canvasSize * 0.8), y, r));
18
+
19
+ return commands;
20
+ }
21
+
22
+ /**
23
+ * Lesson 2: Sunset Colors (Màu sắc buổi chiều)
24
+ * Shows the progression of colors from blue to yellow
25
+ */
26
+ function lessonSunsetColors(canvasSize = 32) {
27
+ const commands = [];
28
+
29
+ // The classic sunset gradient: Blue -> Purple -> Pink -> Orange -> Yellow (top to bottom)
30
+ const sunsetPalette = [
31
+ '#3f51b5', // Blue
32
+ '#9c27b0', // Purple
33
+ '#e91e63', // Pink
34
+ '#ff9800', // Orange
35
+ '#ffeb3b' // Yellow
36
+ ];
37
+
38
+ commands.push(...skyShapes1.drawSkyGradient(0, 0, canvasSize, canvasSize, sunsetPalette));
39
+
40
+ return commands;
41
+ }
42
+
43
+ /**
44
+ * Lesson 3: Sunset Sky (Bầu trời hoàng hôn)
45
+ * Combines the gradient sky with a soft sun setting at the bottom
46
+ */
47
+ function lessonSunsetSky(canvasSize = 32) {
48
+ const commands = [];
49
+
50
+ // 1. Draw the Sky
51
+ const sunsetPalette = [
52
+ '#1a237e', // Dark Blue
53
+ '#512da8', // Deep Purple
54
+ '#c2185b', // Magenta
55
+ '#ff5722', // Deep Orange
56
+ '#ffb300' // Amber
57
+ ];
58
+ commands.push(...skyShapes1.drawSkyGradient(0, 0, canvasSize, canvasSize, sunsetPalette));
59
+
60
+ // 2. Draw the Sun (Soft glowing sun near the bottom)
61
+ const cx = Math.floor(canvasSize / 2);
62
+ const cy = Math.floor(canvasSize * 0.8);
63
+ const r = Math.floor(canvasSize * 0.15);
64
+ commands.push(...skyShapes1.drawSunSoft(cx, cy, r));
65
+
66
+ return commands;
67
+ }
68
+
69
+ function getSkyLesson1Catalog() {
70
+ return [
71
+ { id: 'sky_sun_shapes', name: 'Sun Shapes', description: 'Basic vs Soft vs Rays', fn: lessonSunShapes },
72
+ { id: 'sky_sunset_colors', name: 'Sunset Colors', description: 'Gradient bands from Blue to Yellow', fn: lessonSunsetColors },
73
+ { id: 'sky_sunset_sky', name: 'Sunset Sky', description: 'Combining gradient sky with a soft sun', fn: lessonSunsetSky },
74
+ ];
75
+ }
76
+
77
+ module.exports = {
78
+ lessonSunShapes,
79
+ lessonSunsetColors,
80
+ lessonSunsetSky,
81
+ getSkyLesson1Catalog
82
+ };
@@ -0,0 +1,68 @@
1
+ const shapes = require('./shapes');
2
+
3
+ /**
4
+ * Draws a basic solid sun
5
+ */
6
+ function drawSunBasic(cx, cy, r) {
7
+ return [shapes.drawCircle(cx, cy, r, '#ffeb3b', true)];
8
+ }
9
+
10
+ /**
11
+ * Draws a soft glowing sun using concentric circles with decreasing opacity
12
+ * Since we don't have true opacity in standard shapes yet, we use color stepping
13
+ * (white -> light yellow -> yellow -> orange)
14
+ */
15
+ function drawSunSoft(cx, cy, r) {
16
+ const commands = [];
17
+ commands.push(shapes.drawCircle(cx, cy, Math.floor(r * 1.5), '#ffe082', true)); // Outer glow
18
+ commands.push(shapes.drawCircle(cx, cy, Math.floor(r * 1.2), '#ffeb3b', true)); // Mid glow
19
+ commands.push(shapes.drawCircle(cx, cy, Math.floor(r * 0.8), '#fff59d', true)); // Inner glow
20
+ commands.push(shapes.drawCircle(cx, cy, Math.floor(r * 0.4), '#ffffff', true)); // Core
21
+ return commands;
22
+ }
23
+
24
+ /**
25
+ * Draws a sun with distinct rays
26
+ */
27
+ function drawSunRays(cx, cy, r) {
28
+ const commands = [];
29
+ const color = '#ffeb3b';
30
+ // Central body
31
+ commands.push(shapes.drawCircle(cx, cy, r, color, true));
32
+
33
+ // Outer rays
34
+ const rayL = Math.floor(r * 2);
35
+ for (let i = 0; i < 12; i++) {
36
+ const a = (i / 12) * 2 * Math.PI;
37
+ const x1 = Math.round(cx + (r + 2) * Math.cos(a));
38
+ const y1 = Math.round(cy + (r + 2) * Math.sin(a));
39
+ const x2 = Math.round(cx + rayL * Math.cos(a));
40
+ const y2 = Math.round(cy + rayL * Math.sin(a));
41
+ commands.push(shapes.drawLine(x1, y1, x2, y2, color));
42
+ }
43
+ return commands;
44
+ }
45
+
46
+ /**
47
+ * Draws a simulated gradient sky using horizontal bands of color
48
+ * @param {Array<string>} colors - Array of hex colors from top to bottom
49
+ */
50
+ function drawSkyGradient(x, y, w, h, colors) {
51
+ const commands = [];
52
+ const bandHeight = Math.ceil(h / colors.length);
53
+
54
+ for (let i = 0; i < colors.length; i++) {
55
+ const bandY = y + i * bandHeight;
56
+ // To ensure we cover the total height, the last band fills the rest
57
+ const bh = (i === colors.length - 1) ? (y + h - bandY) : bandHeight;
58
+ commands.push(shapes.drawRectangle(x, bandY, w, bh, colors[i], true));
59
+ }
60
+ return commands;
61
+ }
62
+
63
+ module.exports = {
64
+ drawSunBasic,
65
+ drawSunSoft,
66
+ drawSunRays,
67
+ drawSkyGradient
68
+ };
@@ -0,0 +1,51 @@
1
+ const { z } = require('zod');
2
+ const { registerTool, sendCommand } = require('../../core/server');
3
+ const skyLessons1 = require('./sky_lessons_1');
4
+
5
+ /**
6
+ * Register all sky and sun tools on the MCP server.
7
+ * @param {McpServer} server
8
+ */
9
+ function register(server) {
10
+ // Phase 1 Tools
11
+ registerTool(server, 'art_tree_lesson_sky_sun_shapes',
12
+ 'Lesson: Sun & Sky - Compare Basic, Soft, and Rays sun shapes',
13
+ { canvasSize: z.number().int().min(16).max(64).default(32) },
14
+ async (p) => {
15
+ const commands = skyLessons1.lessonSunShapes(p.canvasSize);
16
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
17
+ return { content: [{ type: 'text', text: `✓ Sun Shapes lesson completed` }] };
18
+ });
19
+
20
+ registerTool(server, 'art_tree_lesson_sky_sunset_colors',
21
+ 'Lesson: Sun & Sky - Draw a sunset gradient (Blue to Yellow)',
22
+ { canvasSize: z.number().int().min(16).max(64).default(32) },
23
+ async (p) => {
24
+ const commands = skyLessons1.lessonSunsetColors(p.canvasSize);
25
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
26
+ return { content: [{ type: 'text', text: `✓ Sunset Colors lesson completed` }] };
27
+ });
28
+
29
+ registerTool(server, 'art_tree_lesson_sky_sunset_sky',
30
+ 'Lesson: Sun & Sky - Combine gradient sky with a soft setting sun',
31
+ { canvasSize: z.number().int().min(16).max(64).default(32) },
32
+ async (p) => {
33
+ const commands = skyLessons1.lessonSunsetSky(p.canvasSize);
34
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
35
+ return { content: [{ type: 'text', text: `✓ Sunset Sky lesson completed` }] };
36
+ });
37
+
38
+ registerTool(server, 'art_tree_lesson_sky_catalog',
39
+ 'Get the catalog of all available Sun & Sky lessons',
40
+ {},
41
+ () => {
42
+ const cat1 = skyLessons1.getSkyLesson1Catalog();
43
+ const catalog = [...cat1];
44
+ const text = catalog.map(l =>
45
+ ` • ${l.id}: ${l.name} — ${l.description}`
46
+ ).join('\n');
47
+ return { content: [{ type: 'text', text: `📚 Sun & Sky Lessons:\n\n${text}` }] };
48
+ });
49
+ }
50
+
51
+ module.exports = { register };