@pixel-normal-edit/mcp 2.1.0 → 2.2.0
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.
- package/clear-firebase.js +37 -0
- package/domains/art-tree/README.md +134 -0
- package/domains/art-tree/REORGANIZATION_PLAN.md +168 -0
- package/domains/art-tree/REORGANIZATION_SUMMARY.md +260 -0
- package/domains/art-tree/index.js +58 -58
- package/domains/art-tree/lessons/3d/box-properties.js +38 -0
- package/domains/art-tree/lessons/3d/cone-properties.js +31 -0
- package/domains/art-tree/lessons/3d/cylinder-properties.js +29 -0
- package/domains/art-tree/lessons/3d/sphere-properties.js +25 -0
- package/domains/art-tree/lessons/advanced/angles.js +30 -0
- package/domains/art-tree/lessons/advanced/distances.js +26 -0
- package/domains/art-tree/lessons/advanced/shape-ratios.js +25 -0
- package/domains/art-tree/lessons/advanced/shape-relationships.js +28 -0
- package/domains/art-tree/lessons/advanced/symmetry-and-axis.js +26 -0
- package/domains/art-tree/{analysis_lessons.js → lessons/analysis/object-analysis.js} +5 -16
- package/domains/art-tree/lessons/basic/circles.js +26 -0
- package/domains/art-tree/lessons/basic/composition.js +34 -0
- package/domains/art-tree/lessons/basic/curves.js +31 -0
- package/domains/art-tree/lessons/basic/ellipses.js +26 -0
- package/domains/art-tree/lessons/basic/freehand.js +19 -0
- package/domains/art-tree/lessons/basic/index.js +36 -0
- package/domains/art-tree/lessons/basic/lines.js +38 -0
- package/domains/art-tree/lessons/basic/parallel-and-intersecting.js +22 -0
- package/domains/art-tree/lessons/basic/polygons.js +25 -0
- package/domains/art-tree/lessons/basic/proportions-and-angles.js +20 -0
- package/domains/art-tree/lessons/basic/spiral.js +21 -0
- package/domains/art-tree/lessons/basic/squares.js +28 -0
- package/domains/art-tree/lessons/basic/strokes.js +23 -0
- package/domains/art-tree/lessons/basic/triangles.js +30 -0
- package/domains/art-tree/lessons/cross-sections/bottle-sections.js +26 -0
- package/domains/art-tree/lessons/cross-sections/glass-sections.js +24 -0
- package/domains/art-tree/lessons/cross-sections/head-sections.js +17 -0
- package/domains/art-tree/lessons/curves/curve-properties.js +50 -0
- package/domains/art-tree/lessons/curves/curve-topology.js +45 -0
- package/domains/art-tree/lessons/curves/curve-types.js +44 -0
- package/domains/art-tree/lessons/ellipses/coaxial-ellipses.js +29 -0
- package/domains/art-tree/lessons/ellipses/ellipse-anatomy.js +26 -0
- package/domains/art-tree/lessons/ellipses/ellipse-orientations.js +28 -0
- package/domains/art-tree/lessons/ellipses/ellipse-proportions.js +27 -0
- package/domains/art-tree/lessons/hidden/hidden-box.js +20 -0
- package/domains/art-tree/lessons/hidden/hidden-cup.js +19 -0
- package/domains/art-tree/lessons/hidden/hidden-head.js +18 -0
- package/domains/art-tree/lessons/index.js +42 -0
- package/domains/art-tree/{layer_lessons.js → lessons/layers/layer-step.js} +5 -16
- package/domains/art-tree/lessons/lighting/contact-shadow.js +33 -0
- package/domains/art-tree/lessons/lighting/light-direction.js +30 -0
- package/domains/art-tree/lessons/lighting/light-zones.js +30 -0
- package/domains/art-tree/lessons/materials/material-glass.js +21 -0
- package/domains/art-tree/lessons/materials/material-shiny.js +26 -0
- package/domains/art-tree/lessons/materials/material-texture.js +26 -0
- package/domains/art-tree/lessons/perspective/1point-perspective.js +30 -0
- package/domains/art-tree/lessons/perspective/2point-perspective.js +30 -0
- package/domains/art-tree/lessons/perspective/3point-perspective.js +45 -0
- package/domains/art-tree/lessons/perspective/foreshortening.js +38 -0
- package/domains/art-tree/lessons/sky/sun-shapes.js +25 -0
- package/domains/art-tree/lessons/sky/sunset-colors.js +27 -0
- package/domains/art-tree/lessons/sky/sunset-sky.js +32 -0
- package/domains/art-tree/{structure_lessons.js → lessons/structure/axis-orientation.js} +3 -53
- package/domains/art-tree/lessons/structure/complex-structure.js +21 -0
- package/domains/art-tree/lessons/structure/x-yzaxes.js +28 -0
- package/domains/art-tree/lessons/surfaces/chair-analysis.js +17 -0
- package/domains/art-tree/lessons/surfaces/cup-analysis.js +18 -0
- package/domains/art-tree/lessons/surfaces/edge-types.js +40 -0
- package/domains/art-tree/lessons/surfaces/surface-types.js +42 -0
- package/domains/art-tree/lessons/transforms/bend.js +27 -0
- package/domains/art-tree/{transform_lessons_2.js → lessons/transforms/combine.js} +3 -54
- package/domains/art-tree/lessons/transforms/cut.js +25 -0
- package/domains/art-tree/lessons/transforms/hollow.js +24 -0
- package/domains/art-tree/lessons/transforms/rotate.js +16 -0
- package/domains/art-tree/lessons/transforms/squash.js +31 -0
- package/domains/art-tree/lessons/transforms/stretch.js +31 -0
- package/domains/art-tree/lessons/transforms/taper-swell.js +33 -0
- package/domains/art-tree/{vocab_lessons.js → lessons/vocab/vocab-house.js} +5 -16
- package/domains/art-tree/lib/tool-register.js +104 -0
- package/domains/art-tree/lib/validators.js +78 -0
- package/domains/art-tree/shapes/grid.js +37 -0
- package/domains/art-tree/shapes/polyline.js +26 -0
- package/domains/art-tree/{3d_tools.js → tools/3d.js} +2 -2
- package/domains/art-tree/{advanced_tools.js → tools/advanced.js} +2 -2
- package/domains/art-tree/{analysis_tools.js → tools/analysis.js} +2 -2
- package/domains/art-tree/{tools.js → tools/basic.js} +3 -3
- package/domains/art-tree/{cross_section_tools.js → tools/cross-sections.js} +2 -2
- package/domains/art-tree/{curve_tools.js → tools/curves.js} +2 -2
- package/domains/art-tree/{ellipse_tools.js → tools/ellipses.js} +2 -2
- package/domains/art-tree/{hidden_tools.js → tools/hidden.js} +2 -2
- package/domains/art-tree/tools/index.js +53 -0
- package/domains/art-tree/{layer_tools.js → tools/layers.js} +2 -2
- package/domains/art-tree/{light_tools.js → tools/lighting.js} +2 -2
- package/domains/art-tree/{material_tools.js → tools/materials.js} +2 -2
- package/domains/art-tree/{perspective_tools.js → tools/perspective.js} +2 -2
- package/domains/art-tree/{sky_tools.js → tools/sky.js} +2 -2
- package/domains/art-tree/{structure_tools.js → tools/structure.js} +2 -2
- package/domains/art-tree/{surface_tools.js → tools/surfaces.js} +2 -2
- package/domains/art-tree/{transform_tools.js → tools/transforms.js} +3 -3
- package/domains/art-tree/{vocab_tools.js → tools/vocab.js} +2 -2
- package/package.json +1 -1
- package/rules/prerequisites.js +30 -30
- package/rules/rules.js +1 -1
- package/rules/workflow.js +28 -20
- package/tools/canvas-tools.js +2 -2
- package/tools/drawing-tools.js +23 -12
- package/tools/workspace-tools.js +5 -4
- package/domains/art-tree/3d_lessons.js +0 -114
- package/domains/art-tree/advanced_lessons.js +0 -124
- package/domains/art-tree/cross_section_lessons.js +0 -65
- package/domains/art-tree/curve_lessons.js +0 -138
- package/domains/art-tree/ellipse_lessons.js +0 -105
- package/domains/art-tree/hidden_lessons.js +0 -55
- package/domains/art-tree/lessons.js +0 -305
- package/domains/art-tree/light_lessons.js +0 -92
- package/domains/art-tree/material_lessons.js +0 -71
- package/domains/art-tree/perspective_lessons.js +0 -138
- package/domains/art-tree/sky_lessons_1.js +0 -82
- package/domains/art-tree/surface_lessons.js +0 -112
- package/domains/art-tree/transform_lessons_1.js +0 -108
- /package/domains/art-tree/{3d_shapes.js → shapes/3d.js} +0 -0
- /package/domains/art-tree/{advanced_shapes.js → shapes/advanced.js} +0 -0
- /package/domains/art-tree/{analysis_shapes.js → shapes/analysis.js} +0 -0
- /package/domains/art-tree/{shapes.js → shapes/basic.js} +0 -0
- /package/domains/art-tree/{cross_section_shapes.js → shapes/cross-sections.js} +0 -0
- /package/domains/art-tree/{curve_shapes.js → shapes/curves.js} +0 -0
- /package/domains/art-tree/{ellipse_shapes.js → shapes/ellipses.js} +0 -0
- /package/domains/art-tree/{hidden_shapes.js → shapes/hidden.js} +0 -0
- /package/domains/art-tree/{layer_shapes.js → shapes/layers.js} +0 -0
- /package/domains/art-tree/{light_shapes.js → shapes/lighting.js} +0 -0
- /package/domains/art-tree/{material_shapes.js → shapes/materials.js} +0 -0
- /package/domains/art-tree/{perspective_shapes.js → shapes/perspective.js} +0 -0
- /package/domains/art-tree/{sky_shapes_1.js → shapes/sky.js} +0 -0
- /package/domains/art-tree/{structure_shapes.js → shapes/structure.js} +0 -0
- /package/domains/art-tree/{surface_shapes.js → shapes/surfaces.js} +0 -0
- /package/domains/art-tree/{transform_shapes.js → shapes/transforms.js} +0 -0
- /package/domains/art-tree/{vocab_shapes.js → shapes/vocab.js} +0 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lesson 6: Polygons — Multi-sided shapes.
|
|
8
|
+
* @param {number} canvasSize - Canvas dimension
|
|
9
|
+
* @param {string} color - Primary color
|
|
10
|
+
* @returns {Array<Object>} Array of command payloads
|
|
11
|
+
*/
|
|
12
|
+
function lessonPolygons(canvasSize = 32, color = '#607d8b') {
|
|
13
|
+
const mid = Math.floor(canvasSize / 2);
|
|
14
|
+
const r = Math.floor(canvasSize * 0.35);
|
|
15
|
+
return [
|
|
16
|
+
// Pentagon
|
|
17
|
+
shapes.drawRegularPolygon(mid, mid, 5, r, '#4caf50', false),
|
|
18
|
+
// Hexagon (filled)
|
|
19
|
+
shapes.drawRegularPolygon(mid, mid, 6, Math.floor(r * 0.7), '#ff9800', true),
|
|
20
|
+
// Octagon
|
|
21
|
+
shapes.drawRegularPolygon(mid, mid, 8, Math.floor(r * 0.5), '#9c27b0', false),
|
|
22
|
+
];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = { lessonPolygons };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lesson 9: Proportions and Angles — Practice maintaining proportions and angles.
|
|
8
|
+
*/
|
|
9
|
+
function lessonProportionsAndAngles(canvasSize = 32, color = '#3f51b5') {
|
|
10
|
+
const mid = Math.floor(canvasSize / 2);
|
|
11
|
+
const size1 = Math.floor(canvasSize * 0.4);
|
|
12
|
+
const size2 = Math.floor(size1 / 2);
|
|
13
|
+
return [
|
|
14
|
+
shapes.drawSquare(mid - Math.floor(size1/2), mid - Math.floor(size1/2), size1, color, false),
|
|
15
|
+
shapes.drawSquare(mid - Math.floor(size2/2), mid - Math.floor(size2/2), size2, '#e91e63', false),
|
|
16
|
+
shapes.drawLine(mid, mid, mid + size1, mid - size1, '#009688')
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = { lessonProportionsAndAngles };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lesson 11: Spiral — Drawing spiral curves.
|
|
8
|
+
*/
|
|
9
|
+
function lessonSpiral(canvasSize = 32, color = '#ff9800') {
|
|
10
|
+
const mid = Math.floor(canvasSize / 2);
|
|
11
|
+
const points = [];
|
|
12
|
+
const maxRadius = Math.floor(canvasSize * 0.4);
|
|
13
|
+
const loops = 3;
|
|
14
|
+
for (let angle = 0; angle < Math.PI * 2 * loops; angle += 0.3) {
|
|
15
|
+
const r = (angle / (Math.PI * 2 * loops)) * maxRadius;
|
|
16
|
+
points.push({ x: Math.round(mid + r * Math.cos(angle)), y: Math.round(mid + r * Math.sin(angle)) });
|
|
17
|
+
}
|
|
18
|
+
return shapes.drawPolyline(points, color);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = { lessonSpiral };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lesson 2: Squares & Rectangles — Understanding right angles and proportions.
|
|
8
|
+
* @param {number} canvasSize - Canvas dimension
|
|
9
|
+
* @param {string} color - Primary color
|
|
10
|
+
* @returns {Array<Object>} Array of command payloads
|
|
11
|
+
*/
|
|
12
|
+
function lessonSquares(canvasSize = 32, color = '#1565c0') {
|
|
13
|
+
const margin = Math.floor(canvasSize * 0.1);
|
|
14
|
+
const small = Math.floor(canvasSize * 0.2);
|
|
15
|
+
const large = Math.floor(canvasSize * 0.35);
|
|
16
|
+
return [
|
|
17
|
+
// Small square (outline)
|
|
18
|
+
shapes.drawSquare(margin, margin, small, color, false),
|
|
19
|
+
// Large square (filled)
|
|
20
|
+
shapes.drawSquare(canvasSize - margin - large, margin, large, '#ff7043', true),
|
|
21
|
+
// Rectangle (horizontal)
|
|
22
|
+
shapes.drawRectangle(margin, canvasSize - margin - small, large + small, small, '#4caf50', false),
|
|
23
|
+
// Rectangle (vertical, filled)
|
|
24
|
+
shapes.drawRectangle(canvasSize - margin - small, canvasSize - margin - large, small, large, '#9c27b0', true),
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = { lessonSquares };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lesson 12: Strokes — Long strokes, short strokes, thick and thin lines.
|
|
8
|
+
*/
|
|
9
|
+
function lessonStrokes(canvasSize = 32, color = '#2196f3') {
|
|
10
|
+
const margin = Math.floor(canvasSize * 0.1);
|
|
11
|
+
const y1 = Math.floor(canvasSize * 0.3);
|
|
12
|
+
const y2 = Math.floor(canvasSize * 0.5);
|
|
13
|
+
const y3 = Math.floor(canvasSize * 0.7);
|
|
14
|
+
return [
|
|
15
|
+
shapes.drawLine(margin, y1, canvasSize - margin, y1, color),
|
|
16
|
+
shapes.drawLine(margin, y2, Math.floor(canvasSize / 2), y2, '#4caf50'),
|
|
17
|
+
shapes.drawLine(margin, y3, canvasSize - margin, y3, '#f44336'),
|
|
18
|
+
shapes.drawLine(margin, y3 + 1, canvasSize - margin, y3 + 1, '#f44336'),
|
|
19
|
+
shapes.drawLine(margin, y3 + 2, canvasSize - margin, y3 + 2, '#f44336'),
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = { lessonStrokes };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lesson 5: Triangles — Three-point shapes and stability.
|
|
8
|
+
* @param {number} canvasSize - Canvas dimension
|
|
9
|
+
* @param {string} color - Primary color
|
|
10
|
+
* @returns {Array<Object>} Array of command payloads
|
|
11
|
+
*/
|
|
12
|
+
function lessonTriangles(canvasSize = 32, color = '#ff5722') {
|
|
13
|
+
const mid = Math.floor(canvasSize / 2);
|
|
14
|
+
const size = Math.floor(canvasSize * 0.4);
|
|
15
|
+
const margin = Math.floor(canvasSize * 0.1);
|
|
16
|
+
return [
|
|
17
|
+
// Equilateral triangle (outline)
|
|
18
|
+
shapes.drawEquilateralTriangle(mid, mid, size, color, false),
|
|
19
|
+
// Right triangle (filled)
|
|
20
|
+
shapes.drawTriangle([
|
|
21
|
+
{ x: margin, y: canvasSize - margin },
|
|
22
|
+
{ x: margin, y: margin },
|
|
23
|
+
{ x: Math.floor(canvasSize * 0.35), y: canvasSize - margin },
|
|
24
|
+
], '#3f51b5', true),
|
|
25
|
+
// Inverted triangle
|
|
26
|
+
shapes.drawEquilateralTriangle(mid, mid, Math.floor(size * 0.6), '#009688', true),
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = { lessonTriangles };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from cross_section_lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lesson: Bottle Sections (Base, Body, Shoulder, Neck, Mouth)
|
|
8
|
+
*/
|
|
9
|
+
function lessonBottleSections(canvasSize = 32, color = '#2196f3') {
|
|
10
|
+
const cx = Math.floor(canvasSize / 2);
|
|
11
|
+
const totalH = Math.floor(canvasSize * 0.8);
|
|
12
|
+
const topY = Math.floor(canvasSize * 0.1);
|
|
13
|
+
const bottomY = topY + totalH;
|
|
14
|
+
|
|
15
|
+
const sections = [
|
|
16
|
+
{ y: topY, r: Math.floor(canvasSize * 0.08) }, // Mouth (slightly open)
|
|
17
|
+
{ y: topY + Math.floor(totalH * 0.1), r: Math.floor(canvasSize * 0.06) }, // Neck (narrow)
|
|
18
|
+
{ y: topY + Math.floor(totalH * 0.3), r: Math.floor(canvasSize * 0.2) }, // Shoulder (shrinking to body)
|
|
19
|
+
{ y: topY + Math.floor(totalH * 0.6), r: Math.floor(canvasSize * 0.25) }, // Body (wide)
|
|
20
|
+
{ y: bottomY, r: Math.floor(canvasSize * 0.25) } // Base (wide)
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
return csShapes.drawLoftedForm(cx, topY, bottomY, sections, color, true);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = { lessonBottleSections };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from cross_section_lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lesson: Glass Sections (Base, Mid, Mouth)
|
|
8
|
+
*/
|
|
9
|
+
function lessonGlassSections(canvasSize = 32, color = '#9c27b0') {
|
|
10
|
+
const cx = Math.floor(canvasSize / 2);
|
|
11
|
+
const totalH = Math.floor(canvasSize * 0.6);
|
|
12
|
+
const topY = Math.floor(canvasSize * 0.2);
|
|
13
|
+
const bottomY = topY + totalH;
|
|
14
|
+
|
|
15
|
+
const sections = [
|
|
16
|
+
{ y: topY, r: Math.floor(canvasSize * 0.3) }, // Mouth (wide)
|
|
17
|
+
{ y: topY + Math.floor(totalH * 0.5), r: Math.floor(canvasSize * 0.2) }, // Mid body (widening)
|
|
18
|
+
{ y: bottomY, r: Math.floor(canvasSize * 0.15) } // Base (small)
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
return csShapes.drawLoftedForm(cx, topY, bottomY, sections, color, true);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = { lessonGlassSections };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from cross_section_lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lesson: Head Sections (Cranium, Face, Jaw)
|
|
8
|
+
*/
|
|
9
|
+
function lessonHeadSections(canvasSize = 32, color = '#ff9800') {
|
|
10
|
+
const cx = Math.floor(canvasSize / 2);
|
|
11
|
+
const cy = Math.floor(canvasSize / 2);
|
|
12
|
+
const height = Math.floor(canvasSize * 0.8);
|
|
13
|
+
|
|
14
|
+
return csShapes.drawHeadStructure(cx, cy, height, color);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = { lessonHeadSections };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from curve_lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lesson 3: Curve Properties (Start, Direction, Curvature, Inflection, Transition)
|
|
8
|
+
*/
|
|
9
|
+
function lessonCurveProperties(canvasSize = 32, color = '#607d8b') {
|
|
10
|
+
const margin = Math.floor(canvasSize * 0.1);
|
|
11
|
+
const commands = [];
|
|
12
|
+
|
|
13
|
+
// 1. Start point and Direction (Top Left Curve)
|
|
14
|
+
const p0 = { x: margin, y: margin * 2 };
|
|
15
|
+
const p1 = { x: margin + canvasSize*0.3, y: margin };
|
|
16
|
+
const p2 = { x: margin + canvasSize*0.3, y: margin + canvasSize*0.3 };
|
|
17
|
+
commands.push(...curveShapes.drawQuadraticCurve(p0, p1, p2, color));
|
|
18
|
+
commands.push(curveShapes.drawStartMarker(p0.x, p0.y, '#00ff00')); // Green dot at start
|
|
19
|
+
commands.push(...curveShapes.drawDirectionArrow(p0, p1, p2, null, '#ff0000')); // Red arrow
|
|
20
|
+
|
|
21
|
+
// 2. Curvature comparison (Bottom Left)
|
|
22
|
+
// Strong vs Weak curve with same endpoints
|
|
23
|
+
const bp0 = { x: margin, y: canvasSize - margin };
|
|
24
|
+
const bp2 = { x: margin + canvasSize*0.4, y: canvasSize - margin };
|
|
25
|
+
// Weak curve
|
|
26
|
+
commands.push(...curveShapes.drawQuadraticCurve(bp0, { x: margin + canvasSize*0.2, y: canvasSize - margin - canvasSize*0.1 }, bp2, '#8bc34a'));
|
|
27
|
+
// Strong curve
|
|
28
|
+
commands.push(...curveShapes.drawQuadraticCurve(bp0, { x: margin + canvasSize*0.2, y: canvasSize - margin - canvasSize*0.4 }, bp2, '#388e3c'));
|
|
29
|
+
|
|
30
|
+
// 3. Inflection point (Top Right S Curve)
|
|
31
|
+
const sP0 = { x: canvasSize - margin - canvasSize*0.3, y: margin };
|
|
32
|
+
const sP1 = { x: canvasSize - margin, y: margin };
|
|
33
|
+
const sP2 = { x: canvasSize - margin - canvasSize*0.4, y: margin + canvasSize*0.3 };
|
|
34
|
+
const sP3 = { x: canvasSize - margin, y: margin + canvasSize*0.3 };
|
|
35
|
+
commands.push(...curveShapes.drawCubicCurve(sP0, sP1, sP2, sP3, '#03a9f4'));
|
|
36
|
+
// Inflection point roughly at t=0.5
|
|
37
|
+
const inflection = curveShapes.cubicBezier(sP0, sP1, sP2, sP3, 0.5);
|
|
38
|
+
commands.push(curveShapes.drawInflectionMarker(inflection.x, inflection.y, '#ff9800'));
|
|
39
|
+
|
|
40
|
+
// 4. Transition (Bottom Right - line transitioning to curve)
|
|
41
|
+
const startLine = { x: canvasSize - margin - canvasSize*0.3, y: canvasSize - margin - canvasSize*0.2 };
|
|
42
|
+
const transPt = { x: canvasSize - margin - canvasSize*0.1, y: canvasSize - margin - canvasSize*0.2 };
|
|
43
|
+
commands.push(shapes.drawLine(startLine.x, startLine.y, transPt.x, transPt.y, '#9e9e9e'));
|
|
44
|
+
// Curve starting tangentially
|
|
45
|
+
commands.push(...curveShapes.drawQuadraticCurve(transPt, { x: canvasSize - margin, y: canvasSize - margin - canvasSize*0.2 }, { x: canvasSize - margin, y: canvasSize - margin }, '#795548'));
|
|
46
|
+
|
|
47
|
+
return commands;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = { lessonCurveProperties };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from curve_lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lesson 2: Curve Topology (Closed, Open, Symmetrical, Asymmetrical)
|
|
8
|
+
*/
|
|
9
|
+
function lessonCurveTopology(canvasSize = 32, color = '#9c27b0') {
|
|
10
|
+
const mid = Math.floor(canvasSize / 2);
|
|
11
|
+
const q = Math.floor(canvasSize / 4);
|
|
12
|
+
const commands = [];
|
|
13
|
+
|
|
14
|
+
// Closed Curve (Circle-like using Bezier for demonstration)
|
|
15
|
+
// Since we already have drawCircle, we just draw a circle to represent a closed curve
|
|
16
|
+
commands.push(shapes.drawCircle(q, q, Math.floor(q*0.8), color, false));
|
|
17
|
+
|
|
18
|
+
// Open Curve (Quadratic)
|
|
19
|
+
commands.push(...curveShapes.drawQuadraticCurve(
|
|
20
|
+
{ x: canvasSize - q*1.5, y: q },
|
|
21
|
+
{ x: canvasSize - q*0.5, y: q - q/2 },
|
|
22
|
+
{ x: canvasSize - q*0.5, y: q + q/2 },
|
|
23
|
+
'#3f51b5'
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// Symmetrical Curve (Parabola-like)
|
|
27
|
+
commands.push(...curveShapes.drawQuadraticCurve(
|
|
28
|
+
{ x: q*0.5, y: canvasSize - q*0.5 },
|
|
29
|
+
{ x: q, y: canvasSize - q*1.5 },
|
|
30
|
+
{ x: q*1.5, y: canvasSize - q*0.5 },
|
|
31
|
+
'#00bcd4'
|
|
32
|
+
));
|
|
33
|
+
|
|
34
|
+
// Asymmetrical Curve
|
|
35
|
+
commands.push(...curveShapes.drawQuadraticCurve(
|
|
36
|
+
{ x: canvasSize - q*1.5, y: canvasSize - q*0.5 },
|
|
37
|
+
{ x: canvasSize - q*0.2, y: canvasSize - q*1.8 }, // Pull point is skewed
|
|
38
|
+
{ x: canvasSize - q*0.5, y: canvasSize - q*0.5 },
|
|
39
|
+
'#ff5722'
|
|
40
|
+
));
|
|
41
|
+
|
|
42
|
+
return commands;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = { lessonCurveTopology };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from curve_lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lesson 1: Curve Types (C, S, Convex, Concave)
|
|
8
|
+
*/
|
|
9
|
+
function lessonCurveTypes(canvasSize = 32, color = '#2196f3') {
|
|
10
|
+
const margin = Math.floor(canvasSize * 0.1);
|
|
11
|
+
const commands = [];
|
|
12
|
+
|
|
13
|
+
const w = Math.floor(canvasSize / 2) - margin * 2;
|
|
14
|
+
const h = Math.floor(canvasSize / 2) - margin * 2;
|
|
15
|
+
|
|
16
|
+
// C Curve (Top Left)
|
|
17
|
+
const cP0 = { x: margin + w, y: margin };
|
|
18
|
+
const cP1 = { x: margin - w, y: margin + Math.floor(h/2) }; // Pulls curve left
|
|
19
|
+
const cP2 = { x: margin + w, y: margin + h };
|
|
20
|
+
commands.push(...curveShapes.drawQuadraticCurve(cP0, cP1, cP2, color));
|
|
21
|
+
|
|
22
|
+
// S Curve (Top Right)
|
|
23
|
+
const sP0 = { x: canvasSize - margin, y: margin };
|
|
24
|
+
const sP1 = { x: canvasSize - margin - w*2, y: margin };
|
|
25
|
+
const sP2 = { x: canvasSize - margin + w, y: margin + h };
|
|
26
|
+
const sP3 = { x: canvasSize - margin - w, y: margin + h };
|
|
27
|
+
commands.push(...curveShapes.drawCubicCurve(sP0, sP1, sP2, sP3, '#e91e63'));
|
|
28
|
+
|
|
29
|
+
// Convex (Bottom Left, viewed from bottom)
|
|
30
|
+
const cvxP0 = { x: margin, y: canvasSize - margin };
|
|
31
|
+
const cvxP1 = { x: margin + Math.floor(w/2), y: canvasSize - margin - h };
|
|
32
|
+
const cvxP2 = { x: margin + w, y: canvasSize - margin };
|
|
33
|
+
commands.push(...curveShapes.drawQuadraticCurve(cvxP0, cvxP1, cvxP2, '#4caf50'));
|
|
34
|
+
|
|
35
|
+
// Concave (Bottom Right, viewed from bottom)
|
|
36
|
+
const cnvP0 = { x: canvasSize - margin - w, y: canvasSize - margin - Math.floor(h/2) };
|
|
37
|
+
const cnvP1 = { x: canvasSize - margin - Math.floor(w/2), y: canvasSize - margin + h };
|
|
38
|
+
const cnvP2 = { x: canvasSize - margin, y: canvasSize - margin - Math.floor(h/2) };
|
|
39
|
+
commands.push(...curveShapes.drawQuadraticCurve(cnvP0, cnvP1, cnvP2, '#ff9800'));
|
|
40
|
+
|
|
41
|
+
return commands;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = { lessonCurveTypes };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from ellipse_lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lesson: Coaxial Ellipses (Multiple ellipses on the same axis)
|
|
8
|
+
*/
|
|
9
|
+
function lessonCoaxialEllipses(canvasSize = 32, color = '#795548') {
|
|
10
|
+
const commands = [];
|
|
11
|
+
const cx = Math.floor(canvasSize / 2);
|
|
12
|
+
const rx = Math.floor(canvasSize * 0.3);
|
|
13
|
+
|
|
14
|
+
// Central axis line
|
|
15
|
+
commands.push(shapes.drawLine(cx, Math.floor(canvasSize * 0.1), cx, canvasSize - Math.floor(canvasSize * 0.1), '#bdbdbd'));
|
|
16
|
+
|
|
17
|
+
// Top ellipse (narrower, viewed from steeper angle)
|
|
18
|
+
commands.push(shapes.drawEllipse(cx, Math.floor(canvasSize * 0.2), rx, Math.floor(rx * 0.2), color, false));
|
|
19
|
+
|
|
20
|
+
// Middle ellipse
|
|
21
|
+
commands.push(shapes.drawEllipse(cx, Math.floor(canvasSize * 0.5), rx, Math.floor(rx * 0.3), color, false));
|
|
22
|
+
|
|
23
|
+
// Bottom ellipse (wider, viewed from less steep angle)
|
|
24
|
+
commands.push(shapes.drawEllipse(cx, canvasSize - Math.floor(canvasSize * 0.2), rx, Math.floor(rx * 0.4), color, false));
|
|
25
|
+
|
|
26
|
+
return commands;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = { lessonCoaxialEllipses };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from ellipse_lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lesson: Ellipse Anatomy (Symmetry, Major/Minor axes, Center)
|
|
8
|
+
*/
|
|
9
|
+
function lessonEllipseAnatomy(canvasSize = 32, color = '#00bcd4') {
|
|
10
|
+
const commands = [];
|
|
11
|
+
const cx = Math.floor(canvasSize / 2);
|
|
12
|
+
const cy = Math.floor(canvasSize / 2);
|
|
13
|
+
const rx = Math.floor(canvasSize * 0.35);
|
|
14
|
+
const ry = Math.floor(canvasSize * 0.2);
|
|
15
|
+
const angle = Math.PI / 6; // 30 degrees tilt to show axes clearly
|
|
16
|
+
|
|
17
|
+
// Draw ellipse
|
|
18
|
+
commands.push(...ellipseShapes.drawRotatedEllipse(cx, cy, rx, ry, angle, color));
|
|
19
|
+
|
|
20
|
+
// Draw anatomy (axes and center)
|
|
21
|
+
commands.push(...ellipseShapes.drawEllipseAxes(cx, cy, rx, ry, angle));
|
|
22
|
+
|
|
23
|
+
return commands;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = { lessonEllipseAnatomy };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from ellipse_lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lesson: Ellipse Orientations (Horizontal, Vertical, Tilted)
|
|
8
|
+
*/
|
|
9
|
+
function lessonEllipseOrientations(canvasSize = 32, color = '#2196f3') {
|
|
10
|
+
const commands = [];
|
|
11
|
+
const midY = Math.floor(canvasSize / 2);
|
|
12
|
+
const qX = Math.floor(canvasSize / 4);
|
|
13
|
+
const rLong = Math.floor(canvasSize * 0.2);
|
|
14
|
+
const rShort = Math.floor(canvasSize * 0.1);
|
|
15
|
+
|
|
16
|
+
// Horizontal (Left)
|
|
17
|
+
commands.push(shapes.drawEllipse(qX, midY, rLong, rShort, color, false));
|
|
18
|
+
|
|
19
|
+
// Vertical (Middle)
|
|
20
|
+
commands.push(shapes.drawEllipse(qX * 2, midY, rShort, rLong, '#e91e63', false));
|
|
21
|
+
|
|
22
|
+
// Tilted (Right) - 45 degrees
|
|
23
|
+
commands.push(...ellipseShapes.drawRotatedEllipse(qX * 3, midY, rLong, rShort, Math.PI / 4, '#4caf50'));
|
|
24
|
+
|
|
25
|
+
return commands;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = { lessonEllipseOrientations };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from ellipse_lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lesson: Ellipse Proportions (Wide vs Narrow)
|
|
8
|
+
*/
|
|
9
|
+
function lessonEllipseProportions(canvasSize = 32, color = '#ff9800') {
|
|
10
|
+
const commands = [];
|
|
11
|
+
const midY = Math.floor(canvasSize / 2);
|
|
12
|
+
const qX = Math.floor(canvasSize / 4);
|
|
13
|
+
const rx = Math.floor(canvasSize * 0.2);
|
|
14
|
+
|
|
15
|
+
// Wide Ellipse (Closer to a circle)
|
|
16
|
+
commands.push(shapes.drawEllipse(qX, midY, rx, Math.floor(rx * 0.8), color, false));
|
|
17
|
+
|
|
18
|
+
// Medium Ellipse
|
|
19
|
+
commands.push(shapes.drawEllipse(qX * 2, midY, rx, Math.floor(rx * 0.4), '#9c27b0', false));
|
|
20
|
+
|
|
21
|
+
// Narrow Ellipse (Almost a line)
|
|
22
|
+
commands.push(shapes.drawEllipse(qX * 3, midY, rx, Math.floor(rx * 0.1), '#f44336', false));
|
|
23
|
+
|
|
24
|
+
return commands;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = { lessonEllipseProportions };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from hidden_lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lesson: Hidden Box (6-sided box)
|
|
8
|
+
* You see 3 faces, but you must understand all 6.
|
|
9
|
+
*/
|
|
10
|
+
function lessonHiddenBox(canvasSize = 32, visibleColor = '#2196f3', hiddenColor = '#e0e0e0') {
|
|
11
|
+
const cx = Math.floor(canvasSize / 2);
|
|
12
|
+
const cy = Math.floor(canvasSize / 2);
|
|
13
|
+
const w = Math.floor(canvasSize * 0.4);
|
|
14
|
+
const h = Math.floor(canvasSize * 0.4);
|
|
15
|
+
const d = Math.floor(canvasSize * 0.4);
|
|
16
|
+
|
|
17
|
+
return hiddenShapes.drawXRayBox(cx, cy, w, h, d, visibleColor, hiddenColor);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = { lessonHiddenBox };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from hidden_lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lesson: Hidden Cup (Cup and inner bottom)
|
|
8
|
+
* You don't see the full inside bottom, but must understand how the wall goes down.
|
|
9
|
+
*/
|
|
10
|
+
function lessonHiddenCup(canvasSize = 32, visibleColor = '#4caf50', hiddenColor = '#e0e0e0') {
|
|
11
|
+
const cx = Math.floor(canvasSize / 2);
|
|
12
|
+
const cy = Math.floor(canvasSize / 2);
|
|
13
|
+
const r = Math.floor(canvasSize * 0.25);
|
|
14
|
+
const h = Math.floor(canvasSize * 0.5);
|
|
15
|
+
|
|
16
|
+
return hiddenShapes.drawXRayCup(cx, cy, r, h, visibleColor, hiddenColor);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = { lessonHiddenCup };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from hidden_lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lesson: Hidden Head (Human head and skull)
|
|
8
|
+
* You don't see the whole skull, but must understand the structure behind it.
|
|
9
|
+
*/
|
|
10
|
+
function lessonHiddenHead(canvasSize = 32, visibleColor = '#ff5722', hiddenColor = '#e0e0e0') {
|
|
11
|
+
const cx = Math.floor(canvasSize / 2);
|
|
12
|
+
const cy = Math.floor(canvasSize * 0.4);
|
|
13
|
+
const r = Math.floor(canvasSize * 0.25);
|
|
14
|
+
|
|
15
|
+
return hiddenShapes.drawXRayHead(cx, cy, r, visibleColor, hiddenColor);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = { lessonHiddenHead };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* lessons/index.js — Art Tree: Lessons Index
|
|
4
|
+
*
|
|
5
|
+
* Exports all lesson functions and catalog getters organized by category.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const basic = require('./basic');
|
|
9
|
+
const advanced = require('./advanced');
|
|
10
|
+
const curves = require('./curves');
|
|
11
|
+
const ellipses = require('./ellipses');
|
|
12
|
+
const forms3d = require('./3d');
|
|
13
|
+
const structure = require('./structure');
|
|
14
|
+
const crossSections = require('./cross-sections');
|
|
15
|
+
const transforms = require('./transforms');
|
|
16
|
+
const surfaces = require('./surfaces');
|
|
17
|
+
const perspective = require('./perspective');
|
|
18
|
+
const hidden = require('./hidden');
|
|
19
|
+
const lighting = require('./lighting');
|
|
20
|
+
const materials = require('./materials');
|
|
21
|
+
const analysis = require('./analysis');
|
|
22
|
+
const layers = require('./layers');
|
|
23
|
+
const sky = require('./sky');
|
|
24
|
+
|
|
25
|
+
module.exports = {
|
|
26
|
+
...basic,
|
|
27
|
+
...advanced,
|
|
28
|
+
...curves,
|
|
29
|
+
...ellipses,
|
|
30
|
+
...forms3d,
|
|
31
|
+
...structure,
|
|
32
|
+
...crossSections,
|
|
33
|
+
...transforms,
|
|
34
|
+
...surfaces,
|
|
35
|
+
...perspective,
|
|
36
|
+
...hidden,
|
|
37
|
+
...lighting,
|
|
38
|
+
...materials,
|
|
39
|
+
...analysis,
|
|
40
|
+
...layers,
|
|
41
|
+
...sky,
|
|
42
|
+
};
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from layer_lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
7
|
* Lesson: Layer Structure (Tư duy Layer)
|
|
@@ -15,18 +18,4 @@ function lessonLayerStep(canvasSize = 32, layerIndex = 1) {
|
|
|
15
18
|
return layerShapes.drawLayeredCup(cx, cy, canvasSize, validLayer);
|
|
16
19
|
}
|
|
17
20
|
|
|
18
|
-
|
|
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
|
-
};
|
|
21
|
+
module.exports = { lessonLayerStep };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-extracted from light_lessons.js
|
|
3
|
+
*/
|
|
4
|
+
const shapes = require('../../shapes');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lesson: Contact Shadow (Point of contact shadow)
|
|
8
|
+
* The darkest area where an object touches the ground.
|
|
9
|
+
*/
|
|
10
|
+
function lessonContactShadow(canvasSize = 32) {
|
|
11
|
+
const commands = [];
|
|
12
|
+
const cx = Math.floor(canvasSize / 2);
|
|
13
|
+
const cy = Math.floor(canvasSize / 2);
|
|
14
|
+
const r = Math.floor(canvasSize * 0.3);
|
|
15
|
+
|
|
16
|
+
// Ground line
|
|
17
|
+
commands.push(shapes.drawLine(0, cy + r, canvasSize, cy + r, '#9e9e9e'));
|
|
18
|
+
|
|
19
|
+
// Sphere outline
|
|
20
|
+
commands.push(shapes.drawCircle(cx, cy, r, '#bdbdbd', false));
|
|
21
|
+
|
|
22
|
+
// Cast shadow (faint)
|
|
23
|
+
commands.push(shapes.drawEllipse(cx + Math.floor(r*0.5), cy + r, Math.floor(r*1.2), Math.floor(r*0.2), '#757575', true));
|
|
24
|
+
|
|
25
|
+
// CONTACT SHADOW (Pitch black, right at the touching point)
|
|
26
|
+
// Draw a very small, very dark ellipse directly under the center
|
|
27
|
+
commands.push(shapes.drawEllipse(cx, cy + r, Math.floor(r * 0.4), 2, '#000000', true));
|
|
28
|
+
commands.push(shapes.drawEllipse(cx, cy + r, Math.floor(r * 0.2), 1, '#000000', true));
|
|
29
|
+
|
|
30
|
+
return commands;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = { lessonContactShadow };
|