feat(lab-graph): удобство и красота — скрытие функций, точки, контролы вида, пинч

«График функции», большой апгрейд UX:
- у каждой функции кнопки «глаз» (скрыть/показать, не удаляя) и «очистить»;
  скрытая — приглушена и зачёркнута, исключается из графика/hover/значений
- плавающие контролы вида поверх canvas: зум +/−, сброс вида, тумблер «Особые точки»
- ОСОБЫЕ ТОЧКИ: нули функций, y-перехваты и пересечения кривых — ringed-точки
  с подписью координат (бисекция по смене знака; правка: точные нули на узлах
  сетки больше не теряются; дедуп; подписи скрываются при «частоколе» >22 точек)
- пинч-зум двумя пальцами к центру жеста (к 1-пальцевой панораме)

Движок: setHidden/setShowPoints/_drawPoints/_findZeros/_visible; hover и
инфобар уважают скрытие. Только фронт. node --check OK; zero-finder 5/5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-24 19:11:21 +03:00
parent fa29332bcd
commit cd0ce17a60
3 changed files with 174 additions and 14 deletions
+12
View File
@@ -14,6 +14,8 @@
<input class="fn-input" id="fn0" placeholder="sin(x)" autocomplete="off" spellcheck="false" oninput="updateFn(0)" />
<div class="fn-math" id="fn0-math" title="Нажми, чтобы изменить"></div>
</div>
<button class="fn-act" id="fn0-eye" type="button" title="Скрыть/показать" onclick="toggleFn(0)"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/></svg></button>
<button class="fn-act" type="button" title="Очистить" onclick="clearFn(0)"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>
</div>
<div class="fn-preview" id="fn0-prev"></div>
<div class="fn-err" id="fn0-err">Синтаксическая ошибка</div>
@@ -28,6 +30,8 @@
<input class="fn-input" id="fn1" placeholder="x^2 - 4" autocomplete="off" spellcheck="false" oninput="updateFn(1)" />
<div class="fn-math" id="fn1-math" title="Нажми, чтобы изменить"></div>
</div>
<button class="fn-act" id="fn1-eye" type="button" title="Скрыть/показать" onclick="toggleFn(1)"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/></svg></button>
<button class="fn-act" type="button" title="Очистить" onclick="clearFn(1)"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>
</div>
<div class="fn-preview" id="fn1-prev"></div>
<div class="fn-err" id="fn1-err">Синтаксическая ошибка</div>
@@ -42,6 +46,8 @@
<input class="fn-input" id="fn2" placeholder="tg(x)" autocomplete="off" spellcheck="false" oninput="updateFn(2)" />
<div class="fn-math" id="fn2-math" title="Нажми, чтобы изменить"></div>
</div>
<button class="fn-act" id="fn2-eye" type="button" title="Скрыть/показать" onclick="toggleFn(2)"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/></svg></button>
<button class="fn-act" type="button" title="Очистить" onclick="clearFn(2)"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></button>
</div>
<div class="fn-preview" id="fn2-prev"></div>
<div class="fn-err" id="fn2-err">Синтаксическая ошибка</div>
@@ -121,6 +127,12 @@
<div class="graph-canvas-outer">
<div class="graph-canvas-wrap">
<canvas id="graph-canvas"></canvas>
<div class="graph-view-ctrls">
<button class="gv-btn" id="graph-pts-btn" type="button" title="Особые точки: нули, пересечения, y-перехват" onclick="toggleGraphPoints()"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="6" cy="7" r="2"/><circle cx="13" cy="15" r="2"/><circle cx="19" cy="6" r="2"/></svg></button>
<button class="gv-btn" type="button" title="Приблизить" onclick="graphZoom(1)"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg></button>
<button class="gv-btn" type="button" title="Отдалить" onclick="graphZoom(-1)"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><line x1="5" y1="12" x2="19" y2="12"/></svg></button>
<button class="gv-btn" type="button" title="Сбросить вид" onclick="graphFit()"><svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="4 9 4 4 9 4"/><polyline points="20 9 20 4 15 4"/><polyline points="4 15 4 20 9 20"/><polyline points="20 15 20 20 15 20"/></svg></button>
</div>
</div>
<div class="graph-info-bar" id="graph-info-bar">
<div class="info-coord">