Как стать автором
Обновить

Рисуем 3D на осциллографе

Время на прочтение4 мин
Количество просмотров7.7K
Oscilloscope Всем привет. В этой небольшой статье я собираюсь рассказать о том, как можно выводить 3D графику на векторном дисплее. Конечно, практической ценности это не несет никакой, но зато как интересно!

Общие слова


Итак, мы собираемся рисовать 3D. И не просто рисовать, а рисовать на осциллографе. Я думаю, что будет достаточно изобразить вращающийся кубик, что бы понять принцип работы, а там и до трехмерной стрелялки не далеко.
Вся фишка заключается в том, что в векторных дисплеях(то есть для нас это экран осциллографа) в отличие от, тех к которым мы привыкли, изображение выводится не с помощью последовательного подсвечивания пикселов, а с помощью установки проецирующего луча в нужное место. То есть мы просто «обводим» контуры будущей картинки лучом по экрану, вместо того что бы отмечать точки.

Как подружить компьютер и осциллограф?


Давайте посмотрим, что происходит внутри осциллографа. В самом простейшем случае осциллограф представляет собой электронную пушку, которая создает и фокусирует луч из электронов. Затем этот луч, проходя через два конденсатора, отклоняется горизонтально или вертикально, в зависимости от напряжения на них. Потом луч попадает на экран, покрытый люминофором, и происходит вспышка в том месте куда попал луч. Таким образом при достаточно быстром движении луча формируется картинка.
Напряжение на отклоняющих конденсаторах зависит от напряжения на входах осциллографа. У нас отклонением по оси X будет управлять один вход, а по оси Y другой. Ура, мы уже можем что-нибудь нарисовать подавая соответствующее напряжение осциллографу.
А подавать его мы будем с помощью звуковой карты компьютера, ведь от частоты звука зависит напряжение на ее выходах. Пусть правый канал у нас отвечает за движение по оси X, а левый по оси Y.
То есть просто присоединив правый канал звуковой карты к первому входу осциллографа, а левый ко второму можно будет управлять движением луча по экрану.

Ставим точку


Для начала научимся просто ставить одну точку. Будем использовать всеми любимый Python и пару библиотек к нему. Посмотрим на такой код:

import sys
import struct
import wave
from math import *

# Открываем и инициализируем wav-файл
WaveF = wave.open("output.wav", "wb")
WaveF.setnchannels(2)
WaveF.setsampwidth(2)
WaveF.setframerate(48000)

WaveF.writeframes(struct.pack("hh", 0.5 * 32767.0, 0.5 * 32767.0))

WaveF.close()

* This source code was highlighted with Source Code Highlighter.


Тут все просто: открываем wav-файл, и записываем туда информацию об одной точке(фрейме в терминологии wav файлов). Мы считаем что координаты точки бывают только от -1 до 1, поэтому смело домножаем их на максимальное значение в типе integer, что бы громкость, а соответственно и выходное напряжение были бы достаточно большими. Звук я вывожу в wav только потому что, так его можно залить на плеер и спокойно донести до осциллографа.
Так, мы научились ставить точку на двухмерной плоскости, но ведь мы хотим 3D. Поэтому применим немного простой математики и научимся проецировать точку из трехмерного пространства в двухмерное, вот так:
# Настройки перспективной прекции
xSize = 0.6 # Размер проекционного экрана по оси X
ySize = 0.4 # Размер проекционного экрана по оси Y
Dist = 1.5 # Расстояние от наблюдателя до экрана

# Проецируем точку, у нас уже три координаты
def PutPoint( x, y, z ):
 # Специальные магические формулы
 sx = (xSize / 2) + ((x * Dist) / (z + Dist))
 sy = (ySize / 2) + ((y * Dist) / (z + Dist))

 # Проверяем на корректность полученые значения
 if (sx < -1):
  sx = -1;
 if (sx > 1):
  sx = 1;
 if (sy < -1):
  sy = -1;
 if (sy > 1):
  sy = 1;
 # И выводим в файл
 WaveF.writeframes(struct.pack("hh", sy * 32767.0, sx * 32767.0))

