@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,105 @@
1
+ const shapes = require('./shapes');
2
+ const ellipseShapes = require('./ellipse_shapes');
3
+
4
+ /**
5
+ * Lesson: Ellipse Orientations (Horizontal, Vertical, Tilted)
6
+ */
7
+ function lessonEllipseOrientations(canvasSize = 32, color = '#2196f3') {
8
+ const commands = [];
9
+ const midY = Math.floor(canvasSize / 2);
10
+ const qX = Math.floor(canvasSize / 4);
11
+ const rLong = Math.floor(canvasSize * 0.2);
12
+ const rShort = Math.floor(canvasSize * 0.1);
13
+
14
+ // Horizontal (Left)
15
+ commands.push(shapes.drawEllipse(qX, midY, rLong, rShort, color, false));
16
+
17
+ // Vertical (Middle)
18
+ commands.push(shapes.drawEllipse(qX * 2, midY, rShort, rLong, '#e91e63', false));
19
+
20
+ // Tilted (Right) - 45 degrees
21
+ commands.push(...ellipseShapes.drawRotatedEllipse(qX * 3, midY, rLong, rShort, Math.PI / 4, '#4caf50'));
22
+
23
+ return commands;
24
+ }
25
+
26
+ /**
27
+ * Lesson: Ellipse Proportions (Wide vs Narrow)
28
+ */
29
+ function lessonEllipseProportions(canvasSize = 32, color = '#ff9800') {
30
+ const commands = [];
31
+ const midY = Math.floor(canvasSize / 2);
32
+ const qX = Math.floor(canvasSize / 4);
33
+ const rx = Math.floor(canvasSize * 0.2);
34
+
35
+ // Wide Ellipse (Closer to a circle)
36
+ commands.push(shapes.drawEllipse(qX, midY, rx, Math.floor(rx * 0.8), color, false));
37
+
38
+ // Medium Ellipse
39
+ commands.push(shapes.drawEllipse(qX * 2, midY, rx, Math.floor(rx * 0.4), '#9c27b0', false));
40
+
41
+ // Narrow Ellipse (Almost a line)
42
+ commands.push(shapes.drawEllipse(qX * 3, midY, rx, Math.floor(rx * 0.1), '#f44336', false));
43
+
44
+ return commands;
45
+ }
46
+
47
+ /**
48
+ * Lesson: Ellipse Anatomy (Symmetry, Major/Minor axes, Center)
49
+ */
50
+ function lessonEllipseAnatomy(canvasSize = 32, color = '#00bcd4') {
51
+ const commands = [];
52
+ const cx = Math.floor(canvasSize / 2);
53
+ const cy = Math.floor(canvasSize / 2);
54
+ const rx = Math.floor(canvasSize * 0.35);
55
+ const ry = Math.floor(canvasSize * 0.2);
56
+ const angle = Math.PI / 6; // 30 degrees tilt to show axes clearly
57
+
58
+ // Draw ellipse
59
+ commands.push(...ellipseShapes.drawRotatedEllipse(cx, cy, rx, ry, angle, color));
60
+
61
+ // Draw anatomy (axes and center)
62
+ commands.push(...ellipseShapes.drawEllipseAxes(cx, cy, rx, ry, angle));
63
+
64
+ return commands;
65
+ }
66
+
67
+ /**
68
+ * Lesson: Coaxial Ellipses (Multiple ellipses on the same axis)
69
+ */
70
+ function lessonCoaxialEllipses(canvasSize = 32, color = '#795548') {
71
+ const commands = [];
72
+ const cx = Math.floor(canvasSize / 2);
73
+ const rx = Math.floor(canvasSize * 0.3);
74
+
75
+ // Central axis line
76
+ commands.push(shapes.drawLine(cx, Math.floor(canvasSize * 0.1), cx, canvasSize - Math.floor(canvasSize * 0.1), '#bdbdbd'));
77
+
78
+ // Top ellipse (narrower, viewed from steeper angle)
79
+ commands.push(shapes.drawEllipse(cx, Math.floor(canvasSize * 0.2), rx, Math.floor(rx * 0.2), color, false));
80
+
81
+ // Middle ellipse
82
+ commands.push(shapes.drawEllipse(cx, Math.floor(canvasSize * 0.5), rx, Math.floor(rx * 0.3), color, false));
83
+
84
+ // Bottom ellipse (wider, viewed from less steep angle)
85
+ commands.push(shapes.drawEllipse(cx, canvasSize - Math.floor(canvasSize * 0.2), rx, Math.floor(rx * 0.4), color, false));
86
+
87
+ return commands;
88
+ }
89
+
90
+ function getEllipseLessonCatalog() {
91
+ return [
92
+ { id: 'ellipse_orientations', name: 'Ellipse Orientations', description: 'Horizontal, Vertical, and Tilted ellipses', fn: lessonEllipseOrientations },
93
+ { id: 'ellipse_proportions', name: 'Ellipse Proportions', description: 'Wide vs Narrow ellipses', fn: lessonEllipseProportions },
94
+ { id: 'ellipse_anatomy', name: 'Ellipse Anatomy', description: 'Symmetry, Major/Minor axes, and Center', fn: lessonEllipseAnatomy },
95
+ { id: 'ellipse_coaxial', name: 'Coaxial Ellipses', description: 'Multiple ellipses sharing the same axis', fn: lessonCoaxialEllipses },
96
+ ];
97
+ }
98
+
99
+ module.exports = {
100
+ lessonEllipseOrientations,
101
+ lessonEllipseProportions,
102
+ lessonEllipseAnatomy,
103
+ lessonCoaxialEllipses,
104
+ getEllipseLessonCatalog
105
+ };
@@ -0,0 +1,63 @@
1
+ const shapes = require('./shapes');
2
+
3
+ /**
4
+ * Draw a rotated ellipse using a polygon approximation
5
+ * @param {number} cx Center X
6
+ * @param {number} cy Center Y
7
+ * @param {number} rx X radius (major axis if rx > ry)
8
+ * @param {number} ry Y radius
9
+ * @param {number} angle Rotation angle in radians
10
+ * @param {string} color Hex color
11
+ * @param {number} segments Number of polygon segments
12
+ */
13
+ function drawRotatedEllipse(cx, cy, rx, ry, angle, color, segments = 36) {
14
+ const points = [];
15
+ const cosA = Math.cos(angle);
16
+ const sinA = Math.sin(angle);
17
+
18
+ for (let i = 0; i <= segments; i++) {
19
+ const t = (i / segments) * 2 * Math.PI;
20
+ const px = rx * Math.cos(t);
21
+ const py = ry * Math.sin(t);
22
+
23
+ const x = Math.round(cx + px * cosA - py * sinA);
24
+ const y = Math.round(cy + px * sinA + py * cosA);
25
+ points.push({ x, y });
26
+ }
27
+
28
+ return shapes.drawPolyline(points, color);
29
+ }
30
+
31
+ /**
32
+ * Draw major and minor axes for an ellipse
33
+ */
34
+ function drawEllipseAxes(cx, cy, rx, ry, angle = 0) {
35
+ const commands = [];
36
+ const cosA = Math.cos(angle);
37
+ const sinA = Math.sin(angle);
38
+
39
+ // Major/Minor Axis calculation based on rotation
40
+ const x1 = Math.round(cx - rx * cosA);
41
+ const y1 = Math.round(cy - rx * sinA);
42
+ const x2 = Math.round(cx + rx * cosA);
43
+ const y2 = Math.round(cy + rx * sinA);
44
+
45
+ const y3_x = Math.round(cx + ry * sinA);
46
+ const y3_y = Math.round(cy - ry * cosA);
47
+ const y4_x = Math.round(cx - ry * sinA);
48
+ const y4_y = Math.round(cy + ry * cosA);
49
+
50
+ // Red for Major, Blue for Minor
51
+ commands.push(shapes.drawLine(x1, y1, x2, y2, '#ff0000'));
52
+ commands.push(shapes.drawLine(y3_x, y3_y, y4_x, y4_y, '#2196f3'));
53
+
54
+ // Center dot
55
+ commands.push(shapes.drawCircle(cx, cy, 1, '#000000', true));
56
+
57
+ return commands;
58
+ }
59
+
60
+ module.exports = {
61
+ drawRotatedEllipse,
62
+ drawEllipseAxes
63
+ };
@@ -0,0 +1,60 @@
1
+ const { z } = require('zod');
2
+ const { registerTool, sendCommand } = require('../../core/server');
3
+ const ellipseLessons = require('./ellipse_lessons');
4
+
5
+ const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g. #ff0000');
6
+
7
+ /**
8
+ * Register all ellipse tools on the MCP server.
9
+ * @param {McpServer} server
10
+ */
11
+ function register(server) {
12
+ registerTool(server, 'art_tree_lesson_ellipse_orientations',
13
+ 'Lesson: Ellipses - Orientations (Horizontal, Vertical, Tilted)',
14
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#2196f3') },
15
+ async (p) => {
16
+ const commands = ellipseLessons.lessonEllipseOrientations(p.canvasSize, p.color);
17
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
18
+ return { content: [{ type: 'text', text: `✓ Ellipse Orientations lesson completed` }] };
19
+ });
20
+
21
+ registerTool(server, 'art_tree_lesson_ellipse_proportions',
22
+ 'Lesson: Ellipses - Proportions (Wide vs Narrow)',
23
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#ff9800') },
24
+ async (p) => {
25
+ const commands = ellipseLessons.lessonEllipseProportions(p.canvasSize, p.color);
26
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
27
+ return { content: [{ type: 'text', text: `✓ Ellipse Proportions lesson completed` }] };
28
+ });
29
+
30
+ registerTool(server, 'art_tree_lesson_ellipse_anatomy',
31
+ 'Lesson: Ellipses - Anatomy (Symmetry, Axes, Center)',
32
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#00bcd4') },
33
+ async (p) => {
34
+ const commands = ellipseLessons.lessonEllipseAnatomy(p.canvasSize, p.color);
35
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
36
+ return { content: [{ type: 'text', text: `✓ Ellipse Anatomy lesson completed` }] };
37
+ });
38
+
39
+ registerTool(server, 'art_tree_lesson_coaxial_ellipses',
40
+ 'Lesson: Ellipses - Coaxial (Multiple ellipses on the same axis)',
41
+ { canvasSize: z.number().int().min(16).max(64).default(32), color: hexColor.default('#795548') },
42
+ async (p) => {
43
+ const commands = ellipseLessons.lessonCoaxialEllipses(p.canvasSize, p.color);
44
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
45
+ return { content: [{ type: 'text', text: `✓ Coaxial Ellipses lesson completed` }] };
46
+ });
47
+
48
+ registerTool(server, 'art_tree_lesson_ellipse_catalog',
49
+ 'Get the catalog of all available Ellipse lessons',
50
+ {},
51
+ () => {
52
+ const catalog = ellipseLessons.getEllipseLessonCatalog();
53
+ const text = catalog.map(l =>
54
+ ` • ${l.id}: ${l.name} — ${l.description}`
55
+ ).join('\n');
56
+ return { content: [{ type: 'text', text: `📚 Ellipse Lessons:\n\n${text}` }] };
57
+ });
58
+ }
59
+
60
+ module.exports = { register };
@@ -0,0 +1,55 @@
1
+ const hiddenShapes = require('./hidden_shapes');
2
+
3
+ /**
4
+ * Lesson: Hidden Box (6-sided box)
5
+ * You see 3 faces, but you must understand all 6.
6
+ */
7
+ function lessonHiddenBox(canvasSize = 32, visibleColor = '#2196f3', hiddenColor = '#e0e0e0') {
8
+ const cx = Math.floor(canvasSize / 2);
9
+ const cy = Math.floor(canvasSize / 2);
10
+ const w = Math.floor(canvasSize * 0.4);
11
+ const h = Math.floor(canvasSize * 0.4);
12
+ const d = Math.floor(canvasSize * 0.4);
13
+
14
+ return hiddenShapes.drawXRayBox(cx, cy, w, h, d, visibleColor, hiddenColor);
15
+ }
16
+
17
+ /**
18
+ * Lesson: Hidden Head (Human head and skull)
19
+ * You don't see the whole skull, but must understand the structure behind it.
20
+ */
21
+ function lessonHiddenHead(canvasSize = 32, visibleColor = '#ff5722', hiddenColor = '#e0e0e0') {
22
+ const cx = Math.floor(canvasSize / 2);
23
+ const cy = Math.floor(canvasSize * 0.4);
24
+ const r = Math.floor(canvasSize * 0.25);
25
+
26
+ return hiddenShapes.drawXRayHead(cx, cy, r, visibleColor, hiddenColor);
27
+ }
28
+
29
+ /**
30
+ * Lesson: Hidden Cup (Cup and inner bottom)
31
+ * You don't see the full inside bottom, but must understand how the wall goes down.
32
+ */
33
+ function lessonHiddenCup(canvasSize = 32, visibleColor = '#4caf50', hiddenColor = '#e0e0e0') {
34
+ const cx = Math.floor(canvasSize / 2);
35
+ const cy = Math.floor(canvasSize / 2);
36
+ const r = Math.floor(canvasSize * 0.25);
37
+ const h = Math.floor(canvasSize * 0.5);
38
+
39
+ return hiddenShapes.drawXRayCup(cx, cy, r, h, visibleColor, hiddenColor);
40
+ }
41
+
42
+ function getHiddenLessonCatalog() {
43
+ return [
44
+ { id: 'hidden_box', name: 'Hidden Box', description: 'See all 6 faces of a box, not just 3', fn: lessonHiddenBox },
45
+ { id: 'hidden_head', name: 'Hidden Head', description: 'Understand the cranium sphere behind the face', fn: lessonHiddenHead },
46
+ { id: 'hidden_cup', name: 'Hidden Cup', description: 'Understand the inner depth and bottom of a cup', fn: lessonHiddenCup },
47
+ ];
48
+ }
49
+
50
+ module.exports = {
51
+ lessonHiddenBox,
52
+ lessonHiddenHead,
53
+ lessonHiddenCup,
54
+ getHiddenLessonCatalog
55
+ };
@@ -0,0 +1,97 @@
1
+ const shapes = require('./shapes');
2
+ const ellipseShapes = require('./ellipse_shapes');
3
+
4
+ function drawXRayBox(cx, cy, w, h, d, visibleColor, hiddenColor) {
5
+ const commands = [];
6
+ const dx = Math.floor(d * 0.7);
7
+ const dy = Math.floor(d * 0.5);
8
+
9
+ const fX = cx - Math.floor(w/2);
10
+ const fY = cy + Math.floor(h/2);
11
+
12
+ // Hidden edges (Back, Bottom, Left side)
13
+ // Back face
14
+ const bX = fX + dx;
15
+ const bY = fY - dy;
16
+ commands.push(shapes.drawLine(fX, fY, bX, bY, hiddenColor)); // Bottom left depth
17
+ commands.push(shapes.drawLine(bX, bY, bX + w, bY, hiddenColor)); // Back bottom
18
+ commands.push(shapes.drawLine(bX, bY, bX, bY - h, hiddenColor)); // Back left vertical
19
+
20
+ // Visible edges (Front, Top, Right side)
21
+ // Front face
22
+ commands.push(shapes.drawRectangle(fX, fY - h, w, h, visibleColor, false));
23
+ // Top face
24
+ commands.push(shapes.drawLine(fX, fY - h, bX, bY - h, visibleColor));
25
+ commands.push(shapes.drawLine(fX + w, fY - h, bX + w, bY - h, visibleColor));
26
+ commands.push(shapes.drawLine(bX, bY - h, bX + w, bY - h, visibleColor));
27
+ // Right side depth
28
+ commands.push(shapes.drawLine(fX + w, fY, bX + w, bY, visibleColor));
29
+ // Right back vertical
30
+ commands.push(shapes.drawLine(bX + w, bY, bX + w, bY - h, visibleColor));
31
+
32
+ return commands;
33
+ }
34
+
35
+ function drawXRayHead(cx, cy, r, visibleColor, hiddenColor) {
36
+ const commands = [];
37
+
38
+ // Hidden/Structural elements (Cranium sphere behind the face)
39
+ commands.push(shapes.drawCircle(cx, cy, r, hiddenColor, false));
40
+ // Cranium cross-contours (hidden structure)
41
+ commands.push(shapes.drawEllipse(cx, cy, r, Math.floor(r*0.3), hiddenColor, false)); // Equator
42
+ commands.push(shapes.drawEllipse(cx, cy, Math.floor(r*0.3), r, hiddenColor, false)); // Prime meridian
43
+
44
+ // Visible features (Face and Jaw)
45
+ const jawY = cy + Math.floor(r * 1.4);
46
+ const jawW = Math.floor(r * 0.6);
47
+ // Cheek lines dropping down
48
+ commands.push(shapes.drawLine(cx - r, cy, cx - jawW, jawY, visibleColor));
49
+ commands.push(shapes.drawLine(cx + r, cy, cx + jawW, jawY, visibleColor));
50
+ // Jaw line
51
+ commands.push(shapes.drawEllipse(cx, jawY, jawW, Math.floor(jawW * 0.4), visibleColor, false));
52
+ // Center facial axis
53
+ commands.push(shapes.drawLine(cx, cy - r, cx, jawY, visibleColor));
54
+
55
+ return commands;
56
+ }
57
+
58
+ function drawXRayCup(cx, cy, r, h, visibleColor, hiddenColor) {
59
+ const commands = [];
60
+ const topY = cy - Math.floor(h/2);
61
+ const botY = cy + Math.floor(h/2);
62
+ const ry = Math.floor(r * 0.3);
63
+
64
+ // Hidden elements (Inner bottom surface, back curve of outer base)
65
+ // Back curve of the outer base (hidden from viewer)
66
+ commands.push(shapes.drawEllipse(cx, botY, r, ry, hiddenColor, false));
67
+
68
+ // Inner bottom surface (showing the depth/thickness of the cup bottom)
69
+ const innerBotY = botY - Math.floor(h * 0.1);
70
+ const innerR = Math.floor(r * 0.85);
71
+ const innerRy = Math.floor(ry * 0.85);
72
+ commands.push(shapes.drawEllipse(cx, innerBotY, innerR, innerRy, hiddenColor, false));
73
+
74
+ // Visible elements (Outer walls, front curve of base, rim, inner wall)
75
+ // Walls
76
+ commands.push(shapes.drawLine(cx - r, topY, cx - r, botY, visibleColor));
77
+ commands.push(shapes.drawLine(cx + r, topY, cx + r, botY, visibleColor));
78
+ // Rim / Lip
79
+ commands.push(shapes.drawEllipse(cx, topY, r, ry, visibleColor, false));
80
+ commands.push(shapes.drawEllipse(cx, topY, innerR, innerRy, visibleColor, false));
81
+
82
+ // To draw just the front curve of the base in visible color, we approximate with a polyline
83
+ const frontCurve = [];
84
+ for (let i = 0; i <= 18; i++) {
85
+ const angle = (i / 18) * Math.PI; // 0 to 180 degrees (bottom half)
86
+ frontCurve.push({ x: Math.round(cx + r * Math.cos(angle)), y: Math.round(botY + ry * Math.sin(angle)) });
87
+ }
88
+ commands.push(shapes.drawPolyline(frontCurve, visibleColor));
89
+
90
+ return commands;
91
+ }
92
+
93
+ module.exports = {
94
+ drawXRayBox,
95
+ drawXRayHead,
96
+ drawXRayCup
97
+ };
@@ -0,0 +1,51 @@
1
+ const { z } = require('zod');
2
+ const { registerTool, sendCommand } = require('../../core/server');
3
+ const hiddenLessons = require('./hidden_lessons');
4
+
5
+ const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/).describe('Hex color e.g. #ff0000');
6
+
7
+ /**
8
+ * Register all hidden shapes tools on the MCP server.
9
+ * @param {McpServer} server
10
+ */
11
+ function register(server) {
12
+ registerTool(server, 'art_tree_lesson_hidden_box',
13
+ 'Lesson: Hidden - Understand all 6 faces of a box (X-Ray vision)',
14
+ { canvasSize: z.number().int().min(16).max(64).default(32), visibleColor: hexColor.default('#2196f3'), hiddenColor: hexColor.default('#e0e0e0') },
15
+ async (p) => {
16
+ const commands = hiddenLessons.lessonHiddenBox(p.canvasSize, p.visibleColor, p.hiddenColor);
17
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
18
+ return { content: [{ type: 'text', text: `✓ Hidden Box lesson completed` }] };
19
+ });
20
+
21
+ registerTool(server, 'art_tree_lesson_hidden_head',
22
+ 'Lesson: Hidden - Understand the cranium sphere behind the face (X-Ray vision)',
23
+ { canvasSize: z.number().int().min(16).max(64).default(32), visibleColor: hexColor.default('#ff5722'), hiddenColor: hexColor.default('#e0e0e0') },
24
+ async (p) => {
25
+ const commands = hiddenLessons.lessonHiddenHead(p.canvasSize, p.visibleColor, p.hiddenColor);
26
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
27
+ return { content: [{ type: 'text', text: `✓ Hidden Head lesson completed` }] };
28
+ });
29
+
30
+ registerTool(server, 'art_tree_lesson_hidden_cup',
31
+ 'Lesson: Hidden - Understand the inner depth of a cup (X-Ray vision)',
32
+ { canvasSize: z.number().int().min(16).max(64).default(32), visibleColor: hexColor.default('#4caf50'), hiddenColor: hexColor.default('#e0e0e0') },
33
+ async (p) => {
34
+ const commands = hiddenLessons.lessonHiddenCup(p.canvasSize, p.visibleColor, p.hiddenColor);
35
+ await Promise.all(commands.map(cmd => sendCommand(cmd)));
36
+ return { content: [{ type: 'text', text: `✓ Hidden Cup lesson completed` }] };
37
+ });
38
+
39
+ registerTool(server, 'art_tree_lesson_hidden_catalog',
40
+ 'Get the catalog of all available Hidden Shapes lessons',
41
+ {},
42
+ () => {
43
+ const catalog = hiddenLessons.getHiddenLessonCatalog();
44
+ const text = catalog.map(l =>
45
+ ` • ${l.id}: ${l.name} — ${l.description}`
46
+ ).join('\n');
47
+ return { content: [{ type: 'text', text: `📚 Hidden Shapes Lessons:\n\n${text}` }] };
48
+ });
49
+ }
50
+
51
+ module.exports = { register };
@@ -10,6 +10,22 @@
10
10
  const shapes = require('./shapes');
