пятница, 24 октября 2014 г.

SVG: Построение 3D-чертежей

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

Построение 3D-чертежей

(принято к публикации 2014-10-24)

Довольно часто возникают ситуации, когда требуется сделать некий чертеж на веб-странице. Обычно для этих целей хватает возможностей SVG. Но иногда появляется необходимость запечатлеть нечто трехмерное. Вовсе необязательно при этом ожидать реалистичной графики - например, для иллюстрации задач по стереометрии используются схематичные "каркасные" рисунки, состоящие из прямых линий или несложных кривых.

Столкнувшись с такой ситуацией мы быстро выяснили, что нам лень самим придумывать, как будет выглядеть тот или иной параллелепипед на чертеже, поэтому решили приложить некоторые усилия и научиться задавать и отображать средствами SVG + Javascript несложную трехмерную графику.

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

Сначала немного теории из проективной геометрии и почти забытого курса компьютерной графики. Физически процесс наблюдения трехмерной схемы выглядит следующим образом:

Наблюдатель Сцена Экран (картинная плоскость)
Наблюдатель находится в некой точке пространства за экраном и наблюдает на экране проекцию сцены. Для расчета, как эта самая сцена выглядит на экране, полезно ввести декартову систему координат. Или даже две:
X Y Z X' Y' Z'
До сих пор мы смотрели картину, что называется, "со стороны" - в системе координат XYZ, связанной со сценой. Попробуем взглянуть на то же самое в системе координат X'Y'Z', связанной с экраном:
X Y Z X' Y' Z'
Как видим, с точки зрения понимания, как всё будет выглядеть на экране, обстановка резко улучшилась. Но не до конца: нам ещё нужно как-то обработать тот факт, что рассматриваемая сцена должна быть спроектирована на экран. Для этого мы "угоним" наблюдателя влево на "минус бесконечность". В этом, главным образом, и заключается отсылка к проективной геометрии. При этом справа к нам "приползёт" "плюс бесконечность", наша сцена чудовищно деформируется и станет похожа на то, что мы видим в реальности (в реальности более далёкие предметы кажутся меньше, чем предметы такого же размера, но расположенные ближе к нам - так называемая перспектива). Если представить эту операцию в виде некого процесса, то он будет выглядеть примерно так:
Но, разумеется, данная анимация приведена только для наглядности, а нам важен конечный результат. Короче, после такого преобразования те воображаемые линии, которые тянулись от каждой точки сцены, непонятно где пересекали экран и сходились в глазу наблюдателя, станут параллельными координатной оси 0Y, в результате чего нам останется останется лишь отбросить у точек сцены координату Y и вывести на экран получившиеся двумерные точки с координатами (X, Z), отвечающие соответствующим элементам сцены.

Теперь нужно записать эти действия строгим языком математики. Итак, для точек сцены введем четырехмерные векторы (x, y, z, 1), где x, y, z - обычные трехмерные координаты точки в системе XYZ, а четвертая единица - дань проективной геометрии, о которой было сказано выше и будет сказано дальше.

Кроме того, пусть задана координатная система, связанная с экраном и имеющая в системе XYZ следующие направляющие векторы осей:

0'X': (ax, bx, cx, 1)T
0'Y': (ay, by, cy, 1)T
0'Z': (az, bz, cz, 1)T
Наконец, будем считать, что положение наблюдателя в системе X'Y'Z'1 задано координатами (0, yн, 0, 1)T, а, в свою очередь, центр системы X'Y'Z'1 в системе XYZ1 имеет координаты (x0, y0, z0, 1)T.

Преобразование, переводящее сцену в координаты, связанные с экраном, является линейным и может быть представлено в виде произведения двух матриц:

ax bx cx 0
ay by cy 0
az bz cz 0
0 0 0 1
*
1 0 0 -x0
0 1 0 -y0
0 0 1 -z0
0 0 0 1
Правая матрица отвечает за параллельный перенос начала координат системы XYZ в начало координат системы X'Y'Z', левая - за переход из системы XYZ в систему X'Y'Z'.

Проективное же преобразование, которое оставляет 0 на месте, а точку с координатами (0, yн, 0, 1) перегоняет в минус бесконечность, имеет вид:

1 0 0 0
0 1 0 0
0 0 1 0
0 -1/yн 0 1

