@pixel-normal-edit/mcp 2.0.6 → 2.0.7

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 (39) 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/cross_section_lessons.js +65 -0
  8. package/domains/art-tree/cross_section_shapes.js +84 -0
  9. package/domains/art-tree/cross_section_tools.js +51 -0
  10. package/domains/art-tree/curve_lessons.js +138 -0
  11. package/domains/art-tree/curve_shapes.js +90 -0
  12. package/domains/art-tree/curve_tools.js +51 -0
  13. package/domains/art-tree/ellipse_lessons.js +105 -0
  14. package/domains/art-tree/ellipse_shapes.js +63 -0
  15. package/domains/art-tree/ellipse_tools.js +60 -0
  16. package/domains/art-tree/hidden_lessons.js +55 -0
  17. package/domains/art-tree/hidden_shapes.js +97 -0
  18. package/domains/art-tree/hidden_tools.js +51 -0
  19. package/domains/art-tree/index.js +22 -0
  20. package/domains/art-tree/lessons.js +112 -0
  21. package/domains/art-tree/light_lessons.js +92 -0
  22. package/domains/art-tree/light_shapes.js +136 -0
  23. package/domains/art-tree/light_tools.js +49 -0
  24. package/domains/art-tree/perspective_lessons.js +138 -0
  25. package/domains/art-tree/perspective_shapes.js +96 -0
  26. package/domains/art-tree/perspective_tools.js +60 -0
  27. package/domains/art-tree/shapes.js +15 -0
  28. package/domains/art-tree/structure_lessons.js +102 -0
  29. package/domains/art-tree/structure_shapes.js +74 -0
  30. package/domains/art-tree/structure_tools.js +51 -0
  31. package/domains/art-tree/surface_lessons.js +112 -0
  32. package/domains/art-tree/surface_shapes.js +87 -0
  33. package/domains/art-tree/surface_tools.js +60 -0
  34. package/domains/art-tree/tools.js +54 -0
  35. package/domains/art-tree/transform_lessons_1.js +108 -0
  36. package/domains/art-tree/transform_lessons_2.js +102 -0
  37. package/domains/art-tree/transform_shapes.js +109 -0
  38. package/domains/art-tree/transform_tools.js +104 -0
  39. package/package.json +1 -1
@@ -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: Tỉ lệ (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: Đối xứng & Trục giữa (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: Góc (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: Khoảng cách (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: Quan hệ hình khối (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: '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 và lồng nhau của các hình', 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: Đặc tính - Tỉ lệ hình (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: `✓ Bài học tỉ lệ hình hoàn thành` }] };
19
+ });
20
+
21
+ registerTool(server, 'art_tree_lesson_symmetry',
22
+ 'Lesson: Đặc tính - Đối xứng & Trục (Symmetry and 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: `✓ Bài học đối xứng & trục hoàn thành` }] };
28
+ });
29
+
30
+ registerTool(server, 'art_tree_lesson_angles',
31
+ 'Lesson: Đặc tính - Góc (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: `✓ Bài học góc hoàn thành` }] };
37
+ });
38
+
39
+ registerTool(server, 'art_tree_lesson_distances',
40
+ 'Lesson: Đặc tính - Khoảng cách (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: `✓ Bài học khoảng cách hoàn thành` }] };
46
+ });
47
+
48
+ registerTool(server, 'art_tree_lesson_relationships',
49
+ 'Lesson: Đặc tính - Quan hệ hình (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: `✓ Bài học quan hệ hình hoàn thành` }] };
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: `📚 Danh sách bài học Art Tree (Đặc tính):\n\n${text}` }] };
66
+ });
67
+ }
68
+
69
+ module.exports = { register };
@@ -0,0 +1,65 @@
1
+ const csShapes = require('./cross_section_shapes');
2
+
3
+ /**
4
+ * Lesson: Bottle Sections (Base, Body, Shoulder, Neck, Mouth)
5
+ */
6
+ function lessonBottleSections(canvasSize = 32, color = '#2196f3') {
7
+ const cx = Math.floor(canvasSize / 2);
8
+ const totalH = Math.floor(canvasSize * 0.8);
9
+ const topY = Math.floor(canvasSize * 0.1);
10
+ const bottomY = topY + totalH;
11
+
12
+ const sections = [
13
+ { y: topY, r: Math.floor(canvasSize * 0.08) }, // Mouth (slightly open)
14
+ { y: topY + Math.floor(totalH * 0.1), r: Math.floor(canvasSize * 0.06) }, // Neck (narrow)
15
+ { y: topY + Math.floor(totalH * 0.3), r: Math.floor(canvasSize * 0.2) }, // Shoulder (shrinking to body)
16
+ { y: topY + Math.floor(totalH * 0.6), r: Math.floor(canvasSize * 0.25) }, // Body (wide)
17
+ { y: bottomY, r: Math.floor(canvasSize * 0.25) } // Base (wide)
18
+ ];
19
+
20
+ return csShapes.drawLoftedForm(cx, topY, bottomY, sections, color, true);
21
+ }
22
+
23
+ /**
24
+ * Lesson: Glass Sections (Base, Mid, Mouth)
25
+ */
26
+ function lessonGlassSections(canvasSize = 32, color = '#9c27b0') {
27
+ const cx = Math.floor(canvasSize / 2);
28
+ const totalH = Math.floor(canvasSize * 0.6);
29
+ const topY = Math.floor(canvasSize * 0.2);
30
+ const bottomY = topY + totalH;
31
+
32
+ const sections = [
33
+ { y: topY, r: Math.floor(canvasSize * 0.3) }, // Mouth (wide)
34
+ { y: topY + Math.floor(totalH * 0.5), r: Math.floor(canvasSize * 0.2) }, // Mid body (widening)
35
+ { y: bottomY, r: Math.floor(canvasSize * 0.15) } // Base (small)
36
+ ];
37
+
38
+ return csShapes.drawLoftedForm(cx, topY, bottomY, sections, color, true);
39
+ }
40
+
41
+ /**
42
+ * Lesson: Head Sections (Cranium, Face, Jaw)
43
+ */
44
+ function lessonHeadSections(canvasSize = 32, color = '#ff9800') {
45
+ const cx = Math.floor(canvasSize / 2);
46
+ const cy = Math.floor(canvasSize / 2);
47
+ const height = Math.floor(canvasSize * 0.8);
48
+
49
+ return csShapes.drawHeadStructure(cx, cy, height, color);
50
+ }
51
+
52
+ function getCrossSectionLessonCatalog() {
53
+ return [
54
+ { id: 'cs_bottle', name: 'Bottle Cross-sections', description: 'Base, body, shoulder, neck, mouth', fn: lessonBottleSections },
55
+ { id: 'cs_glass', name: 'Glass Cross-sections', description: 'Base, body, mouth', fn: lessonGlassSections },
56
+ { id: 'cs_head', name: 'Head Cross-sections', description: 'Cranium, face, jaw', fn: lessonHeadSections },
57
+ ];
58
+ }
59
+
60
+ module.exports = {
61
+ lessonBottleSections,
62
+ lessonGlassSections,
63
+ lessonHeadSections,
64
+ getCrossSectionLessonCatalog
65
+ };