11
11
  const lessons = require('./lessons');
12
12
  const tools = require('./tools');
13
+ const advTools = require('./advanced_tools');
14
+ const curveTools = require('./curve_tools');
15
+ const ellipseTools = require('./ellipse_tools');
16
+ const tools3d = require('./3d_tools');
17
+ const structureTools = require('./structure_tools');
18
+ const csTools = require('./cross_section_tools');
19
+ const transformTools = require('./transform_tools');
20
+ const surfaceTools = require('./surface_tools');
21
+ const perspTools = require('./perspective_tools');
22
+ const hiddenTools = require('./hidden_tools');
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');
13
29
 
14
30
  /**
15
31
  * Register all Art Tree MCP tools on the server
@@ -17,6 +33,22 @@ const tools = require('./tools');
17
33
  */
18
34
  function registerAll(server) {
19
35
  tools.register(server);
36
+ advTools.register(server);
37
+ curveTools.register(server);
38
+ ellipseTools.register(server);
39
+ tools3d.register(server);
40
+ structureTools.register(server);
41
+ csTools.register(server);
42
+ transformTools.register(server);
43
+ surfaceTools.register(server);
44
+ perspTools.register(server);
45
+ hiddenTools.register(server);
46
+ lightTools.register(server);
47
+ materialTools.register(server);
48
+ analysisTools.register(server);
49
+ layerTools.register(server);
50
+ skyTools.register(server);
51
+ vocabTools.register(server);
20
52
  }
21
53
 
22
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 };