Skip to content

Latest commit

 

History

213 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

HueForge Web

Приложение превращает картинку в многослойную филаментную картину для 3D-печати: загрузить изображение → сократить до 2–8 цветов (слайдер) → ступенчатая карта высот → скачать STL / 3MF. Цвет кодируется высотой слоя — как в HueForge.

Быстрый старт на новом ПК

  1. Установите Node.js LTS (один раз).
  2. Скачайте со страницы релизов архив hueforge-web-vX.Y.Z.zip и распакуйте его.
  3. Дважды щёлкните install.bat в распакованной папке — он:
    • проверит Node.js и поищет новую версию на GitHub (и предложит подтянуть её автоматически, если она есть),
    • установит зависимости в папку проекта,
    • предложит создать ярлык на рабочем столе,
    • запустит приложение и откроет браузер.

Токен GitHub: при первом запуске install.bat предложит вставить токен (создать: https://github.com/settings/personal-access-tokens — fine-grained, только репозиторий hf, права Contents: Read + Metadata: Read). Репозиторий публичный, поэтому от токена можно отказаться: без него update.ps1 ходит в API анонимно и обновления всё равно работают, а токен поднимает лимит запросов. Токен сохраняется только на этом ПК в %APPDATA%\HueForgeWeb\github_token.txt и используется исключительно для проверки и скачивания релизов.

  1. Дальше достаточно ярлыка или двойного щелчка по start.bat — браузер откроется на http://127.0.0.1:5173 (зависимости самовосстанавливаются, если папку перенесли или удалили node_modules).

Чтобы остановить сервер, закройте чёрное окно консоли. Версия установленной сборки видна в верхней панели приложения рядом с заголовком.

Есть ещё вариант «сервер + браузер» без установки: готовый пакет hueforge-web-deploy-v*.zip с того же релиза (не нужны ни npm install, ни исходники) — запускается deploy.bat на http://127.0.0.1:8080, порт можно задать аргументом. Подробности — в DEPLOY.md.

Настольное приложение (Electron)

То же приложение поставляется как нативное приложение Windows — HueForge Desktop: безрамочное окно с собственными кнопками заголовка, значок в панели задач и ярлык в меню «Пуск»; ни браузер, ни Node.js не нужны.

  • Установка: возьмите hueforge-desktop-setup-<version>.exe со страницы релизов и запустите (установщик NSIS, x64; папку установки можно выбрать).
  • Автообновление встроено: приложение проверяет релизы GitHub при запуске (через 15 с) и далее каждые 4 часа. В верхней панели появляется индикатор (⟳ проверка, ↑ доступно обновление, % при скачивании, ↓ готово к установке — нажмите, чтобы действовать). Обновление ставится при выходе или сразу после подтверждения.
  • Доступ к релизам по токену: в настольном приложении токен обязателен — манифест обновления собран для доступа через GitHub API (fine-grained, Contents: Read + Metadata: Read на этот репозиторий — тот же, что использует install.bat). Нажмите индикатор обновления, вставьте токен в диалоге — он будет сохранён зашифрованным на этом ПК (Electron safeStorage / DPAPI). Переменные окружения вида HF_GITHUB_TOKEN настольное приложение не читает.
  • Экспорт: «Скачать STL/3MF/…» открывает нативное окно Windows «Сохранить как»; в браузерной сборке файлы скачиваются как раньше.

Собрать из исходников: npm run dist (сначала нужен npm install). Установщик и манифесты автообновления (latest.yml, blockmap) появляются в release/ и прикрепляются к релизу GitHub — вручную или шагом workflow.

Интерфейс кратко

  • Левая панель разбита на вкладки: Проект (изображение, цвета, режим глубины, размер, палитра), Проверка (печатаемость, производительность, эталон), Экспорт.
  • Области предпросмотра («Исходное», карта высот, 3D-вид) располагаются схемами «друг над другом» или «два рядом»; пропорция тянется сплиттером, двойной щелчок возвращает 50/50, а полоса с шевроном внизу растит рабочую область по высоте. В узких окнах (меньше 900 px) схемы складываются в колонку.
  • У каждой панели единый формат шапки: иконка → название → бейдж → «?» с подсказкой.

Как пользоваться

  1. Перетащите картинку (PNG / JPG / WebP) в панель слева.
  2. Выберите число цветов — от 2 до 8: тяните слайдер, щёлкайте по меткам 2 / 4 / 6 / 8 или введите число в поле. Слайдер в фокусе слушает стрелки.
  3. Выберите режим глубины — тёмные цвета выше (классический HueForge) или светлые выше. Если яркость картинки плохо описывает, что где ближе (портрет на фоне, предмет на столе), переключите источник рельефа на глубину — см. Источник рельефа.
  4. При необходимости поправьте размер (ширина и высота в мм), толщину основания и максимальную высоту.
  5. Загляните в панель проверки печати — она предупредит о слишком тонких полосах цвета, элементах тоньше сопла, большом числе смен филамента и хрупких изолированных участках, а для каждого предупреждения предложит конкретное исправление.
  6. Проверьте палитру — там каждый цвет с порядком печати (#1 = первый филамент) и ближайшим филаментом из библиотеки.
  7. Скачайте STL (цвет высотой слоя — смены филамента выставьте в слайсере на высотах Z из панели палитры) или 3MF (та же геометрия плюс палитра и порядок печати в метаданных). В варианте «сервер + браузер» с deploy-slicer.bat есть кнопка «Открыть в слайсере» — 3MF уходит сразу в Bambu Studio / OrcaSlicer / PrusaSlicer.
  8. Сравните с эталоном — перетащите .3mf (проект HueForge/Bambu или наш собственный экспорт), чтобы прочитать его размер, цвета и расписание смен и перенести их в свою работу одним нажатием, либо перетащите эталонный .stl, чтобы измерить его рельеф (габарит, шаг сетки, шаг высоты, число уровней высот и долю плато, склонов и стенок) и построчно сверить с нашим — с таблицей расхождений.
  9. При необходимости откалибруйте τ по образцу: распечатайте образец для цвета, сфотографируйте его — приложение подберёт длину пропускания τ, и предпросмотры будут совпадать с реальной катушкой. По умолчанию τ = 0,3 мм — это типичный непрозрачный PLA: при такой длине пропускания миллиметровый лист читается как сплошной, поэтому предпросмотр показывает почти тот же цвет, который напечатается. Прозрачным катушкам τ ставят больше (вплоть до 6 мм) — тогда предпросмотр честно показывает, что нижние слои просвечивают.

Из чего складывается высота

Модель повторяет схему бесплатного Filapaint (cxwl.org) и режим HueForge Standard: сплошное основание + рельеф, высота которого следует яркости картинки. Параметры бесплатного инструмента ложатся на элементы приложения один к одному:

Параметр Filapaint Что это Где в приложении
Base Thickness сплошная подложка под рельефом поле Основание
Relief Thickness диапазон высот, по которому «рисует» картинка Макс. высота − Основание (разложение печатается строкой под полями размера)
Layer Height шаг высоты: поверхность живёт на сетке слоёв слайсера поле Высота слоя
Contrast как далеко тона расходятся по высоте рельефа слайдер Контраст
Power Deepening изгиб тональной кривой: куда ложатся полутона слайдер Детали
Color layers высоты, на которых меняется филамент Палитра и порядок печати — высоты смен в мм

Порядок обработки: яркость → контраст → проявление деталей → привязка высоты к сетке слоёв. Кривая строго монотонна и оставляет крайние тона на основании и на верхушке модели, поэтому весь диапазон высот используется всегда, а пиксель не может выйти из своей цветовой полосы: высоты смен двигаются вместе с рельефом, а не остаются на месте. Дизеринг, разбиение на полосы и назначение цветов пикселям считаются до тональной кривой, поэтому контраст и «Детали» не сдвигают ни одного пикселя цвета — меняется только высота.

Замер на тестовой картинке 320×240 (4 цвета, 0,8–8 мм, слой 0,2 мм):

Настройка Высоты смен филамента Уровней высот
нейтрально (100/100) 3,60 / 4,40 / 5,20 мм 37
контраст 200% 3,00 / 4,60 / 6,00 мм 37
контраст 50% 4,00 / 4,40 / 4,80 мм 35
детали 200% 1,80 / 2,60 / 3,60 мм 37

Контраст раздвигает смены к краям, «Детали» больше 100% опускает полутона (смены уезжают вниз), меньше 100% — поднимает. При этом поверхность всё равно живёт на сетке слоёв: 35–37 уровней из 37 возможных на рельефе 7,2 мм.

Стили рельефа

Два процента («Контраст», «Детали») задают настройку точно, но не говорят, что получится. Поэтому над ними стоит ряд именованных стилей — каждый выставляет оба ползунка сразу (src/lib/tonePresets.ts):

Стиль Контраст Детали Что делает
Мягко 60 % 90 % тона держатся ближе к середине: склоны плавные, ступеней почти нет
Как на фото 100 % 100 % высота повторяет яркость один к одному, ничего не преувеличено
Глубокий рельеф 130 % 250 % полутона уходят к основанию, светлые участки остаются высокими
Графика 200 % 100 % тона расходятся к краям, переходы между цветами резкие

Стиль — это те же два ползунка, а не отдельный режим: выбрав стиль, его можно тут же уточнить, и тогда выбор помечается бейджем «Свой» (в это же состояние попадает результат автоподбора по эталону). Стиль запоминается вместе с проектом и сохраняется в localStorage, как любая другая настройка.

Пол рельефа: стиль не может сжать цветовую полосу ниже сопла (0,40 мм + один слой запаса на округление по сетке слоёв). Кривая тона двигает высоты смен вместе с рельефом, поэтому сильный стиль вроде «Глубокого рельефа» умел сжать нижний цвет до одного слоя — такой лист почти прозрачен, и тени печатались одним мутным пятном вместо трёх разных цветов. Теперь границы полос поднимаются до минимума монотонным пересчётом: высоты пересчитываются той же кривой, и пиксель не может выйти из своей полосы. На тестовой картинке (4 цвета, 0,8–8 мм, слой 0,2 мм) нижняя полоса «Глубокого рельефа» из 0,20 мм стала 0,60 мм — вместо предупреждения «тоньше сопла» проверка печатаемости показывает чистые переходы.

Два способа назначить цвета

Переключатель в панели Цвета задаёт, откуда пиксель берёт филамент (src/lib/quantize.ts, mapToLuminanceBands и mapToImageColors):

Режим Палитра Деталь
По яркости (HueForge) средний цвет каждого среза яркости яркость задаёт высоту, полоса высоты — цвет; то, что документировано выше и с чем сверяется эталонная модель
По цветам изображения median cut по RGB самой картинки пиксель берёт ближайший цвет, каждый цвет получает равную долю высоты

Разница проявляется там, где яркость не различает детали. На тестовом закате (детализированная картинка, теневой герой на фоне красного неба) палитра «по яркости» — три тёмно-коричневых плюс оранжевый: красное небо усредняется со светлым камнем в коричневый, а тень лица неотличима по яркости от плаща. При 6 цветах «по цветам изображения» палитра получается такой:

Режим Палитра (6 цветов)
по яркости #312b2d #792f2c #9f382a #a05133 #bd6435 #d1943b
по цветам изображения #433129 #4c4243 #dc3318 #71655d #c48049 #df8a1d

То есть небо печатается красным, а не коричневым, и детали, совпадающие по яркости, но разные по оттенку, получают каждая свой филамент. Внутри каждой террасы светотень по-прежнему следует локальной яркости, поэтому цветовое пятно остаётся вылепленным, а не плоской плитой; высота всегда попадает в долю своего цвета, так что верхний слой в любой точке — тот филамент, который показывает предпросмотр.

Режим «по цветам изображения» больше не следует модели «высота следует яркости», поэтому эталонная модель HueForge с ним не совпадёт — для сверки с эталоном остаётся режим «по яркости» (он же режим по умолчанию и то, что записывают старые проекты без поля colorMode).

Цвет = высота означает, что каждая граница цвета обязана быть ступенью в 1/n высоты: два соседних пикселя разных филаментов не могут стоять на одной высоте. На детализированной картинке таких границ сотни, и без обработки модель печатается частоколом вертикальных шипов (замер тестового заката, 6 цветов: 806 областей одного цвета, у 5,4 % соседних пар перепад две полосы и больше). Поэтому после раскладки геометрия проходит ограничитель крутизны: ни один пиксель не может стоять выше соседа больше чем на два слоя высоты на клетку (45° — самый крутой свес, который FDM печатает без поддержек). Ступень в 1,2 мм превращается в плечо шириной 3 клетки, плоские площадки не трогаются, а цвета не меняются: у самой границы верхний слой просто попадает в тот или другой филамент на клетку раньше или позже. Широкое сглаживание рельефа (ползунок «Сглаживание») в этом режиме не применяется — оно размыло бы сами террасы и увело бы поверхность из своей полосы; там ползунок чистит только цвета.

Замер на тестовом градиенте 64×64 (4 цвета, 0,6–8 мм, слой 0,2 мм) — разброс поверхности по высоте (дисперсия распределения, в корзинах 16-уровневого профиля):

Стиль Разброс Центр масс
Мягко 9,1 8,5
Как на фото 15,7 8,1
Глубокий рельеф 22,3 4,8
Графика 26,5 8,2

То есть «Мягко» и вправду собирает поверхность вместе, «Глубокий рельеф» опускает её массу почти вдвое ниже середины, а «Графика» расходится шире всех — эти свойства закреплены тестами (src/test/tonePresets.test.ts), а не только названием. Полный диапазон высот при этом используют все стили: кривая прибита к 0 и 1, поэтому самый тёмный и самый светлый тон остаются на основании и на верхушке модели.

Под стилями печатается итоговая строка — она отвечает на вопрос «что выйдет»: основание + рельеф = общая высота, число цветов и высоты, на которых меняется филамент (те же числа, что в строках палитры и в модели).

Свои стили

Текущую настройку можно сохранить под своим именем: введите название в поле «Название стиля» и нажмите «Сохранить стиль» — рядом с готовыми стилями появится ваш собственный, а имя применяет его так же, как встроенное. Крестик на чипе удаляет стиль. Имя не обязательно: пустое поле сохранит стиль как «Мой стиль», а настройка, у которой уже есть сохранённый стиль, второй раз не сохраняется — в подписи будет назван существующий (список вмещает 24 стиля).

Стиль хранится в двух местах (src/lib/customTones.ts):

Где Что даёт
localStorage (hf-custom-tones) ваши стили остаются между запусками, независимо от открытого проекта
файл проекта (settings.customTones) проект переносит свои стили на другую машину — при открытии они встают в ряд рядом с местными

Стиль — это те же два числа (контраст и детали), а не копия картинки, поэтому сохранение и удаление не трогают геометрию: сохранённый стиль в выделенном виде подсвечивается, а его значения можно тут же уточнить ползунками. Идентификаторы сохраняются при переносе через файл, поэтому повторное открытие того же проекта не плодит дубли. Правила списка и чтение/запись в файл закреплены тестами (src/test/customTones.test.ts): дубликат по значениям, обрезка длинного имени, отказ на испорченный список, круговорот «проект → браузер → проект».

Источник рельефа: яркость, глубина или карта из файла

По умолчанию высота пикселя следует его яркости. Но яркость не знает, что ближе: тёмный фон и светлое лицо на нём получают разную высоту не потому, что лицо ближе, а потому что оно светлее, и портрет на светлой стене выходит плоским. В блоке «Режим глубины» → «Источник рельефа» три варианта:

Источник Откуда высота Когда брать
Яркость картинки (по умолчанию) яркость пикселя; всё как описано выше графика, логотипы, эталон HueForge
Глубина (нейросеть, офлайн) относительная глубина, которую оценивает Depth Anything V2 Small портреты, предметы на фоне, пейзажи с планами
Карта глубины из файла (PNG) ваша готовая карта глубины или высот глубина из другой программы, Blender, скульптора, 3D-реконструкции

Как это устроено. Слои по-прежнему режутся так, чтобы у каждого цвета была своя доля высоты, но порядок задаёт глубина: ближе — выше. Область с одинаковой глубиной (плоский фон, плоский передний план) целиком попадает в один слой — фон вырезки остаётся на основании, а не режется на полосы. Цвет каждого филамента — это средний цвет самой картинки внутри своего слоя глубины, поэтому предмет переднего плана получает собственные филаменты, а не смешивается с фоном той же яркости. Контраст, «Детали», сглаживание, дизеринг и слияние близких цветов работают поверх глубинного рельефа так же, как поверх яркостного. Переключатель светлое/тёмное выше и режим «по цветам изображения» при этом неактивны (они читают яркость или цвет как высоту, а здесь высоту задаёт глубина); режим каталога катушек и рельеф по глубине взаимоисключающи — включение одного выключает другой.

Нейросеть работает на вашем компьютере. Модель (model_quantized.onnx, 27 МБ, uint8) и рантайм WebAssembly (14 МБ) вшиты в приложение: интернет не нужен, картинка никуда не отправляется. Первая оценка занимает несколько секунд (в тестовом прогоне — около 5 с на картинку 640×480, включая загрузку модели), потом она переиспользуется: слайдеры, размер и число цветов пересчитываются без повторной оценки. Оценка идёт в отдельном потоке, интерфейс не замирает. Если модель по какой-то причине не загрузилась, картинка всё равно обрабатывается по яркости, а причина показывается в строке статуса.

Что глубина даёт, а что нет. Это порядок «ближе / дальше», а не расстояния: миллиметры задают ваши основание и максимальная высота. Скрытых сторон нейросеть не знает, а ошибиться в плоской или однотонной сцене может — для таких случаев есть «Инвертировать глубину (дальше = выше)»: если сеть прочитала картинку наоборот или нужен эффект врезанного, гравированного изображения. Если на картинке почти нет перепада глубины, приложение так и скажет и в этот раз построит рельеф по яркости — плоская плита с полосами цвета никому не нужна, а выдумывать глубину оно не станет.

Своя карта глубины. Подойдёт PNG: серый, серый+альфа, RGB или RGBA, 8 или 16 бит. 16 бит важны: они сохраняют все уровни, поэтому плавный градиент печатается плавным склоном, а не террасами (<canvas> браузера сводит PNG к 8 битам, поэтому читается собственным декодером). По умолчанию светлее — ближе; если у вашей карты обратное соглашение (у многих инструментов белое — дальнее), включите «Инвертировать глубину». Полностью прозрачные пиксели остаются на уровне основания. Карта растягивается под картинку; если пропорции разные, приложение об этом говорит. Ограничения: файл до 64 МБ, до 16 мегапикселей (4096×4096), без чересстрочной развёртки; в памяти карта хранится не крупнее 2048 px по длинной стороне (печатная сетка всё равно мельче). Ошибки (не PNG, повреждён, неподдерживаемый тип, слишком большой) показываются понятно и не ломают текущий результат. Однотонная карта (нет перепада значений) не используется — рельеф строится по яркости. Сохранённый проект несёт копию файла карты (как и саму картинку), поэтому палитра, которая записана в порядке слоёв глубины, открывается с тем же рельефом; проект с картой становится больше на размер PNG. Новая картинка сбрасывает карту, а отмена диалога выбора возвращает на яркость.

Если рельеф проекта восстановить не удалось (карта в файле повреждена, модель глубины недоступна), проект открывается по яркости с автоматическими цветами и приложение говорит об этом: накладывать сохранённые цвета на другой порядок слоёв было бы хуже, чем не накладывать. Экспорт знает, откуда рельеф: Describe.txt пишет «relief from a depth map», а 3MF получает метаданные ReliefSource и InvertDepth.

Источник рельефа и «инвертировать глубину» запоминаются в настройках, в проекте и в шагах отмены/повтора; старые проекты без этого поля открываются по яркости. Инверсия не задевает прозрачный фон карты: он остаётся на основании при любом направлении глубины.

Откуда взяты модель и идея. Идея — «относительная глубина как приор» — из навыка img2threejs, который использует тот же Depth Anything V2; сам навык (Python, PyTorch) в браузерное приложение не встраивается. Модель — onnx-community/depth-anything-v2-small (Apache-2.0, файл без изменений, контрольная сумма записана в NOTICE.md); среда выполнения — ONNX Runtime Web (MIT). Тексты лицензий лежат в сборке: THIRD_PARTY_NOTICES.txt в корне приложения, а уведомления о стороннем коде, статически собранном в рантайм, — THIRD_PARTY_NOTICES_ONNXRUNTIME.txt (копия из репозитория ONNX Runtime, тег v1.30.0). Другие размеры модели (Base, Large) выпущены под CC-BY-NC-4.0 (некоммерческая лицензия) и не включены.

Откуда взять готовую карту: любой инструмент, умеющий сохранить PNG глубины или высот. Один из вариантов — Unique3D (MIT): он восстанавливает меш и карты нормалей по одному снимку, а его скрипт scripts/normal_to_height_map.py превращает карту нормалей в 16-битную карту высот. Сам конвейер требует видеокарту NVIDIA и гигабайтов весов, поэтому запускается отдельно, а не внутри HueForge; этот путь мы не прогоняли — берите его как направление, а не как проверенный рецепт.

Подбор тона по эталонному STL

Кнопка «Подобрать тон по эталону» в блоке сравнения с эталоном сама находит контраст и детализацию: приложение измеряет распределение высот эталона тем же инструментом, которым измеряет свой рельеф (measureRelief в src/lib/reliefCompare.ts — 16 корзин площади верхней поверхности по её же диапазону высот), затем прогоняет конвейер по сетке значений «Контраст» и «Детали» и шагает в сторону самого близкого значения, пока расхождение падает. Поиск (см. src/lib/toneFit.ts) идёт в воркере, поэтому интерфейс не замирает, а в подписи кнопки видно «Замер N из M».

Почему сравнимо с чужим STL: профиль нормируется на собственный диапазон высот, поэтому эталон HueForge на 2,16 мм с шагом 0,08 мм сопоставляется с нашей печатью на 11,4 мм с шагом 0,2 мм — важно, куда картинка кладёт массу, а не в миллиметрах. Габарит, шаг сетки и число уровней остаются делом остальных настроек и в оценку не входят.

Правила поиска:

  • Текущий тон — тоже вариант. Он измеряется первым и возвращается неизменным, если ни одно значение его не улучшило: подбор никогда не делает хуже того, что уже стоит, а повторное нажатие на подобранном тоне отвечает «текущий тон уже самый близкий» и ничего не сдвигает.
  • Меняются только высоты. Цвета, палитра и назначение филаментов пикселям не затрагиваются: кривая применяется к высотам и границам полос одновременно.
  • Шаг поиска — 10 %, как у самих ползунков, поэтому найденное значение в них помещается без округления.
  • Ограничение области: в режиме «квантование по катушкам» высота идёт от палитры, а не от тонов картинки, поэтому кнопка там выключена и объясняет почему.

Замер на реальном эталоне (match_Front_200x114.stl, HueForge: 200×113,6 мм, 2,16 мм, 983 460 треугольников, 14 уровней) против нашей печати 500×284 мм при 1024 px:

Состояние Расхождение распределения высот Расходятся метрики Тон
сразу после загрузки (100/100) 33,8 % 8 из 10 —
после подбора 11,7 % 5 из 10 контраст 80 %, детали 300 %

Цена: 34 прогона конвейера — около минуты на картинке 1024 px на этой машине; прогресс показывается в подписи, поиск идёт в воркере. Разница в габарите, шаге сетки и уровнях высот сохраняется: подбор выравнивает тон, а не размер печати.

Версии и релизы

  • Версионирование семантическое (vMAJOR.MINOR.PATCH). Единственный источник истины — поле version в package.json; тег релиза обязан с ним совпадать, и workflow релиза отвергает несовпадающие теги.

  • Выпуск релиза идёт в порядке «ноты → бамп → CHANGELOG → тег → публикация»: сначала опишите версию в release-notes/vX.Y.Z.md, затем одной командой получите бамп package.json/package-lock.json, запись в CHANGELOG до тега и сам тег:

    VERSION=0.8.4   # замените на выпускаемую версию
    git pull --rebase origin main   # забрать коммит CI о канале прошлого релиза
    npm run release:prepare -- --version "$VERSION"
    git push origin main "v$VERSION"

    git pull нужен потому, что канал вышедшего релиза дописывает в main сам CI (последний шаг workflow), — без него release:prepare остановится.

    Запись обязана попасть в дерево тега: git show "v$VERSION:CHANGELOG.md" должен её содержать — этого требует release:publish. Подробнее — в DEPLOY.md.

    Пуш тега v* запускает workflow Release, и он доводит релиз до конца без ручных шагов: сверяет тег с package.json и с записью версии в CHANGELOG.md, прогоняет typecheck, тесты и продакшн-сборку на Ubuntu, собирает архивы исходников и deploy-пакета, на windows-latest собирает установщик Electron — и затем сам публикует черновик: флаги канала выводятся из тега (включён pre-release, если в теге есть -), тело берётся из release-notes/<тег>.md, а если releases/latest оказался бы кандидатом, workflow падает. Последним шагом в CHANGELOG.md на ветке по умолчанию обновляется канал релиза.

  • Локальная публикация (npm run dist → npm run release:publish) — путь, которым пользуемся, пока счёт GitHub Actions заблокирован: вторая команда сама выводит флаги канала из тега — pre-release для vX.Y.Z-rc.N, latest для стабильной версии, — заливает установщик, blockmap и latest.yml, сверяет их со сборкой и падает с ненулевым кодом, если releases/latest указывает на кандидата — или если в дереве тега нет записи этой версии в CHANGELOG.md. Тело релиза он берёт из release-notes/<тег>.md и сверяет с опубликованным, так что страница релиза и CHANGELOG повторяют один текст. --dry-run печатает изменения, ничего не выполняя.

  • Стабильный канал и pre-release: настольное приложение следит только за latest-релизом репозитория и никогда не подписывается на pre-release (allowPrerelease = false, канал latest.yml). releases/latest пропускает pre-release, поэтому каждый RC обязан публиковаться как pre-release GitHub. Флаги конвейер выводит из самого тега (или их ставит release:publish в ручном пути); вручную это выглядело бы так:

    # RC — в теге есть предрелизный суффикс (v0.8.3-rc.5):
    gh release edit "v$VERSION" --draft=false --prerelease
    # Стабильная версия — обычный номер (v0.8.3):
    gh release edit "v$VERSION" --draft=false --latest

    Если опубликовать RC как обычный релиз, он станет latest и будет предложен всем стабильным установкам (так однажды и вышло с v0.8.3-rc.4; сейчас он снова pre-release). Установки RC получают следующий стабильный релиз, когда он выходит, а стабильные установки RC не видят никогда.

  • install.bat при каждом запуске сравнивает локальную версию (из package.json) с последним релизом GitHub и предлагает скачать и применить обновление на месте (собственно вызовы API делает update.ps1). Перекачивать архив для обновления не нужно — кроме случаев, когда меняются сами install.bat / start.bat / update.ps1: тогда требуется свежая распаковка.

  • Ноты релизов живут в репозитории: CHANGELOG.md повторяет все релизы — версия, дата, канал и полный текст нот — чтобы историю можно было читать из кода, а не только со страницы релиза. Ноты будущей версии пишутся заранее в release-notes/<тег>.md (правила каталога), поэтому запись появляется в CHANGELOG до тега и остаётся в дереве самого релиза. npm run changelog:sync перегенерирует файл из релизов GitHub (нужен авторизованный gh); после публикации он лишь заменяет «Ожидает тега» на канал релиза, а версии без страницы релиза берёт из release-notes/.

Темы

Тёмная / Светлая / Nord / Solar — переключатель в верхней панели, выбор сохраняется.

Разработка

npm install     # один раз
npm run dev     # dev-сервер на http://127.0.0.1:5173
npm test        # 523 теста (48 файлов); тест на настоящей модели глубины идёт ~8 с
npm run build   # продакшн-сборка в dist/

Заметки

  • Всё считается локально в браузере — обработки на сервере нет, изображения не покидают ПК. Это относится и к нейросети глубины: модель вшита в сборку, а политика безопасности страницы (connect-src 'self') не пускает запросы наружу — это закреплено тестом (src/test/csp.test.ts).
  • Перед обработкой изображение приводится к рабочему разрешению: до 1024 px по большой стороне (это привязка к соплу — примерно ячейка на сопло 0,4 мм), а превью «Исходное» декодируется отдельно до 2048 px, чтобы мелкие детали были видны на карточке.
  • Рельеф — высотная сетка с общими вершинами соседних ячеек: перепад высот между соседями становится диагональной гранью, вертикальные стенки идут только по внешнему контуру, дно — плоская плита. Сетка водонепроницаемая (проверяется тестами), поэтому режется без ошибок.

Документация

Все документы репозитория ведутся на русском языке (команды, имена файлов и фрагменты кода — как есть). Текущий состав:

About

Превращает изображение в многослойную филаментную картину для 3D-печати: рельеф по карте яркости, экспорт STL/3MF, настольное приложение с автообновлением.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages