📜 ⬆️ ⬇️

We draw 3D on an oscillograph

Oscilloscope Hello. In this short article I’m going to talk about how to display 3D graphics on a vector display. Of course, it does not carry any practical value, but how interesting!

Common words


So, we are going to draw 3D. And not just to draw, but to draw on an oscilloscope. I think that it will be enough to portray a rotating cube to understand the principle of operation, and there it’s not far from a three-dimensional shooter.
The whole point is that in vector displays (that is, for us it is an oscilloscope screen), unlike those we are used to, the image is not displayed by sequentially highlighting the pixels, but by setting the projecting beam in the right place. That is, we simply “circle” the outlines of the future image with a ray on the screen, instead of marking points.

How to make friends with a computer and an oscilloscope?


Let's see what happens inside the oscilloscope. In the simplest case, an oscilloscope is an electron gun that creates and focuses a beam from electrons. Then this beam, passing through two capacitors, deviates horizontally or vertically, depending on the voltage on them. Then the beam hits the screen, covered with phosphor, and there is a flash in the place where the beam hit. Thus, with a fairly rapid movement of the beam, a picture is formed.
The voltage on the deflection capacitors depends on the voltage at the inputs of the oscilloscope. In our case, the deviation along the X axis will be controlled by one input, and along the Y axis by another. Hooray, we can already draw something by applying the appropriate voltage to the oscilloscope.
And we will deliver it with the help of a computer sound card, because the sound at its outputs depends on the sound frequency. Let the right channel in our country is responsible for movement along the X axis, and the left one along the Y axis.
That is, simply by connecting the right channel of the sound card to the first input of the oscilloscope, and the left channel to the second one can control the movement of the beam on the screen.

Put an end


To begin, let's learn how to just put one point. We will use everyone's favorite Python and a couple of libraries to it. Let's look at this code:
')
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 .
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 .
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 .


Everything is simple: open the wav-file, and write there information about one point (the frame in the terminology of wav files). We believe that the coordinates of the point are only from -1 to 1, so feel free to multiply them by the maximum value in the type of integer, so that the volume, and accordingly the output voltage would be large enough. The sound I bring to wav just because, so it can be poured onto the player and quietly conveyed to the oscilloscope.
So, we learned to put a point on a two-dimensional plane, but we want 3D. So let's apply some simple math and learn how to project a point from three-dimensional space into two-dimensional, like this:
#
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 .
#
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 .

Great, now you can put a point with three coordinates and it will be correctly projected. The most important thing is done, very little is left.

Draw a cube!


It's still easier. We look at the intelligent code:

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 .
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 .

What do the DrawXLine feed functions do, I think, okay? The last parameter in them denotes the beam pitch and is selected by eye =) Thus, we drew a static cube at the origin, but it’s too fixed, which is so boring. Therefore let's add some animation.
All we need is to change the PutPoint function code quite a bit.

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 .
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 .

We added two global variables, AngleX and AngleY, to control rotation angles, and simply apply the appropriate transformations to the original coordinates of the point. So now we draw the cube:

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 .
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 .

Everything is extremely simple.
Look at the result:

How is it? Grieving strange noises in the tops of the cube, I hope this is due to the poor connection of the player and the oscilloscope.
That's all, thank you for your attention, good luck to all =)

Source: https://habr.com/ru/post/307352/


All Articles