blob: 35a670c5c1ff066fcf60421073d9d7f009b495d1 [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
Ravago Jones6d460fe2021-07-03 16:59:55 -07005from color import palette
6from graph import Graph
Tabitha Jarvis1007a132018-12-12 21:47:54 -08007import gi
8import numpy as np
Tabitha Jarvis1007a132018-12-12 21:47:54 -08009gi.require_version('Gtk', '3.0')
Andrew Runke6842bf92019-01-26 15:38:25 -080010from gi.repository import Gdk, Gtk, GLib
Tabitha Jarvis1007a132018-12-12 21:47:54 -080011import cairo
Ravago Jones6d460fe2021-07-03 16:59:55 -070012from libspline import Spline
Tabitha Jarvis1007a132018-12-12 21:47:54 -080013import enum
John Park91e69732019-03-03 13:12:43 -080014import json
Ravago Jones797c49c2021-07-31 14:51:59 -070015from constants import FIELD
16from constants import get_json_folder
17from constants import ROBOT_SIDE_TO_BALL_CENTER, ROBOT_SIDE_TO_HATCH_PANEL, HATCH_PANEL_WIDTH, BALL_RADIUS
Ravago Jones6d460fe2021-07-03 16:59:55 -070018from drawing_constants import set_color, draw_px_cross, draw_px_x, display_text, draw_control_points
John Park91e69732019-03-03 13:12:43 -080019from points import Points
Ravago Jones6d460fe2021-07-03 16:59:55 -070020import time
Andrew Runke6842bf92019-01-26 15:38:25 -080021
Tabitha Jarvis1007a132018-12-12 21:47:54 -080022
23class Mode(enum.Enum):
24 kViewing = 0
25 kPlacing = 1
26 kEditing = 2
Andrew Runke6842bf92019-01-26 15:38:25 -080027
28
Ravago Jones6d460fe2021-07-03 16:59:55 -070029class FieldWidget(Gtk.DrawingArea):
Andrew Runke6842bf92019-01-26 15:38:25 -080030 """Create a GTK+ widget on which we will draw using Cairo"""
Ravago Jones26f7ad02021-02-05 15:45:59 -080031
Tabitha Jarvis1007a132018-12-12 21:47:54 -080032 def __init__(self):
Ravago Jones6d460fe2021-07-03 16:59:55 -070033 super(FieldWidget, self).__init__()
Ravago Jones086a8872021-08-07 15:49:40 -070034 self.set_field(FIELD)
Ravago Jones797c49c2021-07-31 14:51:59 -070035 self.set_size_request(
Ravago Jones086a8872021-08-07 15:49:40 -070036 self.mToPx(self.field.width), self.mToPx(self.field.length))
Tabitha Jarvis1007a132018-12-12 21:47:54 -080037
John Park91e69732019-03-03 13:12:43 -080038 self.points = Points()
Ravago Jones6d460fe2021-07-03 16:59:55 -070039 self.graph = Graph()
40 self.set_vexpand(True)
41 self.set_hexpand(True)
Ryan Yind8be3882021-10-13 20:59:41 -070042 # list of multisplines
43 self.multispline_stack = []
Tabitha Jarvis1007a132018-12-12 21:47:54 -080044 # init field drawing
45 # add default spline for testing purposes
46 # init editing / viewing modes and pointer location
47 self.mode = Mode.kPlacing
Ravago Jones6d460fe2021-07-03 16:59:55 -070048 self.mousex = 0
49 self.mousey = 0
50 self.module_path = os.path.dirname(os.path.realpath(sys.argv[0]))
51 self.path_to_export = os.path.join(self.module_path,
John Park91e69732019-03-03 13:12:43 -080052 'points_for_pathedit.json')
53
Tabitha Jarvis1007a132018-12-12 21:47:54 -080054 # For the editing mode
Andrew Runke6842bf92019-01-26 15:38:25 -080055 self.index_of_edit = -1 # Can't be zero beause array starts at 0
Tabitha Jarvis1007a132018-12-12 21:47:54 -080056 self.held_x = 0
Andrew Runke6842bf92019-01-26 15:38:25 -080057 self.spline_edit = -1
Tabitha Jarvis1007a132018-12-12 21:47:54 -080058
Ravago Jones54dafeb2022-03-02 20:41:47 -080059 self.zoom_transform = cairo.Matrix()
Ravago Jones797c49c2021-07-31 14:51:59 -070060
Ravago Jones76ecec82021-08-07 14:37:08 -070061 self.set_events(Gdk.EventMask.BUTTON_PRESS_MASK
62 | Gdk.EventMask.BUTTON_PRESS_MASK
63 | Gdk.EventMask.BUTTON_RELEASE_MASK
64 | Gdk.EventMask.POINTER_MOTION_MASK
65 | Gdk.EventMask.SCROLL_MASK)
66
Ravago Jones086a8872021-08-07 15:49:40 -070067 def set_field(self, field):
68 self.field = field
Ravago Jonesc26b9162021-06-30 20:12:48 -070069 try:
Ravago Jones6d460fe2021-07-03 16:59:55 -070070 self.field_png = cairo.ImageSurface.create_from_png(
Ravago Jones086a8872021-08-07 15:49:40 -070071 "frc971/control_loops/python/field_images/" +
72 self.field.field_id + ".png")
Ravago Jonesc26b9162021-06-30 20:12:48 -070073 except cairo.Error:
74 self.field_png = None
Ravago Jones54dafeb2022-03-02 20:41:47 -080075
Ravago Jones086a8872021-08-07 15:49:40 -070076 self.queue_draw()
Ravago Jonesc26b9162021-06-30 20:12:48 -070077
Ravago Jones54dafeb2022-03-02 20:41:47 -080078 def invert(self, transform):
79 xx, yx, xy, yy, x0, y0 = transform
80 matrix = cairo.Matrix(xx, yx, xy, yy, x0, y0)
81 matrix.invert()
82 return matrix
83
Ravago Jones797c49c2021-07-31 14:51:59 -070084 # returns the transform from widget space to field space
85 @property
86 def input_transform(self):
Ravago Jones797c49c2021-07-31 14:51:59 -070087 # the transform for input needs to be the opposite of the transform for drawing
Ravago Jones54dafeb2022-03-02 20:41:47 -080088 return self.invert(self.field_transform.multiply(self.zoom_transform))
89
90 @property
91 def field_transform(self):
92 field_transform = cairo.Matrix()
93 field_transform.scale(1, -1) # flipped y-axis
94 field_transform.scale(1 / self.pxToM_scale(), 1 / self.pxToM_scale())
95 field_transform.translate(self.field.width / 2, -1 * self.field.length / 2)
96 return field_transform
Ravago Jones797c49c2021-07-31 14:51:59 -070097
98 # returns the scale from pixels in field space to meters in field space
99 def pxToM_scale(self):
100 available_space = self.get_allocation()
Ravago Jones086a8872021-08-07 15:49:40 -0700101 return np.maximum(self.field.width / available_space.width,
102 self.field.length / available_space.height)
Ravago Jones797c49c2021-07-31 14:51:59 -0700103
104 def pxToM(self, p):
105 return p * self.pxToM_scale()
106
107 def mToPx(self, m):
108 return m / self.pxToM_scale()
109
John Park91e69732019-03-03 13:12:43 -0800110 def draw_robot_at_point(self, cr, i, p, spline):
Ravago Jones54dafeb2022-03-02 20:41:47 -0800111 p1 = [spline.Point(i)[0], spline.Point(i)[1]]
Ravago Jones797c49c2021-07-31 14:51:59 -0700112 p2 = [
Ravago Jones54dafeb2022-03-02 20:41:47 -0800113 spline.Point(i + p)[0],
114 spline.Point(i + p)[1]
Ravago Jones797c49c2021-07-31 14:51:59 -0700115 ]
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800116
John Park91e69732019-03-03 13:12:43 -0800117 #Calculate Robot
118 distance = np.sqrt((p2[1] - p1[1])**2 + (p2[0] - p1[0])**2)
119 x_difference_o = p2[0] - p1[0]
120 y_difference_o = p2[1] - p1[1]
Ravago Jones54dafeb2022-03-02 20:41:47 -0800121 x_difference = x_difference_o * (
Ravago Jones086a8872021-08-07 15:49:40 -0700122 self.field.robot.length / 2) / distance
Ravago Jones54dafeb2022-03-02 20:41:47 -0800123 y_difference = y_difference_o * (
Ravago Jones086a8872021-08-07 15:49:40 -0700124 self.field.robot.length / 2) / distance
John Park91e69732019-03-03 13:12:43 -0800125
126 front_middle = []
127 front_middle.append(p1[0] + x_difference)
128 front_middle.append(p1[1] + y_difference)
129
130 back_middle = []
131 back_middle.append(p1[0] - x_difference)
132 back_middle.append(p1[1] - y_difference)
133
134 slope = [-(1 / x_difference_o) / (1 / y_difference_o)]
135 angle = np.arctan(slope)
136
Ravago Jones54dafeb2022-03-02 20:41:47 -0800137 x_difference = np.sin(angle[0]) * (
Ravago Jones086a8872021-08-07 15:49:40 -0700138 self.field.robot.width / 2)
Ravago Jones54dafeb2022-03-02 20:41:47 -0800139 y_difference = np.cos(angle[0]) * (
Ravago Jones086a8872021-08-07 15:49:40 -0700140 self.field.robot.width / 2)
John Park91e69732019-03-03 13:12:43 -0800141
142 front_1 = []
143 front_1.append(front_middle[0] - x_difference)
144 front_1.append(front_middle[1] - y_difference)
145
146 front_2 = []
147 front_2.append(front_middle[0] + x_difference)
148 front_2.append(front_middle[1] + y_difference)
149
150 back_1 = []
151 back_1.append(back_middle[0] - x_difference)
152 back_1.append(back_middle[1] - y_difference)
153
154 back_2 = []
155 back_2.append(back_middle[0] + x_difference)
156 back_2.append(back_middle[1] + y_difference)
157
Ravago Jones54dafeb2022-03-02 20:41:47 -0800158 x_difference = x_difference_o * (
Ravago Jones086a8872021-08-07 15:49:40 -0700159 self.field.robot.length / 2 + ROBOT_SIDE_TO_BALL_CENTER) / distance
Ravago Jones54dafeb2022-03-02 20:41:47 -0800160 y_difference = y_difference_o * (
Ravago Jones086a8872021-08-07 15:49:40 -0700161 self.field.robot.length / 2 + ROBOT_SIDE_TO_BALL_CENTER) / distance
John Park91e69732019-03-03 13:12:43 -0800162
163 #Calculate Ball
164 ball_center = []
165 ball_center.append(p1[0] + x_difference)
166 ball_center.append(p1[1] + y_difference)
167
Ravago Jones54dafeb2022-03-02 20:41:47 -0800168 x_difference = x_difference_o * (
Ravago Jones086a8872021-08-07 15:49:40 -0700169 self.field.robot.length / 2 + ROBOT_SIDE_TO_HATCH_PANEL) / distance
Ravago Jones54dafeb2022-03-02 20:41:47 -0800170 y_difference = y_difference_o * (
Ravago Jones086a8872021-08-07 15:49:40 -0700171 self.field.robot.length / 2 + ROBOT_SIDE_TO_HATCH_PANEL) / distance
John Park91e69732019-03-03 13:12:43 -0800172
173 #Calculate Panel
174 panel_center = []
175 panel_center.append(p1[0] + x_difference)
176 panel_center.append(p1[1] + y_difference)
177
Ravago Jones54dafeb2022-03-02 20:41:47 -0800178 x_difference = np.sin(angle[0]) * (HATCH_PANEL_WIDTH / 2)
179 y_difference = np.cos(angle[0]) * (HATCH_PANEL_WIDTH / 2)
John Park91e69732019-03-03 13:12:43 -0800180
181 panel_1 = []
182 panel_1.append(panel_center[0] + x_difference)
183 panel_1.append(panel_center[1] + y_difference)
184
185 panel_2 = []
186 panel_2.append(panel_center[0] - x_difference)
187 panel_2.append(panel_center[1] - y_difference)
188
189 #Draw Robot
190 cr.move_to(front_1[0], front_1[1])
191 cr.line_to(back_1[0], back_1[1])
192 cr.line_to(back_2[0], back_2[1])
193 cr.line_to(front_2[0], front_2[1])
194 cr.line_to(front_1[0], front_1[1])
195
196 cr.stroke()
197
198 #Draw Ball
199 set_color(cr, palette["ORANGE"], 0.5)
200 cr.move_to(back_middle[0], back_middle[1])
201 cr.line_to(ball_center[0], ball_center[1])
Ravago Jones54dafeb2022-03-02 20:41:47 -0800202 cr.arc(ball_center[0], ball_center[1], BALL_RADIUS, 0,
John Park91e69732019-03-03 13:12:43 -0800203 2 * np.pi)
204 cr.stroke()
205
206 #Draw Panel
207 set_color(cr, palette["YELLOW"], 0.5)
208 cr.move_to(panel_1[0], panel_1[1])
209 cr.line_to(panel_2[0], panel_2[1])
210
211 cr.stroke()
212 cr.set_source_rgba(0, 0, 0, 1)
213
Ravago Jones6d460fe2021-07-03 16:59:55 -0700214 def do_draw(self, cr): # main
Ravago Jones54dafeb2022-03-02 20:41:47 -0800215 cr.set_matrix(self.field_transform.multiply(self.zoom_transform).multiply(cr.get_matrix()))
Ravago Jones797c49c2021-07-31 14:51:59 -0700216
James Kuszmaul1c933e02020-03-07 16:17:51 -0800217 cr.save()
Ravago Jones797c49c2021-07-31 14:51:59 -0700218
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800219 set_color(cr, palette["BLACK"])
Ravago Jones5f787df2021-01-23 16:26:27 -0800220
Ravago Jones54dafeb2022-03-02 20:41:47 -0800221 cr.set_line_width(self.pxToM(2))
222 cr.rectangle(-0.5 * self.field.width, -0.5 * self.field.length, self.field.width,
223 self.field.length)
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800224 cr.set_line_join(cairo.LINE_JOIN_ROUND)
225 cr.stroke()
Ravago Jones5f787df2021-01-23 16:26:27 -0800226
Ravago Jonesc26b9162021-06-30 20:12:48 -0700227 if self.field_png:
228 cr.save()
Ravago Jones54dafeb2022-03-02 20:41:47 -0800229 cr.translate(-0.5 * self.field.width, 0.5 * self.field.length)
Ravago Jonesc26b9162021-06-30 20:12:48 -0700230 cr.scale(
Ravago Jones54dafeb2022-03-02 20:41:47 -0800231 self.field.width / self.field_png.get_width(),
232 -self.field.length / self.field_png.get_height(),
Ravago Jones6d460fe2021-07-03 16:59:55 -0700233 )
Ravago Jonesc26b9162021-06-30 20:12:48 -0700234 cr.set_source_surface(self.field_png)
235 cr.paint()
236 cr.restore()
Andrew Runke6842bf92019-01-26 15:38:25 -0800237
John Park91e69732019-03-03 13:12:43 -0800238 # update everything
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800239
Ravago Jones54dafeb2022-03-02 20:41:47 -0800240 cr.set_line_width(self.pxToM(2))
John Park91e69732019-03-03 13:12:43 -0800241 if self.mode == Mode.kPlacing or self.mode == Mode.kViewing:
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800242 set_color(cr, palette["BLACK"])
Ravago Jones36c92f02021-07-24 16:35:33 -0700243 for i, point in enumerate(self.points.getPoints()):
Ravago Jones54dafeb2022-03-02 20:41:47 -0800244 draw_px_x(cr, point[0], point[1], self.pxToM(5))
John Park91e69732019-03-03 13:12:43 -0800245 set_color(cr, palette["WHITE"])
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800246 elif self.mode == Mode.kEditing:
247 set_color(cr, palette["BLACK"])
John Park91e69732019-03-03 13:12:43 -0800248 if self.points.getSplines():
249 self.draw_splines(cr)
250 for i, points in enumerate(self.points.getSplines()):
Ravago Jones36c92f02021-07-24 16:35:33 -0700251 points = [
Ravago Jones54dafeb2022-03-02 20:41:47 -0800252 np.array([x, y])
Ravago Jones36c92f02021-07-24 16:35:33 -0700253 for (x, y) in points
254 ]
Ravago Jones54dafeb2022-03-02 20:41:47 -0800255 draw_control_points(cr, points, width=self.pxToM(10), radius=self.pxToM(4))
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800256
Ravago Jones36c92f02021-07-24 16:35:33 -0700257 p0, p1, p2, p3, p4, p5 = points
John Park91e69732019-03-03 13:12:43 -0800258 first_tangent = p0 + 2.0 * (p1 - p0)
259 second_tangent = p5 + 2.0 * (p4 - p5)
260 cr.set_source_rgb(0, 0.5, 0)
261 cr.move_to(p0[0], p0[1])
Ravago Jones54dafeb2022-03-02 20:41:47 -0800262 cr.set_line_width(self.pxToM(1.0))
John Park91e69732019-03-03 13:12:43 -0800263 cr.line_to(first_tangent[0], first_tangent[1])
264 cr.move_to(first_tangent[0], first_tangent[1])
265 cr.line_to(p2[0], p2[1])
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800266
John Park91e69732019-03-03 13:12:43 -0800267 cr.move_to(p5[0], p5[1])
268 cr.line_to(second_tangent[0], second_tangent[1])
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800269
John Park91e69732019-03-03 13:12:43 -0800270 cr.move_to(second_tangent[0], second_tangent[1])
271 cr.line_to(p3[0], p3[1])
272
273 cr.stroke()
Ravago Jones54dafeb2022-03-02 20:41:47 -0800274 cr.set_line_width(self.pxToM(2))
John Park91e69732019-03-03 13:12:43 -0800275 set_color(cr, palette["WHITE"])
276
277 cr.paint_with_alpha(0.2)
278
Ravago Jones54dafeb2022-03-02 20:41:47 -0800279 draw_px_cross(cr, self.mousex, self.mousey, self.pxToM(8))
James Kuszmaul1c933e02020-03-07 16:17:51 -0800280 cr.restore()
Ravago Jones6d460fe2021-07-03 16:59:55 -0700281
John Park91e69732019-03-03 13:12:43 -0800282 def draw_splines(self, cr):
Ravago Jones76ecec82021-08-07 14:37:08 -0700283 for i, spline in enumerate(self.points.getLibsplines()):
Ravago Jones54dafeb2022-03-02 20:41:47 -0800284 for k in np.linspace(0.02, 1, 200):
Ravago Jones26f7ad02021-02-05 15:45:59 -0800285 cr.move_to(
Ravago Jones54dafeb2022-03-02 20:41:47 -0800286 spline.Point(k - 0.008)[0],
287 spline.Point(k - 0.008)[1])
Ravago Jones26f7ad02021-02-05 15:45:59 -0800288 cr.line_to(
Ravago Jones54dafeb2022-03-02 20:41:47 -0800289 spline.Point(k)[0],
290 spline.Point(k)[1])
John Park91e69732019-03-03 13:12:43 -0800291 cr.stroke()
John Park91e69732019-03-03 13:12:43 -0800292 if i == 0:
Ravago Jones54dafeb2022-03-02 20:41:47 -0800293 self.draw_robot_at_point(cr, 0, 0.008, spline)
294 self.draw_robot_at_point(cr, 1, 0.008, spline)
John Park91e69732019-03-03 13:12:43 -0800295
John Park909c0392020-03-05 23:56:30 -0800296 def export_json(self, file_name):
Ravago Jones09f59722021-03-03 21:11:41 -0800297 self.path_to_export = os.path.join(
298 self.module_path, # position of the python
299 "../../..", # root of the repository
Ravago Jones086a8872021-08-07 15:49:40 -0700300 get_json_folder(self.field), # path from the root
Ravago Jones09f59722021-03-03 21:11:41 -0800301 file_name # selected file
302 )
Ravago Jones3b92afa2021-02-05 14:27:32 -0800303
Ravago Jones6d460fe2021-07-03 16:59:55 -0700304 # Will export to json file
305 multi_spline = self.points.toMultiSpline()
306 print(multi_spline)
307 with open(self.path_to_export, mode='w') as points_file:
308 json.dump(multi_spline, points_file)
John Park909c0392020-03-05 23:56:30 -0800309
310 def import_json(self, file_name):
Ravago Jones09f59722021-03-03 21:11:41 -0800311 self.path_to_export = os.path.join(
312 self.module_path, # position of the python
313 "../../..", # root of the repository
Ravago Jones086a8872021-08-07 15:49:40 -0700314 get_json_folder(self.field), # path from the root
Ravago Jones09f59722021-03-03 21:11:41 -0800315 file_name # selected file
316 )
317
Ravago Jones6d460fe2021-07-03 16:59:55 -0700318 # import from json file
319 print("LOADING LOAD FROM " + file_name) # Load takes a few seconds
320 with open(self.path_to_export) as points_file:
321 multi_spline = json.load(points_file)
John Park909c0392020-03-05 23:56:30 -0800322
Ravago Jones6d460fe2021-07-03 16:59:55 -0700323 # if people messed with the spline json,
324 # it might not be the right length
325 # so give them a nice error message
326 try: # try to salvage as many segments of the spline as possible
327 self.points.fromMultiSpline(multi_spline)
328 except IndexError:
329 # check if they're both 6+5*(k-1) long
330 expected_length = 6 + 5 * (multi_spline["spline_count"] - 1)
331 x_len = len(multi_spline["spline_x"])
332 y_len = len(multi_spline["spline_x"])
333 if x_len is not expected_length:
334 print(
335 "Error: spline x values were not the expected length; expected {} got {}"
336 .format(expected_length, x_len))
337 elif y_len is not expected_length:
338 print(
339 "Error: spline y values were not the expected length; expected {} got {}"
340 .format(expected_length, y_len))
Ravago Jones3b92afa2021-02-05 14:27:32 -0800341
Ravago Jones6d460fe2021-07-03 16:59:55 -0700342 print("SPLINES LOADED")
343 self.mode = Mode.kEditing
Ravago Jones76ecec82021-08-07 14:37:08 -0700344 self.queue_draw()
345 self.graph.schedule_recalculate(self.points)
John Park909c0392020-03-05 23:56:30 -0800346
Ryan Yind8be3882021-10-13 20:59:41 -0700347 def attempt_append_multispline(self):
348 if (len(self.multispline_stack) == 0 or
349 self.points.toMultiSpline() != self.multispline_stack[-1]):
350 self.multispline_stack.append(self.points.toMultiSpline())
351
352 def clear_graph(self, should_attempt_append=True):
353 if should_attempt_append:
354 self.attempt_append_multispline()
Ryan Yin85f861f2021-09-16 17:55:11 -0700355 self.points = Points()
356 #recalulate graph using new points
357 self.graph.axis.clear()
358 self.graph.queue_draw()
359 #allow placing again
360 self.mode = Mode.kPlacing
361 #redraw entire graph
362 self.queue_draw()
Ryan Yind8be3882021-10-13 20:59:41 -0700363
364
365 def undo(self):
366 try:
367 self.multispline_stack.pop()
368 except IndexError:
369 return
370 if len(self.multispline_stack) == 0:
371 self.clear_graph(should_attempt_append=False) #clear, don't do anything
372 return
373 multispline = self.multispline_stack[-1]
374 if multispline['spline_count'] > 0:
375 self.points.fromMultiSpline(multispline)
376 self.mode= Mode.kEditing
377 else:
378 self.mode = Mode.kPlacing
379 self.clear_graph(should_attempt_append=False)
380 self.queue_draw()
381
382
Ryan Yin85f861f2021-09-16 17:55:11 -0700383
Ravago Jones76ecec82021-08-07 14:37:08 -0700384 def do_key_press_event(self, event):
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800385 keyval = Gdk.keyval_to_lower(event.keyval)
Ryan Yind8be3882021-10-13 20:59:41 -0700386 if keyval == Gdk.KEY_z and event.state & Gdk.ModifierType.CONTROL_MASK:
387 self.undo()
Ravago Jones6d460fe2021-07-03 16:59:55 -0700388 # TODO: This should be a button
Andrew Runke6842bf92019-01-26 15:38:25 -0800389 if keyval == Gdk.KEY_p:
390 self.mode = Mode.kPlacing
391 # F0 = A1
392 # B1 = 2F0 - E0
393 # C1= d0 + 4F0 - 4E0
John Park91e69732019-03-03 13:12:43 -0800394 spline_index = len(self.points.getSplines()) - 1
395 self.points.resetPoints()
396 self.points.extrapolate(
397 self.points.getSplines()[len(self.points.getSplines()) - 1][5],
398 self.points.getSplines()[len(self.points.getSplines()) - 1][4],
399 self.points.getSplines()[len(self.points.getSplines()) - 1][3])
Ravago Jones128fb992021-07-31 13:56:58 -0700400 self.queue_draw()
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800401
Ravago Jones76ecec82021-08-07 14:37:08 -0700402 def do_button_release_event(self, event):
Ryan Yind8be3882021-10-13 20:59:41 -0700403 self.attempt_append_multispline()
Ravago Jones76ecec82021-08-07 14:37:08 -0700404 self.mousex, self.mousey = self.input_transform.transform_point(
405 event.x, event.y)
406 if self.mode == Mode.kEditing:
407 if self.index_of_edit > -1 and self.held_x != self.mousex:
Ravago Jones76ecec82021-08-07 14:37:08 -0700408 self.points.setSplines(self.spline_edit, self.index_of_edit,
Ravago Jones54dafeb2022-03-02 20:41:47 -0800409 self.mousex,
410 self.mousey)
Ravago Jones76ecec82021-08-07 14:37:08 -0700411
412 self.points.splineExtrapolate(self.spline_edit)
413
414 self.points.update_lib_spline()
415 self.graph.schedule_recalculate(self.points)
416
417 self.index_of_edit = -1
418 self.spline_edit = -1
419
420 def do_button_press_event(self, event):
Ravago Jones797c49c2021-07-31 14:51:59 -0700421 self.mousex, self.mousey = self.input_transform.transform_point(
422 event.x, event.y)
Ravago Jones6d460fe2021-07-03 16:59:55 -0700423
Andrew Runke6842bf92019-01-26 15:38:25 -0800424 if self.mode == Mode.kPlacing:
Ravago Jones36c92f02021-07-24 16:35:33 -0700425 if self.points.add_point(
Ravago Jones54dafeb2022-03-02 20:41:47 -0800426 self.mousex, self.mousey):
John Park91e69732019-03-03 13:12:43 -0800427 self.mode = Mode.kEditing
Ravago Jones76ecec82021-08-07 14:37:08 -0700428 self.graph.schedule_recalculate(self.points)
Andrew Runke6842bf92019-01-26 15:38:25 -0800429 elif self.mode == Mode.kEditing:
430 # Now after index_of_edit is not -1, the point is selected, so
431 # user can click for new point
Ravago Jones128fb992021-07-31 13:56:58 -0700432 if self.index_of_edit == -1:
Andrew Runke6842bf92019-01-26 15:38:25 -0800433 # Get clicked point
434 # Find nearest
435 # Move nearest to clicked
Ravago Jones54dafeb2022-03-02 20:41:47 -0800436 cur_p = [self.mousex, self.mousey]
Andrew Runke6842bf92019-01-26 15:38:25 -0800437 # Get the distance between each for x and y
438 # Save the index of the point closest
John Park13d3e282019-01-26 20:16:48 -0800439 nearest = 1 # Max distance away a the selected point can be in meters
Andrew Runke6842bf92019-01-26 15:38:25 -0800440 index_of_closest = 0
James Kuszmaul1c933e02020-03-07 16:17:51 -0800441 for index_splines, points in enumerate(
442 self.points.getSplines()):
Andrew Runke6842bf92019-01-26 15:38:25 -0800443 for index_points, val in enumerate(points):
Andrew Runke6842bf92019-01-26 15:38:25 -0800444 distance = np.sqrt((cur_p[0] - val[0])**2 +
445 (cur_p[1] - val[1])**2)
446 if distance < nearest:
447 nearest = distance
448 index_of_closest = index_points
449 print("Nearest: " + str(nearest))
450 print("Index: " + str(index_of_closest))
451 self.index_of_edit = index_of_closest
452 self.spline_edit = index_splines
Ravago Jones6d460fe2021-07-03 16:59:55 -0700453 self.held_x = self.mousex
Ravago Jones128fb992021-07-31 13:56:58 -0700454 self.queue_draw()
455
Ravago Jones76ecec82021-08-07 14:37:08 -0700456 def do_motion_notify_event(self, event):
457 old_x = self.mousex
458 old_y = self.mousey
Ravago Jones797c49c2021-07-31 14:51:59 -0700459 self.mousex, self.mousey = self.input_transform.transform_point(
460 event.x, event.y)
Ravago Jones76ecec82021-08-07 14:37:08 -0700461 dif_x = self.mousex - old_x
462 dif_y = self.mousey - old_y
Ravago Jones54dafeb2022-03-02 20:41:47 -0800463 difs = np.array([dif_x, dif_y])
Ravago Jones128fb992021-07-31 13:56:58 -0700464
Ravago Jones76ecec82021-08-07 14:37:08 -0700465 if self.mode == Mode.kEditing and self.spline_edit != -1:
466 self.points.updates_for_mouse_move(self.index_of_edit,
467 self.spline_edit,
Ravago Jones54dafeb2022-03-02 20:41:47 -0800468 self.mousex,
469 self.mousey, difs)
Ravago Jones128fb992021-07-31 13:56:58 -0700470
Ravago Jones76ecec82021-08-07 14:37:08 -0700471 self.points.update_lib_spline()
472 self.graph.schedule_recalculate(self.points)
473 self.queue_draw()
Ravago Jones128fb992021-07-31 13:56:58 -0700474
Ravago Jones76ecec82021-08-07 14:37:08 -0700475 def do_scroll_event(self, event):
Ravago Jones54dafeb2022-03-02 20:41:47 -0800476
Ravago Jones01781202021-08-01 15:25:11 -0700477 self.mousex, self.mousey = self.input_transform.transform_point(
478 event.x, event.y)
479
Ravago Jones54dafeb2022-03-02 20:41:47 -0800480 step_size = self.pxToM(20) # px
Ravago Jones01781202021-08-01 15:25:11 -0700481
482 if event.direction == Gdk.ScrollDirection.UP:
483 # zoom out
484 scale_by = step_size
485 elif event.direction == Gdk.ScrollDirection.DOWN:
486 # zoom in
487 scale_by = -step_size
488 else:
489 return
490
Ravago Jones54dafeb2022-03-02 20:41:47 -0800491 scale = (self.field.width + scale_by) / self.field.width
Ravago Jones01781202021-08-01 15:25:11 -0700492
Ribhav Kaulfa68ba82021-09-11 16:26:57 -0700493 # This restricts the amount it can be scaled.
Ravago Jones54dafeb2022-03-02 20:41:47 -0800494 if self.zoom_transform.xx <= 0.5:
Ribhav Kaulfa68ba82021-09-11 16:26:57 -0700495 scale = max(scale, 1)
Ravago Jones54dafeb2022-03-02 20:41:47 -0800496 elif self.zoom_transform.xx >= 16:
Ribhav Kaulfa68ba82021-09-11 16:26:57 -0700497 scale = min(scale, 1)
498
Ravago Jones01781202021-08-01 15:25:11 -0700499 # move the origin to point
Ravago Jones54dafeb2022-03-02 20:41:47 -0800500 self.zoom_transform.translate(event.x, event.y)
Ravago Jones01781202021-08-01 15:25:11 -0700501
502 # scale from new origin
Ravago Jones54dafeb2022-03-02 20:41:47 -0800503 self.zoom_transform.scale(scale, scale)
Ravago Jones01781202021-08-01 15:25:11 -0700504
505 # move back
Ravago Jones54dafeb2022-03-02 20:41:47 -0800506 self.zoom_transform.translate(-event.x, -event.y)
Ravago Jones01781202021-08-01 15:25:11 -0700507
508 # snap to the edge when near 1x scaling
Ravago Jones54dafeb2022-03-02 20:41:47 -0800509 if 0.99 < self.zoom_transform.xx < 1.01 and -50 < self.zoom_transform.x0 < 50:
510 self.zoom_transform.x0 = 0
511 self.zoom_transform.y0 = 0
Ravago Jones01781202021-08-01 15:25:11 -0700512 print("snap")
513
514 self.queue_draw()