
Штучные кейкапы для механических клавиатур обычно изображают череп, котика или персонажа аниме, и все трое давно стоят у кого-нибудь на Esc. Для своих клавиатур я уже сделал четыре кейкапа с глубоководной живностью (радиолярия, глаз, жабры и коралл, фотки прикладывать не буду, мне за них, честно говоря, стыдно), а пятым выбрал предмет, который узнается с любого расстояния и в любом масштабе: советский граненый стакан. Его предстояло уменьшить в четыре с лишним раза, напечатать из прозрачного фотополимера и посадить на крестовину Cherry MX. Клавиша при этом должна нажиматься, а стакан обязан остаться стаканом.
Сам стакан я смоделировал в Blender, и про его форму здесь будет один раздел с арифметикой. Гораздо больше времени ушло на вопрос, на который редактор ответить неспособен: что случится с прозрачной моделью в ванне фотополимерного принтера. Ради него пришлось написать две программы. Первая считает, в каких углах детали после печати останется жидкая смола, вторая ищет в модели присоски, которые при каждом слое пытаются оторвать деталь от стола.
Дальше все идет по порядку: пропорции и грани, посадка на шток, симулятор стекания смолы (по дороге он сделал одно открытие про стаканы) и присоска, от которой стакан неспособен избавиться по определению. Шестнадцать граней из заголовка считаются каноном, и с них я начинал, а сколько граней дожило до печати, выяснится уже в первом разделе. Начинать пришлось с линейки, потому что у граненого стакана есть пропорции, и глаз замечает их нарушение раньше, чем успевает сосчитать грани.
Стакан в масштабе один к четырем
Настоящий стакан на 250 мл (если лить до края) имеет высоту 110 мм, диаметр 73 мм по краю и 55 мм по дну. Высота относится к диаметру края как 1.5, дно составляет три четверти края. Сверху идет гладкий ободок, под ним грани с закругленным верхом, а внизу лежит толстое дно. Выпускать такие стаканы начали в 1943 году в Гусь-Хрустальном, и форму приписывают Вере Мухиной. Число граней у разных заводов гуляло от 10 до 20. Каноническим числом принято считать 16.
Кейкап размера 1u занимает в плане около 18 мм, иначе он заденет соседей (шаг клавиш у моей клавиатуры 19 мм). Диаметр края получился 17.7 мм, масштаб вышел примерно 1:4.1, и высота стакана в этом масштабе составила 26.5 мм. Обычный кейкап профиля Cherry поднимается над свитчем на 9.4 мм, так что стакан выше соседей почти втрое. Первый заход я из осторожности сделал приземистым, высотой от 11 до 13 мм, в трех вариантах: вверх дном (как в автомате с газировкой), налитый до краев и в подстаканнике.


Приземистые варианты оказались похожи на что угодно, кроме стакана: без пропорции 1.5 граненый цилиндр читается как гайка или крышка от бутылки. Во втором заходе стакан получил настоящую высоту, а я сел считать грани. Грань на окружности представляет собой хорду, и ее глубина относительно описанного круга равна r·(1 − cos(π/n)). У настоящего стакана это 36.5·(1 − cos 11.25°) = 0.70 мм, у уменьшенного с радиусом 8.5 мм всего 0.16 мм. Много это или мало для принтера? Пиксель у него около 0.035 мм, то есть на всю глубину грани приходится четыре или пять пикселей, которые потом еще зальет покрытие для глянца.
С 12 гранями глубина вырастает до 0.29 мм, зато двенадцатигранник выглядит грубее оригинала. В финальной модели граней 20, и каждая сделана слегка вогнутой, на 0.18 мм (плоская грань на таком диаметре дала бы 0.10 мм, и принтер бы ее попросту сгладил). Между желобками остаются острые ребра, которые ловят блик, а гладкий пояс занимает около пятой части высоты и выступает на полмиллиметра.
Отдельная неприятность связана с геометрией свитча. Круглый стакан диаметром 17.7 мм неспособен накрыть квадратный корпус Cherry MX со стороной 15.6 мм, потому что диагональ корпуса равна 22 мм. Стакан поэтому сидит на ножке диаметром 5.5 мм и начинается на высоте 4.4 мм: ход свитча составляет 4.0 мм, и при полном нажатии дно останавливается чуть выше корпуса. Побочный эффект мне даже нравится, ведь сквозь прозрачное дно виден шток переключателя.

Для тех, кому голый свитч под стаканом кажется неприличным, есть четыре основы: квадратная плита, скругленная подушка, столик под скатертью и кружевная салфетка с фестонами. У скатерти складки на каждой стороне идут со своим шагом, а на углах ткань собирается в конус. Глубину складок ограничивает полость под свитч: ткань может уходить внутрь, пока до полости остается 0.9 мм стенки. Драпировку на столике шириной 18 мм в итоге кроит корпус Cherry MX, и тот же корпус диктует самый строгий размер во всей модели.

Крест 4.15 на 1.30 на 1.15
Шток Cherry MX входит в крестообразный паз кейкапа, и для смолы допуск здесь измеряется сотыми долями миллиметра. Если паз на 0.05 мм уже нужного, втулка может треснуть при надевании, а если на 0.05 мм шире, кейкап болтается и слетает. В модели рукава креста имеют длину 4.15 мм, ширина паза равна 1.30 мм в одном направлении и 1.15 мм в другом (размеры из спецификации Cherry с запасом на смолу). Доживают ли эти цифры до файла, который уходит в слайсер?
На глаз это проверить нельзя, поэтому первой появилась маленькая программа-измеритель. Она режет треугольники готового STL плоскостью на высоте 2.0 мм, а библиотека shapely склеивает отрезки в замкнутые контуры. Среди них находится отверстие вокруг оси штока, и по четырем лучам измеряются длины рукавов и ширины пазов. Заодно скрипт проверяет, что сетка замкнута и состоит из одной детали.
Сам измеритель тоже однажды сломался, и причина оказалась поучительной. Когда плоскость среза проходит ровно через вершины сетки, контуры в срезе рвутся, и скрипт сообщает, что креста в кейкапе нет. Лечится это сдвигом плоскости на 0.00017 мм. По числу измененных символов это самая маленькая правка в проекте, и без нее проверка креста врала бы через раз.
Автотест гарантирует, что файл совпадает с замыслом, но совпадает ли замысел со смолой, знает только принтер. Для этого есть тестер посадки: планка с тремя втулками, у которых паз отличается от модельного на −0.05, 0 и +0.05 мм. На площадках сверху выдавлены одна, две и три лунки, чтобы втулки было трудно перепутать. Тестер печатается первым, каждая втулка надевается на свитч, и побеждает та, что садится туго и без треска.

Посадка решает, удержится ли стакан на клавише. Останется ли он при этом прозрачным, зависит уже от совсем другой физики, и для нее понадобилась программа посерьезнее.
Куда девается смола
У прозрачной смолы есть неприятное свойство: жидкий остаток, запертый в углублении, при финальной засветке твердеет частично и становится белесым. В непрозрачной детали такой дефект спрятан внутри, в прозрачной он виден насквозь. Я наступил на это с Кораллом, одним из первых четырех кейкапов, у которого сплошное ядро сидело в ажурной оболочке с зазором в миллиметр. Зазор сработал как капилляр и отдавать смолу отказался.

После Коралла появился скрипт, который ищет такие места до печати. Он переводит модель в воксели с шагом 0.12 мм (такой кубик втрое крупнее пикселя принтера, для луж этого хватает) и дальше работает с двумя массивами: где смола и где воздух. Первая половина скрипта моделирует ванночки, то есть углубления, из которых жидкость под собственным весом вытечь неспособна. Вторая половина ищет щели, где смолу держит поверхностное натяжение, и сила тяжести там уже ничего не решает.
С ванночками идея такая: деталь мысленно заливается жидкостью с головой, после чего жидкости разрешают стекать. Для каждой точки воздуха скрипт ищет путь наружу с самым низким порогом, и уровень этого порога становится уровнем жидкости в точке. Если порог выше самой точки, в ней остается лужа, и разность высот дает ее глубину. Перебирать пути руками при этом никто не заставляет: ровно эту задачу решает морфологическая реконструкция из библиотеки scikit-image (обычно ею заливают дырки на фотографиях), и вся ванночка умещается в десять строк.
# фрагмент скрипта def traps(air, coords, g, count): h = -(coords[0] g[0] + coords[1] g[1] + coords[2] * g[2]) big = h.max() + 10.0 mask = np.where(air, h, big) seed = np.full_like(mask, big) level = reconstruction(seed, mask, method="erosion", footprint=fp) return np.where(air & count, level - h, 0.0)
Направление силы тяжести здесь параметр, и расчет повторяется 18 раз. Семнадцать положений соответствуют детали, которая висит на столе принтера после печати (наклон 0, 20 и 40 градусов, азимут через 45), восемнадцатое описывает деталь, стоящую на столе. Лужи делятся на два сорта. Открытая лужа видна сверху, ее можно стряхнуть или смыть, а запертая накрыта самой деталью, и добраться до нее нечем. Различает их луч, пущенный из лужи против силы тяжести: если он уперся в смолу, лужа считается запертой