Таким образом получаем окончательную формулу преобразования вектора (x, y, z, 1) в координаты, связанные с экраном:

xэ
yэ
zэ
tэ
=
1 0 0 0
0 1 0 0
0 0 1 0
0 -1/yн 0 1
*
ax bx cx 0
ay by cy 0
az bz cz 0
0 0 0 1
*
1 0 0 -x0
0 1 0 -y0
0 0 1 -z0
0 0 0 1
*
x
y
z
1
В результате на экране у счастливого пользователя появится точка с координатами (xэ/tэ, zэ/tэ), возможно, умноженными на некий масштабирующий коэффициент, зависящий от того, насколько далеко от экрана расположена сцена.

Конечно, координата yэ/tэ в реальности тоже эффективно используется - она имеет непосредственное отношение к глубине, по ней могут быть упорядочены элементы сцены при отрисовке, чтобы дальние элементы не заслоняли ближние.

Теперь уже можно, будучи вооруженными вышеизложенной теорией, приступить к реализации своих амбициозных планов. Вот, например, простейшая сцена:

Как видим, необязательно ограничивать себя каркасными моделями. Действительно, перейти от отрезков к многоугольникам-граням достаточно просто. Во-первых, следует упорядочить грани, скажем, по координате Y их центров (т.е., по среднему, 1/n ∑ (yэ(i)/tэ(i)), Y-координат их вершин), тогда последовательная отрисовка граней от дальней к ближней даст примерно верную картину перекрытия их друг другом. Во-вторых, закрашивание граней может быть сделано ориентируясь на абсолютную величину косинуса угла между вектором нормали к грани и вектором 0'Y' - направлением взгляда наблюдателя. Чем меньше этот самый модуль косинуса, тем "темнее" должна быть нарисована грань. Вектор же нормали легко определить, посчитав векторное произведение любых двух смежных рёбер. В результате мы получаем возможность строить нечто более красивое:

Здесь, конечно, видно, что наша реализация несвободна от артефактов (координатные оси должны, по-хорошему, частично перекрываться тором), но в данном случае это связано с несовершенством критерия сортировки "по центрам" граней и отрезков. С этим можно побороться, разбивая объекты сцены на элементы примерно соизмеримых размеров.

пятница, 17 октября 2014 г.

Windows 7: первый после бога

Всё началось с мелочи - на одном компьютере оказалось невозможным получить доступ к съемному диску. То есть, драйвер установился, диск в Проводнике виден, но при двойном клике пишет стабильно Access denied. Вообще-то, компьютер включен в домен, в которм групповой политикой запрещен доступ к сменным носителям, но добавление его в группу исключений с последующей перезагрузкой ситуацию почему-то не исправило.

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

Возникла мысль, что не хватает прав. Запустили командную строку от имени администратора и попытались там выполнить команду:
takeown /A /F E:
Получили всё тот же Access denied. Это насторожило. Если не администратор, то кто?

Оказалось, что можно запустить командную строку от имени системной учетной записи. Как минимум, тремя способами:

1. Через утилиту psexec командой:
psexec -s cmd

2. Через создание назначенного задания:
at 16:50 /interactive "cmd.exe"
Не очень хороший вариант: во-первых, не сработало из соображений безопасности - система предложила для создания интерактивных заданий использовать schtasks, и, во-вторых, после использования задание остается висеть в планировщике.

3. Через создание специального сервиса:
sc create testsvc binpath= "cmd /K start" type= own type= interact
sc start testsvc
В первой строке пробелы после знаков равенства важны. Тоже, конечно, не сильно изящно, так как после этой процедуры в списке сервисов будет значиться некий testsvc, но зато не требует сторонних утилит, и, главное - работает.

В общем, из-под системной учетной записи к съемному диску достучаться удалось. Но takeown всё равно сделать не получилось - файловая система fat32, таблицы ACL не существует, гуляйте отсюда. Правда, и диск оказался какой-то странный - несколько папок остались недоступными. То ли поврежден, то ли так и задумано.

А вот как позволить увидеть диск непривилегированному пользователю? Это - вопрос...

понедельник, 13 октября 2014 г.

Переход на новые часовые пояса 26 окт 2014

