blob: f0b84a43433f09789e9aa3dd9151a6d704897cf4 [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 Jonesb170ed32022-06-01 21:16:15 -070012from libspline import Spline, DistanceSpline
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"""
Tabitha Jarvis1007a132018-12-12 21:47:54 -080031 def __init__(self):
Ravago Jones6d460fe2021-07-03 16:59:55 -070032 super(FieldWidget, self).__init__()
Ravago Jones086a8872021-08-07 15:49:40 -070033 self.set_field(FIELD)
Ravago Jones8da89c42022-07-17 19:34:06 -070034 self.set_size_request(self.mToPx(self.field.width),
35 self.mToPx(self.field.length))
Tabitha Jarvis1007a132018-12-12 21:47:54 -080036
John Park91e69732019-03-03 13:12:43 -080037 self.points = Points()
Ravago Jones6d460fe2021-07-03 16:59:55 -070038 self.graph = Graph()
Ravago Jones0a1d4092022-06-03 12:47:32 -070039 self.graph.cursor_watcher = self
Ravago Jones6d460fe2021-07-03 16:59:55 -070040 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()
Ravago Jones8da89c42022-07-17 19:34:06 -070093 field_transform.scale(1, -1) # flipped y-axis
Ravago Jones54dafeb2022-03-02 20:41:47 -080094 field_transform.scale(1 / self.pxToM_scale(), 1 / self.pxToM_scale())
Ravago Jones8da89c42022-07-17 19:34:06 -070095 field_transform.translate(self.field.width / 2,
96 -1 * self.field.length / 2)
Ravago Jones54dafeb2022-03-02 20:41:47 -080097 return field_transform
Ravago Jones797c49c2021-07-31 14:51:59 -070098
99 # returns the scale from pixels in field space to meters in field space
100 def pxToM_scale(self):
101 available_space = self.get_allocation()
Ravago Jones086a8872021-08-07 15:49:40 -0700102 return np.maximum(self.field.width / available_space.width,
103 self.field.length / available_space.height)
Ravago Jones797c49c2021-07-31 14:51:59 -0700104
105 def pxToM(self, p):
106 return p * self.pxToM_scale()
107
108 def mToPx(self, m):
109 return m / self.pxToM_scale()
110
Ravago Jonesb170ed32022-06-01 21:16:15 -0700111 def draw_robot_at_point(self, cr, spline, t):
112 """Draws the robot at a point along a Spline or DistanceSpline"""
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800113
Ravago Jonesb170ed32022-06-01 21:16:15 -0700114 # we accept both Spline and DistanceSpline
115 if type(spline) is Spline:
116 point = spline.Point(t)
117 theta = spline.Theta(t)
118 elif type(spline) is DistanceSpline:
119 point = spline.XY(t)
120 theta = spline.Theta(t)
121 else:
122 raise TypeError(
123 f"expected Spline or DistanceSpline (got {type(spline)})")
John Park91e69732019-03-03 13:12:43 -0800124
Ravago Jonesb170ed32022-06-01 21:16:15 -0700125 # Transform so that +y is forward along the spline
126 transform = cairo.Matrix()
127 transform.translate(*point)
128 transform.rotate(theta - np.pi / 2)
John Park91e69732019-03-03 13:12:43 -0800129
Ravago Jonesb170ed32022-06-01 21:16:15 -0700130 cr.save()
131 cr.set_matrix(transform.multiply(cr.get_matrix()))
John Park91e69732019-03-03 13:12:43 -0800132
Ravago Jonesb170ed32022-06-01 21:16:15 -0700133 # Draw Robot
134 set_color(cr, palette["BLACK"])
135 cr.rectangle(-self.field.robot.width / 2, -self.field.robot.length / 2,
136 self.field.robot.width, self.field.robot.length)
John Park91e69732019-03-03 13:12:43 -0800137 cr.stroke()
138
139 #Draw Ball
140 set_color(cr, palette["ORANGE"], 0.5)
Ravago Jonesb170ed32022-06-01 21:16:15 -0700141 cr.arc(0, self.field.robot.length / 2 + BALL_RADIUS, BALL_RADIUS, 0,
142 2 * np.pi)
John Park91e69732019-03-03 13:12:43 -0800143 cr.stroke()
144
Ravago Jonesb170ed32022-06-01 21:16:15 -0700145 # undo the transform
146 cr.restore()
John Park91e69732019-03-03 13:12:43 -0800147
Ravago Jones6d460fe2021-07-03 16:59:55 -0700148 def do_draw(self, cr): # main
Ravago Jones8da89c42022-07-17 19:34:06 -0700149 cr.set_matrix(
150 self.field_transform.multiply(self.zoom_transform).multiply(
151 cr.get_matrix()))
Ravago Jones797c49c2021-07-31 14:51:59 -0700152
James Kuszmaul1c933e02020-03-07 16:17:51 -0800153 cr.save()
Ravago Jones797c49c2021-07-31 14:51:59 -0700154
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800155 set_color(cr, palette["BLACK"])
Ravago Jones5f787df2021-01-23 16:26:27 -0800156
Henry Speiser51be5c62022-03-13 23:14:36 -0700157 cr.set_line_width(self.pxToM(1))
Ravago Jones8da89c42022-07-17 19:34:06 -0700158 cr.rectangle(-0.5 * self.field.width, -0.5 * self.field.length,
159 self.field.width, self.field.length)
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800160 cr.set_line_join(cairo.LINE_JOIN_ROUND)
161 cr.stroke()
Ravago Jones5f787df2021-01-23 16:26:27 -0800162
Ravago Jonesc26b9162021-06-30 20:12:48 -0700163 if self.field_png:
164 cr.save()
Ravago Jones54dafeb2022-03-02 20:41:47 -0800165 cr.translate(-0.5 * self.field.width, 0.5 * self.field.length)
Ravago Jonesc26b9162021-06-30 20:12:48 -0700166 cr.scale(
Ravago Jones54dafeb2022-03-02 20:41:47 -0800167 self.field.width / self.field_png.get_width(),
168 -self.field.length / self.field_png.get_height(),
Ravago Jones6d460fe2021-07-03 16:59:55 -0700169 )
Ravago Jonesc26b9162021-06-30 20:12:48 -0700170 cr.set_source_surface(self.field_png)
171 cr.paint()
172 cr.restore()
Andrew Runke6842bf92019-01-26 15:38:25 -0800173
John Park91e69732019-03-03 13:12:43 -0800174 # update everything
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800175
Henry Speiser51be5c62022-03-13 23:14:36 -0700176 cr.set_line_width(self.pxToM(1))
John Park91e69732019-03-03 13:12:43 -0800177 if self.mode == Mode.kPlacing or self.mode == Mode.kViewing:
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800178 set_color(cr, palette["BLACK"])
Ravago Jones36c92f02021-07-24 16:35:33 -0700179 for i, point in enumerate(self.points.getPoints()):
Henry Speiser51be5c62022-03-13 23:14:36 -0700180 draw_px_x(cr, point[0], point[1], self.pxToM(2))
John Park91e69732019-03-03 13:12:43 -0800181 set_color(cr, palette["WHITE"])
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800182 elif self.mode == Mode.kEditing:
183 set_color(cr, palette["BLACK"])
John Park91e69732019-03-03 13:12:43 -0800184 if self.points.getSplines():
185 self.draw_splines(cr)
186 for i, points in enumerate(self.points.getSplines()):
Ravago Jones8da89c42022-07-17 19:34:06 -0700187 points = [np.array([x, y]) for (x, y) in points]
188 draw_control_points(cr,
189 points,
190 width=self.pxToM(5),
191 radius=self.pxToM(2))
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800192
Ravago Jones36c92f02021-07-24 16:35:33 -0700193 p0, p1, p2, p3, p4, p5 = points
John Park91e69732019-03-03 13:12:43 -0800194 first_tangent = p0 + 2.0 * (p1 - p0)
195 second_tangent = p5 + 2.0 * (p4 - p5)
196 cr.set_source_rgb(0, 0.5, 0)
Ravago Jonesb170ed32022-06-01 21:16:15 -0700197 cr.move_to(*p0)
Ravago Jones54dafeb2022-03-02 20:41:47 -0800198 cr.set_line_width(self.pxToM(1.0))
Ravago Jonesb170ed32022-06-01 21:16:15 -0700199 cr.line_to(*first_tangent)
200 cr.move_to(*first_tangent)
201 cr.line_to(*p2)
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800202
Ravago Jonesb170ed32022-06-01 21:16:15 -0700203 cr.move_to(*p5)
204 cr.line_to(*second_tangent)
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800205
Ravago Jonesb170ed32022-06-01 21:16:15 -0700206 cr.move_to(*second_tangent)
207 cr.line_to(*p3)
John Park91e69732019-03-03 13:12:43 -0800208
209 cr.stroke()
Ravago Jones54dafeb2022-03-02 20:41:47 -0800210 cr.set_line_width(self.pxToM(2))
John Park91e69732019-03-03 13:12:43 -0800211 set_color(cr, palette["WHITE"])
212
213 cr.paint_with_alpha(0.2)
214
Henry Speiser51be5c62022-03-13 23:14:36 -0700215 draw_px_cross(cr, self.mousex, self.mousey, self.pxToM(2))
James Kuszmaul1c933e02020-03-07 16:17:51 -0800216 cr.restore()
Ravago Jones6d460fe2021-07-03 16:59:55 -0700217
John Park91e69732019-03-03 13:12:43 -0800218 def draw_splines(self, cr):
Ravago Jones76ecec82021-08-07 14:37:08 -0700219 for i, spline in enumerate(self.points.getLibsplines()):
Ravago Jones54dafeb2022-03-02 20:41:47 -0800220 for k in np.linspace(0.02, 1, 200):
Ravago Jonesb170ed32022-06-01 21:16:15 -0700221 cr.move_to(*spline.Point(k - 0.008))
222 cr.line_to(*spline.Point(k))
John Park91e69732019-03-03 13:12:43 -0800223 cr.stroke()
John Park91e69732019-03-03 13:12:43 -0800224 if i == 0:
Ravago Jonesb170ed32022-06-01 21:16:15 -0700225 self.draw_robot_at_point(cr, spline, 0)
226 self.draw_robot_at_point(cr, spline, 1)
227
228 mouse = np.array((self.mousex, self.mousey))
229
230 # Find the distance along the spline that is closest to the mouse
231 result, distance_spline = self.points.nearest_distance(mouse)
232
233 # if the mouse is close enough, draw the robot to show its width
234 if result and result.fun < 2:
235 self.draw_robot_at_point(cr, distance_spline, result.x)
Ravago Jones0a1d4092022-06-03 12:47:32 -0700236 self.graph.place_cursor(result.x[0])
237 elif self.graph.cursor:
238 x = self.graph.find_cursor()
239 self.draw_robot_at_point(cr, distance_spline, x)
240
241 # clear the cursor each draw so that it does not persist
242 # after you move off the spline
243 self.graph.cursor = None
John Park91e69732019-03-03 13:12:43 -0800244
John Park909c0392020-03-05 23:56:30 -0800245 def export_json(self, file_name):
Ravago Jones09f59722021-03-03 21:11:41 -0800246 self.path_to_export = os.path.join(
247 self.module_path, # position of the python
248 "../../..", # root of the repository
Ravago Jones086a8872021-08-07 15:49:40 -0700249 get_json_folder(self.field), # path from the root
Ravago Jones09f59722021-03-03 21:11:41 -0800250 file_name # selected file
251 )
Ravago Jones3b92afa2021-02-05 14:27:32 -0800252
Ravago Jones6d460fe2021-07-03 16:59:55 -0700253 # Will export to json file
254 multi_spline = self.points.toMultiSpline()
255 print(multi_spline)
256 with open(self.path_to_export, mode='w') as points_file:
257 json.dump(multi_spline, points_file)
John Park909c0392020-03-05 23:56:30 -0800258
259 def import_json(self, file_name):
Ravago Jones09f59722021-03-03 21:11:41 -0800260 self.path_to_export = os.path.join(
261 self.module_path, # position of the python
262 "../../..", # root of the repository
Ravago Jones086a8872021-08-07 15:49:40 -0700263 get_json_folder(self.field), # path from the root
Ravago Jones09f59722021-03-03 21:11:41 -0800264 file_name # selected file
265 )
266
Ravago Jones6d460fe2021-07-03 16:59:55 -0700267 # import from json file
268 print("LOADING LOAD FROM " + file_name) # Load takes a few seconds
269 with open(self.path_to_export) as points_file:
270 multi_spline = json.load(points_file)
John Park909c0392020-03-05 23:56:30 -0800271
Ravago Jones6d460fe2021-07-03 16:59:55 -0700272 # if people messed with the spline json,
273 # it might not be the right length
274 # so give them a nice error message
275 try: # try to salvage as many segments of the spline as possible
276 self.points.fromMultiSpline(multi_spline)
277 except IndexError:
278 # check if they're both 6+5*(k-1) long
279 expected_length = 6 + 5 * (multi_spline["spline_count"] - 1)
280 x_len = len(multi_spline["spline_x"])
281 y_len = len(multi_spline["spline_x"])
282 if x_len is not expected_length:
283 print(
284 "Error: spline x values were not the expected length; expected {} got {}"
285 .format(expected_length, x_len))
286 elif y_len is not expected_length:
287 print(
288 "Error: spline y values were not the expected length; expected {} got {}"
289 .format(expected_length, y_len))
Ravago Jones3b92afa2021-02-05 14:27:32 -0800290
Ravago Jones6d460fe2021-07-03 16:59:55 -0700291 print("SPLINES LOADED")
292 self.mode = Mode.kEditing
Ravago Jones76ecec82021-08-07 14:37:08 -0700293 self.queue_draw()
294 self.graph.schedule_recalculate(self.points)
John Park909c0392020-03-05 23:56:30 -0800295
Ryan Yind8be3882021-10-13 20:59:41 -0700296 def attempt_append_multispline(self):
Ravago Jones8da89c42022-07-17 19:34:06 -0700297 if (len(self.multispline_stack) == 0
298 or self.points.toMultiSpline() != self.multispline_stack[-1]):
Ryan Yind8be3882021-10-13 20:59:41 -0700299 self.multispline_stack.append(self.points.toMultiSpline())
300
301 def clear_graph(self, should_attempt_append=True):
302 if should_attempt_append:
303 self.attempt_append_multispline()
Ryan Yin85f861f2021-09-16 17:55:11 -0700304 self.points = Points()
305 #recalulate graph using new points
306 self.graph.axis.clear()
307 self.graph.queue_draw()
308 #allow placing again
309 self.mode = Mode.kPlacing
310 #redraw entire graph
311 self.queue_draw()
Ryan Yind8be3882021-10-13 20:59:41 -0700312
Ryan Yind8be3882021-10-13 20:59:41 -0700313 def undo(self):
314 try:
315 self.multispline_stack.pop()
316 except IndexError:
317 return
318 if len(self.multispline_stack) == 0:
Ravago Jones8da89c42022-07-17 19:34:06 -0700319 self.clear_graph(
320 should_attempt_append=False) #clear, don't do anything
Ryan Yind8be3882021-10-13 20:59:41 -0700321 return
322 multispline = self.multispline_stack[-1]
323 if multispline['spline_count'] > 0:
324 self.points.fromMultiSpline(multispline)
Ravago Jones8da89c42022-07-17 19:34:06 -0700325 self.mode = Mode.kEditing
Ryan Yind8be3882021-10-13 20:59:41 -0700326 else:
327 self.mode = Mode.kPlacing
328 self.clear_graph(should_attempt_append=False)
329 self.queue_draw()
330
Ravago Jones76ecec82021-08-07 14:37:08 -0700331 def do_key_press_event(self, event):
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800332 keyval = Gdk.keyval_to_lower(event.keyval)
Ryan Yind8be3882021-10-13 20:59:41 -0700333 if keyval == Gdk.KEY_z and event.state & Gdk.ModifierType.CONTROL_MASK:
334 self.undo()
Ravago Jones6d460fe2021-07-03 16:59:55 -0700335 # TODO: This should be a button
Andrew Runke6842bf92019-01-26 15:38:25 -0800336 if keyval == Gdk.KEY_p:
337 self.mode = Mode.kPlacing
338 # F0 = A1
339 # B1 = 2F0 - E0
340 # C1= d0 + 4F0 - 4E0
John Park91e69732019-03-03 13:12:43 -0800341 spline_index = len(self.points.getSplines()) - 1
342 self.points.resetPoints()
343 self.points.extrapolate(
344 self.points.getSplines()[len(self.points.getSplines()) - 1][5],
345 self.points.getSplines()[len(self.points.getSplines()) - 1][4],
346 self.points.getSplines()[len(self.points.getSplines()) - 1][3])
Ravago Jones128fb992021-07-31 13:56:58 -0700347 self.queue_draw()
Tabitha Jarvis1007a132018-12-12 21:47:54 -0800348
Ravago Jones76ecec82021-08-07 14:37:08 -0700349 def do_button_release_event(self, event):
Ryan Yind8be3882021-10-13 20:59:41 -0700350 self.attempt_append_multispline()
Ravago Jones76ecec82021-08-07 14:37:08 -0700351 self.mousex, self.mousey = self.input_transform.transform_point(
352 event.x, event.y)
353 if self.mode == Mode.kEditing:
Ravago Jones6b24b8d2022-03-16 21:02:37 -0700354 if self.index_of_edit > -1:
Ravago Jones76ecec82021-08-07 14:37:08 -0700355 self.points.setSplines(self.spline_edit, self.index_of_edit,
Ravago Jones8da89c42022-07-17 19:34:06 -0700356 self.mousex, self.mousey)
Ravago Jones76ecec82021-08-07 14:37:08 -0700357
358 self.points.splineExtrapolate(self.spline_edit)
359
360 self.points.update_lib_spline()
361 self.graph.schedule_recalculate(self.points)
362
363 self.index_of_edit = -1
364 self.spline_edit = -1
365
366 def do_button_press_event(self, event):
Ravago Jones797c49c2021-07-31 14:51:59 -0700367 self.mousex, self.mousey = self.input_transform.transform_point(
368 event.x, event.y)
Ravago Jones6d460fe2021-07-03 16:59:55 -0700369
Andrew Runke6842bf92019-01-26 15:38:25 -0800370 if self.mode == Mode.kPlacing:
Ravago Jones8da89c42022-07-17 19:34:06 -0700371 if self.points.add_point(self.mousex, self.mousey):
John Park91e69732019-03-03 13:12:43 -0800372 self.mode = Mode.kEditing
Ravago Jones76ecec82021-08-07 14:37:08 -0700373 self.graph.schedule_recalculate(self.points)
Andrew Runke6842bf92019-01-26 15:38:25 -0800374 elif self.mode == Mode.kEditing:
375 # Now after index_of_edit is not -1, the point is selected, so
376 # user can click for new point
Ravago Jones128fb992021-07-31 13:56:58 -0700377 if self.index_of_edit == -1:
Andrew Runke6842bf92019-01-26 15:38:25 -0800378 # Get clicked point
379 # Find nearest
380 # Move nearest to clicked
Ravago Jones54dafeb2022-03-02 20:41:47 -0800381 cur_p = [self.mousex, self.mousey]
Andrew Runke6842bf92019-01-26 15:38:25 -0800382 # Get the distance between each for x and y
383 # Save the index of the point closest
John Park13d3e282019-01-26 20:16:48 -0800384 nearest = 1 # Max distance away a the selected point can be in meters
Andrew Runke6842bf92019-01-26 15:38:25 -0800385 index_of_closest = 0
James Kuszmaul1c933e02020-03-07 16:17:51 -0800386 for index_splines, points in enumerate(
387 self.points.getSplines()):
Andrew Runke6842bf92019-01-26 15:38:25 -0800388 for index_points, val in enumerate(points):
Andrew Runke6842bf92019-01-26 15:38:25 -0800389 distance = np.sqrt((cur_p[0] - val[0])**2 +
390 (cur_p[1] - val[1])**2)
391 if distance < nearest:
392 nearest = distance
393 index_of_closest = index_points
394 print("Nearest: " + str(nearest))
395 print("Index: " + str(index_of_closest))
396 self.index_of_edit = index_of_closest
397 self.spline_edit = index_splines
Ravago Jones6d460fe2021-07-03 16:59:55 -0700398 self.held_x = self.mousex
Ravago Jones128fb992021-07-31 13:56:58 -0700399 self.queue_draw()
400
Ravago Jones76ecec82021-08-07 14:37:08 -0700401 def do_motion_notify_event(self, event):
402 old_x = self.mousex
403 old_y = self.mousey
Ravago Jones797c49c2021-07-31 14:51:59 -0700404 self.mousex, self.mousey = self.input_transform.transform_point(
405 event.x, event.y)
Ravago Jones76ecec82021-08-07 14:37:08 -0700406 dif_x = self.mousex - old_x
407 dif_y = self.mousey - old_y
Ravago Jones54dafeb2022-03-02 20:41:47 -0800408 difs = np.array([dif_x, dif_y])
Ravago Jones128fb992021-07-31 13:56:58 -0700409
Ravago Jones76ecec82021-08-07 14:37:08 -0700410 if self.mode == Mode.kEditing and self.spline_edit != -1:
411 self.points.updates_for_mouse_move(self.index_of_edit,
Ravago Jones8da89c42022-07-17 19:34:06 -0700412 self.spline_edit, self.mousex,
Ravago Jones54dafeb2022-03-02 20:41:47 -0800413 self.mousey, difs)
Ravago Jones128fb992021-07-31 13:56:58 -0700414
Ravago Jones76ecec82021-08-07 14:37:08 -0700415 self.points.update_lib_spline()
416 self.graph.schedule_recalculate(self.points)
417 self.queue_draw()
Ravago Jones128fb992021-07-31 13:56:58 -0700418
Ravago Jones76ecec82021-08-07 14:37:08 -0700419 def do_scroll_event(self, event):
Ravago Jones54dafeb2022-03-02 20:41:47 -0800420
Ravago Jones01781202021-08-01 15:25:11 -0700421 self.mousex, self.mousey = self.input_transform.transform_point(
422 event.x, event.y)
423
Ravago Jones54dafeb2022-03-02 20:41:47 -0800424 step_size = self.pxToM(20) # px
Ravago Jones01781202021-08-01 15:25:11 -0700425
426 if event.direction == Gdk.ScrollDirection.UP:
427 # zoom out
428 scale_by = step_size
429 elif event.direction == Gdk.ScrollDirection.DOWN:
430 # zoom in
431 scale_by = -step_size
432 else:
433 return
434
Ravago Jones54dafeb2022-03-02 20:41:47 -0800435 scale = (self.field.width + scale_by) / self.field.width
Ravago Jones01781202021-08-01 15:25:11 -0700436
Ribhav Kaulfa68ba82021-09-11 16:26:57 -0700437 # This restricts the amount it can be scaled.
Ravago Jones54dafeb2022-03-02 20:41:47 -0800438 if self.zoom_transform.xx <= 0.5:
Ribhav Kaulfa68ba82021-09-11 16:26:57 -0700439 scale = max(scale, 1)
Ravago Jones54dafeb2022-03-02 20:41:47 -0800440 elif self.zoom_transform.xx >= 16:
Ribhav Kaulfa68ba82021-09-11 16:26:57 -0700441 scale = min(scale, 1)
442
Ravago Jonesde18dfe2022-03-16 20:57:19 -0700443 # undo the scaled translation that the old zoom transform did
Ravago Jones8da89c42022-07-17 19:34:06 -0700444 x, y = self.invert(self.zoom_transform).transform_point(
445 event.x, event.y)
Ravago Jonesde18dfe2022-03-16 20:57:19 -0700446
Ravago Jones01781202021-08-01 15:25:11 -0700447 # move the origin to point
Ravago Jonesde18dfe2022-03-16 20:57:19 -0700448 self.zoom_transform.translate(x, y)
Ravago Jones01781202021-08-01 15:25:11 -0700449
450 # scale from new origin
Ravago Jones54dafeb2022-03-02 20:41:47 -0800451 self.zoom_transform.scale(scale, scale)
Ravago Jones01781202021-08-01 15:25:11 -0700452
453 # move back
Ravago Jonesde18dfe2022-03-16 20:57:19 -0700454 self.zoom_transform.translate(-x, -y)
Ravago Jones01781202021-08-01 15:25:11 -0700455
456 self.queue_draw()