blob: c186711bcd1f84a5233b77ca996b0d6200479d26 [file] [log] [blame]
Tabitha Jarvis1007a132018-12-12 21:47:54 -08001#!/usr/bin/python3
Tabitha Jarvis1007a132018-12-12 21:47:54 -08002from __future__ import print_function
3import os
Andrew Runke0f945fd2019-01-27 21:10:37 -08004import sys
Andrew Runke6842bf92019-01-26 15:38:25 -08005import copy
Tabitha Jarvis1007a132018-12-12 21:47:54 -08006from color import Color, palette
7import random
8import gi
9import numpy as np
10import scipy.spatial.distance
11gi.require_version('Gtk', '3.0')
John Park91e69732019-03-03 13:12:43 -080012gi.require_version('Gdk', '3.0')
Andrew Runke6842bf92019-01-26 15:38:25 -080013from gi.repository import Gdk, Gtk, GLib
Tabitha Jarvis1007a132018-12-12 21:47:54 -080014import cairo
John Park91e69732019-03-03 13:12:43 -080015from libspline import Spline, DistanceSpline, Trajectory
Tabitha Jarvis1007a132018-12-12 21:47:54 -080016import enum
John Park91e69732019-03-03 13:12:43 -080017import json
18from basic_window import *
19from constants import *
20from drawing_constants import *
21from points import Points
22from graph import Graph
Andrew Runke6842bf92019-01-26 15:38:25 -080023
Tabitha Jarvis1007a132018-12-12 21:47:54 -080024
25class Mode(enum.Enum):
26 kViewing = 0
27 kPlacing = 1
28 kEditing = 2
29 kExporting = 3
30 kImporting = 4
Andrew Runke6842bf92019-01-26 15:38:25 -080031
32
John Park91e69732019-03-03 13:12:43 -080033class GTK_Widget(BaseWindow):
Andrew Runke6842bf92019-01-26 15:38:25 -080034 """Create a GTK+ widget on which we will draw using Cairo"""
Tabitha Jarvis1007a132018-12-12 21:47:54 -080035 def __init__(self):
36 super(GTK_Widget, self).__init__()
37
John Park91e69732019-03-03 13:12:43 -080038 self.points = Points()
39
Tabitha Jarvis1007a132018-12-12 21:47:54 -080040 # init field drawing
41 # add default spline for testing purposes
42 # init editing / viewing modes and pointer location
43 self.mode = Mode.kPlacing
44 self.x = 0
45 self.y = 0
Andrew Runke0f945fd2019-01-27 21:10:37 -080046 module_path = os.path.dirname(os.path.realpath(sys.argv[0]))
47 self.path_to_export = os.path.join(module_path,
John Park91e69732019-03-03 13:12:43 -080048 'points_for_pathedit.json')
49
Tabitha Jarvis1007a132018-12-12 21:47:54 -080050 # update list of control points
51 self.point_selected = False
52 # self.adding_spline = False
53 self.index_of_selected = -1
54 self.new_point = []
55
56 # For the editing mode
Andrew Runke6842bf92019-01-26 15:38:25 -080057 self.index_of_edit = -1 # Can't be zero beause array starts at 0
Tabitha Jarvis1007a132018-12-12 21:47:54 -080058 self.held_x = 0
Andrew Runke6842bf92019-01-26 15:38:25 -080059 self.spline_edit = -1
Tabitha Jarvis1007a132018-12-12 21:47:54 -080060
Andrew Runke6842bf92019-01-26 15:38:25 -080061 self.curves = []
62
63 self.colors = []
64
65 for c in palette:
66 self.colors.append(palette[c])
67
Tabitha Jarvis1007a132018-12-12 21:47:54 -080068 self.reinit_extents()
69
Andrew Runke6842bf92019-01-26 15:38:25 -080070 self.inStart = None
71 self.inEnd = None
Andrew Runke6842bf92019-01-26 15:38:25 -080072 self.inValue = None
73 self.startSet = False
74
John Park909c0392020-03-05 23:56:30 -080075 self.module_path = os.path.dirname(os.path.realpath(sys.argv[0]))
76
Andrew Runke6842bf92019-01-26 15:38:25 -080077 """set extents on images"""
78
Tabitha Jarvis1007a132018-12-12 21:47:54 -080079 def reinit_extents(self):
John Park91e69732019-03-03 13:12:43 -080080 self.extents_x_min = -1.0 * SCREEN_SIZE
81 self.extents_x_max = SCREEN_SIZE
82 self.extents_y_min = -1.0 * SCREEN_SIZE
83 self.extents_y_max = SCREEN_SIZE
Tabitha Jarvis1007a132018-12-12 21:47:54 -080084
85 # this needs to be rewritten with numpy, i dont think this ought to have
86 # SciPy as a dependecy
87 def get_index_of_nearest_point(self):
88 cur_p = [[self.x, self.y]]
89 distances = scipy.spatial.distance.cdist(cur_p, self.all_controls)
90
91 return np.argmin(distances)
92
93 # return the closest point to the loc of the click event
94 def get_nearest_point(self):
95 return self.all_controls[self.get_index_of_nearest_point()]
96
John Parka30a7782019-02-01 18:47:26 -080097 def draw_field_elements(self, cr):
John Parkcf545162020-02-23 20:07:25 -080098 if FIELD == 2019:
99 draw_HAB(cr)
100 draw_rockets(cr)
101 draw_cargo_ship(cr)
102 elif FIELD == 2020:
103 set_color(cr, palette["BLACK"])
104 markers(cr)
105 draw_shield_generator(cr)
106 draw_trench_run(cr)
107 draw_init_lines(cr)
108 draw_control_panel(cr)
John Parka30a7782019-02-01 18:47:26 -0800109
John Park91e69732019-03-03 13:12:43 -0800110 def draw_robot_at_point(self, cr, i, p, spline):
111 p1 = [mToPx(spline.Point(i)[0]), mToPx(spline.Point(i)[1])]
112 p2 = [mToPx(spline.Point(i + p)[0]), mToPx(spline.Point(i + p)[1])]
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800113
John Park91e69732019-03-03 13:12:43 -0800114 #Calculate Robot
115 distance = np.sqrt((p2[1] - p1[1])**2 + (p2[0] - p1[0])**2)
116 x_difference_o = p2[0] - p1[0]
117 y_difference_o = p2[1] - p1[1]
118 x_difference = x_difference_o * mToPx(LENGTH_OF_ROBOT / 2) / distance
119 y_difference = y_difference_o * mToPx(LENGTH_OF_ROBOT / 2) / distance
120
121 front_middle = []
122 front_middle.append(p1[0] + x_difference)
123 front_middle.append(p1[1] + y_difference)
124
125 back_middle = []
126 back_middle.append(p1[0] - x_difference)
127 back_middle.append(p1[1] - y_difference)
128
129 slope = [-(1 / x_difference_o) / (1 / y_difference_o)]
130 angle = np.arctan(slope)
131
132 x_difference = np.sin(angle[0]) * mToPx(WIDTH_OF_ROBOT / 2)
133 y_difference = np.cos(angle[0]) * mToPx(WIDTH_OF_ROBOT / 2)
134
135 front_1 = []
136 front_1.append(front_middle[0] - x_difference)
137 front_1.append(front_middle[1] - y_difference)
138
139 front_2 = []
140 front_2.append(front_middle[0] + x_difference)
141 front_2.append(front_middle[1] + y_difference)
142
143 back_1 = []
144 back_1.append(back_middle[0] - x_difference)
145 back_1.append(back_middle[1] - y_difference)
146
147 back_2 = []
148 back_2.append(back_middle[0] + x_difference)
149 back_2.append(back_middle[1] + y_difference)
150
151 x_difference = x_difference_o * mToPx(
152 LENGTH_OF_ROBOT / 2 + ROBOT_SIDE_TO_BALL_CENTER) / distance
153 y_difference = y_difference_o * mToPx(
154 LENGTH_OF_ROBOT / 2 + ROBOT_SIDE_TO_BALL_CENTER) / distance
155
156 #Calculate Ball
157 ball_center = []
158 ball_center.append(p1[0] + x_difference)
159 ball_center.append(p1[1] + y_difference)
160
161 x_difference = x_difference_o * mToPx(
162 LENGTH_OF_ROBOT / 2 + ROBOT_SIDE_TO_HATCH_PANEL) / distance
163 y_difference = y_difference_o * mToPx(
164 LENGTH_OF_ROBOT / 2 + ROBOT_SIDE_TO_HATCH_PANEL) / distance
165
166 #Calculate Panel
167 panel_center = []
168 panel_center.append(p1[0] + x_difference)
169 panel_center.append(p1[1] + y_difference)
170
171 x_difference = np.sin(angle[0]) * mToPx(HATCH_PANEL_WIDTH / 2)
172 y_difference = np.cos(angle[0]) * mToPx(HATCH_PANEL_WIDTH / 2)
173
174 panel_1 = []
175 panel_1.append(panel_center[0] + x_difference)
176 panel_1.append(panel_center[1] + y_difference)
177
178 panel_2 = []
179 panel_2.append(panel_center[0] - x_difference)
180 panel_2.append(panel_center[1] - y_difference)
181
182 #Draw Robot
183 cr.move_to(front_1[0], front_1[1])
184 cr.line_to(back_1[0], back_1[1])
185 cr.line_to(back_2[0], back_2[1])
186 cr.line_to(front_2[0], front_2[1])
187 cr.line_to(front_1[0], front_1[1])
188
189 cr.stroke()
190
191 #Draw Ball
192 set_color(cr, palette["ORANGE"], 0.5)
193 cr.move_to(back_middle[0], back_middle[1])
194 cr.line_to(ball_center[0], ball_center[1])
195 cr.arc(ball_center[0], ball_center[1], mToPx(BALL_RADIUS), 0,
196 2 * np.pi)
197 cr.stroke()
198
199 #Draw Panel
200 set_color(cr, palette["YELLOW"], 0.5)
201 cr.move_to(panel_1[0], panel_1[1])
202 cr.line_to(panel_2[0], panel_2[1])
203
204 cr.stroke()
205 cr.set_source_rgba(0, 0, 0, 1)
206
207 def handle_draw(self, cr): # main
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800208 # Fill the background color of the window with grey
John Park91e69732019-03-03 13:12:43 -0800209 set_color(cr, palette["WHITE"])
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800210 cr.paint()
211
212 # Draw a extents rectangle
213 set_color(cr, palette["WHITE"])
214 cr.rectangle(self.extents_x_min, self.extents_y_min,
215 (self.extents_x_max - self.extents_x_min),
216 self.extents_y_max - self.extents_y_min)
217 cr.fill()
218
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800219 cr.move_to(0, 50)
220 cr.show_text('Press "e" to export')
221 cr.show_text('Press "i" to import')
222
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800223 set_color(cr, palette["BLACK"])
John Parkcf545162020-02-23 20:07:25 -0800224 if FIELD == 2020:
225 cr.rectangle(0, mToPx(-7.991475), SCREEN_SIZE, SCREEN_SIZE/2)
226 else:
227 cr.rectangle(0, mToPx(-7.991475), SCREEN_SIZE, SCREEN_SIZE)
228 print(mToPx(-7.991475))
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800229 cr.set_line_join(cairo.LINE_JOIN_ROUND)
230 cr.stroke()
John Parka30a7782019-02-01 18:47:26 -0800231 self.draw_field_elements(cr)
Andrew Runke6842bf92019-01-26 15:38:25 -0800232 y = 0
Andrew Runke6842bf92019-01-26 15:38:25 -0800233
John Park91e69732019-03-03 13:12:43 -0800234 # update everything
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800235
John Park91e69732019-03-03 13:12:43 -0800236 if self.mode == Mode.kPlacing or self.mode == Mode.kViewing:
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800237 set_color(cr, palette["BLACK"])
John Park91e69732019-03-03 13:12:43 -0800238 cr.move_to(-SCREEN_SIZE, 170)
239 plotPoints = self.points.getPoints()
240 if plotPoints:
241 for i, point in enumerate(plotPoints):
John Park13d3e282019-01-26 20:16:48 -0800242 draw_px_x(cr, mToPx(point[0]), mToPx(point[1]), 10)
243 cr.move_to(mToPx(point[0]), mToPx(point[1]) - 15)
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800244 display_text(cr, str(i), 0.5, 0.5, 2, 2)
John Park91e69732019-03-03 13:12:43 -0800245 set_color(cr, palette["WHITE"])
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800246
247 elif self.mode == Mode.kEditing:
248 set_color(cr, palette["BLACK"])
John Park91e69732019-03-03 13:12:43 -0800249 cr.move_to(-SCREEN_SIZE, 170)
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800250 display_text(cr, "EDITING", 1, 1, 1, 1)
John Park91e69732019-03-03 13:12:43 -0800251 if self.points.getSplines():
252 self.draw_splines(cr)
253 for i, points in enumerate(self.points.getSplines()):
Andrew Runke6842bf92019-01-26 15:38:25 -0800254
John Park91e69732019-03-03 13:12:43 -0800255 p0 = np.array([mToPx(points[0][0]), mToPx(points[0][1])])
256 p1 = np.array([mToPx(points[1][0]), mToPx(points[1][1])])
257 p2 = np.array([mToPx(points[2][0]), mToPx(points[2][1])])
258 p3 = np.array([mToPx(points[3][0]), mToPx(points[3][1])])
259 p4 = np.array([mToPx(points[4][0]), mToPx(points[4][1])])
260 p5 = np.array([mToPx(points[5][0]), mToPx(points[5][1])])
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800261
John Park91e69732019-03-03 13:12:43 -0800262 draw_control_points(cr, [p0, p1, p2, p3, p4, p5])
263 first_tangent = p0 + 2.0 * (p1 - p0)
264 second_tangent = p5 + 2.0 * (p4 - p5)
265 cr.set_source_rgb(0, 0.5, 0)
266 cr.move_to(p0[0], p0[1])
267 cr.set_line_width(1.0)
268 cr.line_to(first_tangent[0], first_tangent[1])
269 cr.move_to(first_tangent[0], first_tangent[1])
270 cr.line_to(p2[0], p2[1])
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800271
John Park91e69732019-03-03 13:12:43 -0800272 cr.move_to(p5[0], p5[1])
273 cr.line_to(second_tangent[0], second_tangent[1])
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800274
John Park91e69732019-03-03 13:12:43 -0800275 cr.move_to(second_tangent[0], second_tangent[1])
276 cr.line_to(p3[0], p3[1])
277
278 cr.stroke()
279 cr.set_line_width(2.0)
280 self.points.update_lib_spline()
281 set_color(cr, palette["WHITE"])
282
283 cr.paint_with_alpha(0.2)
284
285 mygraph = Graph(cr, self.points)
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800286 draw_px_cross(cr, self.x, self.y, 10)
287
John Park91e69732019-03-03 13:12:43 -0800288 def draw_splines(self, cr):
289 holder_spline = []
290 for i, points in enumerate(self.points.getSplines()):
291 array = np.zeros(shape=(6, 2), dtype=float)
292 for j, point in enumerate(points):
293 array[j, 0] = point[0]
294 array[j, 1] = point[1]
295 spline = Spline(np.ascontiguousarray(np.transpose(array)))
296 for k in np.linspace(0.01, 1, 100):
297 cr.move_to(mToPx(spline.Point(k - 0.01)[0]),
298 mToPx(spline.Point(k - 0.01)[1]))
299 cr.line_to(mToPx(spline.Point(k)[0]),
300 mToPx(spline.Point(k)[1]))
301 cr.stroke()
302 holding = [
303 spline.Point(k - 0.01)[0],
304 spline.Point(k - 0.01)[1]
305 ]
306 holder_spline.append(holding)
307 if i == 0:
308 self.draw_robot_at_point(cr, 0.00, 0.01, spline)
309 self.draw_robot_at_point(cr, 1, 0.01, spline)
310 self.curves.append(holder_spline)
311
312 def mouse_move(self, event):
313 old_x = self.x
314 old_y = self.y
315 self.x = event.x
316 self.y = event.y
317 dif_x = event.x - old_x
318 dif_y = event.y - old_y
319 difs = np.array([pxToM(dif_x), pxToM(dif_y)])
320
321 if self.mode == Mode.kEditing:
322 self.spline_edit = self.points.updates_for_mouse_move(
323 self.index_of_edit, self.spline_edit, self.x, self.y, difs)
324
John Park909c0392020-03-05 23:56:30 -0800325 def export_json(self, file_name):
326 self.path_to_export = os.path.join(self.module_path,
327 "spline_jsons/" + file_name)
328 if file_name[-5:] != ".json":
329 print("Error: Filename doesn't end in .json")
330 else:
331 # Will export to json file
332 self.mode = Mode.kEditing
333 exportList = [l.tolist() for l in self.points.getSplines()]
334 with open(self.path_to_export, mode='w') as points_file:
335 json.dump(exportList, points_file)
336
337 def import_json(self, file_name):
338 self.path_to_export = os.path.join(self.module_path,
339 "spline_jsons/" + file_name)
340 if file_name[-5:] != ".json":
341 print("Error: Filename doesn't end in .json")
342 else:
343 # import from json file
344 self.mode = Mode.kEditing
345 self.points.resetPoints()
346 self.points.resetSplines()
347 print("LOADING LOAD FROM " + file_name) # Load takes a few seconds
348 with open(self.path_to_export) as points_file:
349 self.points.setUpSplines(json.load(points_file))
350
351 self.points.update_lib_spline()
352 print("SPLINES LOADED")
353
John Park91e69732019-03-03 13:12:43 -0800354 def do_key_press(self, event, file_name):
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800355 keyval = Gdk.keyval_to_lower(event.keyval)
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800356 if keyval == Gdk.KEY_q:
357 print("Found q key and exiting.")
358 quit_main_loop()
John Park909c0392020-03-05 23:56:30 -0800359 if keyval == Gdk.KEY_e:
360 export_json(file_name)
John Park91e69732019-03-03 13:12:43 -0800361
John Park909c0392020-03-05 23:56:30 -0800362 if keyval == Gdk.KEY_i:
363 import_json(file_name)
John Park91e69732019-03-03 13:12:43 -0800364
Andrew Runke6842bf92019-01-26 15:38:25 -0800365 if keyval == Gdk.KEY_p:
366 self.mode = Mode.kPlacing
367 # F0 = A1
368 # B1 = 2F0 - E0
369 # C1= d0 + 4F0 - 4E0
John Park91e69732019-03-03 13:12:43 -0800370 spline_index = len(self.points.getSplines()) - 1
371 self.points.resetPoints()
372 self.points.extrapolate(
373 self.points.getSplines()[len(self.points.getSplines()) - 1][5],
374 self.points.getSplines()[len(self.points.getSplines()) - 1][4],
375 self.points.getSplines()[len(self.points.getSplines()) - 1][3])
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800376
377 def button_press_action(self):
Andrew Runke6842bf92019-01-26 15:38:25 -0800378 if self.mode == Mode.kPlacing:
John Park91e69732019-03-03 13:12:43 -0800379 if self.points.add_point(self.x, self.y):
380 self.mode = Mode.kEditing
Andrew Runke6842bf92019-01-26 15:38:25 -0800381 elif self.mode == Mode.kEditing:
382 # Now after index_of_edit is not -1, the point is selected, so
383 # user can click for new point
384 if self.index_of_edit > -1 and self.held_x != self.x:
John Park91e69732019-03-03 13:12:43 -0800385 self.points.setSplines(self.spline_edit, self.index_of_edit,
386 pxToM(self.x), pxToM(self.y))
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800387
John Park91e69732019-03-03 13:12:43 -0800388 self.spline_edit = self.points.splineExtrapolate(
389 self.spline_edit)
Andrew Runke6842bf92019-01-26 15:38:25 -0800390
Andrew Runke6842bf92019-01-26 15:38:25 -0800391 self.index_of_edit = -1
392 self.spline_edit = -1
393 else:
Andrew Runke6842bf92019-01-26 15:38:25 -0800394 # Get clicked point
395 # Find nearest
396 # Move nearest to clicked
John Park13d3e282019-01-26 20:16:48 -0800397 cur_p = [pxToM(self.x), pxToM(self.y)]
Andrew Runke6842bf92019-01-26 15:38:25 -0800398 # Get the distance between each for x and y
399 # Save the index of the point closest
John Park13d3e282019-01-26 20:16:48 -0800400 nearest = 1 # Max distance away a the selected point can be in meters
Andrew Runke6842bf92019-01-26 15:38:25 -0800401 index_of_closest = 0
John Park91e69732019-03-03 13:12:43 -0800402 for index_splines, points in enumerate(self.points.getSplines()):
Andrew Runke6842bf92019-01-26 15:38:25 -0800403 for index_points, val in enumerate(points):
Andrew Runke6842bf92019-01-26 15:38:25 -0800404 distance = np.sqrt((cur_p[0] - val[0])**2 +
405 (cur_p[1] - val[1])**2)
406 if distance < nearest:
407 nearest = distance
408 index_of_closest = index_points
409 print("Nearest: " + str(nearest))
410 print("Index: " + str(index_of_closest))
411 self.index_of_edit = index_of_closest
412 self.spline_edit = index_splines
413 self.held_x = self.x
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800414
415 def do_button_press(self, event):
John Park13d3e282019-01-26 20:16:48 -0800416 # Be consistent with the scaling in the drawing_area
Andrew Runkea9c8de52019-01-26 19:54:29 -0800417 self.x = event.x * 2
418 self.y = event.y * 2
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800419 self.button_press_action()