feat(geom10 r4 viz): переделаны все 6 SVG-рисунков — больше деталей, цветовая кодировка

§11 ПДСК:
- Расширил сцену до 460×340, осей до 3.4 единиц
- Добавил тики 1/2/3 на каждой оси с цветными цифрами
- Точка M(2;1;3) показана с реальными координатами + пунктирные проекции на плоскость Oxy, оси Ox/Oy, ось Oz
- Маркер на проекции M в плоскости Oxy

§11 Расстояние:
- A(0;0;0), B(2;2;2) — простые координаты
- Прямоугольный параллелепипед-подсказка с цветными рёбрами:
  Δx=2 красное, Δy=2 зелёное, Δz=2 синее (с подписями)
- Бледные пунктирные рёбра остальной части коробки
- Жирная фиолетовая главная диагональ AB
- Маркер прямого угла в углу — иллюстрирует 3D-Пифагор

§12 Сложение:
- Параллелограмм-подсказка (стрелка b из O + пунктир B→C)
- Треугольник: a красный из O, b зелёный из конца a
- Сумма a+b — толстая фиолетовая диагональ
- Подпись 'правило треугольника'

§12 Базис:
- Толстые i (красный), j (зелёный), k (синий) — 3.4 ширина
- Вектор a = 2i + 1.5j + 1.5k показан как ломаная-разложение:
  2i (бледно-розовый) → 1.5j (бледно-зелёный) → 1.5k (бледно-синий)
- Итог — толстый фиолетовый с подписью разложения
- Цифры коэффициентов на каждом сегменте

§13 Скалярное произведение:
- Векторы a, b в плоскости z=0 (без лишней глубины)
- Линия проекции (b → точка проекции на a) — серый пунктир
- Отрезок |b|·cos φ — толстый оранжевый вдоль a (геометрический смысл!)
- Маркер прямого угла на проекции
- Угол φ амбер
- Подпись '|b|·cos φ' над отрезком

