blob: df460d34910fd64023d14734a6990b67273a8f42 [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')
John Park91e69732019-03-03 13:12:43 -080010gi.require_version('Gdk', '3.0')
Andrew Runke6842bf92019-01-26 15:38:25 -080011from gi.repository import Gdk, Gtk, GLib
Tabitha Jarvis1007a132018-12-12 21:47:54 -080012import cairo
Ravago Jones6d460fe2021-07-03 16:59:55 -070013from libspline import Spline
Tabitha Jarvis1007a132018-12-12 21:47:54 -080014import enum
John Park91e69732019-03-03 13:12:43 -080015import json
Ravago Jones797c49c2021-07-31 14:51:59 -070016from constants import FIELD
17from constants import get_json_folder
18from constants import ROBOT_SIDE_TO_BALL_CENTER, ROBOT_SIDE_TO_HATCH_PANEL, HATCH_PANEL_WIDTH, BALL_RADIUS
Ravago Jones6d460fe2021-07-03 16:59:55 -070019from drawing_constants import set_color, draw_px_cross, draw_px_x, display_text, draw_control_points
John Park91e69732019-03-03 13:12:43 -080020from points import Points
Ravago Jones6d460fe2021-07-03 16:59:55 -070021import time
Andrew Runke6842bf92019-01-26 15:38:25 -080022
Tabitha Jarvis1007a132018-12-12 21:47:54 -080023
24class Mode(enum.Enum):
25 kViewing = 0
26 kPlacing = 1
27 kEditing = 2
Andrew Runke6842bf92019-01-26 15:38:25 -080028
29
Ravago Jones6d460fe2021-07-03 16:59:55 -070030class FieldWidget(Gtk.DrawingArea):
Andrew Runke6842bf92019-01-26 15:38:25 -080031 """Create a GTK+ widget on which we will draw using Cairo"""
Ravago Jones26f7ad02021-02-05 15:45:59 -080032
Tabitha Jarvis1007a132018-12-12 21:47:54 -080033 def __init__(self):
Ravago Jones6d460fe2021-07-03 16:59:55 -070034 super(FieldWidget, self).__init__()
Ravago Jones086a8872021-08-07 15:49:40 -070035 self.set_field(FIELD)
Ravago Jones797c49c2021-07-31 14:51:59 -070036 self.set_size_request(
Ravago Jones086a8872021-08-07 15:49:40 -070037 self.mToPx(self.field.width), self.mToPx(self.field.length))
Tabitha Jarvis1007a132018-12-12 21:47:54 -080038
John Park91e69732019-03-03 13:12:43 -080039 self.points = Points()
Ravago Jones6d460fe2021-07-03 16:59:55 -070040 self.graph = Graph()
41 self.set_vexpand(True)
42 self.set_hexpand(True)
Ryan Yind8be3882021-10-13 20:59:41 -070043 # list of multisplines
44 self.multispline_stack = []
Tabitha Jarvis1007a132018-12-12 21:47:54 -080045 # init field drawing
46 # add default spline for testing purposes
47 # init editing / viewing modes and pointer location
48 self.mode = Mode.kPlacing
Ravago Jones6d460fe2021-07-03 16:59:55 -070049 self.mousex = 0
50 self.mousey = 0
51 self.module_path = os.path.dirname(os.path.realpath(sys.argv[0]))
52 self.path_to_export = os.path.join(self.module_path,
John Park91e69732019-03-03 13:12:43 -080053 'points_for_pathedit.json')
54
Tabitha Jarvis1007a132018-12-12 21:47:54 -080055 # For the editing mode
Andrew Runke6842bf92019-01-26 15:38:25 -080056 self.index_of_edit = -1 # Can't be zero beause array starts at 0
Tabitha Jarvis1007a132018-12-12 21:47:54 -080057 self.held_x = 0
Andrew Runke6842bf92019-01-26 15:38:25 -080058 self.spline_edit = -1
Tabitha Jarvis1007a132018-12-12 21:47:54 -080059
Ravago Jones797c49c2021-07-31 14:51:59 -070060 self.transform = cairo.Matrix()
61
Ravago Jones76ecec82021-08-07 14:37:08 -070062 self.set_events(Gdk.EventMask.BUTTON_PRESS_MASK
63 | Gdk.EventMask.BUTTON_PRESS_MASK
64 | Gdk.EventMask.BUTTON_RELEASE_MASK
65 | Gdk.EventMask.POINTER_MOTION_MASK
66 | Gdk.EventMask.SCROLL_MASK)
67
Ravago Jones086a8872021-08-07 15:49:40 -070068 def set_field(self, field):
69 self.field = field
Ravago Jonesc26b9162021-06-30 20:12:48 -070070 try:
Ravago Jones6d460fe2021-07-03 16:59:55 -070071 self.field_png = cairo.ImageSurface.create_from_png(
Ravago Jones086a8872021-08-07 15:49:40 -070072 "frc971/control_loops/python/field_images/" +
73 self.field.field_id + ".png")
Ravago Jonesc26b9162021-06-30 20:12:48 -070074 except cairo.Error:
75 self.field_png = None
Ravago Jones086a8872021-08-07 15:49:40 -070076 self.queue_draw()
Ravago Jonesc26b9162021-06-30 20:12:48 -070077
Ravago Jones797c49c2021-07-31 14:51:59 -070078 # returns the transform from widget space to field space
79 @property
80 def input_transform(self):
81 xx, yx, xy, yy, x0, y0 = self.transform
82 matrix = cairo.Matrix(xx, yx, xy, yy, x0, y0)
83 # the transform for input needs to be the opposite of the transform for drawing
84 matrix.invert()
85 return matrix
86
87 # returns the scale from pixels in field space to meters in field space
88 def pxToM_scale(self):
89 available_space = self.get_allocation()
Ravago Jones086a8872021-08-07 15:49:40 -070090 return np.maximum(self.field.width / available_space.width,
91 self.field.length / available_space.height)
Ravago Jones797c49c2021-07-31 14:51:59 -070092
93 def pxToM(self, p):
94 return p * self.pxToM_scale()
95
96 def mToPx(self, m):
97 return m / self.pxToM_scale()
98
John Park91e69732019-03-03 13:12:43 -080099 def draw_robot_at_point(self, cr, i, p, spline):
Ravago Jones797c49c2021-07-31 14:51:59 -0700100 p1 = [self.mToPx(spline.Point(i)[0]), self.mToPx(spline.Point(i)[1])]
101 p2 = [
102 self.mToPx(spline.Point(i + p)[0]),
103 self.mToPx(spline.Point(i + p)[1])
104 ]
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800105
John Park91e69732019-03-03 13:12:43 -0800106 #Calculate Robot
107 distance = np.sqrt((p2[1] - p1[1])**2 + (p2[0] - p1[0])**2)
108 x_difference_o = p2[0] - p1[0]
109 y_difference_o = p2[1] - p1[1]
Ravago Jones797c49c2021-07-31 14:51:59 -0700110 x_difference = x_difference_o * self.mToPx(
Ravago Jones086a8872021-08-07 15:49:40 -0700111 self.field.robot.length / 2) / distance
Ravago Jones797c49c2021-07-31 14:51:59 -0700112 y_difference = y_difference_o * self.mToPx(
Ravago Jones086a8872021-08-07 15:49:40 -0700113 self.field.robot.length / 2) / distance
John Park91e69732019-03-03 13:12:43 -0800114
115 front_middle = []
116 front_middle.append(p1[0] + x_difference)
117 front_middle.append(p1[1] + y_difference)
118
119 back_middle = []
120 back_middle.append(p1[0] - x_difference)
121 back_middle.append(p1[1] - y_difference)
122
123 slope = [-(1 / x_difference_o) / (1 / y_difference_o)]
124 angle = np.arctan(slope)
125
Ravago Jones086a8872021-08-07 15:49:40 -0700126 x_difference = np.sin(angle[0]) * self.mToPx(
127 self.field.robot.width / 2)
128 y_difference = np.cos(angle[0]) * self.mToPx(
129 self.field.robot.width / 2)
John Park91e69732019-03-03 13:12:43 -0800130
131 front_1 = []
132 front_1.append(front_middle[0] - x_difference)
133 front_1.append(front_middle[1] - y_difference)
134
135 front_2 = []
136 front_2.append(front_middle[0] + x_difference)
137 front_2.append(front_middle[1] + y_difference)
138
139 back_1 = []
140 back_1.append(back_middle[0] - x_difference)
141 back_1.append(back_middle[1] - y_difference)
142
143 back_2 = []
144 back_2.append(back_middle[0] + x_difference)
145 back_2.append(back_middle[1] + y_difference)
146
Ravago Jones797c49c2021-07-31 14:51:59 -0700147 x_difference = x_difference_o * self.mToPx(
Ravago Jones086a8872021-08-07 15:49:40 -0700148 self.field.robot.length / 2 + ROBOT_SIDE_TO_BALL_CENTER) / distance
Ravago Jones797c49c2021-07-31 14:51:59 -0700149 y_difference = y_difference_o * self.mToPx(
Ravago Jones086a8872021-08-07 15:49:40 -0700150 self.field.robot.length / 2 + ROBOT_SIDE_TO_BALL_CENTER) / distance
John Park91e69732019-03-03 13:12:43 -0800151
152 #Calculate Ball
153 ball_center = []
154 ball_center.append(p1[0] + x_difference)
155 ball_center.append(p1[1] + y_difference)
156
Ravago Jones797c49c2021-07-31 14:51:59 -0700157 x_difference = x_difference_o * self.mToPx(
Ravago Jones086a8872021-08-07 15:49:40 -0700158 self.field.robot.length / 2 + ROBOT_SIDE_TO_HATCH_PANEL) / distance
Ravago Jones797c49c2021-07-31 14:51:59 -0700159 y_difference = y_difference_o * self.mToPx(
Ravago Jones086a8872021-08-07 15:49:40 -0700160 self.field.robot.length / 2 + ROBOT_SIDE_TO_HATCH_PANEL) / distance
John Park91e69732019-03-03 13:12:43 -0800161
162 #Calculate Panel
163 panel_center = []
164 panel_center.append(p1[0] + x_difference)
165 panel_center.append(p1[1] + y_difference)
166
Ravago Jones797c49c2021-07-31 14:51:59 -0700167 x_difference = np.sin(angle[0]) * self.mToPx(HATCH_PANEL_WIDTH / 2)
168 y_difference = np.cos(angle[0]) * self.mToPx(HATCH_PANEL_WIDTH / 2)
John Park91e69732019-03-03 13:12:43 -0800169
170 panel_1 = []
171 panel_1.append(panel_center[0] + x_difference)
172 panel_1.append(panel_center[1] + y_difference)
173
174 panel_2 = []
175 panel_2.append(panel_center[0] - x_difference)
176 panel_2.append(panel_center[1] - y_difference)
177
178 #Draw Robot
179 cr.move_to(front_1[0], front_1[1])
180 cr.line_to(back_1[0], back_1[1])
181 cr.line_to(back_2[0], back_2[1])
182 cr.line_to(front_2[0], front_2[1])
183 cr.line_to(front_1[0], front_1[1])
184
185 cr.stroke()
186
187 #Draw Ball
188 set_color(cr, palette["ORANGE"], 0.5)
189 cr.move_to(back_middle[0], back_middle[1])
190 cr.line_to(ball_center[0], ball_center[1])
Ravago Jones797c49c2021-07-31 14:51:59 -0700191 cr.arc(ball_center[0], ball_center[1], self.mToPx(BALL_RADIUS), 0,
John Park91e69732019-03-03 13:12:43 -0800192 2 * np.pi)
193 cr.stroke()
194
195 #Draw Panel
196 set_color(cr, palette["YELLOW"], 0.5)
197 cr.move_to(panel_1[0], panel_1[1])
198 cr.line_to(panel_2[0], panel_2[1])
199
200 cr.stroke()
201 cr.set_source_rgba(0, 0, 0, 1)
202
Ravago Jones6d460fe2021-07-03 16:59:55 -0700203 def do_draw(self, cr): # main
Ravago Jones797c49c2021-07-31 14:51:59 -0700204 cr.set_matrix(self.transform.multiply(cr.get_matrix()))
205
James Kuszmaul1c933e02020-03-07 16:17:51 -0800206 cr.save()
Ravago Jones797c49c2021-07-31 14:51:59 -0700207
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800208 set_color(cr, palette["BLACK"])
Ravago Jones5f787df2021-01-23 16:26:27 -0800209
Ravago Jones797c49c2021-07-31 14:51:59 -0700210 cr.set_line_width(1.0)
Ravago Jones086a8872021-08-07 15:49:40 -0700211 cr.rectangle(0, 0, self.mToPx(self.field.width),
212 self.mToPx(self.field.length))
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800213 cr.set_line_join(cairo.LINE_JOIN_ROUND)
214 cr.stroke()
Ravago Jones5f787df2021-01-23 16:26:27 -0800215
Ravago Jonesc26b9162021-06-30 20:12:48 -0700216 if self.field_png:
217 cr.save()
Ravago Jonesc26b9162021-06-30 20:12:48 -0700218 cr.scale(
Ravago Jones086a8872021-08-07 15:49:40 -0700219 self.mToPx(self.field.width) / self.field_png.get_width(),
220 self.mToPx(self.field.length) / self.field_png.get_height(),
Ravago Jones6d460fe2021-07-03 16:59:55 -0700221 )
Ravago Jonesc26b9162021-06-30 20:12:48 -0700222 cr.set_source_surface(self.field_png)
223 cr.paint()
224 cr.restore()
Andrew Runke6842bf92019-01-26 15:38:25 -0800225
John Park91e69732019-03-03 13:12:43 -0800226 # update everything
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800227
Ravago Jones797c49c2021-07-31 14:51:59 -0700228 cr.set_line_width(2.0)
John Park91e69732019-03-03 13:12:43 -0800229 if self.mode == Mode.kPlacing or self.mode == Mode.kViewing:
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800230 set_color(cr, palette["BLACK"])
Ravago Jones36c92f02021-07-24 16:35:33 -0700231 for i, point in enumerate(self.points.getPoints()):
Ravago Jones797c49c2021-07-31 14:51:59 -0700232 draw_px_x(cr, self.mToPx(point[0]), self.mToPx(point[1]), 10)
John Park91e69732019-03-03 13:12:43 -0800233 set_color(cr, palette["WHITE"])
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800234 elif self.mode == Mode.kEditing:
235 set_color(cr, palette["BLACK"])
John Park91e69732019-03-03 13:12:43 -0800236 if self.points.getSplines():
237 self.draw_splines(cr)
238 for i, points in enumerate(self.points.getSplines()):
Ravago Jones36c92f02021-07-24 16:35:33 -0700239 points = [
Ravago Jones797c49c2021-07-31 14:51:59 -0700240 np.array([self.mToPx(x), self.mToPx(y)])
Ravago Jones36c92f02021-07-24 16:35:33 -0700241 for (x, y) in points
242 ]
243 draw_control_points(cr, points)
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800244
Ravago Jones36c92f02021-07-24 16:35:33 -0700245 p0, p1, p2, p3, p4, p5 = points
John Park91e69732019-03-03 13:12:43 -0800246 first_tangent = p0 + 2.0 * (p1 - p0)
247 second_tangent = p5 + 2.0 * (p4 - p5)
248 cr.set_source_rgb(0, 0.5, 0)
249 cr.move_to(p0[0], p0[1])
250 cr.set_line_width(1.0)
251 cr.line_to(first_tangent[0], first_tangent[1])
252 cr.move_to(first_tangent[0], first_tangent[1])
253 cr.line_to(p2[0], p2[1])
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800254
John Park91e69732019-03-03 13:12:43 -0800255 cr.move_to(p5[0], p5[1])
256 cr.line_to(second_tangent[0], second_tangent[1])
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800257
John Park91e69732019-03-03 13:12:43 -0800258 cr.move_to(second_tangent[0], second_tangent[1])
259 cr.line_to(p3[0], p3[1])
260
261 cr.stroke()
262 cr.set_line_width(2.0)
John Park91e69732019-03-03 13:12:43 -0800263 set_color(cr, palette["WHITE"])
264
265 cr.paint_with_alpha(0.2)
266
Ravago Jones6d460fe2021-07-03 16:59:55 -0700267 draw_px_cross(cr, self.mousex, self.mousey, 10)
James Kuszmaul1c933e02020-03-07 16:17:51 -0800268 cr.restore()
Ravago Jones6d460fe2021-07-03 16:59:55 -0700269
John Park91e69732019-03-03 13:12:43 -0800270 def draw_splines(self, cr):
Ravago Jones76ecec82021-08-07 14:37:08 -0700271 for i, spline in enumerate(self.points.getLibsplines()):
John Park91e69732019-03-03 13:12:43 -0800272 for k in np.linspace(0.01, 1, 100):
Ravago Jones26f7ad02021-02-05 15:45:59 -0800273 cr.move_to(
Ravago Jones797c49c2021-07-31 14:51:59 -0700274 self.mToPx(spline.Point(k - 0.01)[0]),
275 self.mToPx(spline.Point(k - 0.01)[1]))
Ravago Jones26f7ad02021-02-05 15:45:59 -0800276 cr.line_to(
Ravago Jones797c49c2021-07-31 14:51:59 -0700277 self.mToPx(spline.Point(k)[0]),
278 self.mToPx(spline.Point(k)[1]))
John Park91e69732019-03-03 13:12:43 -0800279 cr.stroke()
John Park91e69732019-03-03 13:12:43 -0800280 if i == 0:
281 self.draw_robot_at_point(cr, 0.00, 0.01, spline)
282 self.draw_robot_at_point(cr, 1, 0.01, spline)
John Park91e69732019-03-03 13:12:43 -0800283
John Park909c0392020-03-05 23:56:30 -0800284 def export_json(self, file_name):
Ravago Jones09f59722021-03-03 21:11:41 -0800285 self.path_to_export = os.path.join(
286 self.module_path, # position of the python
287 "../../..", # root of the repository
Ravago Jones086a8872021-08-07 15:49:40 -0700288 get_json_folder(self.field), # path from the root
Ravago Jones09f59722021-03-03 21:11:41 -0800289 file_name # selected file
290 )
Ravago Jones3b92afa2021-02-05 14:27:32 -0800291
Ravago Jones6d460fe2021-07-03 16:59:55 -0700292 # Will export to json file
293 multi_spline = self.points.toMultiSpline()
294 print(multi_spline)
295 with open(self.path_to_export, mode='w') as points_file:
296 json.dump(multi_spline, points_file)
John Park909c0392020-03-05 23:56:30 -0800297
298 def import_json(self, file_name):
Ravago Jones09f59722021-03-03 21:11:41 -0800299 self.path_to_export = os.path.join(
300 self.module_path, # position of the python
301 "../../..", # root of the repository
Ravago Jones086a8872021-08-07 15:49:40 -0700302 get_json_folder(self.field), # path from the root
Ravago Jones09f59722021-03-03 21:11:41 -0800303 file_name # selected file
304 )
305
Ravago Jones6d460fe2021-07-03 16:59:55 -0700306 # import from json file
307 print("LOADING LOAD FROM " + file_name) # Load takes a few seconds
308 with open(self.path_to_export) as points_file:
309 multi_spline = json.load(points_file)
John Park909c0392020-03-05 23:56:30 -0800310
Ravago Jones6d460fe2021-07-03 16:59:55 -0700311 # if people messed with the spline json,
312 # it might not be the right length
313 # so give them a nice error message
314 try: # try to salvage as many segments of the spline as possible
315 self.points.fromMultiSpline(multi_spline)
316 except IndexError:
317 # check if they're both 6+5*(k-1) long
318 expected_length = 6 + 5 * (multi_spline["spline_count"] - 1)
319 x_len = len(multi_spline["spline_x"])
320 y_len = len(multi_spline["spline_x"])
321 if x_len is not expected_length:
322 print(
323 "Error: spline x values were not the expected length; expected {} got {}"
324 .format(expected_length, x_len))
325 elif y_len is not expected_length:
326 print(
327 "Error: spline y values were not the expected length; expected {} got {}"
328 .format(expected_length, y_len))
Ravago Jones3b92afa2021-02-05 14:27:32 -0800329
Ravago Jones6d460fe2021-07-03 16:59:55 -0700330 print("SPLINES LOADED")
331 self.mode = Mode.kEditing
Ravago Jones76ecec82021-08-07 14:37:08 -0700332 self.queue_draw()
333 self.graph.schedule_recalculate(self.points)
John Park909c0392020-03-05 23:56:30 -0800334
Ryan Yind8be3882021-10-13 20:59:41 -0700335 def attempt_append_multispline(self):
336 if (len(self.multispline_stack) == 0 or
337 self.points.toMultiSpline() != self.multispline_stack[-1]):
338 self.multispline_stack.append(self.points.toMultiSpline())
339
340 def clear_graph(self, should_attempt_append=True):
341 if should_attempt_append:
342 self.attempt_append_multispline()
Ryan Yin85f861f2021-09-16 17:55:11 -0700343 self.points = Points()
344 #recalulate graph using new points
345 self.graph.axis.clear()
346 self.graph.queue_draw()
347 #allow placing again
348 self.mode = Mode.kPlacing
349 #redraw entire graph
350 self.queue_draw()
Ryan Yind8be3882021-10-13 20:59:41 -0700351
352
353 def undo(self):
354 try:
355 self.multispline_stack.pop()
356 except IndexError:
357 return
358 if len(self.multispline_stack) == 0:
359 self.clear_graph(should_attempt_append=False) #clear, don't do anything
360 return
361 multispline = self.multispline_stack[-1]
362 if multispline['spline_count'] > 0:
363 self.points.fromMultiSpline(multispline)
364 self.mode= Mode.kEditing
365 else:
366 self.mode = Mode.kPlacing
367 self.clear_graph(should_attempt_append=False)
368 self.queue_draw()
369
370
Ryan Yin85f861f2021-09-16 17:55:11 -0700371
Ravago Jones76ecec82021-08-07 14:37:08 -0700372 def do_key_press_event(self, event):
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800373 keyval = Gdk.keyval_to_lower(event.keyval)
Ryan Yind8be3882021-10-13 20:59:41 -0700374 if keyval == Gdk.KEY_z and event.state & Gdk.ModifierType.CONTROL_MASK:
375 self.undo()
Ravago Jones6d460fe2021-07-03 16:59:55 -0700376 # TODO: This should be a button
Andrew Runke6842bf92019-01-26 15:38:25 -0800377 if keyval == Gdk.KEY_p:
378 self.mode = Mode.kPlacing
379 # F0 = A1
380 # B1 = 2F0 - E0
381 # C1= d0 + 4F0 - 4E0
John Park91e69732019-03-03 13:12:43 -0800382 spline_index = len(self.points.getSplines()) - 1
383 self.points.resetPoints()
384 self.points.extrapolate(
385 self.points.getSplines()[len(self.points.getSplines()) - 1][5],
386 self.points.getSplines()[len(self.points.getSplines()) - 1][4],
387 self.points.getSplines()[len(self.points.getSplines()) - 1][3])
Ravago Jones128fb992021-07-31 13:56:58 -0700388 self.queue_draw()
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800389
Ravago Jones76ecec82021-08-07 14:37:08 -0700390 def do_button_release_event(self, event):
Ryan Yind8be3882021-10-13 20:59:41 -0700391 self.attempt_append_multispline()
Ravago Jones76ecec82021-08-07 14:37:08 -0700392 self.mousex, self.mousey = self.input_transform.transform_point(
393 event.x, event.y)
394 if self.mode == Mode.kEditing:
395 if self.index_of_edit > -1 and self.held_x != self.mousex:
Ravago Jones76ecec82021-08-07 14:37:08 -0700396 self.points.setSplines(self.spline_edit, self.index_of_edit,
397 self.pxToM(self.mousex),
398 self.pxToM(self.mousey))
399
400 self.points.splineExtrapolate(self.spline_edit)
401
402 self.points.update_lib_spline()
403 self.graph.schedule_recalculate(self.points)
404
405 self.index_of_edit = -1
406 self.spline_edit = -1
407
408 def do_button_press_event(self, event):
Ravago Jones797c49c2021-07-31 14:51:59 -0700409 self.mousex, self.mousey = self.input_transform.transform_point(
410 event.x, event.y)
Ravago Jones6d460fe2021-07-03 16:59:55 -0700411
Andrew Runke6842bf92019-01-26 15:38:25 -0800412 if self.mode == Mode.kPlacing:
Ravago Jones36c92f02021-07-24 16:35:33 -0700413 if self.points.add_point(
Ravago Jones797c49c2021-07-31 14:51:59 -0700414 self.pxToM(self.mousex), self.pxToM(self.mousey)):
John Park91e69732019-03-03 13:12:43 -0800415 self.mode = Mode.kEditing
Ravago Jones76ecec82021-08-07 14:37:08 -0700416 self.graph.schedule_recalculate(self.points)
Andrew Runke6842bf92019-01-26 15:38:25 -0800417 elif self.mode == Mode.kEditing:
418 # Now after index_of_edit is not -1, the point is selected, so
419 # user can click for new point
Ravago Jones128fb992021-07-31 13:56:58 -0700420 if self.index_of_edit == -1:
Andrew Runke6842bf92019-01-26 15:38:25 -0800421 # Get clicked point
422 # Find nearest
423 # Move nearest to clicked
Ravago Jones797c49c2021-07-31 14:51:59 -0700424 cur_p = [self.pxToM(self.mousex), self.pxToM(self.mousey)]
Andrew Runke6842bf92019-01-26 15:38:25 -0800425 # Get the distance between each for x and y
426 # Save the index of the point closest
John Park13d3e282019-01-26 20:16:48 -0800427 nearest = 1 # Max distance away a the selected point can be in meters
Andrew Runke6842bf92019-01-26 15:38:25 -0800428 index_of_closest = 0
James Kuszmaul1c933e02020-03-07 16:17:51 -0800429 for index_splines, points in enumerate(
430 self.points.getSplines()):
Andrew Runke6842bf92019-01-26 15:38:25 -0800431 for index_points, val in enumerate(points):
Andrew Runke6842bf92019-01-26 15:38:25 -0800432 distance = np.sqrt((cur_p[0] - val[0])**2 +
433 (cur_p[1] - val[1])**2)
434 if distance < nearest:
435 nearest = distance
436 index_of_closest = index_points
437 print("Nearest: " + str(nearest))
438 print("Index: " + str(index_of_closest))
439 self.index_of_edit = index_of_closest
440 self.spline_edit = index_splines
Ravago Jones6d460fe2021-07-03 16:59:55 -0700441 self.held_x = self.mousex
Ravago Jones128fb992021-07-31 13:56:58 -0700442 self.queue_draw()
443
Ravago Jones76ecec82021-08-07 14:37:08 -0700444 def do_motion_notify_event(self, event):
445 old_x = self.mousex
446 old_y = self.mousey
Ravago Jones797c49c2021-07-31 14:51:59 -0700447 self.mousex, self.mousey = self.input_transform.transform_point(
448 event.x, event.y)
Ravago Jones76ecec82021-08-07 14:37:08 -0700449 dif_x = self.mousex - old_x
450 dif_y = self.mousey - old_y
451 difs = np.array([self.pxToM(dif_x), self.pxToM(dif_y)])
Ravago Jones128fb992021-07-31 13:56:58 -0700452
Ravago Jones76ecec82021-08-07 14:37:08 -0700453 if self.mode == Mode.kEditing and self.spline_edit != -1:
454 self.points.updates_for_mouse_move(self.index_of_edit,
455 self.spline_edit,
456 self.pxToM(self.mousex),
457 self.pxToM(self.mousey), difs)
Ravago Jones128fb992021-07-31 13:56:58 -0700458
Ravago Jones76ecec82021-08-07 14:37:08 -0700459 self.points.update_lib_spline()
460 self.graph.schedule_recalculate(self.points)
461 self.queue_draw()
Ravago Jones128fb992021-07-31 13:56:58 -0700462
Ravago Jones76ecec82021-08-07 14:37:08 -0700463 def do_scroll_event(self, event):
Ravago Jones01781202021-08-01 15:25:11 -0700464 self.mousex, self.mousey = self.input_transform.transform_point(
465 event.x, event.y)
466
467 step_size = 20 # px
468
469 if event.direction == Gdk.ScrollDirection.UP:
470 # zoom out
471 scale_by = step_size
472 elif event.direction == Gdk.ScrollDirection.DOWN:
473 # zoom in
474 scale_by = -step_size
475 else:
476 return
477
478 apparent_width, apparent_height = self.transform.transform_distance(
479 self.mToPx(FIELD.width), self.mToPx(FIELD.length))
480 scale = (apparent_width + scale_by) / apparent_width
481
482 # scale from point in field coordinates
483 point = self.mousex, self.mousey
484
Ribhav Kaulfa68ba82021-09-11 16:26:57 -0700485 # This restricts the amount it can be scaled.
Austin Schuh8bfec212021-10-24 17:15:03 -0700486 if self.transform.xx <= 0.75:
Ribhav Kaulfa68ba82021-09-11 16:26:57 -0700487 scale = max(scale, 1)
Austin Schuh8bfec212021-10-24 17:15:03 -0700488 elif self.transform.xx >= 16:
Ribhav Kaulfa68ba82021-09-11 16:26:57 -0700489 scale = min(scale, 1)
490
Ravago Jones01781202021-08-01 15:25:11 -0700491 # move the origin to point
492 self.transform.translate(point[0], point[1])
493
494 # scale from new origin
495 self.transform.scale(scale, scale)
496
497 # move back
498 self.transform.translate(-point[0], -point[1])
499
500 # snap to the edge when near 1x scaling
501 if 0.99 < self.transform.xx < 1.01 and -50 < self.transform.x0 < 50:
502 self.transform.x0 = 0
503 self.transform.y0 = 0
504 print("snap")
505
506 self.queue_draw()