Щели ищутся через евклидово преобразование расстояний. Деталь морфологически замыкается шаром радиусом 1 мм: все места, куда такой шар пролезть неспособен, объявляются заполненными. Воздух, оказавшийся внутри замыкания глубже 0.7 мм, считается капилляром (мелкие канавки на поверхности так отсекаются). Нормы выставлены с запасом на мениски в углах, которые есть у любой детали: до 2 мм³ в ванночках и до 15 мм³ в капиллярах.
У старого Коралла скрипт насчитал в капиллярах 252.3 мм³ смолы при норме 15, это четверть миллилитра мути за решеткой. Ванночки добавили еще 11.4 мм³ при норме 2. Переделанный Коралл (ядро убрано, вместо щели просторная камера с выходом вниз) показал 2.4 и 1.9 мм³. Дорого ли обходится такая проверка? Пересчет одной модели длится две с небольшим минуты, то есть заметно меньше, чем печать, мойка и досветка неудачного кейкапа.

По правде сказать, обновленный дизайн мне нравился меньше, поэтому я смирился с тем, что часть смолы останется. Но получилось, как по мне, неплохо.

Стакан, кстати, прошел все три проверки с нулями, что неудивительно для предмета, придуманного для наливания и выливания. Зато в графе про открытые лужи у стоящего стакана значится 2717.2 мм³. Симулятор стекания смолы самостоятельно открыл, что в стакан можно налить, и измерил его вместимость: 2.7 мл, чуть больше половины чайной ложки. Настоящему стакану в этом масштабе полагалось бы около 3.5 мл, но стенки у моего в пересчете заметно толще.

Единственный FAIL в семействе стаканов получила кружевная салфетка. Когда деталь стоит на столе, в глазках кружева на верхней плоскости остается 3.3 мм³ смолы при норме 2. Переделывать кружево ради кубического миллиметра я отказался, эти глазки открыты сверху и вымываются кистью. Гораздо интереснее оказалась другая проверка, которую стакан провалил с большим запасом и которую я принял.
Присоска с горлом 14 миллиметров
Фотополимерный принтер печатает деталь вверх ногами и отрывает каждый слой от пленки на дне ванны. Если у детали есть полость, открытая к пленке и закрытая со всех остальных сторон, при отрыве в ней возникает разрежение, и получается классическая присоска. Мне нужны были ее площадь в квадратных миллиметрах и ответ на вопрос, существует ли вообще положение детали, в котором присоски нет.
Вторая программа, идет по модели слоями от стола (с шагом 0.25 мм), как это делает принтер. В каждом слое она находит дырки, то есть пустоты, окруженные материалом со всех сторон (в срезе стакана это круг внутри кольца стенки). Дырка считается безопасной, если она перекрывается с пустотой предыдущего слоя, к которой был доступ воздуха. Если же над дыркой лежит сплошной материал, под слоем образуется замкнутый объем, и его площадь идет в зачет. В отчет попадает самая большая такая площадь.

Дальше скрипт перебирает 337 направлений печати (наклон от 0 до 70 градусов с шагом 5, азимут через 15). Для каждого он считает четыре величины по порядку важности: площадь лицевых поверхностей под поддержками, площадь самой большой присоски, общую площадь поддержек и высоту печати. Я надеялся, что среди трех сотен вариантов найдется наклон без присоски. Его там нет: полость стакана закрыта дном при любом наклоне, а косое сечение только увеличивает площадь, примерно со 150 мм² при вертикальной печати до 184 при 45 градусах и 243 при 65.
Избавить стакан от присоски можно двумя способами. Первый состоит в том, чтобы просверлить в дне отверстие для воздуха (так поступают с полыми фигурками), после чего стакан перестает быть стаканом. Второй предлагает печатать его горлом к столу, но тогда поддержки встают на ободок и внутреннюю стенку, то есть на самое видное место прозрачной детали. Раз присоска неизбежна, ориентацию решают поддержки. До наклона 40 градусов под них попадают только ножка и низ дна, а с 45 градусов свесом становится сама стенка: площадь лицевых граней, смотрящих вниз круче 45 градусов, прыгает с 26 до 160 мм².
Стакан в итоге стоит вертикально, ножкой к столу: поддержки занимают 80 мм² под ножкой и дном, на гранях и ободке их нет вообще, а присоска составляет 152 мм² при норме 30. Насколько это страшно для ножки диаметром 5.5 мм? Верхнюю границу силы легко прикинуть, умножив площадь на атмосферное давление: 101 кПа × 152 мм² дают 15.4 Н, то есть около полутора килограммов при каждом отрыве слоя. Настоящая сила заметно меньше, потому что смола успевает затекать под кромку, а пленка прогибается. Красная строка FAIL осталась в отчете как осознанный долг: детали на этом принтере держатся за стол очень крепко, и запасной план с более плотными поддержками под ножкой занимает минуту.

