#!/usr/bin/python3 | |
from __future__ import print_function | |
import os | |
import basic_window | |
import random | |
import gi | |
import numpy as np | |
import scipy.spatial.distance | |
gi.require_version('Gtk', '3.0') | |
from gi.repository import Gdk | |
import cairo | |
import enum | |
import csv # For writing to csv files | |
from basic_window import OverrideMatrix, identity, quit_main_loop | |
LENGTH_OF_FIELD = 323.65 | |
PIXELS_ON_SCREEN = 300 | |
def pxToIn(p): | |
return p*LENGTH_OF_FIELD/PIXELS_ON_SCREEN | |
def inToPx(i): | |
return i*PIXELS_ON_SCREEN/LENGTH_OF_FIELD | |
def px(cr): | |
return OverrideMatrix(cr, identity) | |
def draw_px_cross(cr, x, y, length_px, r, g, b): | |
"""Draws a cross with fixed dimensions in pixel space.""" | |
cr.set_source_rgb(r, g, b) | |
cr.move_to(x, y - length_px) | |
cr.line_to(x, y + length_px) | |
cr.stroke() | |
cr.move_to(x - length_px, y) | |
cr.line_to(x + length_px, y) | |
cr.stroke() | |
def draw_px_x(cr, x, y, length_px1, r, g, b): | |
"""Draws a x with fixed dimensions in pixel space.""" | |
length_px = length_px1 / np.sqrt(2) | |
cr.set_source_rgb(r, g, b) | |
cr.move_to(x - length_px, y - length_px) | |
cr.line_to(x + length_px, y + length_px) | |
cr.stroke() | |
cr.move_to(x - length_px, y + length_px) | |
cr.line_to(x + length_px, y - length_px) | |
cr.stroke() | |
def draw_points(cr, p, size): | |
for i in range(0, len(p)): | |
draw_px_cross(cr, p[i][0], p[i][1], size, 0, np.sqrt(0.2 * i), 0) | |
class Mode(enum.Enum): | |
kViewing = 0 | |
kPlacing = 1 | |
kEditing = 2 | |
kExporting = 3 | |
kImporting = 4 | |
def display_text(cr, text, widtha, heighta, widthb, heightb): | |
cr.scale(widtha, -heighta) | |
cr.show_text(text) | |
cr.scale(widthb, -heightb) | |
# Create a GTK+ widget on which we will draw using Cairo | |
class GTK_Widget(basic_window.BaseWindow): | |
def __init__(self): | |
super(GTK_Widget, self).__init__() | |
# init field drawing | |
# add default spline for testing purposes | |
# init editing / viewing modes and pointer location | |
self.mode = Mode.kPlacing | |
self.x = 0 | |
self.y = 0 | |
self.switch = True | |
# update list of control points | |
self.point_selected = False | |
# self.adding_spline = False | |
self.index_of_selected = -1 | |
self.new_point = [] | |
# For the editing mode | |
self.index_of_edit = -1 # Can't be zero beause array starts at 0 | |
self.held_x = 0 | |
# Theo take them from here? | |
self.selected_points = [] | |
self.reinit_extents() | |
#John also wrote this | |
def add_point(self, x, y): | |
if(len(self.selected_points)<4): | |
self.selected_points.append([x,y]) | |
"""set extents on images, this needs to be redone with proper distances""" | |
def reinit_extents(self): | |
self.extents_x_min = -800 | |
self.extents_x_max = 800 | |
self.extents_y_min = -800 | |
self.extents_y_max = 800 | |
# this needs to be rewritten with numpy, i dont think this ought to have | |
# SciPy as a dependecy | |
def get_index_of_nearest_point(self): | |
cur_p = [[self.x, self.y]] | |
distances = scipy.spatial.distance.cdist(cur_p, self.all_controls) | |
return np.argmin(distances) | |
# return the closest point to the loc of the click event | |
def get_nearest_point(self): | |
return self.all_controls[self.get_index_of_nearest_point()] | |
# Handle the expose-event by updating the Window and drawing | |
def handle_draw(self, cr): | |
print(self.new_point) | |
print("SELF.POINT_SELECTED: " + str(self.point_selected)) | |
# begin drawing | |
# Fill the background color of the window with grey | |
cr.set_source_rgb(0.5, 0.5, 0.5) | |
cr.paint() | |
# Draw a extents rectangle | |
cr.set_source_rgb(1.0, 1.0, 1.0) | |
cr.rectangle(self.extents_x_min, self.extents_y_min, | |
(self.extents_x_max - self.extents_x_min), | |
self.extents_y_max - self.extents_y_min) | |
cr.fill() | |
#Drawing the switch and scale in the field | |
cr.move_to(0, 50) | |
cr.show_text('Press "e" to export') | |
cr.show_text('Press "i" to import') | |
cr.set_source_rgb(0.3,0.3,0.3) | |
cr.rectangle(-150,-150,300,300) | |
cr.fill() | |
cr.set_source_rgb(0, 0, 0) | |
cr.rectangle(-150,-150,300,300) | |
cr.set_line_join(cairo.LINE_JOIN_ROUND) | |
cr.stroke() | |
cr.set_source_rgb(0, 0, 0) | |
cr.rectangle(inToPx(140-161.825),inToPx(76.575),inToPx(56),inToPx(-153.15)) | |
cr.set_line_join(cairo.LINE_JOIN_ROUND) | |
cr.stroke() | |
cr.rectangle(inToPx(161.825-24),inToPx(90),inToPx(24),inToPx(-180)) | |
cr.set_line_join(cairo.LINE_JOIN_ROUND) | |
cr.stroke() | |
cr.set_source_rgb(0.2, 0.2, 0.2) | |
cr.rectangle(inToPx(140-161.825),inToPx(76.575),inToPx(56),inToPx(-153.15)) | |
cr.fill() | |
cr.rectangle(inToPx(161.825-24),inToPx(90),inToPx(24),inToPx(-180)) | |
cr.fill() | |
# update all the things | |
if self.mode == Mode.kViewing: | |
cr.set_source_rgb(0, 0, 0) | |
cr.move_to(-300, 170) | |
cr.show_text("VIEWING") | |
cr.set_source_rgb(0.5, 0.5, 0.5) | |
# its gonna check for points_added from button_press_action | |
# The behavior of the click is such that it runs twice | |
# This is consistant with graph_edit.py which someone smart wrote | |
# So I'm just going to delete the last element in order to not get | |
# repeating points | |
if len(self.selected_points) > 0: | |
print("SELECTED_POINTS: " + str(len(self.selected_points))) | |
print("ITEMS:") | |
for item in self.selected_points: | |
print(str(item)) | |
for i, point in enumerate(self.selected_points): | |
print("I: " + str(i)) | |
draw_px_x(cr, point[0], point[1], 10, 0, | |
0, 0) | |
cr.move_to(point[0], point[1]-15) | |
display_text(cr, str(i), 0.5, 0.5, 2, 2) | |
if self.mode == Mode.kPlacing: | |
cr.set_source_rgb(0, 0, 0) | |
cr.move_to(-300, 170) | |
display_text(cr, "ADD", 1, 1, 1, 1) | |
cr.set_source_rgb(0.5, 0.5, 0.5) | |
# its gonna check for points_added from button_press_action | |
# The behavior of the click is such that it runs twice | |
# This is consistant with graph_edit.py which someone smart wrote | |
# So I'm just going to delete the last element in order to not get | |
# repeating points | |
if len(self.selected_points) > 0: | |
print("SELECTED_POINTS: " + str(len(self.selected_points))) | |
print("ITEMS:") | |
for item in self.selected_points: | |
print(str(item)) | |
for i, point in enumerate(self.selected_points): | |
print("I: " + str(i)) | |
draw_px_x(cr, point[0], point[1], 10, 0, | |
0, 0) | |
cr.move_to(point[0], point[1]-15) | |
display_text(cr, str(i), 0.5, 0.5, 2, 2) | |
if(i==3): | |
self.mode = Mode.kEditing | |
elif self.mode == Mode.kEditing: | |
cr.set_source_rgb(0, 0, 0) | |
cr.move_to(-300, 170) | |
display_text(cr, "EDITING", 1, 1, 1, 1) | |
cr.set_source_rgb(0.5, 0.5, 0.5) | |
if len(self.selected_points) > 0: | |
print("SELECTED_POINTS: " + str(len(self.selected_points))) | |
print("ITEMS:") | |
for item in self.selected_points: | |
print(str(item)) | |
for i, point in enumerate(self.selected_points): | |
print("I: " + str(i)) | |
draw_px_x(cr, point[0], point[1], 10, 0, | |
0, 0) | |
cr.move_to(point[0], point[1]-15) | |
display_text(cr, str(i), 0.5, 0.5, 2, 2) | |
elif self.mode == Mode.kExporting: | |
cr.set_source_rgb(0, 0, 0) | |
cr.move_to(-300, 170) | |
display_text(cr, "VIEWING", 1, 1, 1, 1) | |
cr.set_source_rgb(0.5, 0.5, 0.5) | |
#its gonna check for points_added from button_press_action | |
# The behavior of the click is such that it runs twice | |
# This is consistant with graph_edit.py which someone smart wrote | |
# So I'm just going to delete the last element in order to not get | |
# repeating points | |
if len(self.selected_points) > 0: | |
print("SELECTED_POINTS: " + str(len(self.selected_points))) | |
print("ITEMS:") | |
for item in self.selected_points: | |
print(str(item)) | |
for i, point in enumerate(self.selected_points): | |
print("I: " + str(i)) | |
draw_px_x(cr, point[0], point[1], 10, 0, | |
0, 0) | |
cr.move_to(point[0], point[1]-15) | |
display_text(cr, str(i), 0.5, 0.5, 2, 2) | |
elif self.mode == Mode.kImporting: | |
cr.set_source_rgb(0, 0, 0) | |
cr.move_to(-300, 170) | |
display_text(cr, "VIEWING", 1, 1, 1, 1) | |
cr.set_source_rgb(0.5, 0.5, 0.5) | |
# its gonna check for points_added from button_press_action | |
# The behavior of the click is such that it runs twice | |
# This is consistant with graph_edit.py which someone smart wrote | |
# So I'm just going to delete the last element in order to not get | |
# repeating points | |
if len(self.selected_points) > 0: | |
print("SELECTED_POINTS: " + str(len(self.selected_points))) | |
print("ITEMS:") | |
for item in self.selected_points: | |
print(str(item)) | |
for i, point in enumerate(self.selected_points): | |
print("I: " + str(i)) | |
draw_px_x(cr, point[0], point[1], 10, 0, | |
0, 0) | |
cr.move_to(point[0], point[1]-15) | |
display_text(cr, str(i), 0.5, 0.5, 2, 2) | |
cr.paint_with_alpha(.65) | |
draw_px_cross(cr, self.x, self.y, 10, 1, 0, 0) | |
def do_key_press(self, event): | |
keyval = Gdk.keyval_to_lower(event.keyval) | |
print("Gdk.KEY_" + Gdk.keyval_name(keyval)) | |
if keyval == Gdk.KEY_q: | |
print("Found q key and exiting.") | |
quit_main_loop() | |
if keyval == Gdk.KEY_e: | |
self.mode = Mode.kExporting | |
# Will export to csv file | |
with open('points_for_pathedit.csv', mode='w') as points_file: | |
writer = csv.writer(points_file, delimiter=',', quotechar='"', | |
quoting=csv.QUOTE_MINIMAL) | |
for item in self.selected_points: | |
writer.writerow([str(item[0]), str(item[1])]) | |
print("Wrote: " + str(item[0]) + " " + str(item[1])) | |
if keyval == Gdk.KEY_i: | |
self.mode = Mode.kImporting | |
# import from csv file | |
self.selected_points = [] | |
with open('points_for_pathedit.csv') as points_file: | |
reader = csv.reader(points_file, delimiter=',') | |
for row in reader: | |
self.add_point(float(row[0]), float(row[1])) | |
print("Added: " + row[0] + " " + row[1]) | |
self.redraw() | |
def button_press_action(self): | |
if self.switch: | |
self.switch = False | |
if self.mode == Mode.kPlacing: | |
#Check that the point clicked is on the field | |
if(self.x<150 and self.x>-150 and self.y <150 and self.y >-150): | |
self.add_point(self.x, self.y) | |
if self.mode == Mode.kEditing: | |
# Now after index_of_edit is not -1, the point is selected, so | |
# user can click for new point | |
print("INDEX OF EDIT: " + str(self.index_of_edit)) | |
if self.index_of_edit > -1 and self.held_x != self.x: | |
print("INDEX OF EDIT: " + str(self.index_of_edit)) | |
self.selected_points[self.index_of_edit] = [self.x, self.y] | |
self.index_of_edit = -1 | |
else: | |
print("mode == 2") | |
# Get clicked point | |
# Find nearest | |
# Move nearest to clicked | |
cur_p = [self.x, self.y] | |
print("CUR_P: " + str(self.x) + " " + str(self.y)) | |
# What I wanna do is get each point | |
# Get the distance between each for x and y | |
# Save the index of the point closest | |
nearest = 1000 | |
index = 0 | |
for ind, i in enumerate(self.selected_points): | |
# pythagorean | |
distance = np.sqrt((cur_p[0] - i[0])**2 + (cur_p[1] - i[1])**2) | |
if distance < nearest: | |
nearest = distance | |
index = ind | |
print("Nearest: " + str(nearest)) | |
print("Index: " + str(index)) | |
self.index_of_edit = index | |
self.held_x = self.x | |
else: | |
self.switch = True | |
self.redraw() | |
def do_button_press(self, event): | |
print("button press activated") | |
self.x = event.x | |
self.y = event.y | |
self.button_press_action() | |
silly = GTK_Widget() | |
basic_window.RunApp() |