diff --git a/frontend/textbooks/geometry_8_ch1.html b/frontend/textbooks/geometry_8_ch1.html index 33fa11f..2e3df8c 100644 --- a/frontend/textbooks/geometry_8_ch1.html +++ b/frontend/textbooks/geometry_8_ch1.html @@ -637,7 +637,8 @@ function setupSorter(cfg){ pool.classList.add('dnd-pool'); if(cfg.columnLayout) pool.classList.add('col'); let armed = null; function buildChip(it,isPlaced){ const e=document.createElement('div'); e.className='dnd-chip'+(isPlaced?' placed':''); e.dataset.id=it.id; e.innerHTML=''+it.html+'\xd7'; attach(e,it.id); return e; } - function attach(elm,itId){ let ghost=null,dragging=false,sx=0,sy=0; elm.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return; if(ev.target.classList&&ev.target.classList.contains('dnd-x')){ ev.stopPropagation(); if(placed[itId]){delete placed[itId];render();}else if(armed===itId){armed=null;render();} return; } sx=ev.clientX;sy=ev.clientY; const r=elm.getBoundingClientRect(); const ox=ev.clientX-r.left,oy=ev.clientY-r.top; try{elm.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ const dx=e.clientX-sx,dy=e.clientY-sy; if(!dragging&&Math.hypot(dx,dy)>8){ dragging=true; ghost=elm.cloneNode(true); ghost.classList.remove('armed'); ghost.style.cssText='position:fixed;z-index:9999;pointer-events:none;opacity:.9;transform:rotate(-2.5deg);box-shadow:0 14px 36px rgba(0,0,0,.32);width:'+r.width+'px;left:'+(e.clientX-ox)+'px;top:'+(e.clientY-oy)+'px'; document.body.appendChild(ghost); elm.classList.add('dragging'); } if(dragging&&ghost){ ghost.style.left=(e.clientX-ox)+'px';ghost.style.top=(e.clientY-oy)+'px'; const under=document.elementsFromPoint(e.clientX,e.clientY); scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); const tgt=under.find(n=>n.classList&&(n.classList.contains('drop-box')||n.classList.contains('dnd-pool'))); if(tgt)tgt.classList.add('over'); } } function onUp(e){ elm.removeEventListener('pointermove',onMove);elm.removeEventListener('pointerup',onUp);elm.removeEventListener('pointercancel',onUp);elm.classList.remove('dragging'); if(ghost){ghost.remove();ghost=null;} scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); if(dragging){ const under=document.elementsFromPoint(e.clientX,e.clientY); const box=under.find(n=>n.classList&&n.classList.contains('drop-box')); const pl=under.find(n=>n.classList&&n.classList.contains('dnd-pool')); if(box){const di=box.querySelector('[data-cat]');if(di){placed[itId]=di.dataset.cat;armed=null;render();return;}}else if(pl){delete placed[itId];armed=null;render();return;} }else{ if(placed[itId]){delete placed[itId];armed=null;render();}else{armed=(armed===itId)?null:itId;render();} } dragging=false; } elm.addEventListener('pointermove',onMove);elm.addEventListener('pointerup',onUp);elm.addEventListener('pointercancel',onUp); }); } + function attach(elm,itId){ let ghost=null,dragging=false,sx=0,sy=0; elm.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return; + ev.preventDefault(); if(ev.target.classList&&ev.target.classList.contains('dnd-x')){ ev.stopPropagation(); if(placed[itId]){delete placed[itId];render();}else if(armed===itId){armed=null;render();} return; } sx=ev.clientX;sy=ev.clientY; const r=elm.getBoundingClientRect(); const ox=ev.clientX-r.left,oy=ev.clientY-r.top; try{elm.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ const dx=e.clientX-sx,dy=e.clientY-sy; if(!dragging&&Math.hypot(dx,dy)>8){ dragging=true; ghost=elm.cloneNode(true); ghost.classList.remove('armed'); ghost.style.cssText='position:fixed;z-index:9999;pointer-events:none;opacity:.9;transform:rotate(-2.5deg);box-shadow:0 14px 36px rgba(0,0,0,.32);width:'+r.width+'px;left:'+(e.clientX-ox)+'px;top:'+(e.clientY-oy)+'px'; document.body.appendChild(ghost); elm.classList.add('dragging'); } if(dragging&&ghost){ ghost.style.left=(e.clientX-ox)+'px';ghost.style.top=(e.clientY-oy)+'px'; const under=document.elementsFromPoint(e.clientX,e.clientY); scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); const tgt=under.find(n=>n.classList&&(n.classList.contains('drop-box')||n.classList.contains('dnd-pool'))); if(tgt)tgt.classList.add('over'); } } function onUp(e){ elm.removeEventListener('pointermove',onMove);elm.removeEventListener('pointerup',onUp);elm.removeEventListener('pointercancel',onUp);elm.classList.remove('dragging'); if(ghost){ghost.remove();ghost=null;} scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); if(dragging){ const under=document.elementsFromPoint(e.clientX,e.clientY); const box=under.find(n=>n.classList&&n.classList.contains('drop-box')); const pl=under.find(n=>n.classList&&n.classList.contains('dnd-pool')); if(box){const di=box.querySelector('[data-cat]');if(di){placed[itId]=di.dataset.cat;armed=null;render();return;}}else if(pl){delete placed[itId];armed=null;render();return;} }else{ if(placed[itId]){delete placed[itId];armed=null;render();}else{armed=(armed===itId)?null:itId;render();} } dragging=false; } elm.addEventListener('pointermove',onMove);elm.addEventListener('pointerup',onUp);elm.addEventListener('pointercancel',onUp); }); } function attachBoxTaps(){ scope.querySelectorAll('.drop-box').forEach(box=>{ box.addEventListener('click',ev=>{ if(!armed)return; if(ev.target.closest('.dnd-chip'))return; const di=box.querySelector('[data-cat]'); if(di){placed[armed]=di.dataset.cat;armed=null;render();} }); }); } function render(){ pool.innerHTML=''; cfg.items.forEach(it=>{if(placed[it.id])return;const c=buildChip(it,false);if(armed===it.id)c.classList.add('armed');pool.appendChild(c);}); cfg.cats.forEach(cat=>{const box=scope.querySelector('.drop-items[data-cat="'+cat+'"]');if(!box)return;box.innerHTML='';cfg.items.forEach(it=>{if(placed[it.id]!==cat)return;box.appendChild(buildChip(it,true));});}); if(window.renderMathInElement)try{renderMath(scope);}catch(_){} } attachBoxTaps(); render(); @@ -1046,10 +1047,11 @@ function buildP1(){ el.style.cursor='grab'; el.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return; + ev.preventDefault(); const idx=+el.dataset.i; el.style.cursor='grabbing'; - try{el.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ + e.preventDefault(); const rect=svg.getBoundingClientRect(); const sx=svg.viewBox.baseVal.width/rect.width; const sy=svg.viewBox.baseVal.height/rect.height; @@ -1059,7 +1061,7 @@ function buildP1(){ redraw(); updateInfo(); } function onUp(){ window.removeEventListener('pointermove',onMove);window.removeEventListener('pointerup',onUp);window.removeEventListener('pointercancel',onUp);el.style.cursor='grab'; } - window.addEventListener('pointermove',onMove); + window.addEventListener('pointermove',onMove,{passive:false}); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); }); @@ -2030,9 +2032,10 @@ function buildP4(){ el.style.cursor='grab'; el.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return; + ev.preventDefault(); const vname=el.dataset.v; - try{el.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ + e.preventDefault(); const rect=svgEl.getBoundingClientRect(); const sx=W/rect.width,sy=H/rect.height; const nx=Math.max(10,Math.min(W-10,(e.clientX-rect.left)*sx)); @@ -2042,7 +2045,7 @@ function buildP4(){ redraw(); updateInfo(); } function onUp(){ window.removeEventListener('pointermove',onMove);window.removeEventListener('pointerup',onUp);window.removeEventListener('pointercancel',onUp); } - window.addEventListener('pointermove',onMove); + window.addEventListener('pointermove',onMove,{passive:false}); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); }); @@ -2398,11 +2401,11 @@ function buildP5(){ el.style.cursor='grab'; el.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return; + ev.preventDefault(); const vname=el.dataset.v; - try{el.setPointerCapture(ev.pointerId);}catch(e){} - function onMove(e){ const rect=svgEl.getBoundingClientRect(); const sx=W/rect.width,sy=H/rect.height; const nx=Math.max(10,Math.min(W-10,(e.clientX-rect.left)*sx)); const ny=Math.max(10,Math.min(H-10,(e.clientY-rect.top)*sy)); if(vname==='B') B={x:nx,y:ny}; else if(vname==='D') D={x:nx,y:ny}; redraw(); } + function onMove(e){ e.preventDefault(); const rect=svgEl.getBoundingClientRect(); const sx=W/rect.width,sy=H/rect.height; const nx=Math.max(10,Math.min(W-10,(e.clientX-rect.left)*sx)); const ny=Math.max(10,Math.min(H-10,(e.clientY-rect.top)*sy)); if(vname==='B') B={x:nx,y:ny}; else if(vname==='D') D={x:nx,y:ny}; redraw(); } function onUp(){ window.removeEventListener('pointermove',onMove);window.removeEventListener('pointerup',onUp);window.removeEventListener('pointercancel',onUp); } - window.addEventListener('pointermove',onMove); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); + window.addEventListener('pointermove',onMove,{passive:false}); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); }); }); const C2=getC(); const O2=intersect(A,C2,B,D); @@ -2961,63 +2964,115 @@ function buildP7(){ html += secNav('p6','p8'); box.innerHTML = html; - /* == SVG-прямоугольник == */ + /* == SVG-прямоугольник (redesigned) == */ (function(){ - const W=380, H=270; - let B={x:240,y:200}; - const A={x:60,y:200}; - function getRect(){ return {A:{x:A.x,y:A.y}, B:{x:B.x,y:A.y}, C:{x:B.x,y:B.y}, D:{x:A.x,y:B.y}}; } + const W=400, H=290; + // A = bottom-left (fixed), B = bottom-right (draggable via handle at top-right = C position) + // Rectangle ABCD: A bottom-left, B bottom-right, C top-right, D top-left + // Only the top-right corner (C) is draggable; this changes both width and height. + const Ax=55, Ay=240; + let Cx=300, Cy=60; // draggable top-right corner + function getVerts(){ return { A:{x:Ax,y:Ay}, B:{x:Cx,y:Ay}, C:{x:Cx,y:Cy}, D:{x:Ax,y:Cy} }; } function dist(a,b){ return Math.hypot(b.x-a.x,b.y-a.y); } + function sqMark(ox,oy,dx1,dy1,dx2,dy2,sz,col){ + // L-shape corner mark: from corner (ox,oy) going into the rectangle + const ex1=ox+dx1*sz, ey1=oy+dy1*sz, ex2=ox+dx2*sz, ey2=oy+dy2*sz; + const mx=ex1+dx2*sz, my=ey1+dy2*sz; + return ''; + } function redraw(){ - const r=getRect(); const {A:Ra,B:Rb,C:Rc,D:Rd}=r; - const cx=(Ra.x+Rb.x+Rc.x+Rd.x)/4, cy=(Ra.y+Rb.y+Rc.y+Rd.y)/4; - let s=''; - s+=''; - s+=''; - s+=''; - // right-angle marks — proper L-shape at all 4 corners pointing INSIDE the rectangle - const sq=9; - const xl=Math.min(Ra.x,Rb.x), xr=Math.max(Ra.x,Rb.x); - const yt=Math.min(Ra.y,Rc.y), yb=Math.max(Ra.y,Rc.y); - // bottom-left corner - s+=''; - // bottom-right corner - s+=''; - // top-right corner - s+=''; - // top-left corner - s+=''; - const labels=['A','B','C','D']; const pts=[Ra,Rb,Rc,Rd]; - pts.forEach((v,i)=>{ - const m=(i===2); - s+=''; - s+=''; - const lx=v.x+(v.x-cx)*0.28,ly=v.y+(v.y-cy)*0.28; - s+=''+labels[i]+''; + const v=getVerts(); + const {A,B,C,D}=v; + const cxC=(A.x+B.x+C.x+D.x)/4, cyC=(A.y+B.y+C.y+D.y)/4; + const ab=dist(A,B), bc=dist(B,C), ac=dist(A,C), bd=dist(B,D); + const perimeter=2*(ab+bc), area=ab*bc; + let s=''; + // rectangle fill + s+=''; + // diagonals (dashed, two colors to emphasize AC=BD) + s+=''; + s+=''; + // equal-tick marks on each diagonal (two ticks each, near midpoint) + const acMx=(A.x+C.x)/2, acMy=(A.y+C.y)/2; + const bdMx=(B.x+D.x)/2, bdMy=(B.y+D.y)/2; + const tickLen=6; + // AC tick: perpendicular to AC direction + const acDx=C.x-A.x, acDy=C.y-A.y, acL=Math.hypot(acDx,acDy)||1; + const acPx=-acDy/acL*tickLen, acPy=acDx/acL*tickLen; + s+=''; + s+=''; + // BD tick + const bdDx=D.x-B.x, bdDy=D.y-B.y, bdL=Math.hypot(bdDx,bdDy)||1; + const bdPx=-bdDy/bdL*tickLen, bdPy=bdDx/bdL*tickLen; + s+=''; + s+=''; + // diagonal length labels near midpoints + s+='AC='+ac.toFixed(1)+''; + s+='BD='+bd.toFixed(1)+''; + // right-angle marks at all 4 corners (L-shape pointing INSIDE) + const sq=10; + // A = bottom-left: right goes +x, up goes -y + s+=sqMark(A.x,A.y,+1,0,0,-1,sq,'#7c3aed'); + // B = bottom-right: left goes -x, up goes -y + s+=sqMark(B.x,B.y,-1,0,0,-1,sq,'#7c3aed'); + // C = top-right: left goes -x, down goes +y + s+=sqMark(C.x,C.y,-1,0,0,+1,sq,'#7c3aed'); + // D = top-left: right goes +x, down goes +y + s+=sqMark(D.x,D.y,+1,0,0,+1,sq,'#7c3aed'); + // side length labels + s+='a='+ab.toFixed(1)+''; + s+='b='+bc.toFixed(1)+''; + // vertices: A, D, B are static; C is draggable + const verts=[{p:A,lbl:'A',drag:false},{p:B,lbl:'B',drag:false},{p:C,lbl:'C',drag:true},{p:D,lbl:'D',drag:false}]; + verts.forEach(({p,lbl,drag})=>{ + const lx=p.x+(p.x-cxC)*0.28, ly=p.y+(p.y-cyC)*0.28; + if(drag){ + s+=''; + s+=''; + // drag hint arrow + s+='тащи'; + } else { + s+=''; + } + s+=''+lbl+''; }); s+=''; const wrap=document.getElementById('p7-rect-svg'); wrap.innerHTML=s; const svgEl=wrap.querySelector('svg'); svgEl.querySelectorAll('.p7-vh').forEach(el=>{ - el.style.cursor='grab'; el.addEventListener('pointerdown',ev=>{ - if(ev.button!==undefined&&ev.button!==0)return; - try{el.setPointerCapture(ev.pointerId);}catch(e){} - function onMove(e){ const rect=svgEl.getBoundingClientRect(); const sx=W/rect.width,sy=H/rect.height; B={x:Math.max(A.x+30,Math.min(W-10,(e.clientX-rect.left)*sx)),y:Math.max(30,Math.min(H-10,(e.clientY-rect.top)*sy))}; redraw(); } - function onUp(){ window.removeEventListener('pointermove',onMove);window.removeEventListener('pointerup',onUp);window.removeEventListener('pointercancel',onUp); } - window.addEventListener('pointermove',onMove); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); + if(ev.button!==undefined&&ev.button!==0) return; + ev.preventDefault(); + let active=true; + function onMove(e){ + if(!active) return; + e.preventDefault(); + const rect=svgEl.getBoundingClientRect(); + const sx=W/rect.width, sy=H/rect.height; + Cx=Math.max(Ax+40,Math.min(W-10,(e.clientX-rect.left)*sx)); + Cy=Math.max(10,Math.min(Ay-40,(e.clientY-rect.top)*sy)); + redraw(); + } + function onUp(){ + active=false; + window.removeEventListener('pointermove',onMove); + window.removeEventListener('pointerup',onUp); + window.removeEventListener('pointercancel',onUp); + } + window.addEventListener('pointermove',onMove,{passive:false}); + window.addEventListener('pointerup',onUp); + window.addEventListener('pointercancel',onUp); }); }); - const rr=getRect(); const ab=dist(rr.A,rr.B), bc=dist(rr.B,rr.C), ac=dist(rr.A,rr.C), bd=dist(rr.B,rr.D); - const eq=Math.abs(ac-bd)<0.05; + const eq=Math.abs(ac-bd)<0.1; document.getElementById('p7-rect-info').innerHTML=` -
AB
${ab.toFixed(1)}
-
BC
${bc.toFixed(1)}
-
AC (диагональ)
${ac.toFixed(2)}
-
BD (диагональ)
${bd.toFixed(2)}
-
${eq?'AC = BD':'AC ≠ BD'}
${eq?'Диагонали равны':'Не равны (Δ='+Math.abs(ac-bd).toFixed(2)+')'}
-
Все углы
90°
`; +
Сторона AB = CD
${ab.toFixed(1)}
+
Сторона BC = DA
${bc.toFixed(1)}
+
Периметр
${perimeter.toFixed(1)}
+
Площадь
${area.toFixed(1)}
+
Диагонали AC = BD
AC = ${ac.toFixed(2)} = BD = ${bd.toFixed(2)}
+
Все 4 угла
90°
`; } redraw(); })(); @@ -3251,10 +3306,10 @@ function buildP8(){ el.style.cursor='grab'; el.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0)return; - try{el.setPointerCapture(ev.pointerId);}catch(e){} - function onMove(e){ const rect=svgEl.getBoundingClientRect(); const sx=W/rect.width,sy=H/rect.height; D={x:Math.max(10,Math.min(W-10,(e.clientX-rect.left)*sx)),y:Math.max(10,Math.min(H-10,(e.clientY-rect.top)*sy))}; redraw(); } + ev.preventDefault(); + function onMove(e){ e.preventDefault(); const rect=svgEl.getBoundingClientRect(); const sx=W/rect.width,sy=H/rect.height; D={x:Math.max(10,Math.min(W-10,(e.clientX-rect.left)*sx)),y:Math.max(10,Math.min(H-10,(e.clientY-rect.top)*sy))}; redraw(); } function onUp(){ window.removeEventListener('pointermove',onMove);window.removeEventListener('pointerup',onUp);window.removeEventListener('pointercancel',onUp); } - window.addEventListener('pointermove',onMove); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); + window.addEventListener('pointermove',onMove,{passive:false}); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); }); }); const okCol=eq?'#10b981':'#94a3b8'; @@ -3562,11 +3617,11 @@ function buildP9(){ el.style.cursor='grab'; el.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0)return; + ev.preventDefault(); const vname=el.dataset.v; - try{el.setPointerCapture(ev.pointerId);}catch(e){} - function onMove(e){ const rect=svgEl.getBoundingClientRect(); const sx=W/rect.width,sy=H/rect.height; const nx=(e.clientX-rect.left)*sx; const ny=(e.clientY-rect.top)*sy; if(vname==='B'){ half2=Math.max(20,Math.min(H/2-10,cy-ny)); } else if(vname==='D'){ half2=Math.max(20,Math.min(H/2-10,ny-cy)); } else if(vname==='A'){ half1=Math.max(20,Math.min(W/2-10,cx-nx)); } else if(vname==='C'){ half1=Math.max(20,Math.min(W/2-10,nx-cx)); } redraw(); } + function onMove(e){ e.preventDefault(); const rect=svgEl.getBoundingClientRect(); const sx=W/rect.width,sy=H/rect.height; const nx=(e.clientX-rect.left)*sx; const ny=(e.clientY-rect.top)*sy; if(vname==='B'){ half2=Math.max(20,Math.min(H/2-10,cy-ny)); } else if(vname==='D'){ half2=Math.max(20,Math.min(H/2-10,ny-cy)); } else if(vname==='A'){ half1=Math.max(20,Math.min(W/2-10,cx-nx)); } else if(vname==='C'){ half1=Math.max(20,Math.min(W/2-10,nx-cx)); } redraw(); } function onUp(){ window.removeEventListener('pointermove',onMove);window.removeEventListener('pointerup',onUp);window.removeEventListener('pointercancel',onUp); } - window.addEventListener('pointermove',onMove); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); + window.addEventListener('pointermove',onMove,{passive:false}); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); }); }); const {A:A2,B:B2}=getVerts(half1,half2); const side2=dist(A2,B2); @@ -4289,9 +4344,10 @@ function buildP11(){ svgEl.querySelectorAll('.p11-drag').forEach(el=>{ el.addEventListener('pointerdown', ev=>{ if(ev.button!==undefined&&ev.button!==0) return; + ev.preventDefault(); const vname = el.dataset.v; - try{el.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ + e.preventDefault(); const rect=svgEl.getBoundingClientRect(); const sx=W/rect.width, sy=H/rect.height; const nx=(e.clientX-rect.left)*sx, ny=(e.clientY-rect.top)*sy; @@ -4300,7 +4356,7 @@ function buildP11(){ drawThales(); } function onUp(){ window.removeEventListener('pointermove',onMove); window.removeEventListener('pointerup',onUp); window.removeEventListener('pointercancel',onUp); } - window.addEventListener('pointermove',onMove); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); + window.addEventListener('pointermove',onMove,{passive:false}); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); }); }); @@ -4661,9 +4717,10 @@ function buildP12(){ svgEl.querySelectorAll('.p12-vdrag').forEach(el=>{ el.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return; + ev.preventDefault(); const vname=el.dataset.v; - try{el.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ + e.preventDefault(); const rect=svgEl.getBoundingClientRect(); const sx=W/rect.width, sy=H/rect.height; const nx=Math.max(10,Math.min(W-10,(e.clientX-rect.left)*sx)); @@ -4674,7 +4731,7 @@ function buildP12(){ redraw(); } function onUp(){ window.removeEventListener('pointermove',onMove); window.removeEventListener('pointerup',onUp); window.removeEventListener('pointercancel',onUp); } - window.addEventListener('pointermove',onMove); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); + window.addEventListener('pointermove',onMove,{passive:false}); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); }); }); @@ -4985,9 +5042,10 @@ function buildP13(){ svgEl.querySelectorAll('.p13-vd').forEach(el=>{ el.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return; + ev.preventDefault(); const vname=el.dataset.v; - try{el.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ + e.preventDefault(); const rect=svgEl.getBoundingClientRect(); const nx=Math.max(10,Math.min(W-10,(e.clientX-rect.left)*W/rect.width)); const ny=Math.max(10,Math.min(H-10,(e.clientY-rect.top)*H/rect.height)); @@ -4997,7 +5055,7 @@ function buildP13(){ redraw(); } function onUp(){ window.removeEventListener('pointermove',onMove); window.removeEventListener('pointerup',onUp); window.removeEventListener('pointercancel',onUp); } - window.addEventListener('pointermove',onMove); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); + window.addEventListener('pointermove',onMove,{passive:false}); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); }); }); const bc=dist(B,C), ac=dist(A,C), ab=dist(A,B); @@ -5359,9 +5417,10 @@ function buildP14(){ svgEl.querySelectorAll('.p14-vd').forEach(el=>{ el.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return; + ev.preventDefault(); const vname=el.dataset.v; - try{el.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ + e.preventDefault(); const rect=svgEl.getBoundingClientRect(); const nx=Math.max(10,Math.min(W-10,(e.clientX-rect.left)*W/rect.width)); const ny=Math.max(10,Math.min(H-10,(e.clientY-rect.top)*H/rect.height)); @@ -5372,7 +5431,7 @@ function buildP14(){ redraw(); } function onUp(){ window.removeEventListener('pointermove',onMove); window.removeEventListener('pointerup',onUp); window.removeEventListener('pointercancel',onUp); } - window.addEventListener('pointermove',onMove); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); + window.addEventListener('pointermove',onMove,{passive:false}); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); }); }); const S=(a+b)/2*h; @@ -5767,9 +5826,10 @@ function buildP15(){ svgEl.querySelectorAll('.p15-vd').forEach(el=>{ el.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return; + ev.preventDefault(); const vname=el.dataset.v; - try{el.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ + e.preventDefault(); const rect=svgEl.getBoundingClientRect(); const nx=Math.max(10,Math.min(W-10,(e.clientX-rect.left)*W/rect.width)); const ny=Math.max(10,Math.min(H-10,(e.clientY-rect.top)*H/rect.height)); @@ -5780,7 +5840,7 @@ function buildP15(){ redraw(); } function onUp(){ window.removeEventListener('pointermove',onMove); window.removeEventListener('pointerup',onUp); window.removeEventListener('pointercancel',onUp); } - window.addEventListener('pointermove',onMove); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); + window.addEventListener('pointermove',onMove,{passive:false}); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); }); }); const eq=(Math.abs(diag-dist(B,D))<0.5)?'Равны!':'не равны'; @@ -6153,9 +6213,10 @@ function buildP16(){ svgEl.querySelectorAll('.p16-vd2').forEach(el=>{ el.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return; + ev.preventDefault(); const vname=el.dataset.v; - try{el.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ + e.preventDefault(); const rect=svgEl.getBoundingClientRect(); const nx=Math.max(10,Math.min(W-10,(e.clientX-rect.left)*W/rect.width)); const ny=Math.max(10,Math.min(H-10,(e.clientY-rect.top)*H/rect.height)); @@ -6166,7 +6227,7 @@ function buildP16(){ redraw2(); } function onUp(){ window.removeEventListener('pointermove',onMove); window.removeEventListener('pointerup',onUp); window.removeEventListener('pointercancel',onUp); } - window.addEventListener('pointermove',onMove); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); + window.addEventListener('pointermove',onMove,{passive:false}); window.addEventListener('pointerup',onUp); window.addEventListener('pointercancel',onUp); }); }); const ind=document.getElementById('p16-sign2-ind'); @@ -6176,43 +6237,183 @@ function buildP16(){ redraw2(); })(); - /* == Доказательство признака 1 == */ + /* == Доказательство признака 1 (redesigned) == */ (function(){ - const A={x:50,y:210}, B={x:290,y:210}, C={x:220,y:70}, D={x:110,y:70}; - const Cx2=A.x+(B.x-D.x), Cy2=A.y+(B.y-D.y); - const E={x:Math.min(320, Cx2), y:A.y}; + // Isosceles trapezoid ABCD: A,B = bottom base; C,D = top base + // A bottom-left, B bottom-right, C top-right, D top-left + // AD || BC (bottom base AD is longer) + const A={x:45,y:215}, B={x:295,y:215}, C={x:235,y:75}, D={x:105,y:75}; + // E on segment AB extended: draw CE || BD until it hits AB + // CE is parallel to AB (the lateral side), so E is on AD (bottom base) with BE = DC direction + // CE || AB means: from C go in direction of AB. AB direction = B-A = (250,0), unit = (1,0) + // CE length = AB distance along CE until bottom line y=215 + // Since C=(235,75) and we go parallel to A->B... wait, CE must be parallel to BD (the lateral side B to C already goes up-left) + // The proof: through C draw CE || BD. Let's compute E properly. + // BD direction: D-B = (105-295, 75-215) = (-190,-140) + // From C=(235,75) in direction (-190,-140) scaled to reach y=215: + // 75 + t*(-140) = 215 => t = -140/(-140) = 1 => E = (235+(-190)*1, 75+(-140)*1) = (45, -65) — off screen + // Instead use: CE || AB (lateral side of the parallelogram ABCE approach) + // Actually the standard proof draws CE || AB (a lateral side) to form parallelogram ABCE + // where E is on AD. Let's verify: AB is lateral side A->B going right. No wait — + // In the standard proof, we drop CE parallel to AB (lateral AB = right side). + // Actually the proof uses: through D draw DE || AB where E is on BC. Let's use that: + // Draw DE || AB, E on BC. DE || AB and DE = AB (parallelogram ABDE since AB || DE and AB=DE). + // Then triangle DCE has DC=AB (proven), CE=AD-AE (proven equal)... + // Simplest clean proof: extend through D draw DF || BC until it hits AB extended, forming parallelogram. + // Let's use the standard textbook approach with clean coordinates: + // Trapezoid: A(45,215) B(295,215) C(235,75) D(105,75). Angles A and B are at bottom. + // Draw CE || AB where E on AD. E = A + (some offset). + // AB direction is horizontal. CE || AB means E has same y as A. CE is vertical? No — CE || AB means horizontal. + // From C(235,75) go in direction of AB vector (250,0)... wait that goes to the right, not toward AD. + // The correct construction: through C draw CE PARALLEL to BD (not AB). + // BD vector = D - B = (105-295, 75-215) = (-190,-140). + // From C=(235,75) in direction BD-normalized until y=215: + // y: 75 + t*(-140) = 215 -> t = 1. x: 235 + 1*(-190) = 45. E = (45, 215) = A! + // So E coincides with A when the trapezoid is isosceles. Use a slightly different construction. + // Use the cleaner proof: through B draw BF || DC, F on AD extended. + // DC vector = C-D = (130,0). BF || DC -> F = B + t*(130,0)/130 ... F at y=215, F=(B.x + s, 215). + // That means F is just to the right of B which isn't helpful. + // BEST approach for the proof: use the auxiliary point E on AD such that CE || AB (lateral). + // This means: vector CE = k * vector AB-lateral. + // Lateral AB: from A(45,215) to B... no, lateral sides are AD and BC. + // AD: from A(45,215) to D(105,75). BC: from B(295,215) to C(235,75). + // CE || AD: from C(235,75) in direction of AD = (60,-140). Scale to reach y=215: 75+t*(-140)=215 -> t=-1 (wrong direction). + // CE || BC: from C(235,75) in reverse direction of BC = (60,-140) -> same issue. + // + // SIMPLEST CLEAN APPROACH for the diagram: just show the key steps visually. + // Step 1: Trapezoid with equal angles marked. + // Step 2: Drop perpendiculars from D and C to base AB -> H1, H2. Show right triangles. + // Step 3: The right triangles ADH1 and BCH2 are congruent (hypotenuse-leg). + // Step 4: Conclusion AD = BC. + // This is cleaner to draw and easier to understand. + const H1={x:D.x, y:A.y}; // foot of perpendicular from D to AB + const H2={x:C.x, y:B.y}; // foot of perpendicular from C to AB + const cx=(A.x+B.x+C.x+D.x)/4, cy=(A.y+B.y+C.y+D.y)/4; + const steps=[ - {text:'Дано: трапеция $ABCD$, $AD \\parallel BC$, $\\angle DAB = \\angle CBA$. Доказать: $AB = CD$ (равнобедренная).', h:'base'}, - {text:'Шаг 1. Через $C$ проведём прямую $CE \\parallel AB$ до пересечения с $AD$ в точке $E$.', h:'aux'}, - {text:'Шаг 2. $ABCE$ — параллелограмм ($AB \\parallel CE$, $BC \\parallel AE$). Значит $AB = CE$ и $BC = AE$.', h:'para'}, - {text:'Шаг 3. $\\angle DAB = \\angle CBA = \\angle AEC$ (как накрест лежащие при $AB \\parallel CE$). Тогда $\\triangle AEC$ — равнобедренный: $AE = CE = AB$.', h:'iso'}, - {text:'Вывод. $ED = AD - AE$, $DC = CE = AB$... Итог: $AB = CD$. Трапеция равнобедренная. ч.т.д.', h:'done'}, + { + h:'base', + badge:'Дано', + text:'Дано: трапеция $ABCD$, $AD \\parallel BC$ (основания), $\\angle DAB = \\angle CBA$ (углы при основании $AB$ равны).
Доказать: $AD = BC$ (трапеция равнобедренная).', + }, + { + h:'heights', + badge:'Шаг 1', + text:'Шаг 1. Проведём высоты $DH_1 \\perp AB$ и $CH_2 \\perp AB$ из вершин $D$ и $C$ на основание $AB$. Получаем два прямоугольных треугольника: $\\triangle DH_1A$ и $\\triangle CH_2B$.', + }, + { + h:'triangles', + badge:'Шаг 2', + text:'Шаг 2. В прямоугольных треугольниках $\\triangle DH_1A$ и $\\triangle CH_2B$:
' + + '— $DH_1 = CH_2$ (высоты трапеции с параллельными основаниями равны)
' + + '— $\\angle H_1AD = \\angle H_2BC = \\angle DAB = \\angle CBA$ (дано)', + }, + { + h:'congruent', + badge:'Шаг 3', + text:'Шаг 3. По признаку «угол и катет» прямоугольного треугольника:
$\\triangle DH_1A \\cong \\triangle CH_2B$.
Из равенства следует: $AD = BC$ (гипотенузы равны).', + }, + { + h:'done', + badge:'Вывод', + text:'Вывод. Поскольку $AD = BC$ (боковые стороны равны), трапеция $ABCD$ является равнобедренной. ч.т.д.', + }, ]; let step=0; function draw(h){ - let s=``; - s+=``; - if(h==='aux'||h==='para'||h==='iso'||h==='done'){ - s+=``; - s+=``; - s+=`E`; + let s=``; + // base trapezoid + const trapFill = (h==='base') ? 'rgba(8,145,178,.12)' : 'rgba(8,145,178,.06)'; + s+=``; + + // equal-angle arcs at A and B + if(h==='base'||h==='heights'||h==='triangles'||h==='congruent'||h==='done'){ + const r=24; + // angle at A (between AB rightward and AD upward) + const a1A=Math.atan2(D.y-A.y,D.x-A.x), a2A=Math.atan2(B.y-A.y,B.x-A.x); + s+=``; + // angle at B (between BC leftward and BA leftward) + const a1B=Math.atan2(C.y-B.y,C.x-B.x), a2B=Math.atan2(A.y-B.y,A.x-B.x); + s+=``; + s+=`α`; + s+=`α`; } - if(h==='para'||h==='done'){ - s+=``; + + // heights DH1 and CH2 + if(h==='heights'||h==='triangles'||h==='congruent'||h==='done'){ + s+=``; + s+=``; + const sq=7; + // right angle at H1 + s+=``; + // right angle at H2 + s+=``; + // H1, H2 labels + s+=`H₁`; + s+=`H₂`; } - if(h==='iso'||h==='done'){ - s+=``; + + // triangles highlighted + if(h==='triangles'||h==='congruent'||h==='done'){ + s+=``; + s+=``; } - [[A,'A'],[B,'B'],[C,'C'],[D,'D']].forEach(([P,lbl])=>{ - s+=``; - s+=`${lbl}`; + + // congruence marks on equal sides (height ticks + lateral side ticks) + if(h==='congruent'||h==='done'){ + // height DH1 = CH2: tick marks + const h1mx=(D.x+H1.x)/2, h1my=(D.y+H1.y)/2; + const h2mx=(C.x+H2.x)/2, h2my=(C.y+H2.y)/2; + s+=``; + s+=``; + // lateral AD and BC equal marks + const adMx=(A.x+D.x)/2, adMy=(A.y+D.y)/2; + const bcMx=(B.x+C.x)/2, bcMy=(B.y+C.y)/2; + const adDx=D.x-A.x, adDy=D.y-A.y, adL=Math.hypot(adDx,adDy)||1; + const adPx=-adDy/adL*6, adPy=adDx/adL*6; + s+=``; + s+=``; + const bcDx=C.x-B.x, bcDy=C.y-B.y, bcL=Math.hypot(bcDx,bcDy)||1; + const bcPx=-bcDy/bcL*6, bcPy=bcDx/bcL*6; + s+=``; + s+=``; + } + + // vertices + [[A,'A',[-14,8]],[B,'B',[8,8]],[C,'C',[8,-6]],[D,'D',[-14,-6]]].forEach(([P,lbl,[ox,oy]])=>{ + s+=``; + s+=`${lbl}`; }); + s+=``; document.getElementById('p16-proof-svg').innerHTML=s; } - function show(){ const st=steps[step]; document.getElementById('p16-proof-step').innerHTML=st.text; renderMath(document.getElementById('p16-proof-step')); draw(st.h); document.getElementById('p16-proof-next').textContent=step{ if(step{step=0;show();document.getElementById('p16-proof-next').disabled=false;}); + function show(){ + const st=steps[step]; + const total=steps.length; + // step counter badge + document.getElementById('p16-proof-step').innerHTML= + `
` + + `${st.badge}` + + `${step+1} / ${total}` + + `
${st.text}`; + renderMath(document.getElementById('p16-proof-step')); + draw(st.h); + const btn=document.getElementById('p16-proof-next'); + btn.textContent=step{ + if(step{ + step=0; show(); + }); show(); })();