Exemple #1
0
    class __Game():
        __FPS = 0

        def __init__(self, defaults, window_attributes):
            random.seed()

            Game.__Game.__FPS = defaults["FPS"]

            pygame.init()
            self.time_since_started = pygame.time.get_ticks()
            self.clock = Clock()
            self.delta = 0

            self.screen = pygame.display.set_mode(window_attributes["size"])
            pygame.display.set_caption(window_attributes["caption"])

            from src.scene import Scene
            self.scene = Scene()

            self.is_start = False
            self.is_run = False

        def start(self):
            scene_started = self.scene.start()
            self.is_run = True
            return self.is_run and scene_started

        def process_events(self):
            for evt in pygame.event.get():
                if evt.type == QUIT:
                    self.quit()

        def update(self, delta):
            self.scene.update(delta)

        def render(self, target):
            #target.fill((200, 200, 200))
            self.scene.render(target)
            pygame.display.flip()

        def run(self):
            if self.is_start:
                return
            self.is_start = self.start()
            if not self.is_start:
                raise RuntimeError("could not start")

            while self.is_run:
                self.process_events()
                self.update(self.delta)
                self.render(self.screen)
                self.delta = self.clock.tick(Game.instance.__FPS)
                self.time_since_started = pygame.time.get_ticks()

            pygame.quit()

        def quit(self):
            self.is_run = False
Exemple #2
0
    def load(self):
        Scene.load(self)

        print('loading world')
        self.TestMap.loadMap('pygame-roguelike-layout-01')

        Keyboard.on(Keyboard.keymap['PAUSE'], self.pauseUnpause)

        pass
    def __init__(self):
        Scene.__init__(self)
        self.next = None
        self.music = "assets/music/Phillip_Gross_-_02_-_Neon_Twin.mp3"
        self.background_1 = load_image("assets/images/scenes/background_credits.png")

        self.buttons = [
            ButtonPlay(lambda: self.assign_next_scene("main_menu"))
        ]


        self.mouse_state = 1 # Up
    def __init__(self, data, level):
        Scene.__init__(self)
        self.level = level
        self.music = "assets/music/{}".format(data["music"])
        self.sound_time_over = load_sound("assets/sounds/time_over.wav")
        self.sound_game_over = load_sound("assets/sounds/game_over.wav")
        self.sound_level_completed = load_sound("assets/sounds/level_completed.wav")
        self.background = load_image("assets/images/scenes/{}".format(data["background"]))

        # Things
        self.good_objets_data = data["good_objets"] # List of tuples (object, probability 0-1, needs)
        self.good_objets = []
        for ii in range(len(self.good_objets_data)):
            self.good_objets.append(self.good_objets_data[ii][0](ICON_LOCATIONS[ii]))
        self.bad_objects = data["bad_objets"]
        self.new_object_probability = data["new_object_probability"]
        self.probabilities = [self.good_objets_data[0][1] * self.new_object_probability]
        for ii in range(1, len(self.good_objets_data)):
            self.probabilities.append(self.probabilities[ii-1] + (self.good_objets_data[ii][1] * self.new_object_probability))

        # Variables
        self.things = pygame.sprite.Group()
        self.level_time = self.countdown = data["level_time"] * 1000
        self.start = False
        self.end_completed = False
        self.end_failed_time = False
        self.end_failed_health = False
        self.mouse_state = 1 # Up

        #Characters
        self.player = Player("keyboard", int(WIDTH / 2), GROUND_LEVEL)

        # Buttons
        ## Start level button
        self.start_level_button = load_image("assets/images/buttons/start_level_button.png")
        self.start_level_button_rect = self.start_level_button.get_rect()
        self.start_level_button_rect.center = START_FINISH_BUTTON

        ## Next level button
        self.next_level_button = load_image("assets/images/buttons/next_level_button.png")
        self.next_level_button_rect = self.next_level_button.get_rect()
        self.next_level_button_rect.center = START_FINISH_BUTTON

        ## Time over button
        self.time_over_button = load_image("assets/images/buttons/time_over_button.png")
        self.time_over_button_rect = self.time_over_button.get_rect()
        self.time_over_button_rect.center = START_FINISH_BUTTON

        ## Dead button
        self.dead_button = load_image("assets/images/buttons/dead_button.png")
        self.dead_button_rect = self.dead_button.get_rect()
        self.dead_button_rect.center = START_FINISH_BUTTON