* This source code was highlighted with Source Code Highlighter.

Супер, теперь можно ставить точку с тремя координатами и она будет правильно проецироваться. Самое важное сделано, осталось совсем немного.

Рисуем куб!


Тут все еще проще. Смотрим на интеллектуальный код:

def DrawCube():
 DrawXLine(CPoint(0, 0, 0), CPoint(0.5, 0, 0), 0.0121)
 DrawXLine(CPoint(0, 0.5, 0), CPoint(0.5, 0.5, 0), 0.0121)
 DrawXLine(CPoint(0, 0, 0.5), CPoint(0.5, 0, 0.5), 0.0121)
 DrawXLine(CPoint(0, 0.5, 0.5), CPoint(0.5, 0.5, 0.5), 0.0121)

 DrawYLine(CPoint(0, 0, 0), CPoint(0, 0.5, 0), 0.0121)
 DrawYLine(CPoint(0.5, 0, 0), CPoint(0.5, 0.5, 0), 0.0121)
 DrawYLine(CPoint(0, 0, 0.5), CPoint(0, 0.5, 0.5), 0.0121)
 DrawYLine(CPoint(0.5, 0, 0.5), CPoint(0.5, 0.5, 0.5), 0.0121)

 DrawZLine(CPoint(0, 0, 0), CPoint(0, 0, 0.5), 0.0121)
 DrawZLine(CPoint(0.5, 0, 0), CPoint(0.5, 0, 0.5), 0.0121)
 DrawZLine(CPoint(0, 0.5, 0), CPoint(0, 0.5, 0.5), 0.0121)
 DrawZLine(CPoint(0.5, 0.5, 0), CPoint(0.5, 0.5, 0.5), 0.0121)

* This source code was highlighted with Source Code Highlighter.

Что делаю функции фида DrawXLine, думаю, понятно? Последний параметр в них обозначает шаг луча и выбран на глаз=) Таким образом мы нарисовали статичный куб в начале координат, однако он слишком неподвижен, а это так скучно. Поэтому дабавим анимацию.
Все, что нам потребуется это совсем немного изменить код функции PutPoint.

def PutPoint( x, y, z ):
 global AngleX, AngleY

 # Магический поворот точки вокруг начала координат по оси Y
 nx = x * cos(AngleY) + z * sin(AngleY)
 nz = z * cos(AngleY) - x * sin(AngleY)
 ny = y;

 # И по оси X
 nnx = nx
 nny = ny * cos(AngleX) - nz * sin(AngleX)
 nnz = ny * sin(AngleX) + nz * cos(AngleX)

 # Дальше все по-старому
 sx = (xSize / 2) + ((nnx * Dist) / (nnz + Dist))
 sy = (ySize / 2) + ((nny * Dist) / (nnz + Dist))
 if (sx < -1):
  sx = -1;
 if (sx > 1):
  sx = 1;
 if (sy < -1):
  sy = -1;
 if (sy > 1):
  sy = 1;
 WaveF.writeframes(struct.pack("hh", sy * 32767.0, sx * 32767.0))

* This source code was highlighted with Source Code Highlighter.

Мы добавили две глобальные переменные AngleX и AngleY для управления углами вращения, и просто применили соответствующие преобразования к исходным координатам точки. Вот так теперь выводим кубик:

AngleY = 0
AngleX = 0
for cnt in range(0, 1000):
 DrawCube()

 AngleY += radians(1)
 AngleX += radians(1)

* This source code was highlighted with Source Code Highlighter.

Все предельно просто.
Смотрите на результат:

Ну как оно? Огорчают странные шумы в вершинах кубика, я надеюсь это из-за плохого соединения плеера и осциллографа.
Вот и все, спасибо за внимание, всем удачи=)

Теги:
Хабы:
Всего голосов 237: ↑233 и ↓4+229
Комментарии102

Публикации

Истории

Ближайшие события

2 – 18 декабря
Yandex DataLens Festival 2024
МоскваОнлайн
11 – 13 декабря
Международная конференция по AI/ML «AI Journey»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань