Система стилей AnimaGeo — полный справочник¶
Подробное руководство по всему визуальному слою AnimaGeo: JSON-схеме, ключам отдельных элементов, z-index, подписям и их автоматическому размещению, ImportPolicy, шрифтам, единицам, пиксельной инвариантности и различиям статического и анимированного вывода.
Где читать о стилях¶
| Документ | Когда нужен |
|---|---|
| Стили | Основной справочник: слои, JSON, resolver, единицы, overlay, рендер и импорт |
| Архитектура | Краткая карта прохождения GGB/DSL через applyStyle, StyleConfig, ImportPolicy, StyleOverlay и рендерер |
| ImportPolicy | Только импорт и адаптация GGB: исходные значения → elem.ggb_style, директивы scale: / quantize: / remap: |
| Поля объектов | Соответствие GGB XML → elem.ggb_raw → elem.ggb_style / elem.style → JSON → рендерер |
Идея слоёв¶
Стиль разделён по ответственности, а не по принципу «куда удобнее записать ключ».
| Слой | Ответственность | Что здесь хранится | Чего здесь нет |
|---|---|---|---|
presets |
Семантические токены | Цвета, размеры, толщины, шрифты, пресеты засечек и стрелок | Правила выбора конкретных объектов |
defaults |
Базовый стиль по типу | «Все точки по умолчанию выглядят так», «у всех углов такой радиус дуги» | Именованные исключения и GGB-remap |
import |
Адаптация GeoGebra | Карта цветов, размеров точек и толщин линий GGB; ImportPolicy для значений, выведенных из сырых |
Стиль проекта по типу или имени |
overlay |
Правила поверх импорта и DSL | per_type, per_name, автоматический радиус углов, размещение подписей |
Разбор исходных значений GGB |
reference |
Эталонный холст стиля | Ширина и высота для превью и масштабируемого экспорта | Физический размер файла |
rendering |
Общие параметры вывода | Окончания линий, фон, поведение слоёв, глобальный показ точек | Стили отдельных объектов и GGB-remap |
elem.ggb_style |
Нормализованный результат импорта и адаптации GGB | Адаптированные цвета, размеры, типы линий и подписи | Ручные изменения из DSL/API |
elem.style |
Явные параметры элемента | Локальные изменения из DSL/API | Общепроектные правила и исходные значения GGB |
Короткое правило: import отвечает, как читать GeoGebra; overlay — как
оформлять проект; rendering — как завершить и экспортировать рисунок.
Содержание¶
- Конвейер
- Единицы и пиксельная инвариантность
- Холст, камера и экспорт
- JSON-схема
- Z-index и слои
- Все ключи элемента
- Подписи и TeX
- Автоматическое размещение подписей
- ImportPolicy: гибкий импорт GeoGebra
- Статика и анимация
- Ключевые кадры и интерполяция подписей
- Групповой API
- Встроенные пресеты
- Рецепты: «как получить X»
- Особенности и подводные камни
- Размеры и пропорции
1. Конвейер¶
.ggb → ggb_parser → Construction (Elements + ggb_raw + ggb_style)
│
▼
applyStyle(style=style.json, reference=..., content=..., export=...)
│
├─ GeoStyle — контекст экспорта сцены
├─ StyleConfig — builtin.json + deep-merge JSON пользователя
│ ├─ presets
│ ├─ defaults.<type> ← база по типам
│ ├─ overlay.per_type ← одинаково для GGB и DSL
│ ├─ overlay.per_name
│ ├─ overlay.angle_radius
│ └─ overlay.label_placement
└─ ImportPolicy — raw GGB → elem.ggb_style
(scale:/quantize:/remap:)
│
▼ addAllGeometry
CreateMObject (Element → Manim Mobject)
└─ resolver.resolve(scene, elem, key):
elem.style → per_name → per_type
→ ggb_style → defaults
│
┌─────────┼─────────┐
▼ ▼ ▼
SVG MP4 Live preview
Каждый элемент несёт три блока стилевых данных:
| Источник | Когда заполняется | За что отвечает |
|---|---|---|
elem.ggb_raw |
ggb_parser |
Исходные значения GGB (point_size, line_thickness, line_opacity, line_type, arc_size, label_offset_px, obj_color.hex и obj_color.opacity) |
elem.ggb_style |
ggb_parser + правила импорта в applyStyle + ImportPolicy |
Нормализованная визуальная база GGB. Resolver читает её, только когда import.enabled не равно false. |
elem.style |
Код DSL/API, помощники раскладки и ключевых кадров | Явные записи по элементу. Важнее overlay, импорта GGB и значений по умолчанию. |
scene.style_config |
StyleConfig.load(style) |
Конфигурация presets, defaults, overlay, rendering, reference. Читается через resolver.resolve(). |
GeoStyle |
applyStyle из JSON или словаря |
Контейнер уровня сцены: палитра, флаги рендерера и вычисленный export. |
Отрисовка в CreateMObject, экспорт в SVG/PNG/MP4 и превью — все читают
визуальные значения через resolver.resolve(). Поэтому импорт GGB, значения по
умолчанию, overlay и прямые записи из DSL проходят через один механизм.
Приоритет resolver¶
resolver.resolve(scene, elem, key) идёт по слоям сверху вниз и возвращает
первое попадание:
1. elem.style[key] — явная запись DSL/API
2. overlay.per_name[name][key] — правило по имени
3. overlay.per_type[type][key] — правило по типу
4. elem.ggb_style[key] — импорт и адаптация GGB, если import.enabled != false
5. defaults.by_type[type][key] — builtin.json + defaults пользователя (глубокое слияние)
6. внутренний стиль геометрического класса
7. default=… из аргумента вызова — последний запасной вариант
Инвариант: overlay (per_type и per_name) всегда сильнее импорта и
адаптации GGB. Прямые записи пользователя в elem.style считаются явными и
имеют высший приоритет. Overlay никогда не копируется в elem.style: рендерер
читает его лениво через resolver.
builtin.json¶
Поставляемый с пакетом файл animageo/style/builtin.json содержит разумные
значения по умолчанию в пикселях для каждого типа элемента. Он загружается
всегда первым, а пользовательский JSON накладывается сверху через
deep_merge. Поэтому короткого файла
достаточно, чтобы переопределить точки и сохранить все остальные умолчания — цвета, толщины, углы и прочее.
Элементы DSL получают overlay¶
ImportPolicy работает только с сырыми значениями GGB, поэтому элементы DSL
через него не стилизуются. Общие правила по типу и по имени должны находиться в
overlay.per_type и overlay.per_name; resolver читает такие правила лениво и
одинаково для GGB и DSL.
2. Единицы и пиксельная инвариантность¶
В проекте сосуществуют три системы единиц, и их важно не путать.
| Пространство | Где живёт | Примеры |
|---|---|---|
| Пиксели GGB | ggb_raw, elem.ggb_style['*_px'], elem.style['*_px'], defaults.<type>.*_px |
Значения прямо из .ggb, слой импорта, пользовательские стили, builtin.json |
| Канонические пиксели стиля | style/*.json (presets, defaults.*, overlay.*) |
Все визуальные размеры в пикселях |
| Внутренние единицы manim | Итоговые координаты mobject'ов и толщины обводок | То, что действительно рисуется |
Преобразования собраны в animageo/style/scaling.py:
# GGB px → elem.ggb_style
ggb_point_size_to_style(x) = x * 2 # pointSize → size_px
ggb_thickness_to_stroke_width(x) = x / 2 # thickness → stroke_width_px
ggb_arc_size_px(x, right=False) = x (или x/√2) # arcSize → arc_size_px
ggb_label_offset_to_style(x, y) = [x, -y] # Y инвертируется (экран GGB ↓ против математики ↑)
# resolved style → Manim во время рендера
stroke_width_to_manim(sw, ptUnit) = sw * 100 / ptUnit
ggb_font_px_to_manim_fontsize(px, ptUnit) = px * 100 / ptUnit
Исправление единиц в четвёртой фазе. В канонической схеме размеры в
presets,defaults.*иoverlay.*хранятся в пикселях, а рендерер делит их наptUnitровно один раз. Это убрало прежнее двойное масштабирование, из-за которого дуги углов на сценах DSL схлопывались до субпиксельного размера.
Контракт пиксельной инвариантности¶
Все видимые размеры — диаметры точек, толщины линий, размер шрифта, радиусы
дуг углов, смещения подписей — хранятся в пикселях. Рендерер делит их на
ptUnit_style, поэтому стиль считается относительно эталонного холста и
масштабируется вместе с эталонной картинкой вплоть до физического
export.size.
ptUnit_ggb хранится отдельно: это исходный масштаб из файла .ggb (пикселей
на единицу GGB). По нему масштабируются подписи, чтобы на маленьком холсте они
не отрывались от геометрии.
Убранные входные ключи¶
Исторические ключи strich_len, strich_rshift, strich_width,
arrow_height, arrow_width, label_r_offset, line_width, ang_width,
ang_rdefault, ang_rshift, ang_right, font_size больше не принимаются в
JSON-стиле. Используйте канонические поля: tick_length_px, tick_shift_px,
tick_width_px, arrow_length_px, label_radial_offset_px,
stroke_width_px, arc_size_px, arc_shift_px, right_angle_size_px,
font_size_px.
3. Холст, камера и экспорт¶
Параметры экспорта¶
scene.style.export = {
'ptUnit': ..., # пикселей на единицу manim в итоговом экспорте
'ptWidth': ..., # px — ширина холста
'ptHeight': ..., # px — высота холста
'ptXZero': ..., # px — положение начала координат (0,0) от левого края
'ptYZero': ..., # px — положение начала координат от верхнего края
'ptUnit_style': ..., # эталонный масштаб для визуальных значений *_px
'referenceWidth': ...,
'referenceHeight': ...,
'geometryScale': ..., # content -> reference
'exportScale': ..., # reference -> физический экспорт
'ptUnit_ggb': ..., # исходный ptUnit из .ggb (для пиксельно-инвариантных подписей)
'fontSize': ..., # размер шрифта GGB (px) из gui.font в XML
}
Современная раскладка разделяет три задачи:
scene.loadGGB(
'x.ggb',
style='default',
reference={'size': {'width': 300, 'height': 220}}, # переопределение во время выполнения
content={'source': 'source_view', 'fit': 'contain'},
export={'size': {'width': 1920, 'height': 1080}, 'fit': 'contain'},
)
Короткие имена пресетов, переданные в style= (default, book_blue,
book_green, book_purple, book_red), разворачиваются в файлы стилей,
поставляемые с библиотекой (см. §13); любой путь или имя файла
.json загружается с диска как обычно, а файл на диске с тем же именем всегда
важнее упакованного пресета.
reference— эталонный холст, под который автор проектировал стиль. Обычно хранится в JSON-стиле и переопределяется аргументом времени выполнения только при необходимости.content— какую область конструкции помещать на эталон:source_view,ggb_viewилиrendered_bounds; такжеfit,padding,anchor,offset,infinite_policyиprominence.export— физический файл результата; это параметр времени выполнения, он не хранится в JSON-стиле.ptUnit_style— масштаб эталонного холста. Через него отрисовываютсяstroke_width_px,size_px,font_size_px,arc_size_px, смещения и все прочие визуальные пиксели.ptUnit— масштаб итогового холста экспорта. По нему SVG и камера размещают всю картинку.geometryScale— насколько прямоугольник content/source масштабирован, чтобы вписаться в эталон.exportScaleиcontentScale— насколько физический файл увеличивает эталонную картинку.content.source='rendered_bounds'сначала строит видимые mobject'ы в исходном масштабе, измеряет их итоговые границы вместе с подписями и затем вписывает этот прямоугольник.content.paddingдобавляет поле в пикселях источника. ДляLineиRayполитика по умолчаниюcontent.infinite_policy='ignore'исключает их из границ;'clip'измеряет их после обрезки камерой источника.- Неравномерное растяжение не реализовано: рендерер по-прежнему использует один
общий
ptUnit.
Эталон можно сохранить прямо в стиле:
reference.source принимает manual, source_view или ggb_view и служит
описанием того, откуда взялся эталон в JSON-стиле. Область конкретной
конструкции выбирается во время выполнения через content.source.
Полная таблица допустимых значений блоков style, reference, content и
export приведена в docs/api.md.
px_size больше не является основной публичной моделью. Используйте
export={'size': {'width': w, 'height': h}}; одна из сторон может быть задана
как "auto".
4. JSON-схема¶
Полная документация живёт в docstring файла animageo/style/schema.py. Ниже —
структура и каждый ключ, который действительно читается.
{
"name": "строка (необязательно)",
"version": 0.1,
"presets": {
"color": {
"main": "#000000",
"bold": "#000000",
"aux": "#888888",
"accent": "#f15b5b",
"background": "#ffffff",
"strong": "#000000"
},
"point_size": { "main": 2.83, "bold": 4.25, "aux": 2.12 },
"line_width": { "main": 1, "bold": 1.5, "aux": 0.75 },
"angle_radius": { "main": 17, "shift": 1.5, "right": 17 },
"tick": { "main": { "tick_length_px": 9, "tick_width_px": 1.5, "tick_shift_px": 2 } },
"arrow": { "main": { "arrow_length_px": 11, "arrow_width_px": 7.5 } },
"font_size": { "main": 14, "bold": 16, "aux": 12 }
},
"defaults": {
"point": {
"size_px": "point_size.main",
"fill": "color.strong"
},
"segment": {
"$include": "tick.main",
"stroke_width_px": "line_width.main"
},
"vector": {
"$include": ["tick.main", "arrow.main"]
}
},
"overlay": {
"per_type": {
"angle": { "arc_size_px": 22 },
"point": { "size_px": 7 }
},
"per_name": {
"A": { "size_px": 99 }
},
"angle_radius": { /* см. §8 */ },
"label_placement": { /* см. §8 */ }
},
"rendering": {
"background": "color.background",
"line_cap": "butt",
"dash_period_px": 10,
"right_angle_joint": "round",
"polygon_boundary_layer": "top",
"points_display": "auto",
"label_anchor": "BL",
"label_value_precision": 1
},
"import": {
"colors": { "#1565c0": "color.main", "#d32f2f": "color.accent" },
"point_size": { "5": "point_size.main" },
"line_width": { "5": "line_width.main" },
"policy": { /* см. §9 */ }
}
}
Секция
overlay(с еёper_typeиper_name) — место для стилизации, применяемой после импорта. Она работает одинаково для элементов GGB и DSL.overlay.angle_radiusиoverlay.label_placement— единственное публичное место для блоков автоматики.
presets — семантические константы¶
presets — это реестр именованных семантических констант. Имена main,
bold, aux — всего лишь соглашение; пользователь может добавить любые
(construction, answer, hidden_helper) и ссылаться на них из defaults,
overlay, rendering и карт импорта через "<группа>.<имя>" — например,
"color.accent" или "line_width.bold".
Основные группы пресетов¶
| Группа.ключ | Назначение | Единицы |
|---|---|---|
color.main / bold / aux / background / strong |
Именованные цвета | hex |
point_size.main / bold / aux |
Диаметры точек (size_px) |
пиксели стиля |
line_width.main / bold / aux |
Толщины линий | пиксели стиля |
angle_radius.main / shift / right |
Радиусы углов и сдвиги | px |
tick.main |
Структура tick_length_px/tick_width_px/tick_shift_px |
px |
arrow.main |
Структура arrow_length_px/arrow_width_px |
px |
font_size.main / bold / aux |
Размер шрифта | px |
Рекомендуемые значения и соотношения, при которых чертёж читается комфортно, — в §16 «Размеры и пропорции».
defaults — база по типам¶
defaults больше не хранит настройки уровня сцены для углов, засечек, стрелок
и шрифта. Это база по типам: defaults.point, defaults.segment,
defaults.angle и так далее. Значения здесь обычно являются ссылками в
presets.
Структурные пресеты подключаются через $include; локальные ключи в том же
блоке типа важнее подключённых значений. Старые формы
defaults.angle/tick/arrow/font загрузчик по-прежнему принимает и нормализует
в семантическую схему до слияния.
rendering — параметры рендера¶
| Ключ | Значения | Действие |
|---|---|---|
background |
hex или color.* |
Фон сцены: камера Manim и MP4, а также область просмотра SVG |
line_cap |
"butt" | "round" | "square" |
Окончания линий |
dash_period_px |
число > 0 (px), по умолчанию 10 |
Общий для стиля период пунктира (штрих + промежуток) для элементов без своего stroke_dash_period_px |
right_angle_joint |
"auto" | "bevel" | "miter" | "round" |
Соединение сторон маркера прямого угла |
polygon_boundary_layer |
"top" | null |
"top": контур многоугольника всегда над заливкой (z_index=10) |
points_display |
"auto" | "only_labels" | "only_points" |
only_labels прячет точку и показывает подпись; only_points — наоборот |
label_anchor |
"TL"/"TC"/"TR"/"ML"/"MC"/"MR"/"BL"/"BC"/"BR" |
Якорь подписи по умолчанию для всей сцены (когда не задан у элемента). Полная сетка — §7 |
label_value_precision |
int | Точность числовых подписей по умолчанию для всей сцены |
fast_value_labels |
bool, по умолчанию true |
При анимации подписи со значением рисуются кэшированными глифами цифр, без перекомпиляции LaTeX на каждом кадре; false включает LaTeX везде. Статический экспорт всегда использует LaTeX |
label_contrast |
"off" | "auto", по умолчанию "off" |
"auto" перекрашивает подпись на заливке близкой яркости в читаемый чёрный или белый |
label_contrast_threshold |
float, по умолчанию 0.35 |
Разница яркости, ниже которой label_contrast: "auto" перекрашивает |
color_interpolation |
"oklab" | "srgb", по умолчанию "oklab" |
Цветовое пространство анимации цветов в дорожках стиля ключевых кадров |
overlay.label_placement и overlay.angle_radius читаются напрямую из
scene.style_config.overlay; эти блоки автоматики не принимаются внутри
rendering.
import — отображение значений GGB¶
По умолчанию применяется поверх разобранных значений GGB после базового
разбора. Если задано "enabled": false, геометрия и elem.ggb_raw
сохраняются, но resolver пропускает elem.ggb_style, берёт базу из
StyleConfig.defaults, а colors, point_size, line_width и policy
пропускаются целиком. После этого overlay.per_type, overlay.per_name и
явные правки из DSL и Python работают в обычном порядке.
enabled: bool, по умолчанию true.
colors: словарь вида "#hex [непрозрачность]" → "имя_цвета|#hex [непрозрачность]".
Позволяет одной строкой в стиле перекрасить всю конструкцию.
"colors": {
"#1565c0": "color.main", // синий GGB → color.main
"#1565c0 0.1": "color.light 1", // тот же цвет, но полупрозрачный → light с alpha=1
"#d32f2f": "color.accent",
"#000000 0.6": "#2581b5" // справа можно и просто hex
}
Результат применения отображения всегда нормализуется в два поля слоя импорта:
цвет записывается в elem.ggb_style["fill"] и elem.ggb_style["stroke"] как
#rrggbb, а непрозрачность попадает в fill_opacity и stroke_opacity только
тогда, когда она явно указана в правой части. Если целевая непрозрачность не
задана, текущая непрозрачность элемента сохраняется.
Например, "#1565c0 0.1": "color.accent 1" даёт fill = "#f15b5b" и
fill_opacity = 1.0; а "#1565c0 0.1": "color.accent" меняет только цвет и
сохраняет прежний fill_opacity.
line_width: отображение толщин "N": "line_width.*" (например,
"5": "line_width.main" — толщина 5 в GGB → line_width.main).
point_size: то же для размеров точек.
policy: см. §9.
Полотно GeoGebra: сетка и оси¶
Во время loadGGB() парсер читает настройки <euclidianView> из
geogebra.xml и сохраняет их в scene.style.export. Эти поля не являются
overlay стиля для геометрических элементов: они описывают фон координатной
области.
| GGB XML | style.export |
Использование |
|---|---|---|
<evSettings axes> |
showAxes |
Включает отрисовку фоновых осей |
<evSettings grid> |
showGrid |
Включает фоновую координатную сетку |
<evSettings gridIsBold> |
gridIsBold |
Делает линии сетки чуть плотнее |
<evSettings gridType> |
gridType |
Сохраняется для совместимости; пока рисуется декартова сетка |
<axesColor r g b> |
axesColor |
Цвет осей, засечек и чисел |
<gridColor r g b> |
gridColor |
Цвет линий сетки |
<grid distX distY distTheta> |
gridDistX, gridDistY, gridDistTheta |
Шаг сетки по X и Y; distTheta сохраняется для будущих полярных и изометрических режимов |
<axis id="0\|1" show> |
axes.x.show, axes.y.show |
Видимость отдельной оси |
<axis ... showNumbers> |
axes.x.showNumbers, axes.y.showNumbers |
Числовые подписи засечек |
<axis ... tickDistance> |
axes.x.tickDistance, axes.y.tickDistance |
Шаг засечек на оси |
<axis ... axisCross positiveAxis> |
axes.*.axisCross, axes.*.positiveAxis |
Сохраняются; полная отрисовка точки пересечения и «только положительная часть» пока не включена |
addAllGeometry() добавляет _coordinate_background перед геометрией: сетка
рисуется на z_index=-20, оси на z_index=-10, засечки и числа на
z_index=-9. Это отдельный фон, а не служебные элементы xAxis и yAxis из
Construction.
5. Z-index и слои¶
┌──────────────────────────────────────────────────────┐
│ Z_POINT = 50 ← точки (самый верх) │
│ Z_LABEL = 50 ← подписи на уровне обводки │
│ Z_STROKE = 5 ← отрезки, засечки, контуры дуг │
│ Z_LINE = 4 ← прямые, окружности │
│ Z_ANGLE = 3 ← дуги углов │
│ Z_FILL_LABEL = 0.1 ← подписи на уровне заливки │
│ Z_FILL = 0.01 ← заливки многоугольников и секторов │
│ Z_FILL_INNER = 0.001 ← зарезервированная нижняя заливка │
└──────────────────────────────────────────────────────┘
(меньше = дальше назад; больше = ближе к зрителю)
Автоматическое назначение¶
При z_auto=True (addAllGeometry, addGeoElement) CreateMObject назначает
z-index по типу элемента:
| Тип | Основной слой | Заливка | Подпись |
|---|---|---|---|
Point |
Z_POINT (50) |
— | Z_LABEL (50) |
Segment / Circle / Arc / Vector |
Z_STROKE (5) |
— | Z_LABEL (50) |
Angle |
Z_ANGLE (3) |
— | Z_LABEL (50) |
Polygon |
Z_FILL (0.01) |
— | Z_FILL_LABEL (0.1) |
CircleSector |
Z_FILL (0.01) |
Z_FILL (0.01) |
Z_FILL_LABEL (0.1) |
Обводка многоугольника всегда ≥ max(Z_STROKE, zz+0.1) — контур остаётся над
заливкой. Если rendering.polygon_boundary_layer = "top", отрезки сторон
многоугольника размещаются на z_index=10, то есть выше всего, кроме точек и
подписей.
Ради устойчивости анимации MP4 AnimaGeo добавляет к каждому фактическому
z_index в Manim крошечное смещение по порядку элемента в конструкции
(construction_index × 1e-6). Это не меняет уровни fill, stroke и point,
но делает порядок внутри одного слоя детерминированным даже тогда, когда
Polygon пересоздаётся через remove + add во время updateGeoElements().
Явное переопределение¶
scene.element('poly').style['z_index'] = 100 # многоугольник над точками
scene.element('sector').style['z_index_fill'] = 0.2 # только для CircleSector
Если ключ z_index задан явно, автоматическое назначение не срабатывает.
Элемент, не определённый в загруженном .ggb (координаты NaN, например
пересечение, которого нет в сохранённом состоянии), но определяющийся позже,
сохраняет слой и умолчания подписи своего типа.
6. Все ключи элемента¶
elem.style — обычный словарь Python, который можно менять на лету. Ниже —
исчерпывающий список того, что рендерер действительно читает.
Видимость¶
| Ключ | Тип | По умолчанию | Что делает |
|---|---|---|---|
elem.visible (атрибут, не ключ) |
bool | True |
Полностью скрывает mobject |
visible |
bool | True |
«Показывать объект» из GGB; читается через resolver |
label_visible |
bool | Зависит от элемента | Рисовать подпись |
Обводка (имена, совместимые с SVG)¶
| Ключ | Тип | По умолчанию |
|---|---|---|
stroke |
hex | style.strong |
stroke_width_px |
float (px) | defaults.<type>.stroke_width_px |
stroke_opacity |
float 0..1 | 1 |
stroke_dash_ratio |
float 0..1 | None | None (сплошная); доля штриха в периоде, 0/None или >= 1 — сплошная |
stroke_dash_period_px |
float (px) | rendering.dash_period_px (10) |
stroke_linecap |
"butt"/"round"/"square" (рантайм принимает и "auto") |
rendering.line_cap |
right_angle_joint |
"auto"/"bevel"/"miter"/"round" |
rendering.right_angle_joint |
Заливка¶
| Ключ | Тип | По умолчанию |
|---|---|---|
fill |
hex | style.background (для точек — style.strong) |
fill_opacity |
float 0..1 | 1 |
Точки¶
| Ключ | Тип | По умолчанию | Действие |
|---|---|---|---|
size_px |
float (px) | style.dot_size |
диаметр = size_px / 2 / ptUnit |
point_shape |
строковый enum | "circle" |
Форма: "circle", "square", "diamond", "triangle_up", "triangle_down", "triangle_left", "triangle_right", "cross", "plus". Соответствие пресетам GGB — в docs/field_names.md §3.3. |
Углы¶
| Ключ | Тип | По умолчанию | Действие |
|---|---|---|---|
arc_size_px |
float (px) | defaults.angle.arc_size_px |
Базовый радиус дуги |
arc_shift_px |
float (px) | defaults.angle.arc_shift_px |
Радиальный сдвиг между концентрическими дугами при tick_count > 1 |
angle_range |
"minor" | "reflex" |
Из GGB | "minor" — меньший сектор (≤π), "reflex" — больший (>π) |
right_angle_marker |
bool | Автоматически: np.isclose(angle, π/2) |
Принудительно включить квадратный маркер прямого угла |
right_angle_size_px |
float (px) | defaults.angle.right_angle_size_px |
Размер квадратного маркера прямого угла |
tick_count |
int | 1 | Несколько дуг (двойные, тройные метки) |
Отрезки и векторы¶
| Ключ | Тип | По умолчанию | Действие |
|---|---|---|---|
tick_count |
int | None (ключ может отсутствовать) | Количество засечек в середине |
tick_style |
"line" | "wave" |
"line" |
"wave" — волнистая метка вместо прямых засечек |
tick_radius_px |
float | tick_shift_px * 0.45 |
Радиус скругления углов для tick_style="wave" |
Подписи¶
| Ключ | Тип | По умолчанию | Действие |
|---|---|---|---|
label_text |
строка TeX | "$" + elem.name + "$" |
Отображаемый текст |
label_mode |
"label" | "value" | "label_value" |
"label" |
Что показывать: подпись, вычисленное значение или label = value |
label_value_precision |
int | 1 |
Число знаков после запятой у вычисленного значения |
label_value_strip_zeros |
bool | True |
Убирать хвостовые нули (5.00 → 5) |
label_angle_unit |
"degree" | "radian" |
"degree" |
Единица для значений углов |
label_value_separator |
str | " = " |
Разделитель в режиме label_value |
label_color |
hex | style.strong |
Цвет текста |
label_anchor |
"TL"…"BR" |
rendering.label_anchor | BL |
Какая часть bbox подписи попадает в точку привязки |
label_offset_px |
[x, y] (пиксели GGB) |
[0, 0] |
Смещение подписи после позиционирования; делится на ptUnit_ggb |
font_size_px |
float (px) | defaults.<type>.font_size_px |
Переопределение размера шрифта у элемента |
label_radial_offset_px |
float (px) | defaults.angle.label_radial_offset_px (0) |
Радиальное смещение подписи от геометрии (используется для углов) |
label_placement_locked |
bool | False |
Защищает подпись от автоматического размещения |
label_hint_px |
[x, y] (пиксели GGB, ось y вверх) |
— | Где должен быть центр подписи относительно опорной точки элемента; мягкая просьба к авторазмещению (§ «Подсказка вместо фиксации») |
_auto_placed |
bool | False (внутренний) |
Устанавливается авторазмещением; отключает поправку на выносной элемент GGB в create_label |
Z-index (см. §5)¶
| Ключ | Тип | По умолчанию |
|---|---|---|
z_index |
float | По типу |
z_index_fill |
float | Z_FILL |
7. Подписи и TeX¶
Подпись из текстового объекта GeoGebra¶
Начиная с 1.7.15, режимы GeoGebra 3 (надпись) и 9 (надпись и значение)
читают текстовый объект, указанный в <dynamicCaption val="textName"/>.
Источник может быть скрыт и находиться дальше в файле; при изменении
переменной в анимации подпись снова читает его текущее содержимое.
Если источник отсутствует или не является текстом, используется обычная
надпись либо имя объекта. Явный label_text, текст подписи в
overlay.per_name / overlay.per_type и переопределение label_text
в политике импорта имеют приоритет. При import.enabled=false надписи
GeoGebra не импортируются.
Импортированный текст с isLaTeX=true считается формулой: \varphi
работает без добавления \[...\]. Уже заданные $...$, \(...\),
\[...\], математические окружения и обычный текст со встроенными
явными формулами сохраняются. Обычный текст по-прежнему экранируется
для TeX. Созданный из Python Text обрабатывает LaTeX как прежде;
для такого же неявного режима формулы можно явно передать
latex_math_mode=True.
Привязка по девяти точкам¶
Якорь задаётся в elem.style['label_anchor'] или для всей сцены в
rendering.label_anchor. MC означает «подпись по центру точки» — удобно для
углов и для взаимодействия с авторазмещением (см. canonicalize_anchor).
GeoGebra по умолчанию использует BL — левый нижний угол, то есть базовую
линию заглавных букв.
Шаблон TeX RusTex¶
animageo/ui.py::RusTex — pdflatex плюс T2A, babel russian и utf8,
собственная отрисовка дробей, уменьшенные \angle и \triangle.
Автоматическая замена Unicode → TeX¶
correctedLabel(label) прогоняет текст через словарь из 84 замен (·→\cdot,
α→\alpha, △→\triangle, …), поэтому формулы в подписях можно писать
обычным Unicode.
Подписи, расставленные в GeoGebra¶
Подпись GGB-элемента, у которого нет явного label_anchor (в стиле элемента,
overlay или defaults) и который не размещён автоматически, ставится так же,
как в GeoGebra. GeoGebra берёт базовую точку, зависящую от типа элемента, и
добавляет сохранённый labelOffset:
| Элемент | Базовая точка |
|---|---|
| Точка | на 4 px правее и на 2·размер точки выше точки |
| Отрезок | середина, плюс 16 px вдоль нормали |
| Вектор | середина, сдвинутая вбок на четверть размера стрелки |
| Луч | середина между вершиной и второй точкой, сдвиг на 16 px по отражённому направлению (uₓ, −u_y) (правило GeoGebra) |
| Многоугольник | среднее вершин |
| Окружность, эллипс | точка контура в левой верхней части дуги, 20 px внутрь |
| Дуга, сектор | точка дуги в середине, со смещением (6, 6) px |
| Прямая | правила нет: прежнее общее размещение (GeoGebra отсчитывает от границы окна апплета) |
| Угол | правила нет: у углов собственное размещение |
Правила находятся в animageo/label_anchor.py (ggb_label_anchor). Общий
rendering.label_anchor на такие подписи не действует; чтобы переопределить,
задайте label_anchor у элемента (или включите автоматическое размещение).
Если рисунок экспортируется в другом масштабе, чем апплет, подпись заново
привязывается к ближайшей точке своего элемента относительно того места, где её
нарисовал апплет (label_anchor.nearest_point; ничего не сохраняется, точка
пересчитывается из смещения при каждой отрисовке). Эта точка масштабируется
вместе с рисунком, а в пространстве шрифта остаётся только остаток смещения, так
что расстояние до линии сохраняется при любом размере шрифта. Подпись внутри
области (многоугольник, сектор, внутренность окружности или эллипса) остаётся на
своём месте в области. Точки не затрагиваются. Окружности и эллипсы рисуют
подписи, как и остальные элементы.
Поправка на выносной элемент GGB¶
Смещение в GGB отсчитывается от низа поля ввода вместе с запасом под выносные
элементы букв. Bbox формулы TeX плотный, поэтому подпись просела бы вниз.
create_label автоматически поднимает её на ggb_font_px * 0.25 / ptUnit —
кроме случая, когда установлен _auto_placed (у автоматически размещённых
подписей смещения уже верные).
8. Автоматическое размещение подписей¶
Работает в трёх режимах: однократное статическое, снимки ключевых
кадров (для play_keyframes) и покадровый трекер (для addUpdater).
Статическое — по умолчанию¶
scene.loadGGB(
'x.ggb',
style='default',
export={'size': {'width': 800, 'height': 600}},
)
scene.autoPlaceLabels()
scene.exportSVG('out.svg')
Жадный решатель раскладывает подписи, минимизируя наложения. Кандидатов восемь (В/СВ/С/СЗ/З/ЮЗ/Ю/ЮВ); первыми размещаются самые «стеснённые» подписи — те, у которых меньше свободных позиций.
Параметры overlay.label_placement¶
| Ключ | По умолчанию | Описание |
|---|---|---|
enabled |
false |
Автоматически вызывать autoPlaceLabels() в конце loadGGB |
distance_px |
6 |
Базовое расстояние от точки привязки до центра подписи, px |
padding_px |
2 |
Поле вокруг bbox при проверке наложений, px |
angle_gap_arc_px |
3 |
Зазор между внешней дугой угла и его подписью, px. Не зависит от зазора до сторон |
angle_gap_sides_px |
3 |
Зазор между сторонами угла и bbox подписи (для узких углов), px |
w_anchor |
1.0 |
Вес штрафа за отклонение от предпочтительного направления |
w_label |
10.0 |
Вес наложения подписи на подпись |
w_geom |
8.0 |
Вес наложения подписи на геометрию |
dynamic_angles |
false |
Биссектрисы углов пересчитываются на каждом кадре |
keyframe_snapshots |
false |
Раскладка вычисляется на каждом ключевом кадре и интерполируется между ними. Действует только при enabled: true; снимки определяют положение всех подписей в play_keyframes (дорожка label_offset_px в ключевом кадре игнорируется) |
canonicalize_anchor |
false |
Переписывает все якоря в MC с компенсирующим смещением. Убирает скачки при интерполяции. По умолчанию выключено: перезапись сохранённых якорей сдвигает уже существующие результаты |
interpolation |
"linear" |
Сглаживание смещений подписей между снимками: linear или smooth |
ema_alpha |
0.2 |
Вес свежего результата решателя в EMA (0..1); меньше — плавнее, но медленнее сходится |
anchor_flip_frames |
6 |
Триггер Шмитта: сколько кадров подряд решатель должен предлагать другой якорь |
solver_every_n_frames |
2 |
Троттлинг: решатель запускается раз в N кадров |
Углы: аналитическое размещение вместо перебора кандидатов¶
Для угла подпись всегда ставится на биссектрису. Расстояние:
dist = arc_radius_effective + max(half_w, half_h) + gap_arc
# дополнительно для узких углов:
dist ≥ (√(hw² + hh²) + gap_sides) / sin(half_angle)
arc_radius_effective учитывает несколько дуг: при
elem.style['tick_count'] = N внешний радиус равен
arc_size_px + (N - 1) * arc_shift_px, поэтому подпись никогда не садится на
самую внешнюю дугу, даже когда их несколько.
gap_arc (angle_gap_arc_px) управляет просветом между дугой и подписью, а
gap_sides (angle_gap_sides_px) — просветом между подписью и сторонами угла
(он входит в ограничение для узких углов). Значения по умолчанию обычно
подходят; отдельные настройки полезны, например, когда угол очень острый и
подпись нужно «утопить» ближе к дуге, не увеличивая общий отступ.
Якорь всегда MC.
Ручная фиксация подписи¶
scene.element('A').style['label_placement_locked'] = True
scene.element('A').style['label_offset_px'] = [10, -5]
Подсказка вместо фиксации¶
Зафиксированная подпись стоит ровно там, куда её ставит label_offset_px,
что бы там ни было нарисовано. Если известно только, с какой стороны
подпись должна быть — место взято с другого чертежа, от модели, из общего
правила, — дайте авторазмещению подсказку, остальное оно сделает само:
label_hint_px — желаемое смещение центра подписи от опорной точки
элемента (сама точка, середина отрезка, вершина угла) в тех же пикселях, что
label_offset_px, ось y вверх. У точки это те же два числа, что пишутся в
label_offset_px при label_anchor='MC'.
Что с подсказкой делает авторазмещение:
- Подпись сохраняет подсказанную сторону: у вершины она встаёт в сектор, в который смотрит подсказка, и держит в нём подсказанное направление (не ближе ~28° к краям сектора); у свободной точки — в подсказанном направлении. Подпись отрезка встаёт с подсказанной стороны отрезка.
- Расстояние раскладка выбирает сама (компактно, не задевая кружок точки и линии), поэтому подсказка может быть грубой. Если в подсказанном направлении подпись оказалась бы далеко от точки — рядом линия, которую надо обойти, — подпись поворачивается к середине сектора, не больше чем на 45°.
- Подсказанная сторона держится, пока подпись встаёт там не дальше 1,6 кегля от своей точки. Сектор, где нужно дальше — две линии близко друг к другу, линия поперёк подсказанной стороны, — уступает соседнему сектору, ближайшему к подсказке, если там подпись встаёт так близко; иначе остаётся подсказанная сторона. Сектор, в который подпись не помещается вовсе, уступает следующему.
- Подпись не закреплена: если подсказанное место занято линией или другой подписью, она сдвигается, оставаясь как можно ближе к подсказке.
- Подсказка читается на каждом проходе размещения и сильнее ручного смещения и
смещения из GeoGebra;
label_placement_lockedсильнее подсказки. Работает и при выключенномrespect_current_position.
Два правила сильнее подсказки — скопированное место может оказаться неверным:
- подпись угла остаётся внутри своего угла, пока там помещается:
подсказка «снаружи» выносит её, только когда угол для подписи слишком узок.
«Помещается» — не задевает сторон и не выходит за конец короткой стороны;
если
angle_label_max_arm_fractionостанавливает подпись там, где она ещё касается стороны, такая подпись встаёт глубже, где помещается, а не уходит наружу; - подпись точки на замкнутой фигуре (многоугольник или отрезки, замкнутые в цикл) остаётся снаружи фигуры, пока снаружи есть место. Когда места нет — вплотную к фигуре идёт, например, окружность, — подсказка «внутрь» выполняется.
Подсказка действует, только когда размещение считается. loadGGB раскладывает
подписи один раз; для элементов, созданных или получивших подсказку позже
через putCode/loadCode, вызовите после кода scene.autoPlaceLabels().
Без подсказки ничего не меняется.
Динамика и снимки ключевых кадров¶
См. §11.
Автоматический радиус дуги угла: overlay.angle_radius¶
Когда угол узкий, его дуга при фиксированном arc_size_px визуально пропадает
между двумя близкими сторонами. При включённом angle_radius базовый радиус
масштабируется как base * (pivot / angle) ** exp, а затем ограничивается
диапазоном [min_px, max_arm_fraction · min(|v1|,|v2|) · ptUnit].
Место в JSON — overlay.angle_radius.
| Ключ | По умолчанию | Описание |
|---|---|---|
enabled |
false |
Включается явно. По умолчанию выключено, чтобы импорт GGB оставался побайтово точным |
exp |
0.25 |
Показатель степени в (pivot / angle)^exp. 0 — без автомасштабирования |
pivot_rad |
π/2 |
Величина угла, при которой масштаб равен 1.0 (более широкие углы → мельче, более узкие → крупнее) |
min_px |
12 |
Нижняя граница радиуса, пиксели |
max_arm_fraction |
0.65 |
Верхняя граница как доля длины более короткой стороны |
apply_to_right |
false |
Применять ли ограничения (min/max) к маркеру прямого угла |
Отключение для элемента: elem.style['auto_radius'] = False фиксирует
arc_size_px у конкретного угла, даже когда глобальный флаг включён.
Подпись автоматически следует за новым радиусом: _collect_labels и рендерер
используют одну и ту же функцию compute_effective_arc_size_px, поэтому подпись
всегда остаётся за внешней дугой.
"overlay": {
"angle_radius": {
"enabled": true,
"exp": 0.3,
"min_px": 14,
"max_arm_fraction": 0.55
}
}
9. ImportPolicy: гибкий импорт GeoGebra¶
ImportPolicy управляет тем, как значения из файла .ggb превращаются в
elem.ggb_style во время loadGGB. Полный сборник рецептов —
docs/import_policies.md.
Откуда берётся¶
Приоритет (от низшего к высшему):
- Значение по умолчанию:
ImportPolicy.faithful()— как в GGB. import.policyвнутри JSON-стиля.loadGGB(..., import_policy=...)— явный аргумент.setElementStyle()— уже после загрузки.
Поля¶
Каждое поле принимает: None (запасной вариант), литерал (число, bool, список,
словарь, hex), вызываемый объект Python fn(raw, defaults, elem) или строку
DSL.
| Поле ImportPolicy | Источник в GGB | Ключ в elem.ggb_style |
|---|---|---|
size_px |
<pointSize val> |
size_px |
stroke_width_px |
<lineStyle thickness> |
stroke_width_px |
arc_size_px |
<arcSize val> |
arc_size_px |
label_offset_px |
<labelOffset x y> |
label_offset_px |
label_color |
<objColor> как obj_color.hex |
label_color |
label_visible |
<show label> |
label_visible |
visible |
<show object> |
visible |
label_text |
<caption> (явное переопределение в политике заменяет динамическую подпись) |
label_text |
angle_range |
<angleStyle val> |
angle_range |
tick_count |
<decoration type> |
tick_count |
font_size_px |
литерал или вызываемый объект (raw=None) |
font_size_px |
stroke |
<objColor> как obj_color.hex |
stroke |
fill |
<objColor> как obj_color.hex |
fill |
fill_opacity |
<objColor alpha> как obj_color.opacity |
fill_opacity |
point_shape |
<pointStyle val> |
point_shape |
stroke_opacity |
<lineStyle opacity> |
stroke_opacity |
stroke_dash_ratio |
<lineStyle type> |
stroke_dash_ratio |
stroke_dash_period_px |
— | stroke_dash_period_px |
stroke_linecap |
— | stroke_linecap |
В elem.ggb_raw['obj_color'] хранятся r/g/b, устаревшая alpha, а также
нормализованные hex и opacity, поэтому переназначения могут работать прямо
с #rrggbb и непрозрачностью.
Мини-DSL¶
| Директива | Действие |
|---|---|
"const:3" |
Фиксированное значение 3 |
"scale:1.5" |
Умножить сырое значение GGB на 1.5 |
"quantize:[1,2,4]" |
Округлить до ближайшего элемента списка |
"remap:{'#f00':'#c00'}" |
Поиск по словарю; промах → исходное значение |
"match_element" |
Скопировать цвет обводки в подпись (маркер) |
"auto" |
Делегировать нижележащему алгоритму (маркер) |
Горячая замена без повторного разбора¶
Использует закэшированный elem.ggb_raw; XML заново не читается.
10. Статика и анимация¶
| Аспект | Статика (SVG) | Анимация (MP4) |
|---|---|---|
| Подписи | Однократный autoPlaceLabels() |
keyframe_snapshots плюс интерполяция смещений либо покадровый трекер |
| Углы | Биссектриса вычисляется один раз | dynamic_angles → аналитический пересчёт на каждом кадре |
| Z-index | Читается во время экспорта | Переназначается в updateGeoElements; равные слои стабилизируются смещением по порядку в конструкции |
| stroke_linecap | Виден | Виден; butt даёт резкий конец, round — сглаженный |
| Шрифт | Поправка на выносной элемент GGB | Та же поправка, но _auto_placed=True её отключает |
| ptUnit | Фиксируется после applyStyle |
Фиксируется; изменения камеры его не пересчитывают |
Размер холста и анимация¶
При рендере MP4 manim использует config.pixel_width/pixel_height, а не
style.export.ptWidth. applyStyle согласует камеру с размером экспорта по
соотношению сторон; если оно не совпадает с холстом manim, активная область
вписывается по меньшей стороне.
Для MP4 потребитель обязан явно согласовать конфигурацию Manim с физическим
размером экспорта: config.pixel_width и config.pixel_height должны
соответствовать export["size"]. Библиотека не выбирает пресет «под веб» и не
должна угадывать битрейт и частоту кадров: эти решения остаются на уровне
приложения или сервиса.
11. Ключевые кадры и интерполяция подписей¶
Формат JSON¶
{
"keyframes": [
{"t": 0, "values": {"A": [0, 0], "x": 35, "D": {"tparam": 0.0}}},
{"t": 2, "values": {"A": [4, 4], "x": 110,
"D": {"tparam": 3.14, "direction": "ccw"}},
"show": ["line1"], "hide": ["aux"], "easing": "smooth"},
{"t": 4, "values": {"A": [0, 0], "x": 35}}
]
}
Типы независимых элементов¶
| Тип | Формат JSON | Интерполяция |
|---|---|---|
free_point |
[x, y] |
Линейная по координатам |
tparam_point (окружность) |
{"tparam": рад, "direction": "short"\|"cw"\|"ccw"} |
Угловая |
tparam_point (отрезок, прямая) |
{"tparam": 0..1} |
Линейная |
number / measure |
float |
Линейная |
angle |
float (радианы) |
Линейная |
boolean |
true/false |
Переключение в t=0.5 |
Easing¶
linear, smooth (по умолчанию), smootherstep, in, out, in_out,
ease_in_sine, ease_out_sine, ease_in_out_sine, ease_in_cubic,
ease_out_cubic, ease_in_out_cubic, rush_into, rush_from,
ease_out_back, ease_out_elastic, ease_out_bounce.
Совместная работа с авторазмещением¶
Включаются вместе:
"overlay": {
"label_placement": {
"keyframe_snapshots": true,
"dynamic_angles": true,
"canonicalize_anchor": true,
"interpolation": "smooth"
}
}
keyframe_snapshots— (нуженenabled: true) предварительный проход вычисляет раскладку на каждом ключевом кадре (с сохранением и восстановлением состояния); смещения между снимками интерполируются.dynamic_angles— биссектрисы углов аналитически пересчитываются на каждом кадре.canonicalize_anchor— все статические якоря переводятся вMCс компенсацией, что убирает дискретные скачки.
Без keyframe_snapshots или autoPlaceLabels(dynamic=True) флаг
dynamic_angles ничего не делает — это сделано намеренно, чтобы точка
интеграции была явной.
Трекер addUpdater¶
scene.loadGGB(
'x.ggb',
style='style.json',
export={'size': {'width': 800, 'height': 600}},
)
x = scene.addVar('x', 0)
scene.autoPlaceLabels(dynamic=True) # устанавливает LabelTracker
with scene.animating(x):
scene.play(x.animate.set_value(1), run_time=3)
scene.clearLabelTracker()
Сглаживание EMA (ema_alpha) и гистерезис якоря по триггеру Шмитта
(anchor_flip_frames) не дают подписям дрожать.
12. Групповой API¶
setElementStyle¶
Проходит по списку имён и записывает каждую пару в elem.style.
setVisible¶
Контекстный менеджер animating¶
Эквивалентно addUpdater → try/play/clearUpdater.
Помощники анимации¶
| Метод | Описание |
|---|---|
Show(names, mode='Fade'\|'Create') / playShow |
Показать элементы |
Hide(names) / playHide |
Скрыть |
Shade(names) / playShade |
Приглушить (меняет stroke и fill на col_shade) |
Restore(names) / playRestore |
Вернуть из приглушения |
Update(names) / playUpdate |
FadeOut → пересоздание → FadeIn |
UpdateAll() |
То же для всей сцены |
ShowCreate(name) |
Create для линий, Fade для заливок и углов |
13. Встроенные пресеты¶
Внутри пакета поставляются пять пресетов стиля
(animageo/style/presets/). Передайте короткое имя пресета в аргумент
style=, и оно развернётся в упакованный JSON
(animageo/style/config.py::resolve_style_input); файл на диске с тем же
именем всегда важнее упакованного пресета.
default Сине-красная палитра (базовая; та же, что у book_blue)
book_blue Под печать, синяя
book_green Под печать, зелёная
book_purple Под печать, фиолетовая
book_red Под печать, красная (синий акцент)
available_style_presets() из animageo.style.config возвращает актуальный
список имён упакованных пресетов.
14. Рецепты: «как получить X»¶
Привести все подписи к одному цвету и размеру¶
Точки одного размера¶
Квантовать толщины линий¶
Брендирование: замена палитры¶
В JSON:
Или через ImportPolicy:
Двойная дуга угла¶
scene.element('α').style['tick_count'] = 2
scene.element('α').style['arc_shift_px'] = 3 # на 3 px дальше наружу
Многоугольник «поверх всего»¶
Или для конкретного элемента: scene.element('poly').style['z_index'] = 100.
Скрыть все подписи¶
Масштабируемый экспорт 2×¶
scene.loadGGB(
'x.ggb',
style='default',
reference={'size': {'width': 800, 'height': 600}},
export={'size': {'width': 1600, 'height': 1200}},
)
Геометрия и визуальные размеры *_px масштабируются вдвое относительно
эталонного холста [800, 600].
Волнистые засечки на равных сторонах¶
scene.element('a').style['tick_count'] = 2
scene.element('a').style['tick_style'] = 'wave'
scene.element('a').style['tick_radius_px'] = 1.5
Подпись над точкой¶
scene.element('A').style['label_anchor'] = 'BC' # якорь снизу по центру = подпись над точкой
scene.element('A').style['label_offset_px'] = [0, 10] # на 10 px выше (положительный Y — вверх в математических координатах)
Только подписи, без точек (чертёж «из букв»)¶
15. Особенности и подводные камни¶
Подробности — в docs/gotchas.md. Коротко:
dynamic_angles=trueсам по себе ничего не делает — нужен либоkeyframe_snapshots=true, либоautoPlaceLabels(dynamic=True). Так сделано намеренно, чтобы флаг было дёшево держать в конфигурации.canonicalize_anchor=trueпереписывает сохранённые якоря подписей, поэтому уже существующие результаты сдвигаются. По умолчанию выключено; включайте только ради плавной анимации.- Кэш bbox для TeX общий на процесс. Ключ —
(label_text, font_size), поэтому между сценами в одном процессе кэш переиспользуется корректно. После ручных измененийRusTexнуженclear_bbox_cache(). Polygon.become()в manim работает с ошибками →updateGeoElementsобрабатывает многоугольники через remove + add. Порядок слоёв при этом не должен уезжать: фактические значенияz_indexв Manim получают микросмещение по порядку элемента в конструкции.- Updater на анимируемом ValueTracker видит только начало и конец — именно
поэтому
play_keyframesиспользует объект-часовой. elem.ggb_rawпуст у элементов, созданных через Python DSL —ImportPolicy.resolve_overrides_only()получитraw=None.right_angle_markerопределяется автоматически черезnp.isclose(angle, π/2)— может ложно срабатывать примерно на 89.5°–91°; когда важна точность, задавайтеright_angle_marker=True/Falseявно.arc_size_pxперебиваетr_offset— если заданы оба,r_offsetигнорируется.- Убранные визуальные поля отвергаются. Старые ключи
line_width,font_size,strich_*,arrow_*,label_r_offset,ang_*в JSON-стиле отвергаются; используйте канонические ключи*_px.
16. Размеры и пропорции¶
Каждый размер *_px не зависит от разрешения (§2), поэтому «комфортным»
чертёж делают не абсолютные числа, а, во-первых, соотношения между
семействами размеров и, во-вторых, их величина относительно эталонного
холста. В этом разделе собраны выверенные диапазоны, которыми пользуются
поставляемые пресеты: и встроенные умолчания, и стартовый стиль из ai-guide, и
рабочий стиль Pandora укладываются в них.
Система соотношений¶
Считайте line_width.main базовой единицей визуального веса. На эталонном
холсте около 800×600 комфортные диапазоны такие:
| Семейство | main |
bold |
aux |
Опорное соотношение |
|---|---|---|---|---|
line_width |
1.5–2 | ≈1.6× main (2.5–3.3) | ≈0.6× main (0.75–1.5) | базовая единица |
point_size (диаметр) |
6–8 | 9–10 | 4–5 | 3.5–4.5 × толщины линии |
font_size |
14–17 | 16–20 | 12–14 | 2–2.5 × диаметра точки |
angle_radius |
17–20 | 24 | 12 | 1.0–1.2 × размера шрифта; right ≈ 0.8–0.9 × main; shift 1.5–3 |
tick.tick_length_px |
9–10 | — | — | 5–6 × толщины линии |
arrow.arrow_length_px |
10–11 | — | — | 6–7 × толщины линии |
Относительно холста: диаметр точки — примерно 1% ширины холста, подпись — примерно 2.5–3% высоты холста. Меняя одно из семейств, сохраняйте соотношения между ними — классические признаки плохого чертежа как раз и означают нарушенные пропорции:
- Огромные точки на тонких линиях — отношение точки к линии заметно выше
4.5. Нужно либо увеличить
line_width, либо уменьшитьpoint_size, но не что-то одно. - Дуги углов, подавляющие треугольник — радиус дуги должен оставаться
заметно короче самой короткой стороны, которую он отмечает. Если в
конструкции есть маленькие углы или короткие стороны, не подкручивайте каждый
arc_size_pxвручную: включитеoverlay.angle_radius(автомасштабирование с ограничениямиmin_pxиmax_arm_fraction, §6) и дайте рендереру и решателю подписей пользоваться общим вычисленным радиусом. - Подписи, перекрикивающие фигуру —
font_size.mainвыше примерно 2.5× диаметра точки начинает конкурировать с геометрией. Уменьшайте подписи (aux), а не увеличивайте геометрию. - Незаметные засечки — длина засечки меньше примерно 5× толщины линии пропадает при типичном DPI; встроенные 9 px и так консервативны, ниже опускаться не стоит.
Масштабирование вместе с холстом¶
Эталонный холст — это то, относительно чего измеряются значения *_px
(§2–§3). Правила:
- Держите эталонный холст в классе 700–1000 px и увеличивайте только
физический вывод (
export.sizeилиconfig.pixel_*для рендеров manim). Картинка масштабируется без потерь, менять стиль не нужно (см. «Масштабируемый экспорт 2×» в §14). - Если вы всё же проектируете под другой класс эталона (например, плакаты 1600×1200), масштабируйте все пиксельные семейства одним и тем же множителем — хорошим множителем служит отношение диагоналей холста (800×600 → 1600×1200 означает ×2 для каждого пиксельного значения).
- Для видео 16:9 берите эталон 960×540: его диагональ примерно на 10% больше, чем у 800×600, поэтому тот же пресет читается чуть тоньше — это допустимо как есть, либо умножьте пиксельные семейства на 1.1.
- Чертёж, который выглядит мелким при непропорционально крупных точках, —
это проблема кадрирования, а не стиля. Декорации привязаны к пикселям, с
областью просмотра масштабируется только геометрия. Если конструкция
занимает небольшую долю холста, сначала поправьте вписывание (отступ
fitView, целевой охват 70–85%), а уже потом трогайте размеры.
Один регулятор на всё: prominence¶
applyStyle(content={'prominence': k, ...}) умножает все размеры
декораций (точки, обводки, шрифты, дуги, засечки) на k за один приём, не
трогая геометрию, кадрирование и раскладку подписей. Это нужный инструмент,
когда вся фигура должна читаться «крупнее» или «тоньше» с сохранением
внутренних пропорций:
scene.applyStyle(
reference={'size': {'width': 800, 'height': 600}},
content={'source': 'rendered_bounds', 'padding': 40, 'prominence': 1.25},
export={'size': {'width': 800, 'height': 600}},
)
Парный параметр content.decoration_scale_source определяет, к чему привязана
плотность декораций: frame (по умолчанию — декорации следуют за вписанным
кадром), reference (постоянная пропорция «декорации к геометрии» при разных
кадрированиях), output (фиксированный размер в пикселях вывода независимо
от масштаба), ggb (пропорции аплета GeoGebra). Полная семантика — в
ai_style_generation_context.md.
Поправки на плотность¶
- Плотный чертёж (много подписанных точек, пересекающиеся вспомогательные
линии): опустите всю систему подписей и точек на ступень (используйте
значения
auxкакmain), включите рекомендуемый пресетoverlay.label_placement(§8) и подумайте о более крупном эталонном холсте, чтобы геометрии стало просторнее. - Разреженный демонстрационный чертёж (3–5 элементов для слайда):
prominence1.2–1.4 читается издалека лучше, чем увеличение отдельных семейств. - Смежные метки углов при одной вершине: разносите дуги шагами по 6–10 px
в
arc_size_px(плюсarc_shift_pxдля классов с несколькими засечками) — меньшие шаги визуально сливаются, большие читаются как не связанные друг с другом дуги.
См. также¶
- docs/api.md — полный справочник методов
AnimaGeoScene - docs/import_policies.md — практический сборник рецептов по
ImportPolicy - docs/gotchas.md — подводные камни manim, Python и архитектуры
- docs/architecture.md — обзор модулей и зависимостей
animageo/style/schema.py— исчерпывающий docstring с JSON-схемойanimageo/style/scaling.py— все формулы преобразования единиц в одном файле