Exemple #5
0
    def __init__(self):
        Scene.__init__(self)
        self.next = None
        self.music = "assets/music/Phillip_Gross_-_02_-_Neon_Twin.mp3"
        self.background_1 = load_image(
            "assets/images/scenes/background_tutorial.png")

        self.buttons = [
            ButtonClose(LOCATION_BUTTON_CLOSE,
                        lambda: self.assign_next_scene("main_menu"))
        ]

        self.mouse_state = 1  # Up
    def __init__(self):
        Scene.__init__(self)
        self.next = None
        self.music = "assets/music/Phillip_Gross_-_02_-_Neon_Twin.mp3"
        self.background_1 = load_image(
            "assets/images/scenes/background_menu_1.png")
        self.background_2 = load_image(
            "assets/images/scenes/background_menu_2.png")

        self.buttons = [
            ButtonPlay(lambda: self.assign_next_scene("intro")),
            ButtonCredits(lambda: self.assign_next_scene("credits")),
            ButtonTutorial(lambda: self.assign_next_scene("tutorial")),
        ]

        self.mouse_state = 1  # Up
Exemple #7
0
    def __init__(self, w, h):
        super().__init__()

        self.connect('destroy', Gtk.main_quit)
        self.set_default_size(w, h)

        drawingarea = Gtk.DrawingArea()
        drawingarea.set_can_focus(True)
        drawingarea.add_events(Gdk.EventMask.KEY_PRESS_MASK)
        drawingarea.connect('draw', self.on_draw)
        drawingarea.connect('key-press-event', self.on_key_press)
        self.add(drawingarea)
        self.drawingarea = drawingarea

        self.center = (w // 2, h // 2)
        self.scene = Scene(120)
Exemple #8
0
        def __init__(self, defaults, window_attributes):
            random.seed()

            Game.__Game.__FPS = defaults["FPS"]

            pygame.init()
            self.time_since_started = pygame.time.get_ticks()
            self.clock = Clock()
            self.delta = 0

            self.screen = pygame.display.set_mode(window_attributes["size"])
            pygame.display.set_caption(window_attributes["caption"])

            from src.scene import Scene
            self.scene = Scene()

            self.is_start = False
            self.is_run = False
 def __init__(self, bot, scenario_data):
     self.bot = bot
     self.chat_id = None  # not thread-safe
     self.players = defaultdict(dict)
     self.picture_sender = PictureSender(self.bot)
     self.scenario = {}
     for scene_data in scenario_data:
         scene = Scene.from_data(scene_data)
         self.scenario[scene.name] = scene
Exemple #10
0
    def __init__(self):
        Scene.__init__(self)
        self.next = None
        self.persiana_sube = True
        self.fade = False
        self.changing = False
        self.alpha = 255

        self.music = "assets/music/Phillip_Gross_-_02_-_Neon_Twin.mp3"
        self.background_persiana = load_image("assets/images/scenes/background_menu_2.png")
        self.rect_background_persiana = self.background_persiana.get_rect()

        self.background_blurry = load_image("assets/images/scenes/background_game_blurry.png")
        self.background_clear = load_image("assets/images/scenes/background_game_clear.png")

        self.buttons = [
            ButtonPlay(lambda: self.assign_next_scene())
        ]
        self.mouse_state = 1 # Up
    def __init__(self, level):
        Scene.__init__(self)
        self.level = level
        self.next = None
        self.music = "assets/music/Phillip_Gross_-_03_-_Optimistic_Bits.mp3"
        self.sound_notification = load_sound("assets/sounds/notification.wav")
        self.background = load_image("assets/images/scenes/background_chat.png")
        self.chat = []
        for i in range(6):
            self.chat.append(pygame.transform.scale(load_image("assets/images/scenes/{}-{}.png".format(level, i)), CHAT_SURFACE))
        self.current_chat = -1
        self.chat_rect = self.chat[0].get_rect()
        self.chat_rect.center = (int(WIDTH / 2) , int(HEIGHT / 2))

        # Next chat button
        self.next_chat_button = load_image("assets/images/buttons/next_chat_button.png")
        self.next_chat_button_rect = self.next_chat_button.get_rect()
        self.next_chat_button_rect.center = NEXT_CHAT_BUTTON

        self.mouse_state = 1 # Up
Exemple #12
0
  def initUI(self):
    self.scene = Scene(10, 20)

    self.view = View(self.scene)
    self.view.setCacheMode(QGraphicsView.CacheBackground)
    self.view.setViewportUpdateMode(QGraphicsView.FullViewportUpdate);
    self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
    self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
    self.setCentralWidget(self.view)

    self._createActions()
    self._createMenuBar()
    self._createToolBar()

    statusbar = self.statusBar()
    statusbar.setStyleSheet('QStatusBar{border-top: 1px outset grey;}')

    self.resize(768, 720 + self.statusBar().sizeHint().height() + self.menuBar().sizeHint().height() + self.toolbar.sizeHint().height()) #Toolbar
    self.center()
    self.setWindowTitle('Stackmaker')

    self.show()
Exemple #13
0
    def __init__(self):
        Scene.__init__(self)
        self.paused = False
        self.Character = CharacterController(Character(), self.CHARACTER_GROUP)
        self.TestMap = Map()

        Scene.add(self, 'CharacterController', self.Character)
        Scene.add(self, 'TestMap', self.TestMap)

        pass
Exemple #14
0
    def new_file(self):

        if not self.close_file():
            return

        dialog = NewFileDialog()
        code = dialog.exec()

        if code == 0:
            return

        self.root.scene = Scene(self.root)
        self.root.ui.graphicsView.setScene(self.root.scene)

        white_sheet = QPixmap(dialog.ui.width_spin.value(), dialog.ui.height_spin.value())
        white_sheet.fill()
        bg = QGraphicsPixmapItem(white_sheet)
        self.root.scene.addItem(bg)
Exemple #15
0
    def __init__(self, kernel_filename, scene_filename, width, height, noise,
                 obj, animation, record, no_gui):

        self._run = self.animation_run if animation else self.normal_run
        self.record = record
        self.no_gui = no_gui
        if self.record:
            os.makedirs("animation")
        self.frame = 0
        self.wait = animation
        self.width, self.height = width, height
        self.actions_list = self.get_action_list()
        scene = Scene(None, None)
        scene.load_from_json(scene_filename)
        if obj:
            scene.load_from_mesh(obj)
        self.camera = Camera(width, height)
        scene.add_object(self.camera)
        self.connector = Connector(kernel_filename, scene, width, height,
                                   noise)

        if not self.no_gui:
            self.parent_conn, child_conn = Pipe()
            self.gui_process = Process(target=gui_worker,
                                       args=(child_conn, width, height))
            self.gui_process.start()

        self.running = True
        self.denoiser = Denoiser.create("CnnAutoencoder", width, height)

        self.next_frame = True

        self.actions = {
            a: False
            for a in [
                "w", "a", "s", "d", "Up", "Left", "Down", "Right", "q", "e",
                "plus", "minus"
            ]
        }
Exemple #16
0
    def open_file(self):

        if not self.close_file():
            return

        new_file_name, _ = QFileDialog.getOpenFileName(self.root, "Open File", "",
                                                       "Images (*.jpg *.jpeg *.png)",
                                                       options=self.file_dialog_options)
        if not new_file_name:
            return

        self.root.file_name = new_file_name
        self.root.scene = Scene(self.root)
        self.root.ui.graphicsView.setScene(self.root.scene)

        img = QPixmap(new_file_name)
        white_sheet = QPixmap(img.width(), img.height())
        white_sheet.fill()

        bg = QGraphicsPixmapItem(white_sheet)
        insertion = QGraphicsPixmapItem(img)
        self.root.scene.addItem(bg)
        self.root.scene.addItem(insertion)
Exemple #17
0
    def update(self):
        Scene.update(self)

        self.mainCamera.focus(self.Character.character.pos)

        pass
Exemple #18
0
    def end(self):
        Scene.end(self)

        print('ending world scene')

        pass
Exemple #19
0
    def start(self):
        Scene.start(self)

        print('starting world scene')

        pass
Exemple #20
0
class StackMaker(QMainWindow):
  def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.initUI()

  def _createActions(self):
    # https://realpython.com/python-menus-toolbars/ for menu stuff later
    self.exitAct = QAction(QIcon(resource_path('./assets/icons/exit.png')), '&Exit', self)
    self.exitAct.setShortcut('Ctrl+Q')
    self.exitAct.setStatusTip('Exit application')
    self.exitAct.triggered.connect(qApp.quit)

    self.copyAct = QAction(QIcon(resource_path('./assets/icons/copy.png')), '&Copy', self)
    self.copyAct.setShortcut('Ctrl+C')
    self.copyAct.setStatusTip('Copy entire board')
    self.copyAct.triggered.connect(self.copy)

    self.mainActs = QActionGroup(self)

    self.selectAct = QAction(QIcon(resource_path('./assets/icons/select.ico')), '&Select', self.mainActs)
    self.selectAct.setShortcut('Shift+S')
    self.selectAct.setStatusTip('Select board part for copying.')
    self.selectAct.setCheckable(True)
    self.selectAct.setEnabled(True)
    self.selectAct.toggled.connect(self.view.toggleBand)

    self.undoAct = QAction(QIcon(resource_path('./assets/icons/undo.ico')), '&Undo', self)
    self.undoAct.setShortcut('Ctrl+Z')
    self.undoAct.setStatusTip('Undo most recent action')
    self.undoAct.setEnabled(True)
    self.undoAct.triggered.connect(self.scene.undo)

    self.redoAct = QAction(QIcon(resource_path('./assets/icons/redo.ico')), '&Redo', self)
    self.redoAct.setShortcut('Ctrl+Y')
    self.redoAct.setStatusTip('Redo most recent action')
    self.redoAct.setEnabled(True)
    self.redoAct.triggered.connect(self.scene.redo)


    self.ccwAct = QAction(QIcon(), '&Counterclockwise')
    self.ccwAct.setShortcut('Q')
    self.ccwAct.setStatusTip('Rotate counterclockwise')
    self.ccwAct.setEnabled(False)
    self.ccwAct.triggered.connect(self.scene.cursor.ccw)

    self.cwAct = QAction(QIcon(), '&Clockwise')
    self.cwAct.setShortcut('W')
    self.cwAct.setStatusTip('Rotate clockwise')
    self.cwAct.setEnabled(False)
    self.cwAct.triggered.connect(self.scene.cursor.cw)

    pieceNames = ['T','J','Z','O','S','L','I']
    self.pieceActs = []
    for i in range(7):
      pieceAct = QAction(QIcon(resource_path(f'./assets/icons/icon{pieceNames[i]}.ico')), pieceNames[i], self.mainActs)
      pieceAct.setShortcut(pieceNames[i])
      pieceAct.setStatusTip(f'Draw the {pieceNames[i]} piece')
      pieceAct.setCheckable(True)
      # Ugly solution but it works
      pieceAct.triggered.connect(lambda x=False,i=i: self.scene.cursor.setType([i, 0]))
      pieceAct.triggered.connect(lambda : self.ccwAct.setEnabled(True))
      pieceAct.triggered.connect(lambda : self.cwAct.setEnabled(True))
      self.pieceActs.append(pieceAct)

    self.cellActs = []
    icons = [
      QIcon(QPixmap(resource_path('./assets/tile0.png')).scaled(16, 16)),
      QIcon(resource_path('./assets/icons/whiteCell.ico')),
      QIcon(resource_path('./assets/icons/darkCell.ico')),
      QIcon(resource_path('./assets/icons/lightCell.ico')),
      QIcon(resource_path('./assets/icons/stack.ico'))
    ]
    names = ['&Erase', '&White Cell', '&Dark Cell', '&Light Cell', '&Stack Mode']
    shortcuts = ['E', '1', '2', '3', 'C']
    tips = ['Erase cell', 'Paint the white cell', 'Paint the dark cell', 'Paint the light cell', 'Fill basic stack']

    for i in range(5):
      cellAct = QAction(icons[i], names[i], self.mainActs)
      cellAct.setShortcut(shortcuts[i])
      cellAct.setStatusTip(tips[i])
      cellAct.setCheckable(True)
      if i == 4:
        cellAct.triggered.connect(lambda : self.scene.setColCursor())
      else:
        cellAct.triggered.connect(lambda x=False,i=i : self.scene.setCellState(i))
      cellAct.triggered.connect(lambda : self.ccwAct.setEnabled(False))
      cellAct.triggered.connect(lambda : self.cwAct.setEnabled(False))
      self.cellActs.append(cellAct)

    self.cellActs[1].setChecked(True)

    self.transparentAct = QAction(QIcon(), '&Transparent')
    self.transparentAct.setShortcut('Ctrl+V')
    self.transparentAct.setStatusTip('Draw with transparent cells')
    self.transparentAct.setCheckable(True)
    self.transparentAct.triggered.connect(lambda x : self.scene.setTransparentDraw(x))

    self.rgbAct = QAction(QIcon(), '&Option Colors')
    self.rgbAct.setShortcut('Ctrl+A')
    self.rgbAct.setStatusTip('Tint drawn objects with varying colors')
    self.rgbAct.setCheckable(True)

    self.connectStatusAct = QAction(QIcon(), '&Enable/Disable', self)
    self.connectStatusAct.triggered.connect(self.enableTracking)

    self.toggleMirrorAct = QAction(QIcon(), '&Toggle Mirror', self)
    self.toggleMirrorAct.setShortcut('Ctrl+M')
    self.toggleMirrorAct.setEnabled(False)
    self.toggleMirrorAct.triggered.connect(self.toggleMirror)

  def _createMenuBar(self):
    menubar = self.menuBar()
    fileMenu = menubar.addMenu('&File')
    fileMenu.addAction(self.exitAct)
    fileMenu.addAction(self.copyAct)

    drawMenu = menubar.addMenu('&Draw')
    drawMenu.addAction(self.selectAct)
    drawMenu.addAction(self.undoAct)
    drawMenu.addAction(self.redoAct)
    for act in self.cellActs:
      drawMenu.addAction(act)
    pieceMenu = drawMenu.addMenu('&Piece')
    for act in self.pieceActs:
      pieceMenu.addAction(act)
    pieceMenu.addSeparator()
    pieceMenu.addAction(self.ccwAct)
    pieceMenu.addAction(self.cwAct)

    viewMenu = menubar.addMenu('&View')
    viewMenu.addAction(self.transparentAct)
    viewMenu.addAction(self.rgbAct)

    connectMenu = menubar.addMenu('&Connect')
    connectMenu.addAction(self.connectStatusAct)
    connectMenu.addAction(self.toggleMirrorAct)

  def _createToolBar(self):
    self.toolbar = self.addToolBar('test')
    self.toolbar.setMovable(False)
    self.toolbar.addAction(self.selectAct)
    self.toolbar.addAction(self.undoAct)
    self.toolbar.addAction(self.redoAct)
    self.toolbar.addSeparator()
    for act in self.cellActs:
      self.toolbar.addAction(act)
    self.toolbar.addSeparator()
    for act in self.pieceActs:
      self.toolbar.addAction(act)

  def initUI(self):
    self.scene = Scene(10, 20)

    self.view = View(self.scene)
    self.view.setCacheMode(QGraphicsView.CacheBackground)
    self.view.setViewportUpdateMode(QGraphicsView.FullViewportUpdate);
    self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
    self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
    self.setCentralWidget(self.view)

    self._createActions()
    self._createMenuBar()
    self._createToolBar()

    statusbar = self.statusBar()
    statusbar.setStyleSheet('QStatusBar{border-top: 1px outset grey;}')

    self.resize(768, 720 + self.statusBar().sizeHint().height() + self.menuBar().sizeHint().height() + self.toolbar.sizeHint().height()) #Toolbar
    self.center()
    self.setWindowTitle('Stackmaker')

    self.show()

  def center(self):
    '''centers the window on the screen'''

    screen = QDesktopWidget().screenGeometry()
    size = self.geometry()
    self.move(int((screen.width() - size.width()) // 2),
              int((screen.height() - size.height()) // 2))

  # Resizing the viewport is necessary for some reason. Should check why this needs to be used
  def resizeEvent(self, e):
    self.view.viewport().resize(self.width(), self.height() - self.statusBar().sizeHint().height()-self.menuBar().sizeHint().height()-self.toolbar.sizeHint().height())
    print(self.width(), self.height())
    print(self.view.width(), self.view.height())
    # print(self.view.minimumSizeHint())
    # # print(self.view.viewport().width(), self.view.viewport().height())
    # # self.view.fitInView(self.view.sceneRect(), Qt.KeepAspectRatio)
    self.view.setRect()

  def copy(self):
    clipboard = QGuiApplication.clipboard()
    rect = self.scene.sceneRect() if self.view.selection() == QRect() else self.view.sceneSelection()
    board = QImage(rect.size().toSize(), QImage.Format_ARGB32)
    board.fill(Qt.transparent)
    painter = QPainter(board)
    self.scene.render(painter, QRectF(), rect)
    board1 = board.scaled(board.width()*2, board.height()*2)

    clipboard.setImage(board1)
    painter.end()
    self.statusBar().showMessage('Copied!', 500)

  def toggleMirror(self):
    self.scene.ocrHandler.socket.blockSignals(not self.scene.ocrHandler.socket.signalsBlocked())

  def enableTracking(self):
    if not self.scene.ocrHandler.connected:
      print('Awaiting connection...')
      self.scene.ocrHandler.listen(QHostAddress.LocalHost, 3338)
      self.toggleMirrorAct.setEnabled(True)
    else:
      print('Disconnecting')
      self.scene.ocrHandler.exit()
      self.toggleMirrorAct.setEnabled(False)
Exemple #21
0
import os
import fileinput
from shutil import copyfile
from PySide import QtGui
from src.view import View
from src.scene import Scene
from src.model import *

from aether.diagnostics import set_diagnostics_on
from aether.geometry import define_elem_geometry_2d, define_node_geometry_2d, define_data_geometry, define_rad_from_file, define_rad_from_geom, append_units
from aether.exports import export_node_geometry_2d, export_elem_geometry_2d, export_data_geometry
from aether.surface_fitting import fit_surface_geometry
from opencmiss.zinc.scenecoordinatesystem import SCENECOORDINATESYSTEM_WINDOW_PIXEL_TOP_LEFT

app = QtGui.QApplication(sys.argv)
scene = Scene()

datacloudModel = FileModel(scene, 'datacloud')
surfaceModel = FileModel(scene, 'surface')

# the keys in these dicts correspond to the accessibleName in Qt
landmarkCoords = {}
landmarkModels = {}
landmarkMaterials = {
    'apex': 'green',
    'basal': 'red',
    'lateral': 'blue',
    'ventral': 'yellow',
}

Exemple #22
0
              options["lengthLimit"], options["shortestLength"],
              options["rotationLimit"])

    export_node_geometry('.tmp.exnode', 'out')
    export_1d_elem_geometry('.tmp.exelem', 'out')

    airwayModel.load('.tmp.exnode', '.tmp.exelem')


def save(exnode, exelem):
    export_node_geometry(exnode, 'out')
    export_1d_elem_geometry(exelem, 'out')


app = QtGui.QApplication(sys.argv)
scene = Scene()
airwayModel = scene.newModel('airway')
surfaceModel = scene.newModel('surface')

view = View(scene)
view.airwayCallback(loadAirway)
view.surfaceCallback(loadSurface)
view.generateCallback(generate)
view.saveCallback(save)
view.setAirways('airway_tree_FRC.ipnode', 'airway_tree_FRC.ipelem')
view.setOutputs('out.exnode', 'out.exelem')
view.setInfo("""
<h2>Grow airway tree</h2>
<p>This GUI provides an easy interface for the grow_tree Fortran code in lungsim. It allows the visualization of the airway tree and the surface mesh and allows configuring the growth algorithm.</p>
<p>Created for use within the Auckland Bioengineering Institute at the University of Auckland.</p>
<h3>Usage</h3>
Exemple #23
0
class Window(Gtk.Window):
    def __init__(self, w, h):
        super().__init__()

        self.connect('destroy', Gtk.main_quit)
        self.set_default_size(w, h)

        drawingarea = Gtk.DrawingArea()
        drawingarea.set_can_focus(True)
        drawingarea.add_events(Gdk.EventMask.KEY_PRESS_MASK)
        drawingarea.connect('draw', self.on_draw)
        drawingarea.connect('key-press-event', self.on_key_press)
        self.add(drawingarea)
        self.drawingarea = drawingarea

        self.center = (w // 2, h // 2)
        self.scene = Scene(120)

    def __fill_triangle(self, ctx, v1, v2, v3, color):
        # http://www.sunshine2k.de/coding/java/TriangleRasterization/TriangleRasterization.html
        v1, v2, v3 = sorted([v1, v2, v3], key=lambda v: v.y)
        if v2.y == v3.y:
            self.__fill_bottom_flat_triangle(ctx, v1, v2, v3, color)
        elif v1.y == v2.y:
            self.__fill_top_flat_triangle(ctx, v1, v2, v3, color)
        else:
            v = Vector([
                v1.x + (v3.x - v1.x) * (v2.y - v1.y) // (v3.y - v1.y),
                v2.y,
                0,
            ])
            self.__fill_bottom_flat_triangle(ctx, v1, v2, v, color)
            self.__fill_top_flat_triangle(ctx, v2, v, v3, color)

    def __fill_bottom_flat_triangle(self, ctx, v1, v2, v3, color):
        scanlineY = v1.y
        while scanlineY <= v2.y:
            x1 = v1.x + (scanlineY - v1.y) * (v2.x - v1.x) // (v2.y - v1.y)
            x2 = v1.x + (scanlineY - v1.y) * (v3.x - v1.x) // (v3.y - v1.y)
            scanlineY += 1
            self.__draw_line(ctx, x1, x2, scanlineY, color)

    def __fill_top_flat_triangle(self, ctx, v1, v2, v3, color):
        scanlineY = v3.y
        while scanlineY > v1.y:
            x1 = v3.x - (v3.y - scanlineY) * (v3.x - v1.x) // (v3.y - v1.y)
            x2 = v3.x - (v3.y - scanlineY) * (v3.x - v2.x) // (v3.y - v2.y)
            scanlineY -= 1
            self.__draw_line(ctx, x1, x2, scanlineY, color)

    def __draw_line(self, ctx, x1, x2, y, color):
        if x2 < x1:
            x1, x2 = x2, x1

        while x1 < x2:
            # https://en.wikipedia.org/wiki/Dither
            if color * 300 >= rgen():
                ctx.set_source_rgb(0, 0, 0)
            else:
                ctx.set_source_rgb(1, 1, 1)
            ctx.rectangle(x1 + self.center[0], -y + self.center[1], 1, 1)
            ctx.fill()
            x1 += 1

    def on_draw(self, _, ctx):
        ctx.save()
        ctx.set_line_width(1)

        ctx.set_source_rgb(1, 1, 1)
        ctx.new_path()
        ctx.move_to(0, 0)
        ctx.line_to(512, 0)
        ctx.line_to(512, 256)
        ctx.line_to(0, 256)
        ctx.close_path()
        ctx.fill()

        for v1, v2, v3, cl in self.scene.iter_facets():
            self.__fill_triangle(ctx, v1, v2, v3, cl)
            ctx.set_source_rgb(0, 0, 0)
            ctx.new_path()
            ctx.move_to(v1.x + self.center[0], -v1.y + self.center[1])
            ctx.line_to(v2.x + self.center[0], -v2.y + self.center[1])
            ctx.line_to(v3.x + self.center[0], -v3.y + self.center[1])
            ctx.close_path()
            ctx.stroke()

        ctx.restore()

    def on_key_press(self, _, e):
        if e.keyval == Gdk.KEY_Up:
            self.scene.rotate_x(-1)
        elif e.keyval == Gdk.KEY_Down:
            self.scene.rotate_x(1)
        elif e.keyval == Gdk.KEY_Left:
            self.scene.rotate_y(1)
        elif e.keyval == Gdk.KEY_Right:
            self.scene.rotate_y(-1)

        self.drawingarea.queue_draw()