blob: ddc38ac497357af862fbe5665c197af13dc48c83 [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
Andrew Runke6842bf92019-01-26 15:38:25 -080075 """set extents on images"""
76
Tabitha Jarvis1007a132018-12-12 21:47:54 -080077 def reinit_extents(self):
John Park91e69732019-03-03 13:12:43 -080078 self.extents_x_min = -1.0 * SCREEN_SIZE
79 self.extents_x_max = SCREEN_SIZE
80 self.extents_y_min = -1.0 * SCREEN_SIZE
81 self.extents_y_max = SCREEN_SIZE
Tabitha Jarvis1007a132018-12-12 21:47:54 -080082
83 # this needs to be rewritten with numpy, i dont think this ought to have
84 # SciPy as a dependecy
85 def get_index_of_nearest_point(self):
86 cur_p = [[self.x, self.y]]
87 distances = scipy.spatial.distance.cdist(cur_p, self.all_controls)
88
89 return np.argmin(distances)
90
91 # return the closest point to the loc of the click event
92 def get_nearest_point(self):
93 return self.all_controls[self.get_index_of_nearest_point()]
94
John Parka30a7782019-02-01 18:47:26 -080095 def draw_field_elements(self, cr):
John Parkcf545162020-02-23 20:07:25 -080096 if FIELD == 2019:
97 draw_HAB(cr)
98 draw_rockets(cr)
99 draw_cargo_ship(cr)
100 elif FIELD == 2020:
101 set_color(cr, palette["BLACK"])
102 markers(cr)
103 draw_shield_generator(cr)
104 draw_trench_run(cr)
105 draw_init_lines(cr)
106 draw_control_panel(cr)
John Parka30a7782019-02-01 18:47:26 -0800107
John Park91e69732019-03-03 13:12:43 -0800108 def draw_robot_at_point(self, cr, i, p, spline):
109 p1 = [mToPx(spline.Point(i)[0]), mToPx(spline.Point(i)[1])]
110 p2 = [mToPx(spline.Point(i + p)[0]), mToPx(spline.Point(i + p)[1])]
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800111
John Park91e69732019-03-03 13:12:43 -0800112 #Calculate Robot
113 distance = np.sqrt((p2[1] - p1[1])**2 + (p2[0] - p1[0])**2)
114 x_difference_o = p2[0] - p1[0]
115 y_difference_o = p2[1] - p1[1]
116 x_difference = x_difference_o * mToPx(LENGTH_OF_ROBOT / 2) / distance
117 y_difference = y_difference_o * mToPx(LENGTH_OF_ROBOT / 2) / distance
118
119 front_middle = []
120 front_middle.append(p1[0] + x_difference)
121 front_middle.append(p1[1] + y_difference)
122
123 back_middle = []
124 back_middle.append(p1[0] - x_difference)
125 back_middle.append(p1[1] - y_difference)
126
127 slope = [-(1 / x_difference_o) / (1 / y_difference_o)]
128 angle = np.arctan(slope)
129
130 x_difference = np.sin(angle[0]) * mToPx(WIDTH_OF_ROBOT / 2)
131 y_difference = np.cos(angle[0]) * mToPx(WIDTH_OF_ROBOT / 2)
132
133 front_1 = []
134 front_1.append(front_middle[0] - x_difference)
135 front_1.append(front_middle[1] - y_difference)
136
137 front_2 = []
138 front_2.append(front_middle[0] + x_difference)
139 front_2.append(front_middle[1] + y_difference)
140
141 back_1 = []
142 back_1.append(back_middle[0] - x_difference)
143 back_1.append(back_middle[1] - y_difference)
144
145 back_2 = []
146 back_2.append(back_middle[0] + x_difference)
147 back_2.append(back_middle[1] + y_difference)
148
149 x_difference = x_difference_o * mToPx(
150 LENGTH_OF_ROBOT / 2 + ROBOT_SIDE_TO_BALL_CENTER) / distance
151 y_difference = y_difference_o * mToPx(
152 LENGTH_OF_ROBOT / 2 + ROBOT_SIDE_TO_BALL_CENTER) / distance
153
154 #Calculate Ball
155 ball_center = []
156 ball_center.append(p1[0] + x_difference)
157 ball_center.append(p1[1] + y_difference)
158
159 x_difference = x_difference_o * mToPx(
160 LENGTH_OF_ROBOT / 2 + ROBOT_SIDE_TO_HATCH_PANEL) / distance
161 y_difference = y_difference_o * mToPx(
162 LENGTH_OF_ROBOT / 2 + ROBOT_SIDE_TO_HATCH_PANEL) / distance
163
164 #Calculate Panel
165 panel_center = []
166 panel_center.append(p1[0] + x_difference)
167 panel_center.append(p1[1] + y_difference)
168
169 x_difference = np.sin(angle[0]) * mToPx(HATCH_PANEL_WIDTH / 2)
170 y_difference = np.cos(angle[0]) * mToPx(HATCH_PANEL_WIDTH / 2)
171
172 panel_1 = []
173 panel_1.append(panel_center[0] + x_difference)
174 panel_1.append(panel_center[1] + y_difference)
175
176 panel_2 = []
177 panel_2.append(panel_center[0] - x_difference)
178 panel_2.append(panel_center[1] - y_difference)
179
180 #Draw Robot
181 cr.move_to(front_1[0], front_1[1])
182 cr.line_to(back_1[0], back_1[1])
183 cr.line_to(back_2[0], back_2[1])
184 cr.line_to(front_2[0], front_2[1])
185 cr.line_to(front_1[0], front_1[1])
186
187 cr.stroke()
188
189 #Draw Ball
190 set_color(cr, palette["ORANGE"], 0.5)
191 cr.move_to(back_middle[0], back_middle[1])
192 cr.line_to(ball_center[0], ball_center[1])
193 cr.arc(ball_center[0], ball_center[1], mToPx(BALL_RADIUS), 0,
194 2 * np.pi)
195 cr.stroke()
196
197 #Draw Panel
198 set_color(cr, palette["YELLOW"], 0.5)
199 cr.move_to(panel_1[0], panel_1[1])
200 cr.line_to(panel_2[0], panel_2[1])
201
202 cr.stroke()
203 cr.set_source_rgba(0, 0, 0, 1)
204
205 def handle_draw(self, cr): # main
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800206 # Fill the background color of the window with grey
John Park91e69732019-03-03 13:12:43 -0800207 set_color(cr, palette["WHITE"])
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800208 cr.paint()
209
210 # Draw a extents rectangle
211 set_color(cr, palette["WHITE"])
212 cr.rectangle(self.extents_x_min, self.extents_y_min,
213 (self.extents_x_max - self.extents_x_min),
214 self.extents_y_max - self.extents_y_min)
215 cr.fill()
216
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800217 cr.move_to(0, 50)
218 cr.show_text('Press "e" to export')
219 cr.show_text('Press "i" to import')
220
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800221 set_color(cr, palette["BLACK"])
John Parkcf545162020-02-23 20:07:25 -0800222 if FIELD == 2020:
223 cr.rectangle(0, mToPx(-7.991475), SCREEN_SIZE, SCREEN_SIZE/2)
224 else:
225 cr.rectangle(0, mToPx(-7.991475), SCREEN_SIZE, SCREEN_SIZE)
226 print(mToPx(-7.991475))
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800227 cr.set_line_join(cairo.LINE_JOIN_ROUND)
228 cr.stroke()
John Parka30a7782019-02-01 18:47:26 -0800229 self.draw_field_elements(cr)
Andrew Runke6842bf92019-01-26 15:38:25 -0800230 y = 0
Andrew Runke6842bf92019-01-26 15:38:25 -0800231
John Park91e69732019-03-03 13:12:43 -0800232 # update everything
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800233
John Park91e69732019-03-03 13:12:43 -0800234 if self.mode == Mode.kPlacing or self.mode == Mode.kViewing:
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800235 set_color(cr, palette["BLACK"])
John Park91e69732019-03-03 13:12:43 -0800236 cr.move_to(-SCREEN_SIZE, 170)
237 plotPoints = self.points.getPoints()
238 if plotPoints:
239 for i, point in enumerate(plotPoints):
John Park13d3e282019-01-26 20:16:48 -0800240 draw_px_x(cr, mToPx(point[0]), mToPx(point[1]), 10)
241 cr.move_to(mToPx(point[0]), mToPx(point[1]) - 15)
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800242 display_text(cr, str(i), 0.5, 0.5, 2, 2)
John Park91e69732019-03-03 13:12:43 -0800243 set_color(cr, palette["WHITE"])
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800244
245 elif self.mode == Mode.kEditing:
246 set_color(cr, palette["BLACK"])
John Park91e69732019-03-03 13:12:43 -0800247 cr.move_to(-SCREEN_SIZE, 170)
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800248 display_text(cr, "EDITING", 1, 1, 1, 1)
John Park91e69732019-03-03 13:12:43 -0800249 if self.points.getSplines():
250 self.draw_splines(cr)
251 for i, points in enumerate(self.points.getSplines()):
Andrew Runke6842bf92019-01-26 15:38:25 -0800252
John Park91e69732019-03-03 13:12:43 -0800253 p0 = np.array([mToPx(points[0][0]), mToPx(points[0][1])])
254 p1 = np.array([mToPx(points[1][0]), mToPx(points[1][1])])
255 p2 = np.array([mToPx(points[2][0]), mToPx(points[2][1])])
256 p3 = np.array([mToPx(points[3][0]), mToPx(points[3][1])])
257 p4 = np.array([mToPx(points[4][0]), mToPx(points[4][1])])
258 p5 = np.array([mToPx(points[5][0]), mToPx(points[5][1])])
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800259
John Park91e69732019-03-03 13:12:43 -0800260 draw_control_points(cr, [p0, p1, p2, p3, p4, p5])
261 first_tangent = p0 + 2.0 * (p1 - p0)
262 second_tangent = p5 + 2.0 * (p4 - p5)
263 cr.set_source_rgb(0, 0.5, 0)
264 cr.move_to(p0[0], p0[1])
265 cr.set_line_width(1.0)
266 cr.line_to(first_tangent[0], first_tangent[1])
267 cr.move_to(first_tangent[0], first_tangent[1])
268 cr.line_to(p2[0], p2[1])
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800269
John Park91e69732019-03-03 13:12:43 -0800270 cr.move_to(p5[0], p5[1])
271 cr.line_to(second_tangent[0], second_tangent[1])
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800272
John Park91e69732019-03-03 13:12:43 -0800273 cr.move_to(second_tangent[0], second_tangent[1])
274 cr.line_to(p3[0], p3[1])
275
276 cr.stroke()
277 cr.set_line_width(2.0)
278 self.points.update_lib_spline()
279 set_color(cr, palette["WHITE"])
280
281 cr.paint_with_alpha(0.2)
282
283 mygraph = Graph(cr, self.points)
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800284 draw_px_cross(cr, self.x, self.y, 10)
285
John Park91e69732019-03-03 13:12:43 -0800286 def draw_splines(self, cr):
287 holder_spline = []
288 for i, points in enumerate(self.points.getSplines()):
289 array = np.zeros(shape=(6, 2), dtype=float)
290 for j, point in enumerate(points):
291 array[j, 0] = point[0]
292 array[j, 1] = point[1]
293 spline = Spline(np.ascontiguousarray(np.transpose(array)))
294 for k in np.linspace(0.01, 1, 100):
295 cr.move_to(mToPx(spline.Point(k - 0.01)[0]),
296 mToPx(spline.Point(k - 0.01)[1]))
297 cr.line_to(mToPx(spline.Point(k)[0]),
298 mToPx(spline.Point(k)[1]))
299 cr.stroke()
300 holding = [
301 spline.Point(k - 0.01)[0],
302 spline.Point(k - 0.01)[1]
303 ]
304 holder_spline.append(holding)
305 if i == 0:
306 self.draw_robot_at_point(cr, 0.00, 0.01, spline)
307 self.draw_robot_at_point(cr, 1, 0.01, spline)
308 self.curves.append(holder_spline)
309
310 def mouse_move(self, event):
311 old_x = self.x
312 old_y = self.y
313 self.x = event.x
314 self.y = event.y
315 dif_x = event.x - old_x
316 dif_y = event.y - old_y
317 difs = np.array([pxToM(dif_x), pxToM(dif_y)])
318
319 if self.mode == Mode.kEditing:
320 self.spline_edit = self.points.updates_for_mouse_move(
321 self.index_of_edit, self.spline_edit, self.x, self.y, difs)
322
323 def do_key_press(self, event, file_name):
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800324 keyval = Gdk.keyval_to_lower(event.keyval)
John Park91e69732019-03-03 13:12:43 -0800325 module_path = os.path.dirname(os.path.realpath(sys.argv[0]))
326 self.path_to_export = os.path.join(module_path,
327 "spline_jsons/" + file_name)
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800328 if keyval == Gdk.KEY_q:
329 print("Found q key and exiting.")
330 quit_main_loop()
John Park91e69732019-03-03 13:12:43 -0800331 file_name_end = file_name[-5:]
332 if file_name_end != ".json":
333 print("Error: Filename doesn't end in .json")
334 else:
335 if keyval == Gdk.KEY_e:
336 # Will export to json file
337 self.mode = Mode.kEditing
338 print('out to: ', self.path_to_export)
339 exportList = [l.tolist() for l in self.points.getSplines()]
340 with open(self.path_to_export, mode='w') as points_file:
341 json.dump(exportList, points_file)
342
343 if keyval == Gdk.KEY_i:
344 # import from json file
345 self.mode = Mode.kEditing
346 self.points.resetPoints()
347 self.points.resetSplines()
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
Andrew Runke6842bf92019-01-26 15:38:25 -0800353 if keyval == Gdk.KEY_p:
354 self.mode = Mode.kPlacing
355 # F0 = A1
356 # B1 = 2F0 - E0
357 # C1= d0 + 4F0 - 4E0
John Park91e69732019-03-03 13:12:43 -0800358 spline_index = len(self.points.getSplines()) - 1
359 self.points.resetPoints()
360 self.points.extrapolate(
361 self.points.getSplines()[len(self.points.getSplines()) - 1][5],
362 self.points.getSplines()[len(self.points.getSplines()) - 1][4],
363 self.points.getSplines()[len(self.points.getSplines()) - 1][3])
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800364
365 def button_press_action(self):
Andrew Runke6842bf92019-01-26 15:38:25 -0800366 if self.mode == Mode.kPlacing:
John Park91e69732019-03-03 13:12:43 -0800367 if self.points.add_point(self.x, self.y):
368 self.mode = Mode.kEditing
Andrew Runke6842bf92019-01-26 15:38:25 -0800369 elif self.mode == Mode.kEditing:
370 # Now after index_of_edit is not -1, the point is selected, so
371 # user can click for new point
372 if self.index_of_edit > -1 and self.held_x != self.x:
John Park91e69732019-03-03 13:12:43 -0800373 self.points.setSplines(self.spline_edit, self.index_of_edit,
374 pxToM(self.x), pxToM(self.y))
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800375
John Park91e69732019-03-03 13:12:43 -0800376 self.spline_edit = self.points.splineExtrapolate(
377 self.spline_edit)
Andrew Runke6842bf92019-01-26 15:38:25 -0800378
Andrew Runke6842bf92019-01-26 15:38:25 -0800379 self.index_of_edit = -1
380 self.spline_edit = -1
381 else:
Andrew Runke6842bf92019-01-26 15:38:25 -0800382 # Get clicked point
383 # Find nearest
384 # Move nearest to clicked
John Park13d3e282019-01-26 20:16:48 -0800385 cur_p = [pxToM(self.x), pxToM(self.y)]
Andrew Runke6842bf92019-01-26 15:38:25 -0800386 # Get the distance between each for x and y
387 # Save the index of the point closest
John Park13d3e282019-01-26 20:16:48 -0800388 nearest = 1 # Max distance away a the selected point can be in meters
Andrew Runke6842bf92019-01-26 15:38:25 -0800389 index_of_closest = 0
John Park91e69732019-03-03 13:12:43 -0800390 for index_splines, points in enumerate(self.points.getSplines()):
Andrew Runke6842bf92019-01-26 15:38:25 -0800391 for index_points, val in enumerate(points):
Andrew Runke6842bf92019-01-26 15:38:25 -0800392 distance = np.sqrt((cur_p[0] - val[0])**2 +
393 (cur_p[1] - val[1])**2)
394 if distance < nearest:
395 nearest = distance
396 index_of_closest = index_points
397 print("Nearest: " + str(nearest))
398 print("Index: " + str(index_of_closest))
399 self.index_of_edit = index_of_closest
400 self.spline_edit = index_splines
401 self.held_x = self.x
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800402
403 def do_button_press(self, event):
John Park13d3e282019-01-26 20:16:48 -0800404 # Be consistent with the scaling in the drawing_area
Andrew Runkea9c8de52019-01-26 19:54:29 -0800405 self.x = event.x * 2
406 self.y = event.y * 2
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800407 self.button_press_action()