Add color class and a dict of common colors.

Add a color class and a dictionary of commonly used colors so that we
can use set_color() instead of calling the set_source_rgb() function
with rgb values every time we need to change colors. Also, removed an
unused set_color() function from within graph_edit.py.

Change-Id: I113285f1afcb11cfc3a236bedeb5b885da19eaa9
diff --git a/y2018/control_loops/python/BUILD b/y2018/control_loops/python/BUILD
index 2335897..43fa3a8 100644
--- a/y2018/control_loops/python/BUILD
+++ b/y2018/control_loops/python/BUILD
@@ -167,6 +167,7 @@
     name = "basic_window",
     srcs = [
         "basic_window.py",
+        "color.py",
     ],
     restricted_to = ["//tools:k8"],
     deps = [
diff --git a/y2018/control_loops/python/basic_window.py b/y2018/control_loops/python/basic_window.py
index 7caf299..7a34b3a 100644
--- a/y2018/control_loops/python/basic_window.py
+++ b/y2018/control_loops/python/basic_window.py
@@ -4,6 +4,7 @@
 from gi.repository import GLib
 from gi.repository import Gdk
 from gi.repository import GdkX11
+from color import Color, palette
 import cairo
 
 identity = cairo.Matrix()
@@ -25,6 +26,11 @@
 
 mainloop = GLib.MainLoop()
 
+def set_color(cr, color):
+    if color.a == 1.0:
+        cr.set_source_rgb(color.r, color.g, color.b)
+    else:
+        cr.set_source_rgba(color.r, color.g, color.b, color.a)
 
 def quit_main_loop(*args):
     mainloop.quit()
diff --git a/y2018/control_loops/python/color.py b/y2018/control_loops/python/color.py
new file mode 100644
index 0000000..ec53e82
--- /dev/null
+++ b/y2018/control_loops/python/color.py
@@ -0,0 +1,20 @@
+class Color:
+    def __init__(self, r, g, b, a=1.0):
+      self.r = r
+      self.g = g
+      self.b = b
+      self.a = a
+
+palette = {
+    "RED": Color(1, 0, 0),
+    "GREEN": Color(0, 1, 0),
+    "BLUE": Color(0, 0, 1),
+    "YELLOW": Color(1, 1, 0),
+    "MAGENTA": Color(1, 0, 1),
+    "CYAN": Color(0, 1, 1),
+    "BLACK": Color(0, 0, 0),
+    "WHITE": Color(1, 1, 1),
+    "GREY": Color(0.5, 0.5, 0.5),
+    "LIGHT_GREY": Color(0.75, 0.75, 0.75),
+    "DARK_GREY": Color(0.25, 0.25, 0.25)
+}
diff --git a/y2018/control_loops/python/graph_edit.py b/y2018/control_loops/python/graph_edit.py
index e2b0412..a2cf414 100644
--- a/y2018/control_loops/python/graph_edit.py
+++ b/y2018/control_loops/python/graph_edit.py
@@ -3,6 +3,7 @@
 from __future__ import print_function
 import os
 import basic_window
+from color import Color, palette
 import random
 import gi
 import numpy
@@ -14,7 +15,7 @@
 from graph_generate import back_to_xy_loop, subdivide_theta, to_theta_loop
 from graph_generate import l1, l2, joint_center
 
-from basic_window import OverrideMatrix, identity, quit_main_loop
+from basic_window import OverrideMatrix, identity, quit_main_loop, set_color
 
 import shapely
 from shapely.geometry import Polygon
@@ -210,11 +211,11 @@
         # use "with px(cr): blah;" to transform to pixel coordinates.
 
         # Fill the background color of the window with grey
-        cr.set_source_rgb(0.5, 0.5, 0.5)
+        set_color(cr, palette["GREY"])
         cr.paint()
 
         # Draw a extents rectangle
-        cr.set_source_rgb(1.0, 1.0, 1.0)
+        set_color(cr, palette["WHITE"])
         cr.rectangle(self.extents_x_min, self.extents_y_min,
                      (self.extents_x_max - self.extents_x_min),
                      self.extents_y_max - self.extents_y_min)
@@ -222,11 +223,11 @@
 
         if not self.theta_version:
             # Draw a filled white rectangle.
-            cr.set_source_rgb(1.0, 1.0, 1.0)
+            set_color(cr, palette["WHITE"])
             cr.rectangle(-2.0, -2.0, 4.0, 4.0)
             cr.fill()
 
-            cr.set_source_rgb(0.0, 0.0, 1.0)
+            set_color(cr, palette["BLUE"])
             cr.arc(joint_center[0], joint_center[1], l2 + l1, 0,
                    2.0 * numpy.pi)
             with px(cr):
@@ -237,12 +238,12 @@
                 cr.stroke()
         else:
             # Draw a filled white rectangle.
-            cr.set_source_rgb(1.0, 1.0, 1.0)
+            set_color(cr, palette["WHITE"])
             cr.rectangle(-numpy.pi, -numpy.pi, numpy.pi * 2.0, numpy.pi * 2.0)
             cr.fill()
 
         if self.theta_version:
-            cr.set_source_rgb(0.0, 0.0, 1.0)
+            set_color(cr, palette["BLUE"])
             for i in range(-6, 6):
                 cr.move_to(-40, -40 + i * numpy.pi)
                 cr.line_to(40, 40 + i * numpy.pi)
@@ -250,33 +251,19 @@
                 cr.stroke()
 
         if self.theta_version:
-            cr.set_source_rgb(0.5, 0.5, 1.0)
+            set_color(cr, Color(0.5, 0.5, 1.0))
             draw_lines(cr, lines_theta)
         else:
-            cr.set_source_rgb(0.5, 1.0, 1.0)
+            set_color(cr, Color(0.5, 1.0, 1.0))
             draw_lines(cr, lines1)
             draw_lines(cr, lines2)
 
-            def set_color(cr, circular_index):
-                if circular_index == -2:
-                    cr.set_source_rgb(0.0, 0.25, 1.0)
-                elif circular_index == -1:
-                    cr.set_source_rgb(0.5, 0.0, 1.0)
-                elif circular_index == 0:
-                    cr.set_source_rgb(0.5, 1.0, 1.0)
-                elif circular_index == 1:
-                    cr.set_source_rgb(0.0, 0.5, 1.0)
-                elif circular_index == 2:
-                    cr.set_source_rgb(0.5, 1.0, 0.5)
-                else:
-                    cr.set_source_rgb(1.0, 0.0, 0.0)
-
             def get_circular_index(pt):
                 theta1, theta2 = pt
                 circular_index = int(numpy.floor((theta2 - theta1) / numpy.pi))
                 return circular_index
 
-            cr.set_source_rgb(0.0, 0.0, 1.0)
+            set_color(cr, palette["BLUE"])
             lines = subdivide_theta(lines_theta)
             o_circular_index = circular_index = get_circular_index(lines[0])
             p_xy = to_xy(lines[0][0], lines[0][1])
@@ -312,32 +299,30 @@
                 cr.stroke()
 
             cr.move_to(self.last_pos[0], self.last_pos[1])
-            cr.set_source_rgb(0.0, 1.0, 0.2)
+            set_color(cr, Color(0.0, 1.0, 0.2))
             draw_px_cross(cr, 20)
 
         if self.theta_version:
-            cr.set_source_rgb(0.0, 1.0, 0.2)
-
-            cr.set_source_rgb(0.0, 1.0, 0.2)
+            set_color(cr, Color(0.0, 1.0, 0.2))
             cr.move_to(self.last_pos[0], self.last_pos[1])
             draw_px_cross(cr, 5)
 
             c_pt, dist = closest_segment(lines_theta, self.last_pos)
             print("dist:", dist, c_pt, self.last_pos)
-            cr.set_source_rgb(0.0, 1.0, 1.0)
+            set_color(cr, palette["CYAN"])
             cr.move_to(c_pt[0], c_pt[1])
             draw_px_cross(cr, 5)
 
-        cr.set_source_rgb(0.0, 0.5, 1.0)
+        set_color(cr, Color(0.0, 0.5, 1.0))
         for segment in self.segments:
             color = [0, random.random(), 1]
             random.shuffle(color)
-            cr.set_source_rgb(*color)
+            set_color(cr, Color(color[0], color[1], color[2]))
             segment.DrawTo(cr, self.theta_version)
             with px(cr):
                 cr.stroke()
 
-        cr.set_source_rgb(0.0, 1.0, 0.5)
+        set_color(cr, Color(0.0, 1.0, 0.5))
         segment = self.current_seg()
         if segment:
             print(segment)
diff --git a/y2018/control_loops/python/path_edit.py b/y2018/control_loops/python/path_edit.py
index ce2118a..a0aa621 100644
--- a/y2018/control_loops/python/path_edit.py
+++ b/y2018/control_loops/python/path_edit.py
@@ -1,362 +1,358 @@
-#!/usr/bin/python3

-

-from __future__ import print_function

-import os

-import basic_window

-import random

-import gi

-import numpy as np

-import scipy.spatial.distance

-gi.require_version('Gtk', '3.0')

-from gi.repository import Gdk

-import cairo

-

-import enum

-import csv # For writing to csv files

-

-from basic_window import OverrideMatrix, identity, quit_main_loop

-

-LENGTH_OF_FIELD = 323.65

-PIXELS_ON_SCREEN = 300

-

-def pxToIn(p):

-    return p*LENGTH_OF_FIELD/PIXELS_ON_SCREEN

-

-def inToPx(i):

-    return i*PIXELS_ON_SCREEN/LENGTH_OF_FIELD

-

-def px(cr):

-    return OverrideMatrix(cr, identity)

-

-def draw_px_cross(cr, x, y, length_px, r, g, b):

-    """Draws a cross with fixed dimensions in pixel space."""

-    cr.set_source_rgb(r, g, b)

-    cr.move_to(x, y - length_px)

-    cr.line_to(x, y + length_px)

-    cr.stroke()

-

-    cr.move_to(x - length_px, y)

-    cr.line_to(x + length_px, y)

-    cr.stroke()

-

-def draw_px_x(cr, x, y, length_px1, r, g, b):

-    """Draws a x with fixed dimensions in pixel space."""

-    length_px = length_px1 / np.sqrt(2)

-    cr.set_source_rgb(r, g, b)

-    cr.move_to(x - length_px, y - length_px)

-    cr.line_to(x + length_px, y + length_px)

-    cr.stroke()

-

-    cr.move_to(x - length_px, y + length_px)

-    cr.line_to(x + length_px, y - length_px)

-    cr.stroke()

-

-def draw_points(cr, p, size):

-    for i in range(0, len(p)):

-        draw_px_cross(cr, p[i][0], p[i][1], size, 0, np.sqrt(0.2 * i), 0)

-

-class Mode(enum.Enum):

-    kViewing = 0

-    kPlacing = 1

-    kEditing = 2

-    kExporting = 3

-    kImporting = 4

-

-def display_text(cr, text, widtha, heighta, widthb, heightb):

-    cr.scale(widtha, -heighta)

-    cr.show_text(text)

-    cr.scale(widthb, -heightb)

-

-# Create a GTK+ widget on which we will draw using Cairo

-class GTK_Widget(basic_window.BaseWindow):

-    def __init__(self):

-        super(GTK_Widget, self).__init__()

-

-        # init field drawing

-        # add default spline for testing purposes

-        # init editing / viewing modes and pointer location

-        self.mode = Mode.kPlacing

-        self.x = 0

-        self.y = 0

-

-        self.switch = True

-

-        # update list of control points

-        self.point_selected = False

-        # self.adding_spline = False

-        self.index_of_selected = -1

-        self.new_point = []

-

-        # For the editing mode

-        self.index_of_edit = -1 # Can't be zero beause array starts at 0

-        self.held_x = 0

-

-        # Theo take them from here?

-        self.selected_points = []

-

-        self.reinit_extents()

-

-    #John also wrote this

-    def add_point(self, x, y):

-        if(len(self.selected_points)<4):

-            self.selected_points.append([x,y])

-

-    """set extents on images, this needs to be redone with proper distances"""

-    def reinit_extents(self):

-        self.extents_x_min = -800

-        self.extents_x_max = 800

-        self.extents_y_min = -800

-        self.extents_y_max = 800

-

-    # this needs to be rewritten with numpy, i dont think this ought to have

-    # SciPy as a dependecy

-    def get_index_of_nearest_point(self):

-        cur_p = [[self.x, self.y]]

-        distances = scipy.spatial.distance.cdist(cur_p, self.all_controls)

-

-        return np.argmin(distances)

-

-    # return the closest point to the loc of the click event

-    def get_nearest_point(self):

-        return self.all_controls[self.get_index_of_nearest_point()]

-

-      # Handle the expose-event by updating the Window and drawing

-    def handle_draw(self, cr):

-        print(self.new_point)

-        print("SELF.POINT_SELECTED: " + str(self.point_selected))

-

-        # begin drawing

-        # Fill the background color of the window with grey

-        cr.set_source_rgb(0.5, 0.5, 0.5)

-        cr.paint()

-

-        # Draw a extents rectangle

-        cr.set_source_rgb(1.0, 1.0, 1.0)

-        cr.rectangle(self.extents_x_min, self.extents_y_min,

-                     (self.extents_x_max - self.extents_x_min),

-                     self.extents_y_max - self.extents_y_min)

-        cr.fill()

-

-        #Drawing the switch and scale in the field

-        cr.move_to(0, 50)

-        cr.show_text('Press "e" to export')

-        cr.show_text('Press "i" to import')

-

-        cr.set_source_rgb(0.3,0.3,0.3)

-        cr.rectangle(-150,-150,300,300)

-        cr.fill()

-        cr.set_source_rgb(0, 0, 0)

-        cr.rectangle(-150,-150,300,300)

-        cr.set_line_join(cairo.LINE_JOIN_ROUND)

-        cr.stroke()

-        cr.set_source_rgb(0, 0, 0)

-        cr.rectangle(inToPx(140-161.825),inToPx(76.575),inToPx(56),inToPx(-153.15))

-        cr.set_line_join(cairo.LINE_JOIN_ROUND)

-        cr.stroke()

-

-        cr.rectangle(inToPx(161.825-24),inToPx(90),inToPx(24),inToPx(-180))

-        cr.set_line_join(cairo.LINE_JOIN_ROUND)

-        cr.stroke()

-

-        cr.set_source_rgb(0.2, 0.2, 0.2)

-        cr.rectangle(inToPx(140-161.825),inToPx(76.575),inToPx(56),inToPx(-153.15))

-        cr.fill()

-

-        cr.rectangle(inToPx(161.825-24),inToPx(90),inToPx(24),inToPx(-180))

-        cr.fill()

-

-        # update all the things

-

-        if self.mode == Mode.kViewing:

-            cr.set_source_rgb(0, 0, 0)

-            cr.move_to(-300, 170)

-            cr.show_text("VIEWING")

-            cr.set_source_rgb(0.5, 0.5, 0.5)

-            # its gonna check for points_added from button_press_action

-            # The behavior of the click is such that it runs twice

-            # This is consistant with graph_edit.py which someone smart wrote

-            # So I'm just going to delete the last element in order to not get

-            # repeating points

-            if len(self.selected_points) > 0:

-                print("SELECTED_POINTS: " + str(len(self.selected_points)))

-                print("ITEMS:")

-                for item in self.selected_points:

-                    print(str(item))

-                for i, point in enumerate(self.selected_points):

-                    print("I: " + str(i))

-                    draw_px_x(cr, point[0], point[1], 10, 0,

-                                0, 0)

-                    cr.move_to(point[0], point[1]-15)

-                    display_text(cr, str(i), 0.5, 0.5, 2, 2)

-

-        if self.mode == Mode.kPlacing:

-            cr.set_source_rgb(0, 0, 0)

-            cr.move_to(-300, 170)

-            display_text(cr, "ADD", 1, 1, 1, 1)

-            cr.set_source_rgb(0.5, 0.5, 0.5)

-            # its gonna check for points_added from button_press_action

-            # The behavior of the click is such that it runs twice

-            # This is consistant with graph_edit.py which someone smart wrote

-            # So I'm just going to delete the last element in order to not get

-            # repeating points

-            if len(self.selected_points) > 0:

-                print("SELECTED_POINTS: " + str(len(self.selected_points)))

-                print("ITEMS:")

-                for item in self.selected_points:

-                    print(str(item))

-                for i, point in enumerate(self.selected_points):

-                    print("I: " + str(i))

-                    draw_px_x(cr, point[0], point[1], 10, 0,

-                                0, 0)

-                    cr.move_to(point[0], point[1]-15)

-                    display_text(cr, str(i), 0.5, 0.5, 2, 2)

-                    if(i==3):

-                        self.mode = Mode.kEditing

-

-        elif self.mode == Mode.kEditing:

-            cr.set_source_rgb(0, 0, 0)

-            cr.move_to(-300, 170)

-            display_text(cr, "EDITING", 1, 1, 1, 1)

-            cr.set_source_rgb(0.5, 0.5, 0.5)

-            if len(self.selected_points) > 0:

-                print("SELECTED_POINTS: " + str(len(self.selected_points)))

-                print("ITEMS:")

-                for item in self.selected_points:

-                    print(str(item))

-                for i, point in enumerate(self.selected_points):

-                    print("I: " + str(i))

-                    draw_px_x(cr, point[0], point[1], 10, 0,

-                                0, 0)

-                    cr.move_to(point[0], point[1]-15)

-                    display_text(cr, str(i), 0.5, 0.5, 2, 2)

-

-        elif self.mode == Mode.kExporting:

-            cr.set_source_rgb(0, 0, 0)

-            cr.move_to(-300, 170)

-            display_text(cr, "VIEWING", 1, 1, 1, 1)

-            cr.set_source_rgb(0.5, 0.5, 0.5)

-            #its gonna check for points_added from button_press_action

-

-            # The behavior of the click is such that it runs twice

-            # This is consistant with graph_edit.py which someone smart wrote

-            # So I'm just going to delete the last element in order to not get

-            # repeating points

-            if len(self.selected_points) > 0:

-                print("SELECTED_POINTS: " + str(len(self.selected_points)))

-                print("ITEMS:")

-                for item in self.selected_points:

-                    print(str(item))

-                for i, point in enumerate(self.selected_points):

-                    print("I: " + str(i))

-                    draw_px_x(cr, point[0], point[1], 10, 0,

-                                0, 0)

-                    cr.move_to(point[0], point[1]-15)

-                    display_text(cr, str(i), 0.5, 0.5, 2, 2)

-        elif self.mode == Mode.kImporting:

-            cr.set_source_rgb(0, 0, 0)

-            cr.move_to(-300, 170)

-            display_text(cr, "VIEWING", 1, 1, 1, 1)

-            cr.set_source_rgb(0.5, 0.5, 0.5)

-            # its gonna check for points_added from button_press_action

-

-            # The behavior of the click is such that it runs twice

-            # This is consistant with graph_edit.py which someone smart wrote

-            # So I'm just going to delete the last element in order to not get

-            # repeating points

-            if len(self.selected_points) > 0:

-                print("SELECTED_POINTS: " + str(len(self.selected_points)))

-                print("ITEMS:")

-                for item in self.selected_points:

-                    print(str(item))

-                for i, point in enumerate(self.selected_points):

-                    print("I: " + str(i))

-                    draw_px_x(cr, point[0], point[1], 10, 0,

-                                0, 0)

-                    cr.move_to(point[0], point[1]-15)

-                    display_text(cr, str(i), 0.5, 0.5, 2, 2)

-

-        cr.paint_with_alpha(.65)

-

-        draw_px_cross(cr, self.x, self.y, 10, 1, 0, 0)

-

-    def do_key_press(self, event):

-        keyval = Gdk.keyval_to_lower(event.keyval)

-        print("Gdk.KEY_" + Gdk.keyval_name(keyval))

-        if keyval == Gdk.KEY_q:

-            print("Found q key and exiting.")

-            quit_main_loop()

-        if keyval == Gdk.KEY_e:

-            self.mode = Mode.kExporting

-            # Will export to csv file

-            with open('points_for_pathedit.csv', mode='w') as points_file:

-                writer = csv.writer(points_file, delimiter=',', quotechar='"',

-                        quoting=csv.QUOTE_MINIMAL)

-                for item in self.selected_points:

-                    writer.writerow([str(item[0]), str(item[1])])

-                    print("Wrote: " + str(item[0]) + " " +  str(item[1]))

-        if keyval == Gdk.KEY_i:

-            self.mode = Mode.kImporting

-            # import from csv file

-            self.selected_points = []

-            with open('points_for_pathedit.csv') as points_file:

-                reader = csv.reader(points_file, delimiter=',')

-                for row in reader:

-                    self.add_point(float(row[0]), float(row[1]))

-                    print("Added: " + row[0] + " " +  row[1])

-

-        self.redraw()

-

-    def button_press_action(self):

-        if self.switch:

-            self.switch = False

-            if self.mode == Mode.kPlacing:

-                #Check that the point clicked is on the field

-                if(self.x<150 and self.x>-150 and self.y <150 and self.y >-150):

-                    self.add_point(self.x, self.y)

-            if self.mode == Mode.kEditing:

-                # Now after index_of_edit is not -1, the point is selected, so

-                # user can click for new point

-                    print("INDEX OF EDIT: " + str(self.index_of_edit))

-

-                    if self.index_of_edit > -1 and self.held_x != self.x:

-                        print("INDEX OF EDIT: " + str(self.index_of_edit))

-                        self.selected_points[self.index_of_edit] = [self.x, self.y]

-                        self.index_of_edit = -1

-                    else:

-                        print("mode == 2")

-                        # Get clicked point

-                        # Find nearest

-                        # Move nearest to clicked

-                        cur_p = [self.x, self.y]

-                        print("CUR_P: " + str(self.x) + " " + str(self.y))

-                        # What I wanna do is get each point

-                        # Get the distance between each for x and y

-                        # Save the index of the point closest

-                        nearest = 1000

-                        index = 0

-                        for ind, i in enumerate(self.selected_points):

-                            # pythagorean

-                            distance = np.sqrt((cur_p[0] - i[0])**2 + (cur_p[1] - i[1])**2)

-                            if distance < nearest:

-                                nearest = distance

-                                index = ind

-                                print("Nearest: " + str(nearest))

-                                print("Index: " + str(index))

-                                self.index_of_edit = index

-                                self.held_x = self.x

-        else:

-            self.switch = True

-

-        self.redraw()

-

-

-    def do_button_press(self, event):

-        print("button press activated")

-        self.x = event.x

-        self.y = event.y

-        self.button_press_action()

-

-

-

-silly = GTK_Widget()

-basic_window.RunApp()

+#!/usr/bin/python3
+
+from __future__ import print_function
+import os
+import basic_window
+from color import Color, palette
+import random
+import gi
+import numpy as np
+import scipy.spatial.distance
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gdk
+import cairo
+
+import enum
+import csv # For writing to csv files
+
+from basic_window import OverrideMatrix, identity, quit_main_loop, set_color
+
+LENGTH_OF_FIELD = 323.65
+PIXELS_ON_SCREEN = 300
+
+def pxToIn(p):
+    return p*LENGTH_OF_FIELD/PIXELS_ON_SCREEN
+
+def inToPx(i):
+    return i*PIXELS_ON_SCREEN/LENGTH_OF_FIELD
+
+def px(cr):
+    return OverrideMatrix(cr, identity)
+
+def draw_px_cross(cr, x, y, length_px, color=palette["RED"]):
+    """Draws a cross with fixed dimensions in pixel space."""
+    set_color(cr, color)
+    cr.move_to(x, y - length_px)
+    cr.line_to(x, y + length_px)
+    cr.stroke()
+
+    cr.move_to(x - length_px, y)
+    cr.line_to(x + length_px, y)
+    cr.stroke()
+    set_color(cr, palette["LIGHT_GREY"])
+
+def draw_px_x(cr, x, y, length_px1, color=palette["BLACK"]):
+    """Draws a x with fixed dimensions in pixel space."""
+    length_px = length_px1 / np.sqrt(2)
+    set_color(cr, color)
+    cr.move_to(x - length_px, y - length_px)
+    cr.line_to(x + length_px, y + length_px)
+    cr.stroke()
+
+    cr.move_to(x - length_px, y + length_px)
+    cr.line_to(x + length_px, y - length_px)
+    cr.stroke()
+    set_color(cr, palette["LIGHT_GREY"])
+
+def draw_points(cr, p, size):
+    for i in range(0, len(p)):
+        draw_px_cross(cr, p[i][0], p[i][1], size, Color(0, np.sqrt(0.2 * i), 0))
+
+class Mode(enum.Enum):
+    kViewing = 0
+    kPlacing = 1
+    kEditing = 2
+    kExporting = 3
+    kImporting = 4
+
+def display_text(cr, text, widtha, heighta, widthb, heightb):
+    cr.scale(widtha, -heighta)
+    cr.show_text(text)
+    cr.scale(widthb, -heightb)
+
+# Create a GTK+ widget on which we will draw using Cairo
+class GTK_Widget(basic_window.BaseWindow):
+    def __init__(self):
+        super(GTK_Widget, self).__init__()
+
+        # init field drawing
+        # add default spline for testing purposes
+        # init editing / viewing modes and pointer location
+        self.mode = Mode.kPlacing
+        self.x = 0
+        self.y = 0
+
+        self.switch = True
+
+        # update list of control points
+        self.point_selected = False
+        # self.adding_spline = False
+        self.index_of_selected = -1
+        self.new_point = []
+
+        # For the editing mode
+        self.index_of_edit = -1 # Can't be zero beause array starts at 0
+        self.held_x = 0
+
+        # Theo take them from here?
+        self.selected_points = []
+
+        self.reinit_extents()
+
+    #John also wrote this
+    def add_point(self, x, y):
+        if(len(self.selected_points)<4):
+            self.selected_points.append([x,y])
+
+    """set extents on images, this needs to be redone with proper distances"""
+    def reinit_extents(self):
+        self.extents_x_min = -800
+        self.extents_x_max = 800
+        self.extents_y_min = -800
+        self.extents_y_max = 800
+
+    # this needs to be rewritten with numpy, i dont think this ought to have
+    # SciPy as a dependecy
+    def get_index_of_nearest_point(self):
+        cur_p = [[self.x, self.y]]
+        distances = scipy.spatial.distance.cdist(cur_p, self.all_controls)
+
+        return np.argmin(distances)
+
+    # return the closest point to the loc of the click event
+    def get_nearest_point(self):
+        return self.all_controls[self.get_index_of_nearest_point()]
+
+      # Handle the expose-event by updating the Window and drawing
+    def handle_draw(self, cr):
+        print(self.new_point)
+        print("SELF.POINT_SELECTED: " + str(self.point_selected))
+
+        # begin drawing
+        # Fill the background color of the window with grey
+        set_color(cr, palette["GREY"])
+        cr.paint()
+
+        # Draw a extents rectangle
+        set_color(cr, palette["WHITE"])
+        cr.rectangle(self.extents_x_min, self.extents_y_min,
+                     (self.extents_x_max - self.extents_x_min),
+                     self.extents_y_max - self.extents_y_min)
+        cr.fill()
+
+        #Drawing the switch and scale in the field
+        cr.move_to(0, 50)
+        cr.show_text('Press "e" to export')
+        cr.show_text('Press "i" to import')
+
+        set_color(cr, Color(0.3, 0.3, 0.3))
+        cr.rectangle(-150,-150,300,300)
+        cr.fill()
+        set_color(cr, palette["BLACK"])
+        cr.rectangle(-150,-150,300,300)
+        cr.set_line_join(cairo.LINE_JOIN_ROUND)
+        cr.stroke()
+        cr.rectangle(inToPx(140-161.825),inToPx(76.575),inToPx(56),inToPx(-153.15))
+        cr.set_line_join(cairo.LINE_JOIN_ROUND)
+        cr.stroke()
+        cr.rectangle(inToPx(161.825-24),inToPx(90),inToPx(24),inToPx(-180))
+        cr.set_line_join(cairo.LINE_JOIN_ROUND)
+        cr.stroke()
+
+        set_color(cr, Color(0.2, 0.2, 0.2))
+        cr.rectangle(inToPx(140-161.825),inToPx(76.575),inToPx(56),inToPx(-153.15))
+        cr.fill()
+        cr.rectangle(inToPx(161.825-24),inToPx(90),inToPx(24),inToPx(-180))
+        cr.fill()
+
+        # update all the things
+
+        if self.mode == Mode.kViewing:
+            set_color(cr, palette["BLACK"])
+            cr.move_to(-300, 170)
+            cr.show_text("VIEWING")
+            set_color(cr, palette["GREY"])
+            # its gonna check for points_added from button_press_action
+            # The behavior of the click is such that it runs twice
+            # This is consistant with graph_edit.py which someone smart wrote
+            # So I'm just going to delete the last element in order to not get
+            # repeating points
+            if len(self.selected_points) > 0:
+                print("SELECTED_POINTS: " + str(len(self.selected_points)))
+                print("ITEMS:")
+                for item in self.selected_points:
+                    print(str(item))
+                for i, point in enumerate(self.selected_points):
+                    print("I: " + str(i))
+                    draw_px_x(cr, point[0], point[1], 10)
+                    cr.move_to(point[0], point[1]-15)
+                    display_text(cr, str(i), 0.5, 0.5, 2, 2)
+
+        if self.mode == Mode.kPlacing:
+            set_color(cr, palette["BLACK"])
+            cr.move_to(-300, 170)
+            display_text(cr, "ADD", 1, 1, 1, 1)
+            set_color(cr, palette["GREY"])
+            # its gonna check for points_added from button_press_action
+            # The behavior of the click is such that it runs twice
+            # This is consistant with graph_edit.py which someone smart wrote
+            # So I'm just going to delete the last element in order to not get
+            # repeating points
+            if len(self.selected_points) > 0:
+                print("SELECTED_POINTS: " + str(len(self.selected_points)))
+                print("ITEMS:")
+                for item in self.selected_points:
+                    print(str(item))
+                for i, point in enumerate(self.selected_points):
+                    print("I: " + str(i))
+                    draw_px_x(cr, point[0], point[1], 10)
+                    cr.move_to(point[0], point[1]-15)
+                    display_text(cr, str(i), 0.5, 0.5, 2, 2)
+                    if(i==3):
+                        self.mode = Mode.kEditing
+
+        elif self.mode == Mode.kEditing:
+            set_color(cr, palette["BLACK"])
+            cr.move_to(-300, 170)
+            display_text(cr, "EDITING", 1, 1, 1, 1)
+            set_color(cr, palette["GREY"])
+            if len(self.selected_points) > 0:
+                print("SELECTED_POINTS: " + str(len(self.selected_points)))
+                print("ITEMS:")
+                for item in self.selected_points:
+                    print(str(item))
+                for i, point in enumerate(self.selected_points):
+                    print("I: " + str(i))
+                    draw_px_x(cr, point[0], point[1], 10)
+                    cr.move_to(point[0], point[1]-15)
+                    display_text(cr, str(i), 0.5, 0.5, 2, 2)
+
+        elif self.mode == Mode.kExporting:
+            set_color(cr, palette["BLACK"])
+            cr.move_to(-300, 170)
+            display_text(cr, "VIEWING", 1, 1, 1, 1)
+            set_color(cr, palette["GREY"])
+            #its gonna check for points_added from button_press_action
+
+            # The behavior of the click is such that it runs twice
+            # This is consistant with graph_edit.py which someone smart wrote
+            # So I'm just going to delete the last element in order to not get
+            # repeating points
+            if len(self.selected_points) > 0:
+                print("SELECTED_POINTS: " + str(len(self.selected_points)))
+                print("ITEMS:")
+                for item in self.selected_points:
+                    print(str(item))
+                for i, point in enumerate(self.selected_points):
+                    print("I: " + str(i))
+                    draw_px_x(cr, point[0], point[1], 10)
+                    cr.move_to(point[0], point[1]-15)
+                    display_text(cr, str(i), 0.5, 0.5, 2, 2)
+        elif self.mode == Mode.kImporting:
+            set_color(cr, palette["BLACK"])
+            cr.move_to(-300, 170)
+            display_text(cr, "VIEWING", 1, 1, 1, 1)
+            set_color(cr, palette["GREY"])
+            # its gonna check for points_added from button_press_action
+
+            # The behavior of the click is such that it runs twice
+            # This is consistant with graph_edit.py which someone smart wrote
+            # So I'm just going to delete the last element in order to not get
+            # repeating points
+            if len(self.selected_points) > 0:
+                print("SELECTED_POINTS: " + str(len(self.selected_points)))
+                print("ITEMS:")
+                for item in self.selected_points:
+                    print(str(item))
+                for i, point in enumerate(self.selected_points):
+                    print("I: " + str(i))
+                    draw_px_x(cr, point[0], point[1], 10)
+                    cr.move_to(point[0], point[1]-15)
+                    display_text(cr, str(i), 0.5, 0.5, 2, 2)
+
+
+        cr.paint_with_alpha(.65)
+
+        draw_px_cross(cr, self.x, self.y, 10)
+
+    def do_key_press(self, event):
+        keyval = Gdk.keyval_to_lower(event.keyval)
+        print("Gdk.KEY_" + Gdk.keyval_name(keyval))
+        if keyval == Gdk.KEY_q:
+            print("Found q key and exiting.")
+            quit_main_loop()
+        if keyval == Gdk.KEY_e:
+            self.mode = Mode.kExporting
+            # Will export to csv file
+            with open('points_for_pathedit.csv', mode='w') as points_file:
+                writer = csv.writer(points_file, delimiter=',', quotechar='"',
+                        quoting=csv.QUOTE_MINIMAL)
+                for item in self.selected_points:
+                    writer.writerow([str(item[0]), str(item[1])])
+                    print("Wrote: " + str(item[0]) + " " +  str(item[1]))
+        if keyval == Gdk.KEY_i:
+            self.mode = Mode.kImporting
+            # import from csv file
+            self.selected_points = []
+            with open('points_for_pathedit.csv') as points_file:
+                reader = csv.reader(points_file, delimiter=',')
+                for row in reader:
+                    self.add_point(float(row[0]), float(row[1]))
+                    print("Added: " + row[0] + " " +  row[1])
+
+        self.redraw()
+
+    def button_press_action(self):
+        if self.switch:
+            self.switch = False
+            if self.mode == Mode.kPlacing:
+                #Check that the point clicked is on the field
+                if(self.x<150 and self.x>-150 and self.y <150 and self.y >-150):
+                    self.add_point(self.x, self.y)
+            if self.mode == Mode.kEditing:
+                # Now after index_of_edit is not -1, the point is selected, so
+                # user can click for new point
+                    print("INDEX OF EDIT: " + str(self.index_of_edit))
+
+                    if self.index_of_edit > -1 and self.held_x != self.x:
+                        print("INDEX OF EDIT: " + str(self.index_of_edit))
+                        self.selected_points[self.index_of_edit] = [self.x, self.y]
+                        self.index_of_edit = -1
+                    else:
+                        print("mode == 2")
+                        # Get clicked point
+                        # Find nearest
+                        # Move nearest to clicked
+                        cur_p = [self.x, self.y]
+                        print("CUR_P: " + str(self.x) + " " + str(self.y))
+                        # What I wanna do is get each point
+                        # Get the distance between each for x and y
+                        # Save the index of the point closest
+                        nearest = 1000
+                        index = 0
+                        for ind, i in enumerate(self.selected_points):
+                            # pythagorean
+                            distance = np.sqrt((cur_p[0] - i[0])**2 + (cur_p[1] - i[1])**2)
+                            if distance < nearest:
+                                nearest = distance
+                                index = ind
+                                print("Nearest: " + str(nearest))
+                                print("Index: " + str(index))
+                                self.index_of_edit = index
+                                self.held_x = self.x
+        else:
+            self.switch = True
+
+        self.redraw()
+
+
+    def do_button_press(self, event):
+        print("button press activated")
+        self.x = event.x
+        self.y = event.y
+        self.button_press_action()
+
+
+
+silly = GTK_Widget()
+basic_window.RunApp()