Тут, чтобы не расслаблялись, Правительство издало новый закон (Федеральный закон от 21.07.2014 № 248-ФЗ), по которому нам надо будет опять двигать стрелки в ближайшем будущем. В связи с этим предстоят некоторые чудеса.

Во-первых, коварная корпорация Microsoft выпустила обновление KB2998527, в которое, хе-хе, забыла включить WinXP. К счастью, нашлась инструкция, позволяющая это упущение обойти. Написал батничек, который буду выполнять из-под админского аккаунта на рабочих станциях с этой устаревшей ОС:
rem thanks to http://winitpro.ru/index.php/2014/10/10/perexod-na-zimnee-vremya-v-windows-xp/

REG IMPORT TimeZone-WindowsXP.reg
%WINDIR%\System32\tzchange.exe /c "N. Central Asia Standard Time"
Control.exe TIMEDATE.CPL
(Последняя команда нужна, чтобы снять галочку с автоматического перехода на летнее время. Пока не нашел приличного способа сделать это из командной строки).

Во-вторых, на Slackware пришлось проделать определенный ритуал. Вкратце:

1. Стащил файл tzdata2014h.tar.gz

2. Распаковал архив, получил кучку файлов, нашел в них свой Novosibirsk (почему-то он оказался в файле europe, видимо, там же лежит вообще вся Россия)

3. Скомпилировал найденный файл командой:
sudo /usr/sbin/zic europe
(Эта штука заодно обновляет соответствующие файлы в каталоге /usr/share/zoneinfo, при этом Новосибирск оказывается в подкаталоге Asia/Novosibirsk)

3. Обновил информацию о своей зоне командой:
sudo cp /usr/share/zoneinfo/Asia/Novosibirsk /etc/localtime

4. Проверил, что запланирован переход, командой:
$ /usr/sbin/zdump -v /etc/localtime | grep 2014
/etc/localtime  Sat Oct 25 18:59:59 2014 UTC = Sun Oct 26 01:59:59 2014 NOVT isdst=0 gmtoff=25200
/etc/localtime  Sat Oct 25 19:00:00 2014 UTC = Sun Oct 26 01:00:00 2014 NOVT isdst=0 gmtoff=21600

Ну, и, наконец, на дебиане с убунтой, надеюсь, новые настройки приползут вместе с обновлениями.

Вроде, всё ок? Время покажет.

пятница, 10 октября 2014 г.

Linux: передача файлов между компьютерами

Вариант 1, с использованием утилиты scp:
scp   -P ssh-порт   полный-путь-до-моего-файла   удаленный-пользователь@удаленный-хост:полный-путь-до-файла

Вариант 2, с использованием утилиты sshfs:
http://www.linuxjournal.com/article/8904

вторник, 7 октября 2014 г.

Slackware: подключаем microSD от планшета с Android-ом

Это не так тривиально - примонтировать карту памяти от Android-а для того, чтобы перекинуть на неё пару мультимедиа-файлов. При попытке выполнить простейшую команду mount /dev/sde1 myMount получаем ошибку: неизвестный тип файловой системы 'exfat'.

Оказывается, надо поставить поддержку exfat. При этом попытка собрать эту штуку из исходников обернулась новым знанием: в природе существует еще один инструмент для автоматизации сборки по имени scons. Пришлось поставить ещё и его, и уже им собрать эту самую exfat по инструкции.

В процессе гугления этого самого scons вывалилась куча рецептов булочек. Один запишу - надо будет попробовать, вроде не сильно сложный:

Сконы

Ингредиенты:

2 с половиной стакана муки
5 столовых ложек сахара
2 куриных яйца
100 г маргарина или масла
6 столовых ложек молока

Приготовление:

Смешать муку с сахаром. Добавить маргарин, размягченный или растопленный (лучше вытащить его из холодильника за час до, но если забыли - можно растопить в микроволновке). Перемешать руками.

Добавить яйца и молоко. Если тесто жидковато, добавить ещё немного муки.

Скатать шарики, слегка приплюснуть их (до толщины ~ 2 см) и поместить на смазанный и присыпанный мукой противень. На противне эти штуки должны располагаться достаточно далеко друг от друга, ибо раздуваются (с чего бы, вроде, ни соды, ни разрыхлителя?).

Выпекать 10 минут в духовке.

Кстати: небесполезно было бы помазать их яйцом или молоком перед запеканием (и сахаром присыпать, судя по картинке).