§14 Куб в координатах:
- Подсветка цветных осей (не серых) + тики '1'
- Координаты всех 8 вершин показаны как (x;y;z) рядом с буквами
- Главная диагональ AC₁ — толстый фиолетовый пунктир с подписью '|AC₁|=√3'
- Сцена расширена до 500×360
This commit is contained in:
Maxim Dolgolyov
2026-05-29 16:27:47 +03:00
parent 7acc606cc2
commit b0c024be76
+166 -55
View File
@@ -916,88 +916,199 @@ function runQuizInput(id, items, xpReward){
}
/* ===== Stereo3D vizes ===== */
/* === §11: ПДСК с тикaми и точкой M(2;1;3) === */
function buildCoordSystem(){
const S = window.STEREO3D; if(!S) return;
const sc = new S.Scene(400,300,{view:'CABINET',scale:50});
sc.addArrow([0,0,0],[2.8,0,0],{color:'#dc2626',width:2.2,label:'x'});
sc.addArrow([0,0,0],[0,2.8,0],{color:'#16a34a',width:2.2,label:'y'});
sc.addArrow([0,0,0],[0,0,2.6],{color:'#1e3a8a',width:2.2,label:'z'});
sc.addVertex([0,0,0],'O',{dx:-18,dy:16,color:'#0b1d33'});
const M = [1.4,1.8,2.0];
sc.addVertex(M,'M(2;3;4)',{dx:10,dy:-8,color:'#7c3aed',fontSize:13});
sc.addEdge(M,[M[0],M[1],0],{stroke:'#94a3b8',width:1,dash:'3 3'});
sc.addEdge([M[0],M[1],0],[M[0],0,0],{stroke:'#94a3b8',width:1,dash:'3 3'});
sc.addEdge([M[0],M[1],0],[0,M[1],0],{stroke:'#94a3b8',width:1,dash:'3 3'});
const sc = new S.Scene(460,340,{view:'CABINET',scale:48});
/* Оси с длинными стрелками */
sc.addArrow([0,0,0],[3.4,0,0],{color:'#dc2626',width:2.6});
sc.addArrow([0,0,0],[0,3.4,0],{color:'#16a34a',width:2.6});
sc.addArrow([0,0,0],[0,0,3.4],{color:'#1e3a8a',width:2.6});
sc.addLabel('x',[3.4,0,0],{dx:14,dy:8,color:'#dc2626',fontSize:16,anchor:'start'});
sc.addLabel('y',[0,3.4,0],{dx:14,dy:8,color:'#16a34a',fontSize:16,anchor:'start'});
sc.addLabel('z',[0,0,3.4],{dx:-10,dy:-4,color:'#1e3a8a',fontSize:16,anchor:'end'});
/* Тики 1, 2, 3 на каждой оси (короткие штрихи + цифры) */
for(let i = 1; i <= 3; i++){
sc.addEdge([i,-0.08,0],[i,0.08,0],{stroke:'#dc2626',width:1.6});
sc.addLabel(String(i),[i,0,0],{dx:-3,dy:18,color:'#dc2626',fontSize:11,anchor:'middle'});
sc.addEdge([-0.08,i,0],[0.08,i,0],{stroke:'#16a34a',width:1.6});
sc.addLabel(String(i),[0,i,0],{dx:8,dy:16,color:'#16a34a',fontSize:11,anchor:'start'});
sc.addEdge([-0.1,0,i],[0.1,0,i],{stroke:'#1e3a8a',width:1.6});
sc.addLabel(String(i),[0,0,i],{dx:-12,dy:4,color:'#1e3a8a',fontSize:11,anchor:'end'});
}
sc.addVertex([0,0,0],'O',{dx:-20,dy:18,color:'#0b1d33',fontSize:14});
/* Точка M(2;1;3) — числа совпадают с реальными координатами */
const M = [2,1,3];
/* Пунктиры из M к плоскости Oxy (вертикально вниз), затем к осям */
sc.addEdge(M,[2,1,0],{stroke:'#a78bfa',width:1.4,dash:'4 3'});
sc.addEdge([2,1,0],[2,0,0],{stroke:'#a78bfa',width:1.4,dash:'4 3'});
sc.addEdge([2,1,0],[0,1,0],{stroke:'#a78bfa',width:1.4,dash:'4 3'});
sc.addEdge(M,[0,0,3],{stroke:'#a78bfa',width:1.4,dash:'4 3'});
/* Маленькие маркеры на проекциях */
sc.addVertex([2,1,0],'',{r:2.5,fill:'#a78bfa'});
/* Точка M жирная */
sc.addVertex(M,'M (2; 1; 3)',{dx:12,dy:-8,color:'#7c3aed',fontSize:15,r:5,fill:'#7c3aed'});
const el = document.getElementById('viz11-coords'); if(el) el.innerHTML = sc.render();
}
/* === §11: Расстояние между точками (3D-Пифагор) === */
function buildDistanceFormula(){
const S = window.STEREO3D; if(!S) return;
const sc = new S.Scene(400,280,{view:'CABINET',scale:50});
sc.addArrow([0,0,0],[2.4,0,0],{color:'#94a3b8',width:1.4});
sc.addArrow([0,0,0],[0,2.0,0],{color:'#94a3b8',width:1.4});
sc.addArrow([0,0,0],[0,0,1.6],{color:'#94a3b8',width:1.4});
const B = [1.8,1.4,1.2];
sc.addEdge([0,0,0],[1.8,0,0],{stroke:'#dc2626',width:2});
sc.addEdge([1.8,0,0],[1.8,1.4,0],{stroke:'#16a34a',width:2});
sc.addEdge([1.8,1.4,0],B,{stroke:'#1e3a8a',width:2});
sc.addEdge([0,0,0],B,{stroke:'#7c3aed',width:3});
sc.addVertex([0,0,0],'A',{dx:-14,dy:14,color:'#0b1d33'});
sc.addVertex(B,'B',{dx:10,dy:-4,color:'#7c3aed'});
const sc = new S.Scene(460,320,{view:'CABINET',scale:50});
/* Серые оси для контекста */
sc.addArrow([0,0,0],[3.0,0,0],{color:'#cbd5e1',width:1.4});
sc.addArrow([0,0,0],[0,3.0,0],{color:'#cbd5e1',width:1.4});
sc.addArrow([0,0,0],[0,0,2.6],{color:'#cbd5e1',width:1.4});
sc.addLabel('x',[3.0,0,0],{dx:10,dy:8,color:'#94a3b8',fontSize:12,anchor:'start'});
sc.addLabel('y',[0,3.0,0],{dx:10,dy:8,color:'#94a3b8',fontSize:12,anchor:'start'});
sc.addLabel('z',[0,0,2.6],{dx:-8,dy:-2,color:'#94a3b8',fontSize:12,anchor:'end'});
/* A(0;0;0), B(2;2;2) */
const A = [0,0,0];
const B = [2,2,2];
/* Параллелепипед-подсказка: рёбра вдоль осей с цветовой кодировкой */
sc.addEdge(A,[2,0,0],{stroke:'#dc2626',width:2.8});
sc.addEdge([2,0,0],[2,2,0],{stroke:'#16a34a',width:2.8});
sc.addEdge([2,2,0],B,{stroke:'#1e3a8a',width:2.8});
/* Бледные грани куба-подсказки */
sc.addEdge(A,[0,2,0],{stroke:'#94a3b8',width:1,dash:'3 3'});
sc.addEdge([0,2,0],[2,2,0],{stroke:'#94a3b8',width:1,dash:'3 3'});
sc.addEdge(A,[0,0,2],{stroke:'#94a3b8',width:1,dash:'3 3'});
sc.addEdge([0,0,2],B,{stroke:'#94a3b8',width:1,dash:'3 3'});
sc.addEdge([2,0,0],[2,0,2],{stroke:'#94a3b8',width:1,dash:'3 3'});
sc.addEdge([2,0,2],B,{stroke:'#94a3b8',width:1,dash:'3 3'});
/* Главная диагональ */
sc.addEdge(A,B,{stroke:'#7c3aed',width:4});
/* Вершины */
sc.addVertex(A,'A',{dx:-18,dy:16,color:'#0b1d33',fontSize:15,r:5,fill:'#0b1d33'});
sc.addVertex(B,'B',{dx:12,dy:-4,color:'#7c3aed',fontSize:15,r:5,fill:'#7c3aed'});
/* Подписи Δx, Δy, Δz на цветных рёбрах */
sc.addLabel('Δx=2',[1,0,0],{dx:0,dy:18,color:'#dc2626',fontSize:13,anchor:'middle'});
sc.addLabel('Δy=2',[2,1,0],{dx:14,dy:6,color:'#16a34a',fontSize:13,anchor:'start'});
sc.addLabel('Δz=2',[2,2,1],{dx:14,dy:0,color:'#1e3a8a',fontSize:13,anchor:'start'});
/* Маркер прямого угла в B (показывая 3D-Пифагор) */
sc.addRightAngleMark([2,2,0],[1.6,2,0],[2,2,0.4],{color:'#d97706',width:1.6,size:0.20});
const el = document.getElementById('viz11-dist'); if(el) el.innerHTML = sc.render();
}
/* === §12: Сложение векторов — параллелограмм + треугольник === */
function buildVectorAdd(){
const S = window.STEREO3D; if(!S) return;
const sc = new S.Scene(400,240,{view:'CABINET',scale:50});
const O = [0,0,0]; const A = [1.8,0.3,0]; const B = [0.6,1.4,0]; const C = [A[0]+B[0],A[1]+B[1],A[2]+B[2]];
sc.addEdge(O,A,{stroke:'#1e3a8a',width:1.2,dash:'4 3'});
sc.addEdge(O,B,{stroke:'#94a3b8',width:1.2,dash:'4 3'});
sc.addEdge(A,C,{stroke:'#94a3b8',width:1.2,dash:'4 3'});
sc.addEdge(B,C,{stroke:'#1e3a8a',width:1.2,dash:'4 3'});
sc.addArrow(O,A,{color:'#dc2626',width:2.6,label:'a',lx:0,ly:18});
sc.addArrow(A,C,{color:'#16a34a',width:2.6,label:'b',lx:14,ly:-4});
sc.addArrow(O,C,{color:'#7c3aed',width:2.8,label:'a+b',lx:-30,ly:0});
sc.addVertex(O,'O',{dx:-14,dy:14,color:'#0b1d33'});
const sc = new S.Scene(460,260,{view:'CABINET',scale:60});
const O = [0,0,0];
const A = [1.8,0.2,0]; /* конец a */
const B = [0.5,1.5,0]; /* конец b (из O) */
const C = [A[0]+B[0],A[1]+B[1],A[2]+B[2]]; /* конец a+b */
/* Параллелограмм-подсказка: вторая копия b из O и a' из B */
sc.addArrow(O,B,{color:'#16a34a',width:2,label:'',lx:0,ly:0});
sc.addEdge(B,C,{stroke:'#94a3b8',width:1.4,dash:'4 3'});
/* Треугольник: a из O, b' из конца a */
sc.addArrow(O,A,{color:'#dc2626',width:3.2,label:'a',lx:-4,ly:20});
sc.addArrow(A,C,{color:'#16a34a',width:3.2,label:'b',lx:14,ly:0});
/* Сумма a+b — фиолетовая, толстая, диагональ */
sc.addArrow(O,C,{color:'#7c3aed',width:4,label:'a + b',lx:-44,ly:8});
sc.addVertex(O,'O',{dx:-18,dy:18,color:'#0b1d33',fontSize:14,r:4,fill:'#0b1d33'});
/* Подпись правила */
sc.addLabel('правило треугольника',[1.5,0.85,0],{dx:0,dy:30,color:'#475569',fontSize:11,anchor:'middle'});
const el = document.getElementById('viz12-add'); if(el) el.innerHTML = sc.render();
}
/* === §12: Базис i,j,k и разложение вектора === */
function buildVectorBasis(){
const S = window.STEREO3D; if(!S) return;
const sc = new S.Scene(400,280,{view:'CABINET',scale:55});
sc.addArrow([0,0,0],[2.4,0,0],{color:'#94a3b8',width:1.2});
sc.addArrow([0,0,0],[0,2.4,0],{color:'#94a3b8',width:1.2});
sc.addArrow([0,0,0],[0,0,2.2],{color:'#94a3b8',width:1.2});
sc.addArrow([0,0,0],[1,0,0],{color:'#dc2626',width:2.8,label:'i',lx:-2,ly:18});
sc.addArrow([0,0,0],[0,1,0],{color:'#16a34a',width:2.8,label:'j',lx:14,ly:8});
sc.addArrow([0,0,0],[0,0,1],{color:'#1e3a8a',width:2.8,label:'k',lx:-14,ly:-2});
const a = [1.8,1.2,1.4];
sc.addArrow([0,0,0],a,{color:'#7c3aed',width:3,label:'a',lx:8,ly:-4});
sc.addEdge(a,[a[0],a[1],0],{stroke:'#94a3b8',width:1,dash:'3 3'});
const sc = new S.Scene(460,320,{view:'CABINET',scale:60});
/* Бледные оси */
sc.addArrow([0,0,0],[2.8,0,0],{color:'#cbd5e1',width:1.2});
sc.addArrow([0,0,0],[0,2.8,0],{color:'#cbd5e1',width:1.2});
sc.addArrow([0,0,0],[0,0,2.8],{color:'#cbd5e1',width:1.2});
sc.addLabel('x',[2.8,0,0],{dx:10,dy:8,color:'#94a3b8',fontSize:12,anchor:'start'});
sc.addLabel('y',[0,2.8,0],{dx:10,dy:8,color:'#94a3b8',fontSize:12,anchor:'start'});
sc.addLabel('z',[0,0,2.8],{dx:-8,dy:-2,color:'#94a3b8',fontSize:12,anchor:'end'});
/* Базисные единичные векторы — яркие */
sc.addArrow([0,0,0],[1,0,0],{color:'#dc2626',width:3.4,label:'i',lx:-4,ly:22});
sc.addArrow([0,0,0],[0,1,0],{color:'#16a34a',width:3.4,label:'j',lx:18,ly:10});
sc.addArrow([0,0,0],[0,0,1],{color:'#1e3a8a',width:3.4,label:'k',lx:-14,ly:-2});
/* Произвольный вектор a = 2i + 1.5j + 1.5k */
const a = [2,1.5,1.5];
/* Покажем разложение через ступенчатую ломаную (2i, потом 1.5j, потом 1.5k) */
sc.addArrow([0,0,0],[2,0,0],{color:'#fda4af',width:2.2,dash:'5 3'}); /* 2i */
sc.addArrow([2,0,0],[2,1.5,0],{color:'#86efac',width:2.2,dash:'5 3'}); /* +1.5j */
sc.addArrow([2,1.5,0],a,{color:'#bfdbfe',width:2.2,dash:'5 3'}); /* +1.5k */
/* Итоговый вектор a — толстый фиолетовый */
sc.addArrow([0,0,0],a,{color:'#7c3aed',width:3.6,label:'a = 2i + 1.5j + 1.5k',lx:14,ly:-8});
/* Метки координат */
sc.addLabel('2',[1,0,0],{dx:0,dy:18,color:'#dc2626',fontSize:11,anchor:'middle'});
sc.addLabel('1.5',[2,0.75,0],{dx:14,dy:8,color:'#16a34a',fontSize:11,anchor:'start'});
sc.addLabel('1.5',[2,1.5,0.75],{dx:14,dy:0,color:'#1e3a8a',fontSize:11,anchor:'start'});
const el = document.getElementById('viz12-basis'); if(el) el.innerHTML = sc.render();
}
/* === §13: Скалярное произведение + проекция b на a === */
function buildDotProduct(){
const S = window.STEREO3D; if(!S) return;
const sc = new S.Scene(400,240,{view:'CABINET',scale:55});
const O = [0,0,0]; const a = [1.8,0.5,0]; const b = [1.2,1.5,0.4];
sc.addArrow(O,a,{color:'#dc2626',width:3,label:'a',lx:4,ly:18});
sc.addArrow(O,b,{color:'#1e3a8a',width:3,label:'b',lx:-22,ly:-2});
sc.addAngleMark(O,a,b,{r:0.5,color:'#d97706',width:1.8,label:'φ'});
sc.addVertex(O,'O',{dx:-14,dy:14,color:'#0b1d33'});
const sc = new S.Scene(460,280,{view:'CABINET',scale:65});
const O = [0,0,0];
/* В плоскости z=0 для наглядности */
const a = [2.4,0.3,0];
const b = [1.3,1.7,0];
/* Векторы */
sc.addArrow(O,a,{color:'#dc2626',width:3.4,label:'a',lx:-4,ly:22});
sc.addArrow(O,b,{color:'#1e3a8a',width:3.4,label:'b',lx:-22,ly:-2});
/* Проекция b на a: проекция конца b на прямую a */
const aLen2 = a[0]*a[0]+a[1]*a[1]+a[2]*a[2];
const dot = a[0]*b[0]+a[1]*b[1]+a[2]*b[2];
const t = dot/aLen2;
const proj = [a[0]*t,a[1]*t,a[2]*t];
/* Линия проекции (b -> proj) */
sc.addEdge(b,proj,{stroke:'#94a3b8',width:1.4,dash:'3 3'});
/* Жирный отрезок |b|cos φ вдоль a */
sc.addEdge(O,proj,{stroke:'#d97706',width:4});
/* Маркер прямого угла на проекции (между линией проекции и направлением a) */
const aDir = [a[0]-proj[0]||-a[0], a[1]-proj[1]||-a[1], 0];
const perpDir = [b[0]-proj[0], b[1]-proj[1], 0];
sc.addRightAngleMark(proj,[proj[0]-0.18,proj[1]+0,0],[proj[0]+perpDir[0]*0.12,proj[1]+perpDir[1]*0.12,0],{color:'#7c3aed',width:1.4,size:0.16});
/* Метка проекции */
sc.addLabel('|b|·cos φ',[proj[0]/2,proj[1]/2,0],{dx:0,dy:22,color:'#d97706',fontSize:12,anchor:'middle'});
/* Угол φ */
sc.addAngleMark(O,a,b,{r:0.5,color:'#f59e0b',width:1.8,label:'φ'});
sc.addVertex(O,'O',{dx:-16,dy:18,color:'#0b1d33',fontSize:13,r:4,fill:'#0b1d33'});
const el = document.getElementById('viz13-dot'); if(el) el.innerHTML = sc.render();
}
/* === §14: Куб ABCDA1B1C1D1 в ПДСК с координатами всех 8 вершин + диагональ === */
function buildCubeInCoords(){
const S = window.STEREO3D; if(!S) return;
const sc = new S.Scene(420,320,{view:'CABINET',scale:70,center:[100,220]});
sc.addArrow([0,0,0],[1.6,0,0],{color:'#94a3b8',width:1.3});
sc.addArrow([0,0,0],[0,1.6,0],{color:'#94a3b8',width:1.3});
sc.addArrow([0,0,0],[0,0,1.6],{color:'#94a3b8',width:1.3});
sc.addLabel('x',[1.6,0,0],{dx:8,dy:8,color:'#475569',fontSize:12,anchor:'start'});
sc.addLabel('y',[0,1.6,0],{dx:10,dy:8,color:'#475569',fontSize:12,anchor:'start'});
sc.addLabel('z',[0,0,1.6],{dx:-6,dy:-2,color:'#475569',fontSize:12,anchor:'end'});
sc.addBox({center:[0.5,0.5,0.5],size:[1,1,1],labels:true,color:'#fef3c7',opacity:0.25});
const sc = new S.Scene(500,360,{view:'CABINET',scale:75,center:[120,250]});
/* Оси с подписями */
sc.addArrow([0,0,0],[1.7,0,0],{color:'#dc2626',width:1.8});
sc.addArrow([0,0,0],[0,1.7,0],{color:'#16a34a',width:1.8});
sc.addArrow([0,0,0],[0,0,1.7],{color:'#1e3a8a',width:1.8});
sc.addLabel('x',[1.7,0,0],{dx:10,dy:10,color:'#dc2626',fontSize:13,anchor:'start'});
sc.addLabel('y',[0,1.7,0],{dx:12,dy:8,color:'#16a34a',fontSize:13,anchor:'start'});
sc.addLabel('z',[0,0,1.7],{dx:-8,dy:-2,color:'#1e3a8a',fontSize:13,anchor:'end'});
/* Тик "1" на каждой оси */
sc.addEdge([1,-0.06,0],[1,0.06,0],{stroke:'#dc2626',width:1.4});
sc.addLabel('1',[1,0,0],{dx:-3,dy:18,color:'#dc2626',fontSize:11,anchor:'middle'});
sc.addEdge([-0.06,1,0],[0.06,1,0],{stroke:'#16a34a',width:1.4});
sc.addLabel('1',[0,1,0],{dx:8,dy:14,color:'#16a34a',fontSize:11,anchor:'start'});
sc.addEdge([-0.08,0,1],[0.08,0,1],{stroke:'#1e3a8a',width:1.4});
sc.addLabel('1',[0,0,1],{dx:-10,dy:4,color:'#1e3a8a',fontSize:11,anchor:'end'});
/* Куб с ребром 1 */
sc.addBox({center:[0.5,0.5,0.5],size:[1,1,1],labels:true,color:'#fef3c7',opacity:0.30});
/* Главная диагональ AC1 — выделить */
sc.addEdge([0,0,0],[1,1,1],{stroke:'#7c3aed',width:3.4,dash:'6 3'});
sc.addLabel('|AC₁| = √3',[0.5,0.5,0.5],{dx:18,dy:-2,color:'#7c3aed',fontSize:13,anchor:'start'});
/* Координаты всех 8 вершин рядом с буквами */
const vc = [
[[0,0,0],'(0;0;0)',-30,28],
[[1,0,0],'(1;0;0)',-2,28],
[[1,1,0],'(1;1;0)',12,18],
[[0,1,0],'(0;1;0)',-32,18],
[[0,0,1],'(0;0;1)',-44,2],
[[1,0,1],'(1;0;1)',-2,-2],
[[1,1,1],'(1;1;1)',14,-12],
[[0,1,1],'(0;1;1)',-44,-12]
];
vc.forEach(v=>{ sc.addLabel(v[1],v[0],{dx:v[2],dy:v[3],color:'#78350f',fontSize:9,anchor:'start'}); });
const el = document.getElementById('viz14-cube'); if(el) el.innerHTML = sc.render();
}