79aaf27b7f
- KaTeX:
• PARAS p29/p30: убрана математика из psel-карточек
($M$ → M, $\perp$ → ⊥), т.к. psel не рендерил KaTeX.
• Boss "\§29-30" title: $\perp$ → ⊥ (boss-title не рендерился).
• Защитно добавлен renderMath(g) после buildParaSelector
и renderMath(cont) после вставки boss-карточек.
- §27 SVG: чистая 2-панельная схема с разделителем.
Слева: ЛИНЕЙКА (корпус с штрихами без цифр) → ↓ →
пример (точки A, B + прямая).
Справа: ЦИРКУЛЬ (шарнир + игла + грифель) → ↓ →
пример (окружность с центром O и радиусом r).
- §31 SVG: пересчитанные координаты, чёткие плашки-подписи
ГМТ 1 (биссектриса, красная) и ГМТ 2 (окружность, синяя).
Точки K₁, K₂ — крупные зелёные с белой обводкой.
Дуги показывают, что биссектриса делит угол ровно пополам.
1398 lines
104 KiB
HTML
1398 lines
104 KiB
HTML
<!doctype html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||
<meta http-equiv="Pragma" content="no-cache">
|
||
<meta http-equiv="Expires" content="0">
|
||
<title>Геометрия 7 · Глава 5 · Задачи на построение</title>
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
|
||
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
|
||
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"
|
||
onload="renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false})"></script>
|
||
<script src="/js/api.js" defer></script>
|
||
<script src="/js/xp.js" defer></script>
|
||
<script src="/js/geom7_svg.js?v=6" defer></script>
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Manrope:wght@600;700;800;900&family=Unbounded:wght@700;800;900&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
|
||
<style>
|
||
:root{
|
||
--bg:#fafafa; --card:#fff; --card-soft:#f8fafc; --text:#0f172a; --muted:#64748b;
|
||
--border:#e2e8f0; --sh:0 1px 3px rgba(0,0,0,.06); --sh2:0 4px 14px rgba(0,0,0,.08);
|
||
--pri:#db2777; --pri2:#be185d; --pri-soft:#fce7f3;
|
||
--acc:#ec4899; --acc2:#db2777; --acc-soft:#fbcfe8;
|
||
--ok:#10b981; --ok-bg:#d1fae5; --warn:#f59e0b; --warn-bg:#fef3c7;
|
||
--bad:#ef4444; --fail:#dc2626; --fail-bg:#fee2e2;
|
||
}
|
||
.dark{--bg:#0e0612; --card:#180a18; --card-soft:#1f0e1f; --text:#fee0f0; --muted:#a0708a; --border:#321632}
|
||
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
|
||
html,body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.55;font-size:15px}
|
||
button,input,select,textarea{font-family:inherit;font-size:inherit}
|
||
button{cursor:pointer;border:0;background:transparent;color:inherit}
|
||
a{color:inherit;text-decoration:none}
|
||
.ic{width:16px;height:16px;display:inline-block;flex-shrink:0;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;vertical-align:middle}
|
||
|
||
.hdr{position:relative;background:linear-gradient(110deg,#831843 0%,#be185d 55%,#f472b6 100%);color:#fff;padding:46px 22px 30px;overflow:hidden;border-bottom:2px solid rgba(252,231,243,.2);min-height:130px}
|
||
.hdr::before{content:'ГЛАВА 5';position:absolute;right:-12px;top:50%;transform:translateY(-50%);font-family:'Unbounded',sans-serif;font-size:clamp(5rem,15vw,11rem);font-weight:900;letter-spacing:-.04em;color:transparent;-webkit-text-stroke:1.5px rgba(252,230,243,.12);line-height:1;pointer-events:none;z-index:0}
|
||
.hdr-row{position:relative;z-index:1;display:flex;align-items:center;gap:14px;flex-wrap:wrap}
|
||
.hdr h1{font-family:'Unbounded',sans-serif;font-size:1.5rem;font-weight:900;letter-spacing:-.01em;line-height:1.3;padding-top:4px}
|
||
.hdr-sub{font-size:.85rem;opacity:.88;margin-top:6px;font-weight:500;line-height:1.4}
|
||
.hdr-side{margin-left:auto;display:flex;gap:8px;align-items:center;flex-wrap:wrap}
|
||
.hdr-btn{padding:7px 12px;border-radius:9px;background:rgba(255,255,255,.14);color:#fff;font-weight:600;font-size:.82rem;display:inline-flex;align-items:center;gap:6px;transition:background .15s;text-decoration:none}
|
||
.hdr-btn:hover{background:rgba(255,255,255,.24)}
|
||
|
||
.main{max-width:1240px;margin:0 auto;padding:22px;width:100%;display:grid;grid-template-columns:1fr 280px;gap:24px}
|
||
@media(max-width:980px){.main{grid-template-columns:1fr;padding:14px}}
|
||
.col-main{min-width:0}
|
||
|
||
.hero{background:linear-gradient(135deg,var(--pri-soft) 0%,var(--acc-soft) 50%,var(--pri-soft) 100%);background-size:200% 200%;animation:heroShift 12s ease-in-out infinite;border:1px solid var(--border);border-radius:18px;padding:24px 22px;margin-bottom:24px;position:relative;overflow:hidden}
|
||
@keyframes heroShift{0%,100%{background-position:0% 50%}50%{background-position:100% 50%}}
|
||
.hero::before{content:'\25CB';position:absolute;right:-10px;top:-20px;font-size:clamp(2rem,8vw,5.5rem);font-weight:900;color:var(--pri);opacity:.10;line-height:1;pointer-events:none;font-family:'Unbounded',sans-serif}
|
||
.hero h2{font-family:'Unbounded',sans-serif;font-size:1.55rem;font-weight:800;color:var(--pri2);margin-bottom:10px}
|
||
.hero p{font-size:.95rem;color:var(--text);opacity:.88;margin-bottom:14px;max-width:640px}
|
||
.hero-row{display:flex;gap:14px;flex-wrap:wrap;align-items:center}
|
||
.btn-primary{padding:11px 22px;background:linear-gradient(135deg,var(--pri),var(--pri2));color:#fff;border-radius:11px;font-weight:700;font-size:.92rem;display:inline-flex;align-items:center;gap:8px;box-shadow:var(--sh2)}
|
||
.btn-primary:hover{transform:translateY(-1px);box-shadow:0 8px 28px rgba(219,39,119,.32)}
|
||
.hero-progress{flex:1;min-width:200px;max-width:280px}
|
||
.hp-label{font-size:.74rem;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;display:block;margin-bottom:5px}
|
||
.hp-bar{height:8px;background:rgba(219,39,119,.18);border-radius:5px;overflow:hidden}
|
||
.hp-fill{height:100%;background:linear-gradient(90deg,var(--pri),var(--acc));border-radius:5px;width:0%;transition:width .6s cubic-bezier(.16,1,.3,1)}
|
||
.hp-text{font-size:.78rem;color:var(--muted);font-weight:700;margin-top:4px;display:block}
|
||
.hero-xp-badge{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:linear-gradient(135deg,var(--warn,#f59e0b),var(--pri));color:#fff;border-radius:99px;font-size:.82rem;font-weight:800;font-family:'Unbounded',sans-serif}
|
||
|
||
.psel{margin-bottom:24px}
|
||
.psel-title{font-size:.72rem;font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px}
|
||
.psel-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:10px}
|
||
.psel-card{background:var(--card);border:1.5px solid var(--border);border-radius:13px;padding:14px;cursor:pointer;transition:transform .2s,box-shadow .2s,border-color .2s;text-align:left;position:relative}
|
||
.psel-card:hover{transform:translateY(-3px);box-shadow:var(--sh2);border-color:var(--pri)}
|
||
.psel-card.active{border-color:var(--pri);background:linear-gradient(135deg,var(--pri-soft),var(--card));box-shadow:var(--sh2)}
|
||
.psel-card.active::after{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--pri),var(--acc));border-radius:13px 13px 0 0}
|
||
.psel-num{font-family:'Unbounded',sans-serif;font-size:.72rem;font-weight:800;color:var(--pri);text-transform:uppercase;letter-spacing:.08em;margin-bottom:5px}
|
||
.psel-name{font-size:.86rem;font-weight:700;color:var(--text);line-height:1.3;margin-bottom:8px}
|
||
.psel-prog{height:4px;background:rgba(219,39,119,.10);border-radius:3px;overflow:hidden}
|
||
.psel-prog-fill{height:100%;background:var(--pri);width:0%;transition:width .4s}
|
||
.psel-card.final{background:linear-gradient(135deg,#fff5e1,#fef3c7)}
|
||
|
||
.sec[id="sec-p27"] { --sec-acc:#db2777; --sec-acc-d:#be185d; --sec-acc-soft:#fce7f3; }
|
||
.sec[id="sec-p28"] { --sec-acc:#c026d3; --sec-acc-d:#a21caf; --sec-acc-soft:#fae8ff; }
|
||
.sec[id="sec-p29"] { --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
|
||
.sec[id="sec-p30"] { --sec-acc:#0891b2; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
|
||
.sec[id="sec-p31"] { --sec-acc:#059669; --sec-acc-d:#047857; --sec-acc-soft:#d1fae5; }
|
||
.sec[id="sec-final5"]{ --sec-acc:#db2777; --sec-acc-d:#be185d; --sec-acc-soft:#fce7f3; }
|
||
|
||
.sec{display:none;position:relative;animation:fadeIn .35s ease}
|
||
.sec.active{display:block}
|
||
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
|
||
.sec::before{content:attr(data-watermark);position:absolute;right:-20px;top:10%;font-family:'Unbounded',sans-serif;font-size:clamp(6rem,18vw,14rem);font-weight:900;color:transparent;-webkit-text-stroke:1.5px var(--sec-acc-soft,var(--pri-soft));line-height:1;pointer-events:none;z-index:0;opacity:.35}
|
||
.sec-header{margin-bottom:22px;padding-bottom:14px;border-bottom:2px solid var(--sec-acc-soft,var(--pri-soft));position:relative;z-index:1}
|
||
.sec-num{display:inline-block;padding:4px 10px;background:linear-gradient(135deg,var(--sec-acc,var(--pri)),var(--sec-acc-d,var(--pri2)));color:#fff;border-radius:7px;font-family:'Unbounded',sans-serif;font-size:.78rem;font-weight:800;letter-spacing:.04em;margin-bottom:8px}
|
||
.sec-h{font-family:'Unbounded',sans-serif;font-size:1.55rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));line-height:1.25}
|
||
|
||
.card{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:18px 20px;margin-bottom:16px;box-shadow:0 1px 3px rgba(0,0,0,.04),0 8px 24px rgba(219,39,119,.06);position:relative;z-index:1}
|
||
.card-header{display:flex;align-items:center;gap:10px;margin-bottom:12px;padding-bottom:10px;border-bottom:1px dashed var(--border)}
|
||
.card-icon{width:32px;height:32px;border-radius:9px;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:#fff}
|
||
.card-icon.theory{background:#8b5cf6}.card-icon.rule{background:#ec4899}.card-icon.algo{background:#f59e0b}.card-icon.example{background:#10b981}
|
||
.card-icon .ic{width:18px;height:18px}
|
||
.card-title{font-family:'Unbounded',sans-serif;font-size:.82rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);flex:1}
|
||
.card-num{font-size:.74rem;font-weight:700;color:var(--muted);background:var(--sec-acc-soft,var(--pri-soft));padding:3px 7px;border-radius:5px}
|
||
.card-body{font-size:.94rem;line-height:1.65}
|
||
.card-body p{margin-bottom:8px}
|
||
|
||
.wg{background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)));border:1.5px solid var(--sec-acc,var(--pri));border-radius:14px;padding:18px 20px;margin-bottom:18px;box-shadow:var(--sh2);position:relative;z-index:1}
|
||
.wg-header{display:flex;align-items:center;gap:8px;margin-bottom:14px}
|
||
.wg-badge{padding:4px 9px;background:var(--sec-acc,var(--pri));color:#fff;border-radius:6px;font-family:'Unbounded',sans-serif;font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em}
|
||
.wg-title{font-family:'Unbounded',sans-serif;font-size:1.05rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));flex:1}
|
||
.wg-help{font-size:.88rem;color:var(--text);margin-bottom:12px;line-height:1.55;background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--sec-acc-soft,var(--pri-soft)));border-left:4px solid var(--warn,#f59e0b);padding:9px 14px;border-radius:9px}
|
||
|
||
.btn{padding:8px 16px;border-radius:8px;background:var(--card);color:var(--text);border:1.5px solid var(--border);font-weight:600;font-size:.88rem;transition:background .15s,border-color .15s}
|
||
.btn:hover{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
|
||
.btn.primary{background:var(--sec-acc,var(--pri));color:#fff;border-color:var(--sec-acc,var(--pri))}
|
||
.btn.primary:hover{background:var(--sec-acc-d,var(--pri2));border-color:var(--sec-acc-d,var(--pri2))}
|
||
.tinp{padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);font-family:'JetBrains Mono',monospace}
|
||
.tinp:focus{outline:0;border-color:var(--sec-acc,var(--pri));box-shadow:0 0 0 3px var(--sec-acc-soft,var(--pri-soft))}
|
||
|
||
.feedback{padding:10px 14px;border-radius:9px;font-weight:600;font-size:.88rem;margin-top:8px;display:none}
|
||
.feedback.ok{display:block;background:var(--ok-bg);color:#065f46;border-left:4px solid var(--ok)}
|
||
.feedback.fail{display:block;background:var(--fail-bg);color:#7f1d1d;border-left:4px solid var(--fail)}
|
||
|
||
.col-side{position:sticky;top:14px;align-self:start;height:fit-content;max-height:calc(100vh - 28px);overflow-y:auto}
|
||
.sidecard{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:16px;margin-bottom:14px;box-shadow:var(--sh)}
|
||
.sidecard h4{font-family:'Unbounded',sans-serif;font-size:.74rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border)}
|
||
.sidecard-row{margin-bottom:8px;font-size:.86rem;line-height:1.6}
|
||
.sidecard-row b{color:var(--pri);font-weight:700}
|
||
@media(max-width:980px){.col-side{position:static;max-height:none}}
|
||
|
||
.xp-card{background:linear-gradient(135deg,var(--acc-soft),var(--pri-soft));border:1.5px solid var(--acc);border-radius:12px;padding:14px;margin-bottom:14px}
|
||
.xp-card-title{font-size:.68rem;font-weight:800;color:var(--acc2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:8px;display:flex;align-items:center;justify-content:space-between}
|
||
.xp-level{font-size:1.1rem;font-weight:900;color:var(--acc2);font-family:'Unbounded',sans-serif}
|
||
.xp-bar{height:9px;background:rgba(219,39,119,.18);border-radius:6px;overflow:hidden;margin:7px 0}
|
||
.xp-fill{height:100%;background:linear-gradient(90deg,var(--acc),var(--pri));border-radius:6px;transition:width .5s cubic-bezier(.4,0,.2,1)}
|
||
.xp-nums{font-size:.74rem;color:var(--muted);display:flex;justify-content:space-between}
|
||
|
||
.spoiler{border:1px solid var(--border);border-radius:10px;background:var(--card);margin:10px 0;overflow:hidden}
|
||
.spoiler summary{padding:8px 14px;background:var(--sec-acc-soft,var(--pri-soft));font-weight:700;cursor:pointer;font-size:.88rem;color:var(--sec-acc-d,var(--pri2));list-style:none;display:flex;align-items:center;gap:8px}
|
||
.spoiler summary::-webkit-details-marker{display:none}
|
||
.spoiler summary::before{content:'+';font-size:1.2rem;font-weight:900;color:var(--sec-acc,var(--pri));width:18px}
|
||
.spoiler[open] summary::before{content:'\2212'}
|
||
.spoiler-body{padding:10px 14px;font-size:.92rem;line-height:1.6}
|
||
|
||
.sec-nav{display:flex;gap:10px;margin-top:24px;padding-top:20px;border-top:1px solid var(--border);justify-content:space-between;flex-wrap:wrap}
|
||
.foot{text-align:center;padding:30px 16px;color:var(--muted);font-size:.78rem;border-top:1px solid var(--border);margin-top:30px}
|
||
|
||
.ach-popup{position:fixed;top:80px;right:18px;background:linear-gradient(135deg,#db2777,#f472b6);color:#fff;padding:12px 18px;border-radius:11px;font-weight:700;font-size:.9rem;box-shadow:0 8px 28px rgba(219,39,119,.45);z-index:1002;display:none;align-items:center;gap:8px;max-width:340px}
|
||
.ach-popup.show{display:flex}
|
||
|
||
.score-display{display:flex;gap:14px;flex-wrap:wrap;align-items:center;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;margin-bottom:12px}
|
||
.score-display b{color:var(--sec-acc-d,var(--pri2));font-size:1.15rem}
|
||
|
||
.hp-boss{height:14px;background:rgba(220,38,38,.12);border-radius:9px;overflow:hidden;border:1px solid #fecaca;margin:8px 0}
|
||
.hp-boss-fill{height:100%;background:linear-gradient(90deg,#dc2626,#f59e0b);border-radius:9px;transition:width .5s cubic-bezier(.4,0,.2,1)}
|
||
|
||
.col-side-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.42);z-index:9990;display:none}
|
||
.col-side-backdrop.show{display:block}
|
||
@media(max-width:980px){
|
||
.col-side{position:fixed;top:0;right:0;height:100vh;width:300px;max-width:88vw;background:var(--bg);box-shadow:-12px 0 24px rgba(0,0,0,.18);padding:18px 16px;overflow-y:auto;transform:translateX(100%);transition:transform .25s ease;z-index:9991;max-height:none}
|
||
.col-side.open{transform:none}
|
||
}
|
||
|
||
.boss-card{padding:16px;background:var(--card);border-radius:12px;border:2px solid var(--bad,#dc2626);margin-bottom:14px}
|
||
.boss-head{display:flex;align-items:center;gap:10px;margin-bottom:10px}
|
||
.boss-title{font-family:'Unbounded',sans-serif;font-weight:800;color:#7f1d1d;font-size:1.04rem;flex:1}
|
||
.boss-stage{font-size:.85rem;color:var(--muted)}
|
||
.boss-q{font-size:1rem;line-height:1.55;padding:11px 13px;background:var(--card-soft);border-radius:8px;margin-bottom:9px;border-left:3px solid var(--bad,#dc2626)}
|
||
|
||
.svg-host{display:flex;justify-content:center;margin:12px 0}
|
||
.svg-host-row{display:flex;gap:8px;flex-wrap:wrap;justify-content:center;margin:12px 0}
|
||
|
||
.steps{counter-reset:step;margin:14px 0}
|
||
.step{counter-increment:step;display:flex;gap:12px;align-items:flex-start;padding:10px 14px;background:var(--sec-acc-soft);border-radius:10px;margin-bottom:8px;border-left:4px solid var(--sec-acc)}
|
||
.step::before{content:"Шаг " counter(step);flex-shrink:0;font-family:'Unbounded',sans-serif;font-size:.72rem;font-weight:800;color:var(--sec-acc-d);background:#fff;padding:4px 10px;border-radius:7px;letter-spacing:.04em;text-transform:uppercase;align-self:flex-start;min-width:64px;text-align:center}
|
||
.step-text{flex:1;font-size:.92rem;line-height:1.5}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header class="hdr">
|
||
<div class="hdr-row">
|
||
<div>
|
||
<h1>Геометрия 7 · Глава 5</h1>
|
||
<div class="hdr-sub">Задачи на построение циркулем и линейкой · алгоритмы · ГМТ</div>
|
||
</div>
|
||
<div class="hdr-side">
|
||
<a href="/textbook/geometry-7" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> К геометрии 7</a>
|
||
<button id="sidebar-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><line x1="4" y1="6" x2="20" y2="6"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="18" x2="14" y2="18"/></svg> Шпаргалка</button>
|
||
<button id="theme-btn" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg><span id="theme-lab">Тёмная</span></button>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<main class="main">
|
||
<div class="col-main">
|
||
<section class="hero">
|
||
<h2>Как построить геометрический объект только циркулем и линейкой?</h2>
|
||
<p>Финальная глава курса. <b>Линейка</b> проводит прямые. <b>Циркуль</b> рисует окружности. Этих двух инструментов хватит, чтобы построить <b>середину отрезка</b>, <b>биссектрису угла</b>, <b>перпендикуляр</b>, <b>треугольник по 3 сторонам</b> и решать сложные задачи методом <b>геометрических мест точек</b>.</p>
|
||
<div class="hero-row">
|
||
<button class="btn-primary" onclick="goTo('p27')"><svg class="ic" viewBox="0 0 24 24"><polygon points="6 4 20 12 6 20 6 4" fill="currentColor" stroke="none"/></svg> Начать § 27</button>
|
||
<div class="hero-progress">
|
||
<span class="hp-label">Прогресс по главе</span>
|
||
<div class="hp-bar"><div id="hero-hp-fill" class="hp-fill"></div></div>
|
||
<span id="hero-hp-text" class="hp-text">0%</span>
|
||
</div>
|
||
<div id="hero-xp-badge" class="hero-xp-badge"></div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="psel">
|
||
<div class="psel-title">Параграфы главы</div>
|
||
<div id="psel-grid" class="psel-grid"></div>
|
||
</section>
|
||
|
||
<section id="sec-p27" class="sec" data-watermark="○"><div class="sec-header"><span class="sec-num">§ 27</span><h2 class="sec-h">Простейшие построения</h2></div><div id="p27-body"></div></section>
|
||
<section id="sec-p28" class="sec" data-watermark="△"><div class="sec-header"><span class="sec-num">§ 28</span><h2 class="sec-h">Построение треугольника по 3 сторонам</h2></div><div id="p28-body"></div></section>
|
||
<section id="sec-p29" class="sec" data-watermark="↗"><div class="sec-header"><span class="sec-num">§ 29</span><h2 class="sec-h">Построение биссектрисы угла</h2></div><div id="p29-body"></div></section>
|
||
<section id="sec-p30" class="sec" data-watermark="⊥"><div class="sec-header"><span class="sec-num">§ 30</span><h2 class="sec-h">Середина и перпендикуляр</h2></div><div id="p30-body"></div></section>
|
||
<section id="sec-p31" class="sec" data-watermark="ГМТ"><div class="sec-header"><span class="sec-num">§ 31</span><h2 class="sec-h">Метод геометрических мест точек</h2></div><div id="p31-body"></div></section>
|
||
<section id="sec-final5" class="sec" data-watermark="★"><div class="sec-header"><span class="sec-num" style="background:linear-gradient(135deg,#db2777,#f472b6)">Финал главы</span><h2 class="sec-h">Итоги. 5 боссов главы 5</h2></div><div id="final5-body"></div></section>
|
||
</div>
|
||
|
||
<aside class="col-side" id="col-side"><div id="sidebar-content"></div></aside>
|
||
<div class="col-side-backdrop" id="col-side-backdrop"></div>
|
||
</main>
|
||
|
||
<footer class="foot">Интерактивный учебник «Геометрия 7» · Глава 5 · Задачи на построение · LearnSpace</footer>
|
||
|
||
<div id="ach-popup" class="ach-popup"><svg class="ic" viewBox="0 0 24 24" style="width:22px;height:22px"><polygon points="12,2 22,20 2,20"/></svg><span id="ach-text">Достижение!</span></div>
|
||
|
||
<script>
|
||
'use strict';
|
||
|
||
const STATE = { current:'p27', progress:{p27:0,p28:0,p29:0,p30:0,p31:0,final5:0}, achievements:new Map(), xp:0, level:1 };
|
||
const TOTAL_PARAS = 6;
|
||
const _TB_SLUG = 'geometry-7-ch5';
|
||
|
||
function calcLevel(xp){ return Math.floor(Math.sqrt((xp||0)/100))+1; }
|
||
function _xpForLevel(lv){ return (lv-1)*(lv-1)*100; }
|
||
|
||
const ACH_LABELS = {
|
||
start:'Начало главы 5!',
|
||
p27_done:'Базовые построения освоены!',
|
||
p28_done:'Треугольник по 3 сторонам — твой!',
|
||
p29_done:'Биссектриса циркулем построена!',
|
||
p30_done:'Середина и перпендикуляр — мастер!',
|
||
p31_done:'Метод ГМТ — освоен!',
|
||
ch5_done:'Глава 5 пройдена!',
|
||
geom7_done:'Геометрия 7 полностью пройдена!',
|
||
};
|
||
|
||
function loadProgress(){
|
||
try{
|
||
const s=localStorage.getItem('geometry7_ch5_progress'); if(s) Object.assign(STATE.progress, JSON.parse(s));
|
||
const a=localStorage.getItem('geometry7_ch5_achievements');
|
||
if(a){ const p=JSON.parse(a); if(Array.isArray(p)) p.forEach(id=>STATE.achievements.set(id, ACH_LABELS[id]||id)); else if(p&&typeof p==='object'){ for(const[id,t] of Object.entries(p)) STATE.achievements.set(id,(t&&t!==id)?t:(ACH_LABELS[id]||id)); } }
|
||
STATE.xp=+(localStorage.getItem('geometry7_xp')||0); STATE.level=calcLevel(STATE.xp);
|
||
}catch(e){}
|
||
}
|
||
function saveProgress(){
|
||
try{
|
||
localStorage.setItem('geometry7_ch5_progress', JSON.stringify(STATE.progress));
|
||
localStorage.setItem('geometry7_ch5_achievements', JSON.stringify(Object.fromEntries(STATE.achievements)));
|
||
localStorage.setItem('geometry7_xp', String(STATE.xp));
|
||
}catch(e){}
|
||
}
|
||
function bumpProgress(key, delta){
|
||
STATE.progress[key]=Math.max(0,Math.min(100,(STATE.progress[key]||0)+delta));
|
||
saveProgress(); refreshProgressUI();
|
||
if(STATE.progress[key]>=50) markParaRead(key);
|
||
if(STATE.progress[key]>=100){
|
||
if(key==='p27') achievement('p27_done');
|
||
else if(key==='p28') achievement('p28_done');
|
||
else if(key==='p29') achievement('p29_done');
|
||
else if(key==='p30') achievement('p30_done');
|
||
else if(key==='p31') achievement('p31_done');
|
||
else if(key==='final5'){ achievement('ch5_done'); setTimeout(()=>achievement('geom7_done','Геометрия 7 полностью пройдена!'),1500); }
|
||
}
|
||
}
|
||
|
||
const _markedRead=new Set();
|
||
let _pendingProgressBody=null, _progressTimer=null;
|
||
function _flushProgress(){
|
||
const body=_pendingProgressBody; _pendingProgressBody=null; if(!body) return;
|
||
const tok=(window.LS&&LS.getToken)?LS.getToken():''; if(!tok) return;
|
||
fetch('/api/textbooks/'+_TB_SLUG+'/progress',{method:'POST',headers:{'Content-Type':'application/json','Authorization':'Bearer '+tok},body:JSON.stringify(body),keepalive:true}).catch(()=>{});
|
||
}
|
||
function _queueProgress(patch){ _pendingProgressBody=Object.assign(_pendingProgressBody||{},patch); if(_progressTimer) clearTimeout(_progressTimer); _progressTimer=setTimeout(_flushProgress, 600); }
|
||
function markLastPara(id){ _queueProgress({last_para:id}); }
|
||
function markParaRead(id){ if(_markedRead.has(id)) return; _markedRead.add(id); _queueProgress({mark_read:id}); }
|
||
window.addEventListener('beforeunload', _flushProgress);
|
||
|
||
function addXp(n,src){
|
||
if(!n) return;
|
||
const prev=STATE.level; STATE.xp=Math.max(0,(STATE.xp||0)+n); STATE.level=calcLevel(STATE.xp);
|
||
saveProgress(); refreshProgressUI();
|
||
if(window.LS&&window.LS.xp) window.LS.xp.add(n,'geometry7-ch5-'+(src||'misc'));
|
||
if(STATE.level>prev){
|
||
const pop=document.getElementById('ach-popup');
|
||
if(pop){ document.getElementById('ach-text').textContent='Уровень '+STATE.level+'!'; pop.classList.add('show'); setTimeout(()=>pop.classList.remove('show'),2600); }
|
||
}
|
||
}
|
||
|
||
function refreshProgressUI(){
|
||
const total=Math.round(Object.values(STATE.progress).reduce((a,b)=>a+b,0)/TOTAL_PARAS);
|
||
const f=document.getElementById('hero-hp-fill'); if(f) f.style.width=total+'%';
|
||
const t=document.getElementById('hero-hp-text'); if(t) t.textContent=total+'% пройдено';
|
||
document.querySelectorAll('[data-prog-card]').forEach(el=>{ const k=el.dataset.progCard; const fl=el.querySelector('.psel-prog-fill'); if(fl) fl.style.width=(STATE.progress[k]||0)+'%'; });
|
||
const xpBadge=document.getElementById('hero-xp-badge');
|
||
if(xpBadge){ xpBadge.innerHTML='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:13px;height:13px"><polygon points="12 2 22 20 2 20"/></svg> Ур. '+STATE.level+' \xb7 '+(STATE.xp||0)+' XP'; }
|
||
if(STATE.current && document.getElementById('sidebar-content')){ try{ buildSidebar(STATE.current); }catch(e){} }
|
||
}
|
||
|
||
function achievement(id,text){
|
||
if(STATE.achievements.has(id)) return;
|
||
STATE.achievements.set(id, text||ACH_LABELS[id]||id); saveProgress();
|
||
const pop=document.getElementById('ach-popup');
|
||
if(pop){ document.getElementById('ach-text').textContent=text||ACH_LABELS[id]||id; pop.classList.add('show'); setTimeout(()=>pop.classList.remove('show'),3300); }
|
||
addXp(20,'ach-'+id);
|
||
}
|
||
|
||
const PARAS = [
|
||
{ id:'p27', num:'§ 27', name:'Простейшие построения', sub:'циркуль + линейка' },
|
||
{ id:'p28', num:'§ 28', name:'Треугольник по 3 сторонам', sub:'отрезок + 2 окружн.' },
|
||
{ id:'p29', num:'§ 29', name:'Биссектриса угла', sub:'3 дуги + точка M' },
|
||
{ id:'p30', num:'§ 30', name:'Середина и ⊥', sub:'2 окружн. → 2 точки' },
|
||
{ id:'p31', num:'§ 31', name:'Метод ГМТ', sub:'2 ГМТ → пересечение' },
|
||
{ id:'final5', num:'★', name:'Финал главы', sub:'Итоги \xB7 5 боссов', final:true },
|
||
];
|
||
|
||
function buildParaSelector(){
|
||
const g=document.getElementById('psel-grid'); g.innerHTML='';
|
||
PARAS.forEach(p=>{
|
||
const card=document.createElement('div');
|
||
card.className='psel-card'+(p.final?' final':'');
|
||
card.dataset.id=p.id; card.dataset.progCard=p.id;
|
||
card.innerHTML='<div class="psel-num">'+p.num+'</div><div class="psel-name">'+p.name+'</div><div class="psel-prog"><div class="psel-prog-fill"></div></div>';
|
||
card.addEventListener('click', ()=>goTo(p.id));
|
||
g.appendChild(card);
|
||
});
|
||
if(window.renderMathInElement) try{ renderMath(g); }catch(e){}
|
||
}
|
||
|
||
const BUILT=new Set();
|
||
const BUILDERS = { p27:()=>buildP27(), p28:()=>buildP28(), p29:()=>buildP29(), p30:()=>buildP30(), p31:()=>buildP31(), final5:()=>buildFinal5() };
|
||
function ensureBuilt(id){ if(BUILT.has(id)) return; const fn=BUILDERS[id]; if(fn){ fn(); BUILT.add(id); } }
|
||
function goTo(id){
|
||
STATE.current=id; ensureBuilt(id);
|
||
document.querySelectorAll('.sec').forEach(s=>s.classList.remove('active'));
|
||
const el=document.getElementById('sec-'+id); if(el) el.classList.add('active');
|
||
document.querySelectorAll('.psel-card').forEach(c=>c.classList.toggle('active', c.dataset.id===id));
|
||
buildSidebar(id);
|
||
window.scrollTo({top:0,behavior:'smooth'});
|
||
if((STATE.progress[id]||0)<10) bumpProgress(id, 10);
|
||
if(window.renderMathInElement) setTimeout(()=>renderMath(el), 0);
|
||
markLastPara(id);
|
||
}
|
||
|
||
const SIDEBARS = {
|
||
p27:{title:'Шпаргалка \xA727',rows:[
|
||
['Линейка','прямая через 2 точки (без делений!)'],
|
||
['Циркуль','окружность по центру и радиусу'],
|
||
['Базовых опер.','3: прямая, окружность, пересеч.'],
|
||
['Этапы решения','Анализ → Построение → Доказ. → Исслед.'],
|
||
]},
|
||
p28:{title:'Шпаргалка \xA728',rows:[
|
||
['Дано','3 отрезка $a, b, c$'],
|
||
['Построить','$\\triangle ABC$'],
|
||
['Условие','$|b-c| < a < b+c$'],
|
||
['Алгоритм','отрезок $c$ + 2 окружн.'],
|
||
]},
|
||
p29:{title:'Шпаргалка \xA729',rows:[
|
||
['Дано','$\\angle AOB$'],
|
||
['Построить','биссектрису'],
|
||
['Шагов','4: 3 окружн. + луч'],
|
||
['Основа доказ.','ССС'],
|
||
]},
|
||
p30:{title:'Шпаргалка \xA730',rows:[
|
||
['Радиус','$r > \\frac{1}{2}AB$'],
|
||
['Окружностей','2 (из $A$ и $B$ равных радиусов)'],
|
||
['Точек пересеч.','2 ($P_1$ и $P_2$)'],
|
||
['Серед. $\\perp$','прямая $P_1 P_2$'],
|
||
]},
|
||
p31:{title:'Шпаргалка \xA731',rows:[
|
||
['Метод ГМТ','точка $=$ ГМТ$_1 \\cap$ ГМТ$_2$'],
|
||
['Окружность','$\\{X : OX = r\\}$'],
|
||
['Серед. $\\perp$','$\\{X : XA = XB\\}$'],
|
||
['Биссектриса','$\\{X : d(X,a) = d(X,b)\\}$'],
|
||
]},
|
||
final5:{title:'Финал главы',rows:[
|
||
['\xA727–\xA731','теория главы 5'],
|
||
['Боссов','5'],
|
||
['Награда','+100 XP + ачивка «Геометрия 7 пройдена»'],
|
||
]},
|
||
};
|
||
|
||
const TIPS=[
|
||
{sec:'p27',html:'<b>Линейка</b> у нас <b>без делений</b> — она только проводит прямую через 2 точки. <b>Циркуль</b> переносит длины и рисует окружности.'},
|
||
{sec:'p28',html:'Без неравенства треугольника <b>построение невозможно</b>: окружности не пересекутся. Сначала проверь $|b-c| < a < b+c$.'},
|
||
{sec:'p29',html:'Биссектриса строится через <b>3 окружности</b>: одна из вершины, две из точек пересечения со сторонами. Доказательство — по ССС.'},
|
||
{sec:'p30',html:'Серединный $\\perp$ = ось симметрии отрезка. Две окружности <b>равного радиуса</b> ($r > \\frac{1}{2}AB$) из концов.'},
|
||
{sec:'p31',html:'<b>Метод ГМТ:</b> ищешь точку с двумя свойствами → строишь ГМТ для каждого → пересекаешь.'},
|
||
{sec:'final5',html:'Победишь всех 5 боссов — получишь <b>финальную ачивку</b> «Геометрия 7 полностью пройдена»!'},
|
||
];
|
||
|
||
function buildSidebar(id){
|
||
const box=document.getElementById('sidebar-content');
|
||
const sb=SIDEBARS[id]||SIDEBARS.p27;
|
||
let html='';
|
||
const xpForLv=_xpForLevel(STATE.level), xpNext=_xpForLevel(STATE.level+1);
|
||
const xpInLv=STATE.xp-xpForLv, xpRange=xpNext-xpForLv;
|
||
const xpPct=xpRange>0?Math.round(xpInLv/xpRange*100):100;
|
||
html+='<div class="xp-card"><div class="xp-card-title"><span>XP-прогресс</span><span class="xp-level">Ур. '+STATE.level+'</span></div><div class="xp-bar"><div class="xp-fill" style="width:'+xpPct+'%"></div></div><div class="xp-nums"><span>'+STATE.xp+' XP</span><span>'+xpNext+' XP</span></div></div>';
|
||
html+='<div class="sidecard"><h4>'+sb.title+'</h4>';
|
||
sb.rows.forEach(([k,v])=>{ html+='<div class="sidecard-row"><b>'+k+'</b>'+(v?' — '+v:'')+'</div>'; });
|
||
html+='</div>';
|
||
const tip=TIPS.find(t=>t.sec===id)||TIPS[0];
|
||
html+='<div class="sidecard" style="background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--pri-soft));border-color:var(--warn,#f59e0b)"><h4 style="color:#92400e">Подсказка</h4><div class="sidecard-row" style="font-size:.84rem;line-height:1.55">'+tip.html+'</div></div>';
|
||
if(STATE.achievements.size>0){
|
||
html+='<div class="sidecard"><h4>Достижения</h4>';
|
||
[...STATE.achievements.values()].slice(-4).forEach(text=>{ html+='<div class="sidecard-row" style="font-size:.78rem;color:var(--ok)">✓ '+text+'</div>'; });
|
||
html+='</div>';
|
||
}
|
||
box.innerHTML=html;
|
||
if(window.renderMathInElement) try{ renderMath(box); }catch(e){}
|
||
}
|
||
|
||
function initTheme(){
|
||
const t=localStorage.getItem('geometry7_ch5_theme')||'light';
|
||
if(t==='dark') document.documentElement.classList.add('dark');
|
||
document.getElementById('theme-lab').textContent=t==='dark'?'Светлая':'Тёмная';
|
||
document.getElementById('theme-btn').addEventListener('click', ()=>{
|
||
document.documentElement.classList.toggle('dark');
|
||
const dark=document.documentElement.classList.contains('dark');
|
||
localStorage.setItem('geometry7_ch5_theme', dark?'dark':'light');
|
||
document.getElementById('theme-lab').textContent=dark?'Светлая':'Тёмная';
|
||
});
|
||
}
|
||
|
||
function renderMath(root){ if(window.renderMathInElement){ try{ renderMathInElement(root, {delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false}); }catch(e){} } }
|
||
function feedback(elm, ok, text){ if(!elm) return; elm.className='feedback '+(ok?'ok':'fail'); elm.innerHTML=text||(ok?'✓ Верно!':'✗ Неверно'); elm.style.display='block'; try{renderMath(elm);}catch(e){} }
|
||
|
||
const ICONS = {
|
||
theory:'<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>',
|
||
algo:'<svg class="ic" viewBox="0 0 24 24"><polyline points="17 11 21 7 17 3"/><line x1="21" y1="7" x2="9" y2="7"/><polyline points="7 13 3 17 7 21"/><line x1="3" y1="17" x2="15" y2="17"/></svg>',
|
||
rule:'<svg class="ic" viewBox="0 0 24 24"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg>',
|
||
example:'<svg class="ic" viewBox="0 0 24 24"><path d="M9 18h6"/><path d="M10 22h4"/><path d="M12 2a7 7 0 0 0-4 13c1 1 2 2 2 4h4c0-2 1-3 2-4a7 7 0 0 0-4-13z"/></svg>',
|
||
};
|
||
function makeCard(kind, title, num, body){
|
||
const labels={theory:'Теория',algo:'Алгоритм',rule:'Правило',example:'Пример'};
|
||
return '<div class="card"><div class="card-header"><div class="card-icon '+kind+'">'+ICONS[kind]+'</div><div class="card-title">'+(labels[kind]||'')+(title&&title!==labels[kind]?' \xb7 '+title:'')+'</div>'+(num?'<div class="card-num">'+num+'</div>':'')+'</div><div class="card-body">'+body+'</div></div>';
|
||
}
|
||
function secNav(prev, next){
|
||
const NAMES={p27:'\xA727',p28:'\xA728',p29:'\xA729',p30:'\xA730',p31:'\xA731',final5:'Финал'};
|
||
let h='<div class="sec-nav">';
|
||
h+=prev?'<button class="btn" onclick="goTo(\''+prev+'\')"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> '+NAMES[prev]+'</button>':'<span></span>';
|
||
h+=next?'<button class="btn primary" onclick="goTo(\''+next+'\')">'+NAMES[next]+' <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></button>':'<span></span>';
|
||
h+='</div>'; return h;
|
||
}
|
||
|
||
function makeTrainer(opts){
|
||
let i=0, score=0;
|
||
const Q=opts.questions;
|
||
const parser = opts.parser || (v => parseFloat(String(v).replace(',','.')));
|
||
function show(){
|
||
if(i >= Q.length){
|
||
document.getElementById(opts.idPrefix+'-q').innerHTML = '<b>Готово!</b> Результат: '+score+' / '+Q.length;
|
||
if(opts.onComplete) opts.onComplete(score, Q.length);
|
||
return;
|
||
}
|
||
document.getElementById(opts.idPrefix+'-i').textContent = (i+1);
|
||
document.getElementById(opts.idPrefix+'-s').textContent = score;
|
||
document.getElementById(opts.idPrefix+'-q').innerHTML = Q[i].q;
|
||
document.getElementById(opts.idPrefix+'-ans').value = '';
|
||
renderMath(document.getElementById(opts.idPrefix+'-q'));
|
||
document.getElementById(opts.idPrefix+'-fb').style.display = 'none';
|
||
}
|
||
function go(){
|
||
if(i >= Q.length) return;
|
||
const fb = document.getElementById(opts.idPrefix+'-fb');
|
||
const raw = document.getElementById(opts.idPrefix+'-ans').value.trim();
|
||
if(raw === ''){ feedback(fb, false, '✗ Введи ответ.'); return; }
|
||
const expected = Q[i].a;
|
||
let ok = false;
|
||
if(typeof expected === 'function') ok = expected(raw);
|
||
else { const got = parser(raw); ok = !isNaN(got) && Math.abs(got - expected) < 1e-6; }
|
||
if(ok){ score++; feedback(fb, true, '✓ Верно! Дальше ▶'); }
|
||
else feedback(fb, false, '✗ Неверно. Правильно: <b>'+(Q[i].show||expected)+'</b>. Дальше ▶');
|
||
document.getElementById(opts.idPrefix+'-s').textContent = score;
|
||
i++; setTimeout(show, 1100);
|
||
}
|
||
document.getElementById(opts.idPrefix+'-go').addEventListener('click', go);
|
||
document.getElementById(opts.idPrefix+'-ans').addEventListener('keydown', e=>{ if(e.key==='Enter') go(); });
|
||
const restart = document.getElementById(opts.idPrefix+'-start');
|
||
if(restart) restart.addEventListener('click', ()=>{ i=0; score=0; show(); });
|
||
show();
|
||
}
|
||
function trainerHTML(idPrefix, total, placeholder){
|
||
return '<div class="score-display"><span>Задача <b id="'+idPrefix+'-i">1</b> / '+total+'</span><span>Очки: <b id="'+idPrefix+'-s">0</b> / '+total+'</span></div>'
|
||
+'<div id="'+idPrefix+'-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.05rem;margin-bottom:10px;text-align:center"></div>'
|
||
+'<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">'
|
||
+'<input type="text" id="'+idPrefix+'-ans" class="tinp" placeholder="'+(placeholder||'Ответ')+'" style="width:140px;text-align:center">'
|
||
+'<button class="btn primary" id="'+idPrefix+'-go">Проверить</button>'
|
||
+'<button class="btn" id="'+idPrefix+'-start">Заново</button>'
|
||
+'</div><div class="feedback" id="'+idPrefix+'-fb"></div>';
|
||
}
|
||
function readButton(paraId){
|
||
return '<div style="margin-top:18px;display:flex;justify-content:center">'
|
||
+'<button class="btn primary" id="'+paraId+'-read-btn">'
|
||
+'<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>'
|
||
+' Я прочитал \xA7'+paraId.replace('p','')+' (+10 XP)'
|
||
+'</button></div>';
|
||
}
|
||
function wireReadBtn(paraId){
|
||
document.getElementById(paraId+'-read-btn').addEventListener('click', ()=>{
|
||
addXp(10, paraId+'-read'); bumpProgress(paraId, 30);
|
||
const b=document.getElementById(paraId+'-read-btn'); b.textContent='Прочитано! +10 XP'; b.disabled=true; b.style.opacity=.6;
|
||
});
|
||
}
|
||
|
||
function initSidebarToggle(){
|
||
const side=document.getElementById('col-side'),back=document.getElementById('col-side-backdrop'),btn=document.getElementById('sidebar-btn');
|
||
if(!side||!btn) return;
|
||
function open(){ side.classList.add('open'); back.classList.add('show'); }
|
||
function close(){ side.classList.remove('open'); back.classList.remove('show'); }
|
||
btn.addEventListener('click',()=>{ if(side.classList.contains('open')) close(); else open(); });
|
||
back.addEventListener('click',close);
|
||
document.addEventListener('keydown',e=>{ if(e.key==='Escape') close(); });
|
||
}
|
||
|
||
function init(){
|
||
loadProgress(); initTheme(); initSidebarToggle();
|
||
buildParaSelector(); refreshProgressUI(); goTo('p27');
|
||
setTimeout(()=>achievement('start','Начало главы 5!'), 600);
|
||
}
|
||
document.addEventListener('DOMContentLoaded', init);
|
||
|
||
|
||
/* ============================================================
|
||
\xA7 27 — Простейшие построения циркулем и линейкой
|
||
============================================================ */
|
||
function buildP27(){
|
||
const box = document.getElementById('p27-body');
|
||
const G = window.GEOM7;
|
||
let html = '';
|
||
|
||
let svgTools='';
|
||
if(G){
|
||
/* Две панели: ЛИНЕЙКА слева, ЦИРКУЛЬ справа.
|
||
В каждой панели: инструмент сверху → стрелка ↓ → результат снизу. */
|
||
const b=G.svgBox(340,210,{id:'p27-tools',cell:0,grid:false,bg:'#fff'});
|
||
/* Вертикальный разделитель */
|
||
let s=b.open
|
||
+ '<line x1="170" y1="15" x2="170" y2="195" stroke="#e2e8f0" stroke-width="1" stroke-dasharray="3 4"/>'
|
||
/* ===== ЛЕВАЯ ПАНЕЛЬ — ЛИНЕЙКА ===== */
|
||
+ '<text x="85" y="25" text-anchor="middle" font-size="11" font-family="Unbounded,Inter,sans-serif" font-weight="800" fill="#be185d">ЛИНЕЙКА</text>'
|
||
/* Корпус линейки */
|
||
+ '<rect x="22" y="33" width="126" height="20" fill="#fbcfe8" stroke="#be185d" stroke-width="1.8" rx="2"/>';
|
||
/* Маленькие штрихи на верхнем крае линейки (без чисел) */
|
||
for(let i=0;i<=9;i++){ const x=22+i*14; s+='<line x1="'+x+'" y1="33" x2="'+x+'" y2="'+(33+(i%2===0?7:4))+'" stroke="#be185d" stroke-width="1"/>'; }
|
||
s += '<text x="85" y="70" text-anchor="middle" font-size="8" fill="#7c2d52">(без делений)</text>'
|
||
/* Стрелка ↓ */
|
||
+ '<path d="M 85 80 L 85 100 M 80 95 L 85 100 L 90 95" fill="none" stroke="#10b981" stroke-width="2.2" stroke-linecap="round"/>'
|
||
/* Результат: 2 точки и прямая через них */
|
||
+ '<line x1="30" y1="148" x2="140" y2="158" stroke="#0891b2" stroke-width="2.2" stroke-linecap="round"/>'
|
||
+ G.point(30,148,'A',{color:'#1e293b',dx:-12,dy:-2,fontSize:11,r:3.5})
|
||
+ G.point(140,158,'B',{color:'#1e293b',dx:6,dy:-2,fontSize:11,r:3.5})
|
||
+ '<text x="85" y="188" text-anchor="middle" font-size="10" font-family="Unbounded,Inter,sans-serif" font-weight="700" fill="#0891b2">прямая через 2 точки</text>'
|
||
/* ===== ПРАВАЯ ПАНЕЛЬ — ЦИРКУЛЬ ===== */
|
||
+ '<text x="255" y="25" text-anchor="middle" font-size="11" font-family="Unbounded,Inter,sans-serif" font-weight="800" fill="#be185d">ЦИРКУЛЬ</text>'
|
||
/* Две ноги от шарнира вниз */
|
||
+ '<line x1="255" y1="33" x2="228" y2="78" stroke="#be185d" stroke-width="3" stroke-linecap="round"/>'
|
||
+ '<line x1="255" y1="33" x2="282" y2="78" stroke="#be185d" stroke-width="3" stroke-linecap="round"/>'
|
||
/* Шарнир */
|
||
+ '<circle cx="255" cy="33" r="3.5" fill="#fbcfe8" stroke="#be185d" stroke-width="1.5"/>'
|
||
/* Игла (треугольник) на левой ноге */
|
||
+ '<polygon points="228,78 224,85 232,85" fill="#dc2626"/>'
|
||
/* Грифель на правой ноге */
|
||
+ '<polygon points="282,78 278,85 286,85" fill="#0f172a"/>'
|
||
/* Стрелка ↓ */
|
||
+ '<path d="M 255 92 L 255 110 M 250 105 L 255 110 L 260 105" fill="none" stroke="#10b981" stroke-width="2.2" stroke-linecap="round"/>'
|
||
/* Результат: окружность с центром O и пунктирным радиусом */
|
||
+ '<circle cx="255" cy="148" r="26" fill="none" stroke="#7c3aed" stroke-width="2"/>'
|
||
+ '<line x1="255" y1="148" x2="273" y2="129" stroke="#7c3aed" stroke-width="1.3" stroke-dasharray="3 2"/>'
|
||
+ '<text x="265" y="138" font-size="9" fill="#6d28d9" font-weight="700">r</text>'
|
||
+ G.point(255,148,'O',{color:'#1e293b',dx:-10,dy:-2,fontSize:11,r:3})
|
||
+ '<text x="255" y="188" text-anchor="middle" font-size="10" font-family="Unbounded,Inter,sans-serif" font-weight="700" fill="#7c3aed">окружность (O, r)</text>'
|
||
+ b.close;
|
||
svgTools = s;
|
||
}
|
||
|
||
html += makeCard('theory', 'Что такое задача на построение?', '27.1', `
|
||
<p><b>Задача на построение</b> — это задача, в которой требуется построить некоторую фигуру (точку, отрезок, окружность, треугольник, ...) с заданными свойствами, используя только два инструмента: <b>циркуль</b> и <b>линейку</b>.</p>
|
||
<div class="svg-host">`+svgTools+`</div>
|
||
<p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px"><b>Важно.</b> Линейка <b>без делений</b>. Она лишь проводит прямую через две данные точки. Циркуль рисует окружность по заданным центру и радиусу.</p>`);
|
||
|
||
html += makeCard('rule', '3 базовых операции', '27.2', `
|
||
<p>Любое построение состоит из последовательного применения 3 простых операций:</p>
|
||
<ol style="padding-left:22px;line-height:1.9">
|
||
<li><b>Через 2 точки провести прямую</b> (отрезок, луч) — это даёт линейка.</li>
|
||
<li><b>Из точки $O$ радиусом $r$ нарисовать окружность</b> — это даёт циркуль.</li>
|
||
<li><b>Отметить точку пересечения</b> двух построенных линий (прямых или окружностей).</li>
|
||
</ol>
|
||
<p>Всё! Из этих трёх «кирпичиков» строятся <b>все</b> задачи на построение.</p>`);
|
||
|
||
html += makeCard('algo', 'Структура решения задачи', '27.3', `
|
||
<p>Полное решение задачи на построение состоит из <b>4 этапов</b>:</p>
|
||
<ol style="padding-left:22px;line-height:1.9">
|
||
<li><b>Анализ.</b> Предполагаем, что задача решена. Делаем чертёж «как должно получиться» и ищем связи.</li>
|
||
<li><b>Построение.</b> Записываем шаги: «Шаг 1: ..., Шаг 2: ...»</li>
|
||
<li><b>Доказательство.</b> Объясняем, почему получившаяся фигура удовлетворяет условию.</li>
|
||
<li><b>Исследование.</b> Когда задача имеет решение и сколько решений (одно, несколько, ни одного).</li>
|
||
</ol>
|
||
<p>В школе мы обычно сосредотачиваемся на этапах <b>Построение</b> и <b>Доказательство</b>.</p>`);
|
||
|
||
html += makeCard('example', 'Простая задача', '27.4', `
|
||
<p><b>Задача.</b> Дан отрезок $AB$. Построить отрезок $CD$ длиной $AB$ на данном луче от его начала $C$.</p>
|
||
<div class="steps">
|
||
<div class="step"><div class="step-text">На луче отмечаем начало — точку $C$.</div></div>
|
||
<div class="step"><div class="step-text">Циркулем «измеряем» $AB$: ставим иглу в $A$, грифель в $B$.</div></div>
|
||
<div class="step"><div class="step-text">Не меняя раствор циркуля, ставим иглу в $C$ и проводим окружность.</div></div>
|
||
<div class="step"><div class="step-text">Точку пересечения окружности с лучом обозначаем $D$. Тогда $CD = AB$.</div></div>
|
||
</div>
|
||
<p><b>Доказательство.</b> $CD$ — радиус построенной окружности, $AB$ — тоже её радиус (мы взяли именно такой раствор). Значит $CD = AB$.</p>`);
|
||
|
||
html += '<div class="wg" id="p27-iv1">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Можно ли так делать?</div></div>'
|
||
+'<div class="wg-help">Допускается ли действие только циркулем и линейкой (без делений)?</div>'
|
||
+'<div class="score-display"><span>Задача <b id="p27-iv1-i">1</b> / 6</span><span>Очки: <b id="p27-iv1-s">0</b> / 6</span></div>'
|
||
+'<div id="p27-iv1-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;text-align:center;margin-bottom:10px"></div>'
|
||
+'<div style="display:flex;gap:8px;justify-content:center"><button class="btn primary" id="p27-iv1-y" style="background:#10b981;border-color:#10b981">Можно</button><button class="btn primary" id="p27-iv1-n" style="background:#dc2626;border-color:#dc2626">Нельзя</button></div>'
|
||
+'<div class="feedback" id="p27-iv1-fb"></div></div>';
|
||
|
||
html += '<div class="wg" id="p27-iv2">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Терминология</div></div>'
|
||
+'<div class="wg-help">Введи слово или число.</div>'
|
||
+trainerHTML('p27-iv2', 5, 'ответ')
|
||
+'</div>';
|
||
|
||
html += secNav(null, 'p28') + readButton('p27');
|
||
box.innerHTML = html; renderMath(box);
|
||
|
||
(function(){
|
||
const Q=[
|
||
{ e:'Провести прямую через 2 данные точки.', ok:true, why:'Базовая операция линейкой.' },
|
||
{ e:'Измерить длину отрезка линейкой в сантиметрах.', ok:false, why:'Линейка <b>без делений</b>.' },
|
||
{ e:'Нарисовать окружность с центром в $O$ радиусом $r = AB$.', ok:true, why:'Циркулем переносим длину.' },
|
||
{ e:'Отметить точку пересечения двух построенных линий.', ok:true, why:'Это базовая операция.' },
|
||
{ e:'Транспортиром отложить угол $37°$.', ok:false, why:'Транспортир не входит в инструменты.' },
|
||
{ e:'Из $A$ провести окружность радиуса $5$ см, измерив циркулем по линейке.', ok:false, why:'У линейки нет делений — мы не можем взять «5 см».' },
|
||
];
|
||
let i=0,score=0;
|
||
function show(){
|
||
if(i>=Q.length){ document.getElementById('p27-iv1-q').innerHTML='<b>Готово!</b> '+score+' / '+Q.length; if(score===Q.length){addXp(15,'p27-iv1');bumpProgress('p27',25);} else if(score>=4){addXp(8,'p27-iv1');bumpProgress('p27',12);} return; }
|
||
document.getElementById('p27-iv1-i').textContent=(i+1);
|
||
document.getElementById('p27-iv1-s').textContent=score;
|
||
document.getElementById('p27-iv1-q').innerHTML=Q[i].e;
|
||
renderMath(document.getElementById('p27-iv1-q'));
|
||
document.getElementById('p27-iv1-fb').style.display='none';
|
||
}
|
||
function ans(yes){
|
||
if(i>=Q.length) return;
|
||
const fb=document.getElementById('p27-iv1-fb');
|
||
if(yes===Q[i].ok){ score++; feedback(fb,true,'✓ Верно! '+Q[i].why); }
|
||
else feedback(fb,false,'✗ '+(Q[i].ok?'Можно: ':'Нельзя: ')+Q[i].why);
|
||
document.getElementById('p27-iv1-s').textContent=score;
|
||
i++; setTimeout(show,1500);
|
||
}
|
||
document.getElementById('p27-iv1-y').addEventListener('click',()=>ans(true));
|
||
document.getElementById('p27-iv1-n').addEventListener('click',()=>ans(false));
|
||
show();
|
||
})();
|
||
|
||
makeTrainer({
|
||
idPrefix:'p27-iv2',
|
||
parser:(v)=>v,
|
||
questions:[
|
||
{ q:'Сколько инструментов используется в задачах на построение?', a:(v)=>+v===2, show:'2 (циркуль + линейка)' },
|
||
{ q:'Какой инструмент проводит прямую через 2 точки?', a:(v)=>String(v).trim().toLowerCase().startsWith('линей'), show:'линейка' },
|
||
{ q:'Какой инструмент рисует окружность?', a:(v)=>String(v).trim().toLowerCase().startsWith('цирк'), show:'циркуль' },
|
||
{ q:'Имеются ли деления на линейке? «да» или «нет»', a:(v)=>String(v).trim().toLowerCase().startsWith('н'), show:'нет' },
|
||
{ q:'Сколько этапов в полном решении задачи?', a:(v)=>+v===4, show:'4 (Анализ, Построение, Доказательство, Исследование)' },
|
||
],
|
||
onComplete:(s,n)=>{ if(s===n){addXp(15,'p27-iv2');bumpProgress('p27',25);} else if(s>=3){addXp(8,'p27-iv2');bumpProgress('p27',12);} }
|
||
});
|
||
|
||
wireReadBtn('p27');
|
||
}
|
||
|
||
|
||
/* ============================================================
|
||
\xA7 28 — Построение треугольника по 3 сторонам
|
||
============================================================ */
|
||
function buildP28(){
|
||
const box = document.getElementById('p28-body');
|
||
const G = window.GEOM7;
|
||
let html = '';
|
||
|
||
let svgBuild='';
|
||
if(G){
|
||
/* Масштаб 25 px/ед; a=5 (BC), b=4 (AC), c=6 (AB)
|
||
A={30,160}, B={180,160}, C найден из системы */
|
||
const b=G.svgBox(280,200,{id:'p28-build',cell:20});
|
||
const A={x:30,y:160}, B={x:180,y:160};
|
||
/* AC=100, BC=125. Решаем:
|
||
Cx = (100²-125²+(180²-30²))/(2*(180-30)) = (10000-15625+(32400-900))/300 = (-5625+31500)/300 = 25875/300 ≈ 86.25
|
||
Cy = 160 - sqrt(100² - (86.25-30)²) = 160 - sqrt(10000 - 3164) = 160 - sqrt(6836) ≈ 160 - 82.68 = 77.3 */
|
||
const C={x:86,y:77};
|
||
svgBuild = b.open
|
||
/* Отрезок AB */
|
||
+ G.segment(A,B,{color:'#be185d',width:2.5,label:'c = AB',labelOffset:18,labelColor:'#be185d'})
|
||
/* Окружность из A радиуса b=100 */
|
||
+ G.circle(A,100,{color:'#7c3aed',width:1.5,dash:'4 3'})
|
||
+ '<text x="55" y="75" font-size="10" fill="#7c3aed" font-weight="700">r = b</text>'
|
||
/* Окружность из B радиуса a=125 */
|
||
+ G.circle(B,125,{color:'#0891b2',width:1.5,dash:'4 3'})
|
||
+ '<text x="200" y="55" font-size="10" fill="#0891b2" font-weight="700">r = a</text>'
|
||
/* Стороны от A и B к C */
|
||
+ G.segment(A,C,{color:'#7c3aed',width:2.5})
|
||
+ G.segment(B,C,{color:'#0891b2',width:2.5})
|
||
+ G.polygon([A,B,C],{color:'#be185d',width:0.01,fill:'rgba(219,39,119,.06)'})
|
||
+ G.point(A.x,A.y,'A',{color:'#1e293b',dx:-12,dy:14,fontSize:12})
|
||
+ G.point(B.x,B.y,'B',{color:'#1e293b',dx:8,dy:14,fontSize:12})
|
||
+ G.point(C.x,C.y,'C',{color:'#1e293b',dx:-4,dy:-8,fontSize:12})
|
||
+ b.close;
|
||
}
|
||
|
||
html += makeCard('theory', 'Задача', '28.1', `
|
||
<p><b>Дано:</b> три отрезка $a$, $b$, $c$.</p>
|
||
<p><b>Построить:</b> треугольник $ABC$, у которого $BC = a$, $AC = b$, $AB = c$.</p>
|
||
<p><b>Условие существования:</b> неравенство треугольника $|b-c| < a < b+c$ (и аналогично для $b$, $c$). Без него окружности не пересекутся.</p>`);
|
||
|
||
html += makeCard('algo', 'Построение', '28.2', `
|
||
<div class="steps">
|
||
<div class="step"><div class="step-text">На произвольной прямой откладываем отрезок $AB$ длиной $c$ (используя циркуль для переноса длины).</div></div>
|
||
<div class="step"><div class="step-text">Из точки $A$ как центра проводим <b>окружность радиуса $b$</b>.</div></div>
|
||
<div class="step"><div class="step-text">Из точки $B$ как центра проводим <b>окружность радиуса $a$</b>.</div></div>
|
||
<div class="step"><div class="step-text">Точку пересечения окружностей обозначаем $C$. Проводим отрезки $AC$ и $BC$.</div></div>
|
||
</div>
|
||
<div class="svg-host">`+svgBuild+`</div>
|
||
<p><b>Готово!</b> Треугольник $ABC$ построен.</p>`);
|
||
|
||
html += makeCard('rule', 'Доказательство', '28.3', `
|
||
<p>В построенном $\\triangle ABC$:</p>
|
||
<ul style="padding-left:22px;line-height:1.85">
|
||
<li>$AB = c$ — построено на шаге 1;</li>
|
||
<li>$AC = b$ — $C$ лежит на окружности с центром $A$ радиуса $b$;</li>
|
||
<li>$BC = a$ — $C$ лежит на окружности с центром $B$ радиуса $a$.</li>
|
||
</ul>
|
||
<p>Все три стороны соответствуют требуемым. ■</p>
|
||
<p style="background:var(--warn-bg);padding:9px 13px;border-radius:8px;border-left:4px solid var(--warn)"><b>Исследование.</b> Если $a + b > c$, $a + c > b$, $b + c > a$ — задача имеет ровно <b>1 решение</b> (с точностью до симметрии относительно $AB$). Если одно из неравенств нарушено — <b>решений нет</b>.</p>`);
|
||
|
||
html += makeCard('example', 'Когда построение невозможно', '28.4', `
|
||
<p><b>Задача.</b> Построить треугольник со сторонами $3, 4, 8$.</p>
|
||
<p><b>Проверка.</b> $3 + 4 = 7 < 8$ — неравенство треугольника <b>нарушено</b>.</p>
|
||
<p><b>Что произойдёт?</b> После построения $AB = 8$ окружности радиусов $3$ и $4$ <b>не пересекутся</b> — между ними останется зазор (см. §22). Построить треугольник <b>невозможно</b>.</p>`);
|
||
|
||
html += '<div class="wg" id="p28-iv1">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Можно ли построить?</div></div>'
|
||
+'<div class="wg-help">По 3 сторонам реши: можно ли построить треугольник.</div>'
|
||
+'<div class="score-display"><span>Задача <b id="p28-iv1-i">1</b> / 6</span><span>Очки: <b id="p28-iv1-s">0</b> / 6</span></div>'
|
||
+'<div id="p28-iv1-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.05rem;text-align:center;margin-bottom:10px"></div>'
|
||
+'<div style="display:flex;gap:8px;justify-content:center"><button class="btn primary" id="p28-iv1-y" style="background:#10b981;border-color:#10b981">Можно</button><button class="btn primary" id="p28-iv1-n" style="background:#dc2626;border-color:#dc2626">Нельзя</button></div>'
|
||
+'<div class="feedback" id="p28-iv1-fb"></div></div>';
|
||
|
||
html += '<div class="wg" id="p28-iv2">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Какой радиус циркуля?</div></div>'
|
||
+'<div class="wg-help">В построении $\\triangle$ со сторонами $a, b, c$.</div>'
|
||
+trainerHTML('p28-iv2', 5, 'число / слово')
|
||
+'</div>';
|
||
|
||
html += secNav('p27', 'p29') + readButton('p28');
|
||
box.innerHTML = html; renderMath(box);
|
||
|
||
(function(){
|
||
const Q=[
|
||
{ e:'$a = 5, b = 6, c = 7$', ok:true, why:'Все неравенства выполнены.' },
|
||
{ e:'$a = 3, b = 4, c = 8$', ok:false, why:'$3+4 < 8$.' },
|
||
{ e:'$a = 7, b = 7, c = 7$', ok:true, why:'Равносторонний.' },
|
||
{ e:'$a = 2, b = 5, c = 10$', ok:false, why:'$2+5 < 10$.' },
|
||
{ e:'$a = 4, b = 4, c = 7$', ok:true, why:'$4+4 > 7$ ✓.' },
|
||
{ e:'$a = 1, b = 1, c = 3$', ok:false, why:'$1+1 < 3$.' },
|
||
];
|
||
let i=0,score=0;
|
||
function show(){
|
||
if(i>=Q.length){ document.getElementById('p28-iv1-q').innerHTML='<b>Готово!</b> '+score+' / '+Q.length; if(score===Q.length){addXp(15,'p28-iv1');bumpProgress('p28',28);} else if(score>=4){addXp(8,'p28-iv1');bumpProgress('p28',14);} return; }
|
||
document.getElementById('p28-iv1-i').textContent=(i+1);
|
||
document.getElementById('p28-iv1-s').textContent=score;
|
||
document.getElementById('p28-iv1-q').innerHTML=Q[i].e;
|
||
renderMath(document.getElementById('p28-iv1-q'));
|
||
document.getElementById('p28-iv1-fb').style.display='none';
|
||
}
|
||
function ans(yes){
|
||
if(i>=Q.length) return;
|
||
const fb=document.getElementById('p28-iv1-fb');
|
||
if(yes===Q[i].ok){ score++; feedback(fb,true,'✓ Верно! '+Q[i].why); }
|
||
else feedback(fb,false,'✗ '+(Q[i].ok?'Можно: ':'Нельзя: ')+Q[i].why);
|
||
document.getElementById('p28-iv1-s').textContent=score;
|
||
i++; setTimeout(show,1400);
|
||
}
|
||
document.getElementById('p28-iv1-y').addEventListener('click',()=>ans(true));
|
||
document.getElementById('p28-iv1-n').addEventListener('click',()=>ans(false));
|
||
show();
|
||
})();
|
||
|
||
makeTrainer({
|
||
idPrefix:'p28-iv2',
|
||
parser:(v)=>v,
|
||
questions:[
|
||
{ q:'$\\triangle ABC$: $a=5$, $b=4$, $c=6$. Откладываем $AB = c$. Радиус 1-й окружности из $A$?', a:(v)=>+v===4, show:'4 ($= b$)' },
|
||
{ q:'Тот же $\\triangle$. Радиус 2-й окружности из $B$?', a:(v)=>+v===5, show:'5 ($= a$)' },
|
||
{ q:'Сколько окружностей нужно нарисовать?', a:(v)=>+v===2, show:'2' },
|
||
{ q:'Сколько точек пересечения у двух окружностей (если задача разрешима)?', a:(v)=>+v===2, show:'2 (симметричные)' },
|
||
{ q:'Сколько <b>неравных</b> $\\triangle$ можно получить?', a:(v)=>+v===1, show:'1 (симметричный — тот же)' },
|
||
],
|
||
onComplete:(s,n)=>{ if(s===n){addXp(15,'p28-iv2');bumpProgress('p28',25);} else if(s>=3){addXp(8,'p28-iv2');bumpProgress('p28',12);} }
|
||
});
|
||
|
||
wireReadBtn('p28');
|
||
}
|
||
|
||
|
||
/* ============================================================
|
||
\xA7 29 — Построение биссектрисы угла
|
||
============================================================ */
|
||
function buildP29(){
|
||
const box = document.getElementById('p29-body');
|
||
const G = window.GEOM7;
|
||
let html = '';
|
||
|
||
let svgBis='';
|
||
if(G){
|
||
const b=G.svgBox(300,200,{id:'p29-bis',cell:20});
|
||
const O={x:40,y:100};
|
||
const ang=30*Math.PI/180;
|
||
const len=220;
|
||
const Aend={x:O.x+len*Math.cos(-ang), y:O.y+len*Math.sin(-ang)};
|
||
const Bend={x:O.x+len*Math.cos(ang), y:O.y+len*Math.sin(ang)};
|
||
/* Шаг 1: окружность из O радиусом r1 — пересекает стороны в X и Y */
|
||
const r1=80;
|
||
const X={x:O.x+r1*Math.cos(-ang), y:O.y+r1*Math.sin(-ang)};
|
||
const Y={x:O.x+r1*Math.cos(ang), y:O.y+r1*Math.sin(ang)};
|
||
/* Шаг 2: из X и Y окружн. радиуса r2 — точка M на оси симметрии (биссектрисе) */
|
||
const r2=70;
|
||
const dy2 = r1*Math.sin(ang);
|
||
const dx2 = Math.sqrt(r2*r2 - dy2*dy2);
|
||
const Mx = X.x + dx2;
|
||
const M = {x:Mx, y:O.y};
|
||
svgBis = b.open
|
||
/* Стороны угла */
|
||
+ G.segment(O,Aend,{color:'#7c3aed',width:2.5})
|
||
+ G.segment(O,Bend,{color:'#7c3aed',width:2.5})
|
||
/* Шаг 1: окружность из O */
|
||
+ G.circle(O,r1,{color:'#0891b2',width:1.3,dash:'4 3'})
|
||
+ G.point(X.x,X.y,'X',{color:'#dc2626',dx:8,dy:-2,fontSize:12,r:3.5})
|
||
+ G.point(Y.x,Y.y,'Y',{color:'#dc2626',dx:8,dy:14,fontSize:12,r:3.5})
|
||
/* Шаги 2-3: окружности из X и Y */
|
||
+ G.circle(X,r2,{color:'#f59e0b',width:1.2,dash:'3 2'})
|
||
+ G.circle(Y,r2,{color:'#f59e0b',width:1.2,dash:'3 2'})
|
||
/* Шаг 4: точка M */
|
||
+ G.point(M.x,M.y,'M',{color:'#059669',dx:6,dy:-6,fontSize:13,r:4})
|
||
/* Шаг 5: луч OM */
|
||
+ G.segment(O,{x:O.x+(M.x-O.x)*1.5,y:O.y},{color:'#dc2626',width:2.5,dash:'8 3'})
|
||
+ '<text x="'+(O.x+90)+'" y="'+(O.y-6)+'" font-size="11" font-family="Unbounded,Inter,sans-serif" font-weight="700" fill="#dc2626">биссектриса</text>'
|
||
+ G.point(O.x,O.y,'O',{color:'#1e293b',dx:-14,dy:5,fontSize:13})
|
||
+ '<text x="'+(Aend.x-2)+'" y="'+(Aend.y-2)+'" font-size="12" font-family="Unbounded,Inter,sans-serif" font-weight="700" fill="#7c3aed">A</text>'
|
||
+ '<text x="'+(Bend.x-2)+'" y="'+(Bend.y+14)+'" font-size="12" font-family="Unbounded,Inter,sans-serif" font-weight="700" fill="#7c3aed">B</text>'
|
||
+ b.close;
|
||
}
|
||
|
||
html += makeCard('theory', 'Задача', '29.1', `
|
||
<p><b>Дано:</b> $\\angle AOB$.</p>
|
||
<p><b>Построить:</b> биссектрису этого угла — луч $OM$ такой, что $\\angle AOM = \\angle MOB$.</p>`);
|
||
|
||
html += makeCard('algo', 'Построение', '29.2', `
|
||
<div class="steps">
|
||
<div class="step"><div class="step-text">Из вершины $O$ проведём окружность <b>любого радиуса $r_1$</b>. Она пересекает стороны угла в точках $X$ и $Y$.</div></div>
|
||
<div class="step"><div class="step-text">Из $X$ и $Y$ как из центров проводим <b>две окружности равного радиуса $r_2$</b> (так, чтобы они пересекались).</div></div>
|
||
<div class="step"><div class="step-text">Их точку пересечения внутри угла обозначаем $M$.</div></div>
|
||
<div class="step"><div class="step-text">Проводим <b>луч $OM$</b>. Это и есть биссектриса.</div></div>
|
||
</div>
|
||
<div class="svg-host">`+svgBis+`</div>`);
|
||
|
||
html += makeCard('rule', 'Доказательство', '29.3', `
|
||
<p>Рассмотрим треугольники $\\triangle OXM$ и $\\triangle OYM$:</p>
|
||
<ul style="padding-left:22px;line-height:1.85">
|
||
<li>$OX = OY = r_1$ — обе точки лежат на окружности с центром $O$ радиуса $r_1$;</li>
|
||
<li>$XM = YM = r_2$ — точка $M$ лежит на обеих окружностях радиуса $r_2$;</li>
|
||
<li>$OM$ — общая сторона.</li>
|
||
</ul>
|
||
<p>По <b>3-му признаку (ССС)</b> $\\triangle OXM = \\triangle OYM$. Отсюда $\\angle XOM = \\angle YOM$, то есть $OM$ — биссектриса. ■</p>`);
|
||
|
||
html += '<div class="wg" id="p29-iv1">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Что делаем на каждом шаге?</div></div>'
|
||
+'<div class="wg-help">Порядок шагов в построении биссектрисы.</div>'
|
||
+trainerHTML('p29-iv1', 5, 'номер шага')
|
||
+'</div>';
|
||
|
||
html += '<div class="wg" id="p29-iv2">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Доказательство</div></div>'
|
||
+'<div class="wg-help">Почему $OM$ — биссектриса.</div>'
|
||
+trainerHTML('p29-iv2', 4, 'число / слово')
|
||
+'</div>';
|
||
|
||
html += secNav('p28', 'p30') + readButton('p29');
|
||
box.innerHTML = html; renderMath(box);
|
||
|
||
makeTrainer({
|
||
idPrefix:'p29-iv1',
|
||
questions:[
|
||
{ q:'Шаг $?$: провести окружность из $O$ — она пересекает стороны в $X, Y$.', a:1 },
|
||
{ q:'Шаг $?$: провести окружность из $X$ радиуса $r_2$.', a:2 },
|
||
{ q:'Шаг $?$: точка пересечения окружностей внутри угла — это $M$.', a:3 },
|
||
{ q:'Шаг $?$: провести луч $OM$.', a:4 },
|
||
{ q:'Сколько окружностей рисуется в полном построении?', a:3 },
|
||
],
|
||
onComplete:(s,n)=>{ if(s===n){addXp(15,'p29-iv1');bumpProgress('p29',28);} else if(s>=3){addXp(8,'p29-iv1');bumpProgress('p29',14);} }
|
||
});
|
||
|
||
makeTrainer({
|
||
idPrefix:'p29-iv2',
|
||
parser:(v)=>v,
|
||
questions:[
|
||
{ q:'По какому признаку равны $\\triangle OXM$ и $\\triangle OYM$? «ССС», «СУС» или «УСУ»', a:(v)=>String(v).trim().toUpperCase().startsWith('ССС'), show:'ССС' },
|
||
{ q:'Сколько окружностей равного радиуса $r_2$ нужно?', a:(v)=>+v===2, show:'2 (из $X$ и из $Y$)' },
|
||
{ q:'$OX = OY$. Они равны как ... окружности (введи слово).', a:(v)=>String(v).trim().toLowerCase().startsWith('радиус'), show:'радиусы' },
|
||
{ q:'Биссектриса делит угол на $?$ равных частей', a:(v)=>+v===2, show:'2' },
|
||
],
|
||
onComplete:(s,n)=>{ if(s===n){addXp(15,'p29-iv2');bumpProgress('p29',28);} else if(s>=2){addXp(8,'p29-iv2');bumpProgress('p29',14);} }
|
||
});
|
||
|
||
wireReadBtn('p29');
|
||
}
|
||
|
||
|
||
/* ============================================================
|
||
\xA7 30 — Середина и перпендикуляр
|
||
============================================================ */
|
||
function buildP30(){
|
||
const box = document.getElementById('p30-body');
|
||
const G = window.GEOM7;
|
||
let html = '';
|
||
|
||
let svgMid='';
|
||
if(G){
|
||
const b=G.svgBox(300,220,{id:'p30-mid',cell:20});
|
||
const A={x:50,y:110}, B={x:230,y:110};
|
||
/* Радиус больше половины AB. AB = 180, r = 120 */
|
||
const r=120;
|
||
const Mx=(A.x+B.x)/2;
|
||
const d=Math.sqrt(r*r - ((B.x-A.x)/2)*((B.x-A.x)/2));
|
||
const P1={x:Mx, y:A.y - d};
|
||
const P2={x:Mx, y:A.y + d};
|
||
const Mid={x:Mx, y:A.y};
|
||
svgMid = b.open
|
||
+ G.segment(A,B,{color:'#0891b2',width:2.5})
|
||
+ G.circle(A,r,{color:'#7c3aed',width:1.3,dash:'4 3'})
|
||
+ G.circle(B,r,{color:'#f59e0b',width:1.3,dash:'4 3'})
|
||
+ G.segment(P1,P2,{color:'#dc2626',width:2.5,dash:'8 3'})
|
||
+ G.rightAngleMark(Mid,B,P1,{color:'#dc2626',size:10})
|
||
+ G.point(P1.x,P1.y,'P₁',{color:'#dc2626',dx:6,dy:-4,fontSize:11,r:3.5})
|
||
+ G.point(P2.x,P2.y,'P₂',{color:'#dc2626',dx:6,dy:12,fontSize:11,r:3.5})
|
||
+ G.point(Mid.x,Mid.y,'M',{color:'#059669',dx:-8,dy:-8,fontSize:13,r:4})
|
||
+ G.point(A.x,A.y,'A',{color:'#1e293b',dx:-14,dy:14,fontSize:12})
|
||
+ G.point(B.x,B.y,'B',{color:'#1e293b',dx:8,dy:14,fontSize:12})
|
||
+ '<text x="'+Mx+'" y="22" text-anchor="middle" font-size="11" font-family="Unbounded,Inter,sans-serif" font-weight="700" fill="#dc2626">серединный перпендикуляр</text>'
|
||
+ b.close;
|
||
}
|
||
|
||
html += makeCard('theory', 'Две задачи в одной', '30.1', `
|
||
<p>Одно построение решает сразу три задачи:</p>
|
||
<ol style="padding-left:22px;line-height:1.9">
|
||
<li>Построить <b>середину $M$</b> отрезка $AB$.</li>
|
||
<li>Построить <b>серединный перпендикуляр</b> к отрезку $AB$.</li>
|
||
<li>Построить <b>перпендикуляр</b> к прямой через данную точку.</li>
|
||
</ol>
|
||
<p>Идея: серединный перпендикуляр = ось симметрии отрезка. Любая точка на нём равноудалена от концов.</p>`);
|
||
|
||
html += makeCard('algo', 'Построение серединного перпендикуляра (и середины)', '30.2', `
|
||
<div class="steps">
|
||
<div class="step"><div class="step-text">Из точки $A$ проводим окружность <b>радиуса $r$</b>, где $r > \\frac{1}{2}AB$ (например, $r = AB$).</div></div>
|
||
<div class="step"><div class="step-text">Из точки $B$ проводим окружность <b>того же радиуса $r$</b>.</div></div>
|
||
<div class="step"><div class="step-text">Окружности пересекаются в двух точках $P_1$ и $P_2$ (выше и ниже отрезка $AB$).</div></div>
|
||
<div class="step"><div class="step-text">Прямая $P_1 P_2$ — серединный перпендикуляр. Её точка пересечения с $AB$ — середина $M$.</div></div>
|
||
</div>
|
||
<div class="svg-host">`+svgMid+`</div>`);
|
||
|
||
html += makeCard('rule', 'Доказательство', '30.3', `
|
||
<p>Точки $P_1$ и $P_2$ лежат на обеих окружностях $\\Rightarrow$ $AP_1 = BP_1 = r$ и $AP_2 = BP_2 = r$.</p>
|
||
<p>Значит $P_1$ и $P_2$ — точки, <b>равноудалённые</b> от $A$ и $B$. По теореме о серединном перпендикуляре они лежат на нём (§14).</p>
|
||
<p>Через 2 точки проходит единственная прямая $\\Rightarrow$ $P_1 P_2$ — это и есть серединный перпендикуляр.</p>
|
||
<p>Его пересечение с $AB$ — точка $M$, середина отрезка ($AM = MB$). ■</p>`);
|
||
|
||
html += makeCard('example', 'Перпендикуляр через данную точку', '30.4', `
|
||
<p><b>Задача.</b> Дана прямая $a$ и точка $K$ <b>не на ней</b>. Построить перпендикуляр из $K$ к $a$.</p>
|
||
<div class="steps">
|
||
<div class="step"><div class="step-text">Из $K$ проводим окружность так, чтобы она пересекла $a$ в двух точках. Назовём их $A$ и $B$.</div></div>
|
||
<div class="step"><div class="step-text">$KA = KB$ (радиусы) $\\Rightarrow$ $K$ лежит на серединном перпендикуляре к $AB$.</div></div>
|
||
<div class="step"><div class="step-text">Строим серединный перпендикуляр к $AB$ (по алгоритму выше).</div></div>
|
||
<div class="step"><div class="step-text">Прямая $KM$ (где $M$ — середина $AB$) и есть искомый перпендикуляр из $K$ к $a$.</div></div>
|
||
</div>`);
|
||
|
||
html += '<div class="wg" id="p30-iv1">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Параметры построения</div></div>'
|
||
+'<div class="wg-help">Условие на радиус, число точек пересечения.</div>'
|
||
+trainerHTML('p30-iv1', 5, 'число / знак')
|
||
+'</div>';
|
||
|
||
html += '<div class="wg" id="p30-iv2">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Верно / Неверно</div></div>'
|
||
+'<div class="wg-help">Свойства построения.</div>'
|
||
+'<div class="score-display"><span>Задача <b id="p30-iv2-i">1</b> / 5</span><span>Очки: <b id="p30-iv2-s">0</b> / 5</span></div>'
|
||
+'<div id="p30-iv2-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;text-align:center;margin-bottom:10px"></div>'
|
||
+'<div style="display:flex;gap:8px;justify-content:center"><button class="btn primary" id="p30-iv2-y" style="background:#10b981;border-color:#10b981">Да</button><button class="btn primary" id="p30-iv2-n" style="background:#dc2626;border-color:#dc2626">Нет</button></div>'
|
||
+'<div class="feedback" id="p30-iv2-fb"></div></div>';
|
||
|
||
html += secNav('p29', 'p31') + readButton('p30');
|
||
box.innerHTML = html; renderMath(box);
|
||
|
||
makeTrainer({
|
||
idPrefix:'p30-iv1',
|
||
parser:(v)=>v,
|
||
questions:[
|
||
{ q:'$AB = 10$. Минимальный <b>целый</b> радиус $r$ для пересечения окружностей?', a:(v)=>+v===6, show:'6 ($r > 5$)' },
|
||
{ q:'$AB = 8$. Подходит ли радиус $r = 4$? «да»/«нет»', a:(v)=>String(v).trim().toLowerCase().startsWith('н'), show:'нет ($r$ должен быть $> 4$)' },
|
||
{ q:'Сколько точек пересечения у окружностей?', a:(v)=>+v===2, show:'2 ($P_1$ и $P_2$)' },
|
||
{ q:'$P_1$ и $P_2$ равноудалены от $A$ и $B$? «да»/«нет»', a:(v)=>String(v).trim().toLowerCase().startsWith('д'), show:'да' },
|
||
{ q:'Угол между серединным перпендикуляром и $AB$ равен $?$ градусов.', a:(v)=>+v===90, show:'90°' },
|
||
],
|
||
onComplete:(s,n)=>{ if(s===n){addXp(15,'p30-iv1');bumpProgress('p30',25);} else if(s>=3){addXp(8,'p30-iv1');bumpProgress('p30',12);} }
|
||
});
|
||
|
||
(function(){
|
||
const Q=[
|
||
{ e:'Радиусы окружностей в построении серединного $\\perp$ должны быть равны.', ok:true },
|
||
{ e:'Радиус каждой окружности должен быть меньше половины $AB$.', ok:false },
|
||
{ e:'Через 2 точки $P_1, P_2$ проходит ровно одна прямая.', ok:true },
|
||
{ e:'Серединный $\\perp$ проходит через середину $AB$.', ok:true },
|
||
{ e:'Серединный $\\perp$ параллелен отрезку $AB$.', ok:false },
|
||
];
|
||
let i=0,score=0;
|
||
function show(){
|
||
if(i>=Q.length){ document.getElementById('p30-iv2-q').innerHTML='<b>Готово!</b> '+score+' / '+Q.length; if(score===Q.length){addXp(12,'p30-iv2');bumpProgress('p30',22);} else if(score>=3){addXp(6,'p30-iv2');bumpProgress('p30',12);} return; }
|
||
document.getElementById('p30-iv2-i').textContent=(i+1);
|
||
document.getElementById('p30-iv2-s').textContent=score;
|
||
document.getElementById('p30-iv2-q').innerHTML=Q[i].e;
|
||
renderMath(document.getElementById('p30-iv2-q'));
|
||
document.getElementById('p30-iv2-fb').style.display='none';
|
||
}
|
||
function ans(yes){
|
||
if(i>=Q.length) return;
|
||
const fb=document.getElementById('p30-iv2-fb');
|
||
if(yes===Q[i].ok){ score++; feedback(fb,true,'✓ Верно!'); }
|
||
else feedback(fb,false,'✗ '+(Q[i].ok?'Верно.':'Неверно.'));
|
||
document.getElementById('p30-iv2-s').textContent=score;
|
||
i++; setTimeout(show,1300);
|
||
}
|
||
document.getElementById('p30-iv2-y').addEventListener('click',()=>ans(true));
|
||
document.getElementById('p30-iv2-n').addEventListener('click',()=>ans(false));
|
||
show();
|
||
})();
|
||
|
||
wireReadBtn('p30');
|
||
}
|
||
|
||
|
||
/* ============================================================
|
||
\xA7 31 — Метод геометрических мест точек
|
||
============================================================ */
|
||
function buildP31(){
|
||
const box = document.getElementById('p31-body');
|
||
const G = window.GEOM7;
|
||
let html = '';
|
||
|
||
let svgGMT='';
|
||
if(G){
|
||
/* Чистый рисунок: угол ∠AOB слева, биссектриса (ГМТ 1, красная)
|
||
и окружность (ГМТ 2, синяя) пересекаются в двух точках K₁, K₂. */
|
||
const b=G.svgBox(320,230,{id:'p31-gmt',cell:20});
|
||
const O={x:50,y:135};
|
||
const ang=20*Math.PI/180; /* половина раствора угла */
|
||
const len=220;
|
||
const Aend={x:O.x+len*Math.cos(-ang), y:O.y+len*Math.sin(-ang)};
|
||
const Bend={x:O.x+len*Math.cos(ang), y:O.y+len*Math.sin(ang)};
|
||
/* Окружность (ГМТ 2): центр Cc, радиус R.
|
||
Биссектриса — горизонталь y=O.y от O.
|
||
Пересечение: (x-Cc.x)² + (O.y-Cc.y)² = R² → x = Cc.x ± √(R² - dy²) */
|
||
const Cc={x:180,y:90}, R=55;
|
||
const dy = O.y - Cc.y;
|
||
const dx = Math.sqrt(R*R - dy*dy);
|
||
const K1={x:Cc.x-dx, y:O.y}, K2={x:Cc.x+dx, y:O.y};
|
||
/* Конец биссектрисы — заходит чуть за K2 */
|
||
const Bs={x:O.x+len,y:O.y};
|
||
svgGMT = b.open
|
||
/* Стороны угла */
|
||
+ G.segment(O,Aend,{color:'#7c3aed',width:2.5})
|
||
+ G.segment(O,Bend,{color:'#7c3aed',width:2.5})
|
||
+ G.point(O.x,O.y,'O',{color:'#1e293b',dx:-14,dy:5,fontSize:13})
|
||
+ '<text x="'+(Aend.x+3)+'" y="'+(Aend.y-1)+'" font-size="12" font-family="Unbounded,Inter,sans-serif" font-weight="700" fill="#7c3aed">A</text>'
|
||
+ '<text x="'+(Bend.x+3)+'" y="'+(Bend.y+12)+'" font-size="12" font-family="Unbounded,Inter,sans-serif" font-weight="700" fill="#7c3aed">B</text>'
|
||
/* Маленькие дуги показывают, что биссектриса делит угол пополам */
|
||
+ G.arc(O,18,-ang,0,{color:'#dc2626',width:1.4})
|
||
+ G.arc(O,18,0,ang,{color:'#dc2626',width:1.4})
|
||
/* ГМТ 1 — биссектриса (красная пунктирная) */
|
||
+ G.segment(O,Bs,{color:'#dc2626',width:2.2,dash:'7 4'})
|
||
/* ГМТ 2 — окружность (синяя пунктирная) */
|
||
+ G.circle(Cc,R,{color:'#0891b2',width:2,dash:'5 3'})
|
||
+ G.point(Cc.x,Cc.y,'C',{color:'#0891b2',dx:6,dy:-6,fontSize:11,r:3})
|
||
/* Точки пересечения K₁, K₂ — крупные зелёные с обводкой */
|
||
+ '<circle cx="'+K1.x+'" cy="'+K1.y+'" r="5.5" fill="#10b981" stroke="#fff" stroke-width="2"/>'
|
||
+ '<circle cx="'+K2.x+'" cy="'+K2.y+'" r="5.5" fill="#10b981" stroke="#fff" stroke-width="2"/>'
|
||
+ '<text x="'+(K1.x-5)+'" y="'+(K1.y+25)+'" text-anchor="middle" font-size="13" font-family="Unbounded,Inter,sans-serif" font-weight="800" fill="#047857">K₁</text>'
|
||
+ '<text x="'+(K2.x+5)+'" y="'+(K2.y+25)+'" text-anchor="middle" font-size="13" font-family="Unbounded,Inter,sans-serif" font-weight="800" fill="#047857">K₂</text>'
|
||
/* Подпись ГМТ 1 (плашка справа от биссектрисы, за K₂) */
|
||
+ '<rect x="'+(Bs.x-65)+'" y="'+(Bs.y-11)+'" width="62" height="20" rx="4" fill="#fee2e2" stroke="#dc2626" stroke-width="1"/>'
|
||
+ '<text x="'+(Bs.x-34)+'" y="'+(Bs.y+3)+'" text-anchor="middle" font-size="10" font-family="Unbounded,Inter,sans-serif" font-weight="800" fill="#dc2626">ГМТ 1: бис.</text>'
|
||
/* Подпись ГМТ 2 (плашка сверху над окружностью) */
|
||
+ '<rect x="'+(Cc.x-45)+'" y="'+(Cc.y-R-22)+'" width="90" height="20" rx="4" fill="#cffafe" stroke="#0891b2" stroke-width="1"/>'
|
||
+ '<text x="'+Cc.x+'" y="'+(Cc.y-R-8)+'" text-anchor="middle" font-size="10" font-family="Unbounded,Inter,sans-serif" font-weight="800" fill="#0891b2">ГМТ 2: окружн.</text>'
|
||
/* Итог снизу */
|
||
+ '<text x="160" y="218" text-anchor="middle" font-size="11" font-weight="800" fill="#047857">K₁, K₂ = ГМТ 1 ∩ ГМТ 2</text>'
|
||
+ b.close;
|
||
}
|
||
|
||
html += makeCard('theory', 'Идея метода', '31.1', `
|
||
<p><b>Метод геометрических мест точек</b> (метод ГМТ) — мощный способ решать задачи на построение, когда искомая точка обладает <b>двумя</b> свойствами.</p>
|
||
<p style="background:var(--sec-acc-soft);padding:10px 14px;border-radius:8px"><b>Алгоритм.</b></p>
|
||
<ol style="padding-left:22px;line-height:1.9">
|
||
<li>Найди <b>ГМТ 1</b> — множество всех точек, обладающих свойством 1.</li>
|
||
<li>Найди <b>ГМТ 2</b> — множество всех точек, обладающих свойством 2.</li>
|
||
<li>Искомая точка лежит на <b>пересечении</b> $\\text{ГМТ 1} \\cap \\text{ГМТ 2}$.</li>
|
||
</ol>
|
||
<div class="svg-host">`+svgGMT+`</div>`);
|
||
|
||
html += makeCard('rule', 'Знакомые ГМТ', '31.2', `
|
||
<p>Из предыдущих глав мы уже знаем 4 важных ГМТ:</p>
|
||
<ul style="padding-left:22px;line-height:1.85">
|
||
<li><b>Окружность</b> с центром $O$ радиуса $r$ — ГМТ точек, находящихся на расстоянии $r$ от $O$.</li>
|
||
<li><b>Серединный перпендикуляр</b> к $AB$ — ГМТ точек, равноудалённых от $A$ и $B$.</li>
|
||
<li><b>Биссектриса</b> $\\angle AOB$ — ГМТ точек внутри угла, равноудалённых от его сторон.</li>
|
||
<li><b>Прямая</b>, параллельная данной — ГМТ точек, находящихся от неё на заданном расстоянии (с одной стороны).</li>
|
||
</ul>`);
|
||
|
||
html += makeCard('example', 'Окружность через 3 точки', '31.3', `
|
||
<p><b>Задача.</b> Через 3 точки $A, B, C$, не лежащие на одной прямой, провести окружность.</p>
|
||
<p><b>Решение методом ГМТ.</b> Центр $O$ окружности должен быть:</p>
|
||
<ul style="padding-left:22px;line-height:1.85">
|
||
<li>равноудалён от $A$ и $B$ $\\Rightarrow$ $O \\in$ серединного $\\perp$ к $AB$ (<b>ГМТ 1</b>);</li>
|
||
<li>равноудалён от $B$ и $C$ $\\Rightarrow$ $O \\in$ серединного $\\perp$ к $BC$ (<b>ГМТ 2</b>).</li>
|
||
</ul>
|
||
<p>Точка пересечения этих 2 перпендикуляров — искомый центр $O$. Радиус $= OA = OB = OC$.</p>`);
|
||
|
||
html += makeCard('example', 'Точка, равноуд. от 2 прямых, на расст. $r$ от 3-й', '31.4', `
|
||
<p><b>Задача.</b> Даны пересекающиеся прямые $a$ и $b$. Найти точку $K$, равноудалённую от $a$ и $b$ и находящуюся на расстоянии $r$ от данной прямой $c$.</p>
|
||
<p><b>Решение.</b></p>
|
||
<ul style="padding-left:22px;line-height:1.9">
|
||
<li><b>ГМТ 1</b> (равноуд. от $a$ и $b$): <b>биссектрисы</b> углов между $a$ и $b$ — их $2$.</li>
|
||
<li><b>ГМТ 2</b> (на расст. $r$ от $c$): <b>2 прямые</b>, параллельные $c$ на расстоянии $r$.</li>
|
||
<li><b>Искомые точки</b> $K$ — пересечения биссектрис с этими параллельными прямыми. Всего до $2 \\times 2 = 4$ точек.</li>
|
||
</ul>`);
|
||
|
||
html += '<div class="wg" id="p31-iv1">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Узнай ГМТ</div></div>'
|
||
+'<div class="wg-help">Какая фигура — это ГМТ точек с указанным свойством?</div>'
|
||
+'<div class="score-display"><span>Задача <b id="p31-iv1-i">1</b> / 6</span><span>Очки: <b id="p31-iv1-s">0</b> / 6</span></div>'
|
||
+'<div id="p31-iv1-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.02rem;text-align:center;margin-bottom:10px"></div>'
|
||
+'<div style="display:grid;grid-template-columns:repeat(2,1fr);gap:8px;max-width:380px;margin:0 auto"><button class="btn primary" id="p31-iv1-c">Окружность</button><button class="btn primary" id="p31-iv1-b">Биссектриса</button><button class="btn primary" id="p31-iv1-m">Серед. перп.</button><button class="btn primary" id="p31-iv1-p">Парал. прямая</button></div>'
|
||
+'<div class="feedback" id="p31-iv1-fb"></div></div>';
|
||
|
||
html += '<div class="wg" id="p31-iv2">'
|
||
+'<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Сколько решений?</div></div>'
|
||
+'<div class="wg-help">Сколько точек на пересечении двух ГМТ?</div>'
|
||
+trainerHTML('p31-iv2', 5, 'число')
|
||
+'</div>';
|
||
|
||
html += secNav('p30', 'final5') + readButton('p31');
|
||
box.innerHTML = html; renderMath(box);
|
||
|
||
(function(){
|
||
const Q=[
|
||
{ e:'Точки на расстоянии $r$ от $O$.', ans:'c' },
|
||
{ e:'Точки, равноудалённые от концов отрезка $AB$.', ans:'m' },
|
||
{ e:'Точки внутри $\\angle AOB$, равноудалённые от его сторон.', ans:'b' },
|
||
{ e:'Точки на расстоянии $h$ от прямой $\\ell$ (с одной стороны).', ans:'p' },
|
||
{ e:'Точки, равноудалённые от точек $A$ и $B$.', ans:'m' },
|
||
{ e:'Точки, до которых от $O$ ровно $5$ см.', ans:'c' },
|
||
];
|
||
let i=0,score=0;
|
||
function show(){
|
||
if(i>=Q.length){ document.getElementById('p31-iv1-q').innerHTML='<b>Готово!</b> '+score+' / '+Q.length; if(score===Q.length){addXp(15,'p31-iv1');bumpProgress('p31',28);} else if(score>=4){addXp(8,'p31-iv1');bumpProgress('p31',14);} return; }
|
||
document.getElementById('p31-iv1-i').textContent=(i+1);
|
||
document.getElementById('p31-iv1-s').textContent=score;
|
||
document.getElementById('p31-iv1-q').innerHTML=Q[i].e;
|
||
renderMath(document.getElementById('p31-iv1-q'));
|
||
document.getElementById('p31-iv1-fb').style.display='none';
|
||
}
|
||
function ans(a){
|
||
if(i>=Q.length) return;
|
||
const fb=document.getElementById('p31-iv1-fb');
|
||
if(a===Q[i].ans){ score++; feedback(fb,true,'✓ Верно!'); }
|
||
else{ const lab={c:'окружность',b:'биссектриса',m:'серединный перпендикуляр',p:'парал. прямая'}; feedback(fb,false,'✗ Правильно: <b>'+lab[Q[i].ans]+'</b>'); }
|
||
document.getElementById('p31-iv1-s').textContent=score;
|
||
i++; setTimeout(show,1300);
|
||
}
|
||
document.getElementById('p31-iv1-c').addEventListener('click',()=>ans('c'));
|
||
document.getElementById('p31-iv1-b').addEventListener('click',()=>ans('b'));
|
||
document.getElementById('p31-iv1-m').addEventListener('click',()=>ans('m'));
|
||
document.getElementById('p31-iv1-p').addEventListener('click',()=>ans('p'));
|
||
show();
|
||
})();
|
||
|
||
makeTrainer({
|
||
idPrefix:'p31-iv2',
|
||
questions:[
|
||
{ q:'Две пересекающиеся прямые. Сколько точек их пересечения?', a:1 },
|
||
{ q:'Прямая <b>пересекает</b> окружность (секущая). Сколько точек?', a:2 },
|
||
{ q:'Прямая <b>касается</b> окружности. Сколько точек?', a:1 },
|
||
{ q:'Прямая <b>не пересекает</b> окружность. Сколько точек?', a:0 },
|
||
{ q:'Две окружности с разными центрами, пересекаются. Сколько точек?', a:2 },
|
||
],
|
||
onComplete:(s,n)=>{ if(s===n){addXp(15,'p31-iv2');bumpProgress('p31',28);} else if(s>=3){addXp(8,'p31-iv2');bumpProgress('p31',14);} }
|
||
});
|
||
|
||
wireReadBtn('p31');
|
||
}
|
||
|
||
|
||
/* ============================================================
|
||
FINAL 5 — 5 БОССОВ
|
||
============================================================ */
|
||
const BOSSES = [
|
||
{
|
||
n:1, title:'Босс \xA727 — Базовые построения', color:'#db2777',
|
||
steps:[
|
||
{ q:'Сколько инструментов используется в задачах на построение?', verify:(v)=>+v===2, hint:'Циркуль и линейка.' },
|
||
{ q:'Есть ли деления на линейке? «да»/«нет»', verify:(v)=>String(v).trim().toLowerCase().startsWith('н'), hint:'Нет — линейка без делений.' },
|
||
{ q:'Сколько этапов полного решения задачи на построение?', verify:(v)=>+v===4, hint:'Анализ, Построение, Доказ., Исслед.' },
|
||
{ q:'Что делает циркуль: «прямая», «окружность» или «угол»?', verify:(v)=>String(v).trim().toLowerCase().startsWith('окруж'), hint:'Окружность.' },
|
||
{ q:'Что делает линейка: «прямая», «окружность» или «угол»?', verify:(v)=>String(v).trim().toLowerCase().startsWith('прям'), hint:'Прямая.' },
|
||
]
|
||
},
|
||
{
|
||
n:2, title:'Босс \xA728 — Треугольник по 3 сторонам', color:'#c026d3',
|
||
steps:[
|
||
{ q:'$a=4, b=5, c=6$. Возможно ли построить? «да»/«нет»', verify:(v)=>String(v).trim().toLowerCase().startsWith('д'), hint:'Все неравенства выполнены.' },
|
||
{ q:'$a=2, b=3, c=10$. Возможно ли построить? «да»/«нет»', verify:(v)=>String(v).trim().toLowerCase().startsWith('н'), hint:'$2+3 < 10$.' },
|
||
{ q:'Сколько окружностей рисуется при построении $\\triangle$ по 3 сторонам?', verify:(v)=>+v===2, hint:'Из $A$ и из $B$.' },
|
||
{ q:'Сколько точек пересечения у этих окружностей (если задача разрешима)?', verify:(v)=>+v===2, hint:'2 симметричные.' },
|
||
{ q:'Сколько <b>неравных</b> треугольников получаем?', verify:(v)=>+v===1, hint:'1 — симметричный считаем тем же.' },
|
||
]
|
||
},
|
||
{
|
||
n:3, title:'Босс \xA729-30 — Биссектриса, середина, ⊥', color:'#7c3aed',
|
||
steps:[
|
||
{ q:'По какому признаку доказываем равенство $\\triangle OXM = \\triangle OYM$ в построении бис.? «ССС», «СУС», «УСУ»', verify:(v)=>String(v).trim().toUpperCase().startsWith('ССС'), hint:'ССС — три стороны.' },
|
||
{ q:'Сколько окружностей в построении серединного $\\perp$?', verify:(v)=>+v===2, hint:'Из $A$ и $B$.' },
|
||
{ q:'$AB = 12$. Минимальный целый радиус $r$ для серединного перп.?', verify:(v)=>+v===7, hint:'$r > 6$.' },
|
||
{ q:'Угол между серединным $\\perp$ и $AB$ равен $?$ градусов.', verify:(v)=>+v===90, hint:'Перпендикулярно.' },
|
||
{ q:'Биссектриса делит угол $80°$ на 2 угла по $?$ градусов.', verify:(v)=>+v===40, hint:'$80/2$.' },
|
||
]
|
||
},
|
||
{
|
||
n:4, title:'Босс \xA731 — Метод ГМТ', color:'#0891b2',
|
||
steps:[
|
||
{ q:'Точки на расст. $r$ от $O$ — это $?$ Введи слово.', verify:(v)=>String(v).trim().toLowerCase().startsWith('окруж'), hint:'Окружность.' },
|
||
{ q:'Точки, равноуд. от концов отрезка $AB$ — это $?$ Введи: «бис.» или «серед.перп.»', verify:(v)=>String(v).trim().toLowerCase().startsWith('серед'), hint:'Серединный $\\perp$.' },
|
||
{ q:'Точки внутри $\\angle$, равноуд. от его сторон — это $?$ Введи: «бис.» или «серед.перп.»', verify:(v)=>String(v).trim().toLowerCase().startsWith('бис'), hint:'Биссектриса.' },
|
||
{ q:'Окружность через 3 точки: её центр = пересеч. $?$ серединных $\\perp$. (число)', verify:(v)=>+v===2, hint:'К 2 сторонам $\\triangle$, 3-й автоматически.' },
|
||
{ q:'Две окружности касаются. Сколько точек пересечения?', verify:(v)=>+v===1, hint:'Касание — 1 точка.' },
|
||
]
|
||
},
|
||
{
|
||
n:5, title:'Финальный босс — Сборная по всей Геометрии 7', color:'#dc2626',
|
||
steps:[
|
||
{ q:'Сумма углов $\\triangle$ = $?$ градусов.', verify:(v)=>+v===180, hint:'$180°$.' },
|
||
{ q:'Сколько признаков равенства треугольников?', verify:(v)=>+v===3, hint:'СУС, УСУ, ССС.' },
|
||
{ q:'В прямоуг. $\\triangle$ катет напротив $30°$ равен $\\frac{c}{?}$ (число)', verify:(v)=>+v===2, hint:'Половина гипотенузы.' },
|
||
{ q:'$a \\parallel b$, секущая. $\\angle 1 = 60°$ — соотв. угол $\\angle 5 = ?$', verify:(v)=>+v===60, hint:'Равны.' },
|
||
{ q:'Сколько биссектрис в треугольнике?', verify:(v)=>+v===3, hint:'По одной в каждой вершине.' },
|
||
]
|
||
},
|
||
];
|
||
|
||
function buildFinal5(){
|
||
const box = document.getElementById('final5-body');
|
||
let html = '';
|
||
|
||
html += makeCard('theory', 'Что мы изучили', 'Итог', `
|
||
<p>Глава 5 — основы конструктивной геометрии:</p>
|
||
<ul style="padding-left:22px;line-height:1.85">
|
||
<li>познакомились с <b>циркулем и линейкой</b> как инструментами и узнали о 4 этапах решения;</li>
|
||
<li>научились строить <b>треугольник по 3 сторонам</b> и проверять неравенство треугольника;</li>
|
||
<li>освоили построение <b>биссектрисы угла</b> через 3 окружности (доказательство — ССС);</li>
|
||
<li>построили <b>середину отрезка и серединный перпендикуляр</b> двумя одинаковыми окружностями;</li>
|
||
<li>овладели <b>методом ГМТ</b> — искомая точка как пересечение двух геометрических мест.</li>
|
||
</ul>
|
||
<p style="background:var(--warn-bg);padding:10px 14px;border-radius:8px;border-left:4px solid var(--warn)"><b>Финальный босс</b> — сборная задача по <b>всей</b> Геометрии 7. После победы — финальная ачивка <b>«Геометрия 7 полностью пройдена!»</b>.</p>`);
|
||
|
||
html += '<div id="bosses-container"></div>';
|
||
html += '<div style="margin-top:22px;padding:18px 20px;background:linear-gradient(135deg,var(--pri-soft),var(--acc-soft));border-radius:14px;border:1.5px solid var(--pri);text-align:center">'
|
||
+'<div style="font-family:\'Unbounded\',sans-serif;font-weight:800;color:var(--pri2);font-size:1.1rem;margin-bottom:6px">Прогресс по боссам</div>'
|
||
+'<div id="boss-overall" style="font-size:.95rem;color:var(--text);margin-bottom:10px">0 / 5 боссов побеждено</div>'
|
||
+'<div style="height:14px;background:rgba(219,39,119,.12);border-radius:9px;overflow:hidden"><div id="boss-overall-fill" style="height:100%;width:0%;background:linear-gradient(90deg,#db2777,#f472b6);transition:width .5s"></div></div>'
|
||
+'</div>';
|
||
html += secNav('p31', null);
|
||
box.innerHTML = html; renderMath(box);
|
||
|
||
const cont = document.getElementById('bosses-container');
|
||
const BOSS_STATE = (function(){
|
||
try{ const s=localStorage.getItem('geometry7_ch5_bosses'); if(s) return JSON.parse(s); }catch(e){}
|
||
return BOSSES.map(()=>({stage:0,defeated:false}));
|
||
})();
|
||
function saveBosses(){ try{ localStorage.setItem('geometry7_ch5_bosses', JSON.stringify(BOSS_STATE)); }catch(e){} }
|
||
function refreshOverall(){
|
||
const won=BOSS_STATE.filter(b=>b.defeated).length;
|
||
const txt=document.getElementById('boss-overall'); if(txt) txt.textContent=won+' / '+BOSSES.length+' боссов побеждено';
|
||
const fill=document.getElementById('boss-overall-fill'); if(fill) fill.style.width=(won*100/BOSSES.length)+'%';
|
||
if(won>=BOSSES.length){ bumpProgress('final5',60); achievement('ch5_done','Глава 5 пройдена!'); }
|
||
}
|
||
cont.innerHTML = BOSSES.map((b,idx)=>{
|
||
return '<div class="boss-card" id="boss-card-'+idx+'" style="border-color:'+b.color+'">'
|
||
+'<div class="boss-head">'
|
||
+'<svg viewBox="0 0 24 24" fill="none" stroke="'+b.color+'" stroke-width="2.2" style="width:28px;height:28px"><polygon points="12,2 22,20 2,20"/></svg>'
|
||
+'<div class="boss-title" style="color:'+b.color+'">'+b.title+'</div>'
|
||
+'<div class="boss-stage" id="boss-'+idx+'-stage">Этап 1 / '+b.steps.length+'</div>'
|
||
+'</div>'
|
||
+'<div class="hp-boss" style="border-color:'+b.color+'66;background:'+b.color+'1a"><div class="hp-boss-fill" id="boss-'+idx+'-fill" style="width:0%;background:linear-gradient(90deg,'+b.color+',#f59e0b)"></div></div>'
|
||
+'<div class="boss-q" id="boss-'+idx+'-q" style="border-color:'+b.color+'"></div>'
|
||
+'<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">'
|
||
+'<input type="text" id="boss-'+idx+'-input" class="tinp" placeholder="Ответ" style="width:160px;text-align:center">'
|
||
+'<button class="btn primary" id="boss-'+idx+'-go" style="background:'+b.color+';border-color:'+b.color+'">Атака</button>'
|
||
+'<button class="btn" id="boss-'+idx+'-hint">Подсказка</button>'
|
||
+'<button class="btn" id="boss-'+idx+'-restart">↻</button>'
|
||
+'</div>'
|
||
+'<div class="feedback" id="boss-'+idx+'-fb"></div>'
|
||
+'</div>';
|
||
}).join('');
|
||
if(window.renderMathInElement) try{ renderMath(cont); }catch(e){}
|
||
|
||
BOSSES.forEach((b,idx)=>{
|
||
function show(){
|
||
const st=BOSS_STATE[idx];
|
||
const stageEl=document.getElementById('boss-'+idx+'-stage');
|
||
const fill=document.getElementById('boss-'+idx+'-fill');
|
||
const q=document.getElementById('boss-'+idx+'-q');
|
||
const fb=document.getElementById('boss-'+idx+'-fb');
|
||
if(st.defeated){
|
||
stageEl.textContent='✓ Побеждён'; fill.style.width='100%';
|
||
q.innerHTML='<b style="color:'+b.color+'">Босс повержен!</b>';
|
||
document.getElementById('boss-'+idx+'-go').disabled=true;
|
||
document.getElementById('boss-'+idx+'-go').style.opacity=.5;
|
||
return;
|
||
}
|
||
stageEl.textContent='Этап '+(st.stage+1)+' / '+b.steps.length;
|
||
fill.style.width=(st.stage*100/b.steps.length)+'%';
|
||
q.innerHTML=b.steps[st.stage].q;
|
||
document.getElementById('boss-'+idx+'-input').value='';
|
||
fb.style.display='none';
|
||
renderMath(q);
|
||
}
|
||
document.getElementById('boss-'+idx+'-go').addEventListener('click',()=>{
|
||
const st=BOSS_STATE[idx]; if(st.defeated) return;
|
||
const step=b.steps[st.stage];
|
||
const val=document.getElementById('boss-'+idx+'-input').value;
|
||
const fb=document.getElementById('boss-'+idx+'-fb');
|
||
if(!val.trim()){ feedback(fb,false,'✗ Введи ответ.'); return; }
|
||
if(step.verify(val)){
|
||
st.stage++;
|
||
if(st.stage>=b.steps.length){
|
||
st.defeated=true; saveBosses();
|
||
feedback(fb,true,'✓ Босс '+b.n+' побеждён! +20 XP');
|
||
addXp(20,'boss-'+b.n); bumpProgress('final5',18); refreshOverall();
|
||
setTimeout(show,1400);
|
||
}else{
|
||
saveBosses(); feedback(fb,true,'✓ Верно! +3 XP'); addXp(3,'boss-step'); setTimeout(show,1100);
|
||
}
|
||
}else{ feedback(fb,false,'✗ Промах.'); }
|
||
});
|
||
document.getElementById('boss-'+idx+'-hint').addEventListener('click',()=>{
|
||
const st=BOSS_STATE[idx]; if(st.defeated) return;
|
||
const fb=document.getElementById('boss-'+idx+'-fb');
|
||
fb.className='feedback ok';
|
||
fb.innerHTML='<span style="color:#92400e">\u{1F4A1} Подсказка:</span> '+b.steps[st.stage].hint;
|
||
fb.style.display='block';
|
||
fb.style.background='var(--warn-bg)'; fb.style.color='#92400e'; fb.style.borderLeftColor='var(--warn)';
|
||
renderMath(fb);
|
||
});
|
||
document.getElementById('boss-'+idx+'-restart').addEventListener('click',()=>{
|
||
BOSS_STATE[idx]={stage:0,defeated:false}; saveBosses();
|
||
document.getElementById('boss-'+idx+'-go').disabled=false;
|
||
document.getElementById('boss-'+idx+'-go').style.opacity=1;
|
||
show(); refreshOverall();
|
||
});
|
||
show();
|
||
});
|
||
|
||
refreshOverall();
|
||
}
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|