blob: a0aa621562cce749f83e2d763294db8819748e6e [file] [log] [blame]
Tabitha Jarvis1007a132018-12-12 21:47:54 -08001#!/usr/bin/python3
2
3from __future__ import print_function
4import os
5import basic_window
6from color import Color, palette
7import random
8import gi
9import numpy as np
10import scipy.spatial.distance
11gi.require_version('Gtk', '3.0')
12from gi.repository import Gdk
13import cairo
14
15import enum
16import csv # For writing to csv files
17
18from basic_window import OverrideMatrix, identity, quit_main_loop, set_color
19
20LENGTH_OF_FIELD = 323.65
21PIXELS_ON_SCREEN = 300
22
23def pxToIn(p):
24 return p*LENGTH_OF_FIELD/PIXELS_ON_SCREEN
25
26def inToPx(i):
27 return i*PIXELS_ON_SCREEN/LENGTH_OF_FIELD
28
29def px(cr):
30 return OverrideMatrix(cr, identity)
31
32def draw_px_cross(cr, x, y, length_px, color=palette["RED"]):
33 """Draws a cross with fixed dimensions in pixel space."""
34 set_color(cr, color)
35 cr.move_to(x, y - length_px)
36 cr.line_to(x, y + length_px)
37 cr.stroke()
38
39 cr.move_to(x - length_px, y)
40 cr.line_to(x + length_px, y)
41 cr.stroke()
42 set_color(cr, palette["LIGHT_GREY"])
43
44def draw_px_x(cr, x, y, length_px1, color=palette["BLACK"]):
45 """Draws a x with fixed dimensions in pixel space."""
46 length_px = length_px1 / np.sqrt(2)
47 set_color(cr, color)
48 cr.move_to(x - length_px, y - length_px)
49 cr.line_to(x + length_px, y + length_px)
50 cr.stroke()
51
52 cr.move_to(x - length_px, y + length_px)
53 cr.line_to(x + length_px, y - length_px)
54 cr.stroke()
55 set_color(cr, palette["LIGHT_GREY"])
56
57def draw_points(cr, p, size):
58 for i in range(0, len(p)):
59 draw_px_cross(cr, p[i][0], p[i][1], size, Color(0, np.sqrt(0.2 * i), 0))
60
61class Mode(enum.Enum):
62 kViewing = 0
63 kPlacing = 1
64 kEditing = 2
65 kExporting = 3
66 kImporting = 4
67
68def display_text(cr, text, widtha, heighta, widthb, heightb):
69 cr.scale(widtha, -heighta)
70 cr.show_text(text)
71 cr.scale(widthb, -heightb)
72
73# Create a GTK+ widget on which we will draw using Cairo
74class GTK_Widget(basic_window.BaseWindow):
75 def __init__(self):
76 super(GTK_Widget, self).__init__()
77
78 # init field drawing
79 # add default spline for testing purposes
80 # init editing / viewing modes and pointer location
81 self.mode = Mode.kPlacing
82 self.x = 0
83 self.y = 0
84
85 self.switch = True
86
87 # update list of control points
88 self.point_selected = False
89 # self.adding_spline = False
90 self.index_of_selected = -1
91 self.new_point = []
92
93 # For the editing mode
94 self.index_of_edit = -1 # Can't be zero beause array starts at 0
95 self.held_x = 0
96
97 # Theo take them from here?
98 self.selected_points = []
99
100 self.reinit_extents()
101
102 #John also wrote this
103 def add_point(self, x, y):
104 if(len(self.selected_points)<4):
105 self.selected_points.append([x,y])
106
107 """set extents on images, this needs to be redone with proper distances"""
108 def reinit_extents(self):
109 self.extents_x_min = -800
110 self.extents_x_max = 800
111 self.extents_y_min = -800
112 self.extents_y_max = 800
113
114 # this needs to be rewritten with numpy, i dont think this ought to have
115 # SciPy as a dependecy
116 def get_index_of_nearest_point(self):
117 cur_p = [[self.x, self.y]]
118 distances = scipy.spatial.distance.cdist(cur_p, self.all_controls)
119
120 return np.argmin(distances)
121
122 # return the closest point to the loc of the click event
123 def get_nearest_point(self):
124 return self.all_controls[self.get_index_of_nearest_point()]
125
126 # Handle the expose-event by updating the Window and drawing
127 def handle_draw(self, cr):
128 print(self.new_point)
129 print("SELF.POINT_SELECTED: " + str(self.point_selected))
130
131 # begin drawing
132 # Fill the background color of the window with grey
133 set_color(cr, palette["GREY"])
134 cr.paint()
135
136 # Draw a extents rectangle
137 set_color(cr, palette["WHITE"])
138 cr.rectangle(self.extents_x_min, self.extents_y_min,
139 (self.extents_x_max - self.extents_x_min),
140 self.extents_y_max - self.extents_y_min)
141 cr.fill()
142
143 #Drawing the switch and scale in the field
144 cr.move_to(0, 50)
145 cr.show_text('Press "e" to export')
146 cr.show_text('Press "i" to import')
147
148 set_color(cr, Color(0.3, 0.3, 0.3))
149 cr.rectangle(-150,-150,300,300)
150 cr.fill()
151 set_color(cr, palette["BLACK"])
152 cr.rectangle(-150,-150,300,300)
153 cr.set_line_join(cairo.LINE_JOIN_ROUND)
154 cr.stroke()
155 cr.rectangle(inToPx(140-161.825),inToPx(76.575),inToPx(56),inToPx(-153.15))
156 cr.set_line_join(cairo.LINE_JOIN_ROUND)
157 cr.stroke()
158 cr.rectangle(inToPx(161.825-24),inToPx(90),inToPx(24),inToPx(-180))
159 cr.set_line_join(cairo.LINE_JOIN_ROUND)
160 cr.stroke()
161
162 set_color(cr, Color(0.2, 0.2, 0.2))
163 cr.rectangle(inToPx(140-161.825),inToPx(76.575),inToPx(56),inToPx(-153.15))
164 cr.fill()
165 cr.rectangle(inToPx(161.825-24),inToPx(90),inToPx(24),inToPx(-180))
166 cr.fill()
167
168 # update all the things
169
170 if self.mode == Mode.kViewing:
171 set_color(cr, palette["BLACK"])
172 cr.move_to(-300, 170)
173 cr.show_text("VIEWING")
174 set_color(cr, palette["GREY"])
175 # its gonna check for points_added from button_press_action
176 # The behavior of the click is such that it runs twice
177 # This is consistant with graph_edit.py which someone smart wrote
178 # So I'm just going to delete the last element in order to not get
179 # repeating points
180 if len(self.selected_points) > 0:
181 print("SELECTED_POINTS: " + str(len(self.selected_points)))
182 print("ITEMS:")
183 for item in self.selected_points:
184 print(str(item))
185 for i, point in enumerate(self.selected_points):
186 print("I: " + str(i))
187 draw_px_x(cr, point[0], point[1], 10)
188 cr.move_to(point[0], point[1]-15)
189 display_text(cr, str(i), 0.5, 0.5, 2, 2)
190
191 if self.mode == Mode.kPlacing:
192 set_color(cr, palette["BLACK"])
193 cr.move_to(-300, 170)
194 display_text(cr, "ADD", 1, 1, 1, 1)
195 set_color(cr, palette["GREY"])
196 # its gonna check for points_added from button_press_action
197 # The behavior of the click is such that it runs twice
198 # This is consistant with graph_edit.py which someone smart wrote
199 # So I'm just going to delete the last element in order to not get
200 # repeating points
201 if len(self.selected_points) > 0:
202 print("SELECTED_POINTS: " + str(len(self.selected_points)))
203 print("ITEMS:")
204 for item in self.selected_points:
205 print(str(item))
206 for i, point in enumerate(self.selected_points):
207 print("I: " + str(i))
208 draw_px_x(cr, point[0], point[1], 10)
209 cr.move_to(point[0], point[1]-15)
210 display_text(cr, str(i), 0.5, 0.5, 2, 2)
211 if(i==3):
212 self.mode = Mode.kEditing
213
214 elif self.mode == Mode.kEditing:
215 set_color(cr, palette["BLACK"])
216 cr.move_to(-300, 170)
217 display_text(cr, "EDITING", 1, 1, 1, 1)
218 set_color(cr, palette["GREY"])
219 if len(self.selected_points) > 0:
220 print("SELECTED_POINTS: " + str(len(self.selected_points)))
221 print("ITEMS:")
222 for item in self.selected_points:
223 print(str(item))
224 for i, point in enumerate(self.selected_points):
225 print("I: " + str(i))
226 draw_px_x(cr, point[0], point[1], 10)
227 cr.move_to(point[0], point[1]-15)
228 display_text(cr, str(i), 0.5, 0.5, 2, 2)
229
230 elif self.mode == Mode.kExporting:
231 set_color(cr, palette["BLACK"])
232 cr.move_to(-300, 170)
233 display_text(cr, "VIEWING", 1, 1, 1, 1)
234 set_color(cr, palette["GREY"])
235 #its gonna check for points_added from button_press_action
236
237 # The behavior of the click is such that it runs twice
238 # This is consistant with graph_edit.py which someone smart wrote
239 # So I'm just going to delete the last element in order to not get
240 # repeating points
241 if len(self.selected_points) > 0:
242 print("SELECTED_POINTS: " + str(len(self.selected_points)))
243 print("ITEMS:")
244 for item in self.selected_points:
245 print(str(item))
246 for i, point in enumerate(self.selected_points):
247 print("I: " + str(i))
248 draw_px_x(cr, point[0], point[1], 10)
249 cr.move_to(point[0], point[1]-15)
250 display_text(cr, str(i), 0.5, 0.5, 2, 2)
251 elif self.mode == Mode.kImporting:
252 set_color(cr, palette["BLACK"])
253 cr.move_to(-300, 170)
254 display_text(cr, "VIEWING", 1, 1, 1, 1)
255 set_color(cr, palette["GREY"])
256 # its gonna check for points_added from button_press_action
257
258 # The behavior of the click is such that it runs twice
259 # This is consistant with graph_edit.py which someone smart wrote
260 # So I'm just going to delete the last element in order to not get
261 # repeating points
262 if len(self.selected_points) > 0:
263 print("SELECTED_POINTS: " + str(len(self.selected_points)))
264 print("ITEMS:")
265 for item in self.selected_points:
266 print(str(item))
267 for i, point in enumerate(self.selected_points):
268 print("I: " + str(i))
269 draw_px_x(cr, point[0], point[1], 10)
270 cr.move_to(point[0], point[1]-15)
271 display_text(cr, str(i), 0.5, 0.5, 2, 2)
272
273
274 cr.paint_with_alpha(.65)
275
276 draw_px_cross(cr, self.x, self.y, 10)
277
278 def do_key_press(self, event):
279 keyval = Gdk.keyval_to_lower(event.keyval)
280 print("Gdk.KEY_" + Gdk.keyval_name(keyval))
281 if keyval == Gdk.KEY_q:
282 print("Found q key and exiting.")
283 quit_main_loop()
284 if keyval == Gdk.KEY_e:
285 self.mode = Mode.kExporting
286 # Will export to csv file
287 with open('points_for_pathedit.csv', mode='w') as points_file:
288 writer = csv.writer(points_file, delimiter=',', quotechar='"',
289 quoting=csv.QUOTE_MINIMAL)
290 for item in self.selected_points:
291 writer.writerow([str(item[0]), str(item[1])])
292 print("Wrote: " + str(item[0]) + " " + str(item[1]))
293 if keyval == Gdk.KEY_i:
294 self.mode = Mode.kImporting
295 # import from csv file
296 self.selected_points = []
297 with open('points_for_pathedit.csv') as points_file:
298 reader = csv.reader(points_file, delimiter=',')
299 for row in reader:
300 self.add_point(float(row[0]), float(row[1]))
301 print("Added: " + row[0] + " " + row[1])
302
303 self.redraw()
304
305 def button_press_action(self):
306 if self.switch:
307 self.switch = False
308 if self.mode == Mode.kPlacing:
309 #Check that the point clicked is on the field
310 if(self.x<150 and self.x>-150 and self.y <150 and self.y >-150):
311 self.add_point(self.x, self.y)
312 if self.mode == Mode.kEditing:
313 # Now after index_of_edit is not -1, the point is selected, so
314 # user can click for new point
315 print("INDEX OF EDIT: " + str(self.index_of_edit))
316
317 if self.index_of_edit > -1 and self.held_x != self.x:
318 print("INDEX OF EDIT: " + str(self.index_of_edit))
319 self.selected_points[self.index_of_edit] = [self.x, self.y]
320 self.index_of_edit = -1
321 else:
322 print("mode == 2")
323 # Get clicked point
324 # Find nearest
325 # Move nearest to clicked
326 cur_p = [self.x, self.y]
327 print("CUR_P: " + str(self.x) + " " + str(self.y))
328 # What I wanna do is get each point
329 # Get the distance between each for x and y
330 # Save the index of the point closest
331 nearest = 1000
332 index = 0
333 for ind, i in enumerate(self.selected_points):
334 # pythagorean
335 distance = np.sqrt((cur_p[0] - i[0])**2 + (cur_p[1] - i[1])**2)
336 if distance < nearest:
337 nearest = distance
338 index = ind
339 print("Nearest: " + str(nearest))
340 print("Index: " + str(index))
341 self.index_of_edit = index
342 self.held_x = self.x
343 else:
344 self.switch = True
345
346 self.redraw()
347
348
349 def do_button_press(self, event):
350 print("button press activated")
351 self.x = event.x
352 self.y = event.y
353 self.button_press_action()
354
355
356
357silly = GTK_Widget()
358basic_window.RunApp()