Pull to refresh

Рисуем RGB-куб в изометрической проекции с помощью библиотеки Pycairo

Сегодня мы попробуем нарисовать т.н. RGB-куб — куб, каждая точка поверхности которого будет окрашена в цвет, яркость каждой из компонент которого (красной, зеленой и синей) равна соответствующей координате в трехмерном пространстве. Каждому из цветов отвечает ось координат (x — красный, y — зеленый, z — синий).
Для собственно рисования используем замечательную графическую библиотеку cairo, а именно ее python-версию, Pycairo.
Как известно, данная библиотека используется повсеместно на просторах всем известных проектов, таких как GIMP, Inkscape, Mozilla и многих других. Кроме всего прочего, она позволяет рисовать различные 2D-объекты (эллипсы, прямоугольники, точки и т.д.) и сохранять результаты в разные форматы (PNG, SVG, PDF и т.д.). Этим мы и воспользуемся.
Теперь относительно математического аппарата. Нам нужно уметь преобразовать реальные трехмерные координаты точек поверхности кубика в двухмерные (изометрическая проекция на экран). Это мы сможем сделать с помощью следующих формул:
x' = (x — z) * sin 60о = (x — z) * 0.866
y' = (x + z) * cos 60о — y = (x + z) * 0.5 — y,
где x,y,z — реальные координаты точек куба, x',y' — координаты точек на экране.
Основным объектом cairo является т.н. поверхность (surface). Они бывают разных типов (SVG, PNG, PDF...), но в целом работа с ними схожа. Именно с создания такой поверхности мы и начнем:
svg = cairo.SVGSurface(filename, Width, Height)

filename — это имя файла, связанного с данной поверхностью (сюда мы впоследствии запишем результаты своей работы). Width и Height — думаю, понятно.
Получим так называемый «контекст» данной поверхности. Именно в контексте мы позже будем «колдовать».
ctx = cairo.Context(svg)

Cairo позволяет рисовать различные двухмерные фигуры, но в нашем примере нам пригодиться лишь точка (т.е прямоугольник шириной и высотой в один пиксель), нарисовать которую можно следующим образом:
ctx.rectangle(x, y, width, height)    
ctx.set_source_rgb(red,green,blue)                   
ctx.fill()

Как параметр командной строки будем передавать имя файла, в который будет записан результат. Вторым параметром мы будем регулировать то, какую сторону кубика мы хотим увидеть (темную или светлую).
Вот, собственно текст:
#!/usr/bin/env python
import cairo
import sys
 
class _2Dpoint:
    def __init__(self, _x, _y, _z):
        self.x = _x * 0.866 - _z * 0.866
        self.y = _x * 0.5   + _z * 0.5   - _y
 
def draw_point(cntx, x, y, z):
    point = _2Dpoint(x, y, z)
    cntx.rectangle(x0 + point.x, y0 + point.y11)   
    if dark:
        red   = float((max_ax - x) / max_ax)
        green = float((max_ax - y) / max_ax)
        blue  = float((max_ax - z) / max_ax)
    else:
        red   = float(x / max_ax)
        green = float(y / max_ax)
        blue  = float(z / max_ax) 
    cntx.set_source_rgb(red,green,blue)                   
    cntx.fill()
 
Width  = 100
Height = 100
 
svg = cairo.SVGSurface(sys.argv[1], Width, Height)
 
if sys.argv[2]=='true':
    dark = True
else:
    dark = False
 
ctx = cairo.Context(svg)
 
x0 = Width / 2
y0 = Height / 2
 
max_ax = 50.0
 
# full red. red = 1
x = max_ax
for y in range(0, Height - y0):
    for z in range(0, Width - x0):
        draw_point(ctx, x, y, z)
 
# full green. green = 1
y = max_ax
for x in range(0, Height - y0):
    for z in range(0, Width - x0):
        draw_point(ctx, x, y, z)
 
# full blue. blue = 1
z = max_ax
for x in range(0, Height - y0):
    for y in range(0, Width - x0):
        draw_point(ctx, x, y, z)
 
svg.finish()

Для его работы необходимо, чтобы был установлен pycairo (должен быть в репозиториях всех основных дистрибутивов, но можно скачать и с официального сайта).
А вот результаты:
светлый кубик
темный кубик
Конечно, можно украсить данный пример, параметризировав габариты кубика, а также научиться его поворачивать под разными углами.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.