В том же отчете есть строка, которая стакану досталась по наследству от корпуса клавиатуры над которым я работаю. Print-файл обязан отличаться от чистового только поворотом и сдвигом, и скрипт это доказывает, восстанавливая преобразование между двумя сетками через сингулярное разложение. Определитель обязан быть равен +1, масштаб единице, а остаток должен уложиться в 2 мкм. У корпуса с зеркальными половинами такая проверка ловит перепутанную сторону. Стакану зеркало безразлично, зато случайный масштаб 0.99 убил бы посадку на крест.
Печать, вода и лак
Печатаю я на Elegoo Mars 5 прозрачной смолой Anycubic Water-Wash Resin 2.0, режим подобрал я заранее на деталях корпуса клавиатуры: 2.5 секунды на слой и четыре нижних слоя по 32 секунды. На стакан с ножкой уходит 1.7 мл смолы, на вариант с основой около 2.5 мл, так что десяток неудачных экземпляров обойдется дешевле одного покупного артизана. Главные трудности начинаются после того, как деталь снята со стола и промыта.


Промытая прозрачная деталь выглядит как матовый лед и липнет к пальцам. Липкость дает кислородное ингибирование. Кислород воздуха перехватывает радикалы, на которых держится полимеризация, и самый верхний слой смолы под ультрафиолетом остается недоотвержденным. Простой способ убрать кислород из уравнения состоит в том, чтобы досвечивать деталь под водой. Стакан кладется в банку с водой, банка ставится под УФ-лампу на пять минут, и вода работает как барьер.
Матовость лечится отдельной процедурой. Ее дают ступеньки слоев и следы пикселей, которые рассеивают свет, и убрать их можно только заливкой: поверхность покрывается прозрачным составом, который заполняет микрорельеф. Чем именно заливать прозрачную деталь? Я беру глянцевый УФ-топ без липкого слоя (тот самый, которым закрепляют гель-лак на ногтях): он наносится кистью тонким слоем и отверждается в той же лампе. До этого я пытался использовать разные лаки: и акриловый художественный, и универсальный из баллончика, и матовый (без встряхивания, чтобы матовая составляющая осела на дне), но во всех случаях оставался липкий слой после высыхания. В итоге первое место заняла косметика. УФ-топ я выбрал как наиболее долговечный, который кроме как наждачкой снять не удастся, и на клавиатуре такие клавиши переживут год минимум.
У топа есть цена, и она возвращает к арифметике из начала статьи. Желобки граней имеют глубину 0.18 мм, поэтому щедрый слой способен превратить граненый стакан в гладкий, и наносить его приходится почти сухой кистью. В паз под крестовину топ попадать вообще не должен: тестер посадки различает шаг 0.05 мм, и пленка лака такой толщины съедает весь подобранный запас.




Что получилось
За четыре захода набралось целое семейство. Во втором были три высоких стакана: на 16 плоских граней, пониже на 12 граней и стройный с желобками. В третьем к первому из них добавились четыре основы, а в четвертом появился стакан на 20 граней в трех пропорциях, и каждая сделана на ножке и на всех четырех основах. Последняя пачка из 15 моделей прошла 60 проверок сетки и посадки без единого FAIL.


Печатать я в итоге выбрал вариант на основе со скатертью, он и стоит на фотографиях. Стакан на ножке имеет высоту 30.1 мм и возвышается над соседними клавишами примерно на 21 мм. Удобно ли нажимать на такую клавишу? Промахнуться мимо такого Esc трудно, задеть его рукавом заметно проще, а палец при нажатии ложится на ободок, как и положено при обращении со стаканом. Прозрачная основа свитч почти не прячет: сквозь нее видно все то же самое с добавкой преломления, и скрыть механику может лишь покраска изнутри.



Что в итоге
Blender отвечает на вопрос, как стакан выглядит. На вопрос, как он поведет себя в ванне со смолой, отвечают два скрипта общей длиной 440 строк (171 и 269), и оба ответа я получил раньше, чем включил принтер. Один из них избавил меня от кейкапа с четвертью миллилитра мути внутри. Второй сообщил, что стакан является присоской при любом наклоне, и разрешил перестать искать удачный угол.
Ну а если вам понравился этот стаканчик, отдам в хорошие руки.
Остальные кейкапы и все, что я делаю, выкладываю в своем телеграм-канале

