Making a line with (x, y) steps

The purpose of this exercise is to calculate the discreet x and y steps (like we might have from a stepper motor) needed to make a line of any slope. Note: this uses Bresenhan's Algorithm https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm

The general equation of a line through two given points is: $${\frac {y-y_{0}}{y_{1}-y_{0}}}={\frac {x-x_{0}}{x_{1}-x_{0}}}$$

Let's use the example of the two points (0,0) and (20,10)

The equation for this line is then: $$ {\frac {y-0}{10-0}}={\frac {x-0}{20-0}}$$ or $$y={\frac {x}{2}}$$

So, for every time $x$ makes a step, $y$ moves ${\frac {1}{2}}$ step. Since there are no half steps we need to round and return the 'error' to be accumulated as we travel from $(x_{0}, y_{0})$ to $(x_{1}, y_{1})$. Note: we also need to return the current position.


In [1]:
# First do some imports, so we can see what we're doing:
%matplotlib notebook

import matplotlib.pyplot as plt
from math import sqrt

import numpy as np

In [2]:
def get_line(start, end):
    """Bresenham's Line Algorithm
    from: http://www.roguebasin.com/index.php?title=Bresenham%27s_Line_Algorithm
    Produces a list of tuples from start and end
 
    >>> points1 = get_line((0, 0), (3, 4))
    >>> points2 = get_line((3, 4), (0, 0))
    >>> assert(set(points1) == set(points2))
    >>> print points1
    [(0, 0), (1, 1), (1, 2), (2, 3), (3, 4)]
    >>> print points2
    [(3, 4), (2, 3), (1, 2), (1, 1), (0, 0)]
    """
    # Setup initial conditions
    x1, y1 = start
    x2, y2 = end
    dx = x2 - x1
    dy = y2 - y1
 
    # Determine how steep the line is
    is_steep = abs(dy) > abs(dx)
 
    # Rotate line
    if is_steep:
        x1, y1 = y1, x1
        x2, y2 = y2, x2
 
    # Swap start and end points if necessary and store swap state
    swapped = False
    if x1 > x2:
        x1, x2 = x2, x1
        y1, y2 = y2, y1
        swapped = True
 
    # Recalculate differentials
    dx = x2 - x1
    dy = y2 - y1
 
    # Calculate error
    error = int(dx / 2.0)
    ystep = 1 if y1 < y2 else -1
 
    # Iterate over bounding box generating points between start and end
    y = y1
    points = []
    for x in range(x1, x2 + 1):
        coord = (y, x) if is_steep else (x, y)
        points.append(coord)
        error -= abs(dy)
        if error < 0:
            y += ystep
            error += dx
 
    # Reverse the list if the coordinates were swapped
    if swapped:
        points.reverse()
    return points

In [3]:
points = [(20,0), (-20, 0), (20, 10), (10, 20), (0, 20), 
          (-10, 20), (-20, 10), (20, -10), (10, -20), 
          (0, -20), (-10, -20), (-20, -10),
          (20, 3), (-20, 3)]

for point in points:
    line = get_line((0,0), point)
    x, y = zip(*line)
    plt.plot(x, y)


Converting x, y coordinates to cable lengths

           d
m1 o______________o m2
    \          __/
     \      __/
    a \  __/ b
       \/
       *
      (x,y)

Given the coordinates $(x,y)$, we need to calculate the lengths of the two cables which hold the pen, $a$ and $b$.

$a$ is the hypotenuse of a triangle with sides $x$ and $y$, so the length of a is: $$a = {\sqrt {x^2 + y^2}}$$ If the distance between the motors is $d$, then the length of $b$ is given by: $$b = {\sqrt {(d-x)^2 + y^2}}$$

So, if we're looking to draw a line between the "points" $(10, 10)$ and $(25, 50)$, let's first get our cable length targets. Here's a function to do that:


In [60]:
def cable_steps(x, y, d=100, steps_per_unit=1):
    a = int((sqrt(x**2 + y**2))*steps_per_unit)
    b = int((sqrt((d-x)**2 + y**2))*steps_per_unit)
    return a, b

In [109]:
d = 1000

# Get cable lengths (measured in steps) for our two points (measured in inches):
x0, y0 = (850, 400)
x1, y1 = (220, 350)

a0, b0 = cable_steps(x0, y0, d=d)
a1, b1 = cable_steps(x1, y1, d=d)

In [110]:
a_move = a1-a0
b_move = b1-b0
print a0, b0
print a1, b1
print a_move, b_move


939 427
413 854
-526 427

In [111]:
# plot what we know about a couple of points

fig = plt.figure(figsize=(6, 6))

# the motors
m1 = plt.Circle((0, 0), 4, color='black')
m2 = plt.Circle((d, 0), 4, color='black')

# the line between the motors
m_line = plt.Line2D([0, d], [0, 0], color='black')

# the radius of the cables and pen position - start
m1_circle0 = plt.Circle((0, 0), a0, color='gray', fill=False)
m2_circle0 = plt.Circle((d, 0), b0, color='gray', fill=False)
m1_line0 = plt.Line2D([0, x0], [0, -y0], color='red')
m2_line0 = plt.Line2D([d, x0], [0, -y0], color='red')
pen_pos0 = plt.Circle([x0, -y0], 1.0, color='red', fill=False)

# the radius of the cables and pen position - end
m1_circle1 = plt.Circle((0, 0), a1, color='blue', fill=False)
m2_circle1 = plt.Circle((d, 0), b1, color='blue', fill=False)
m1_line1 = plt.Line2D([0, x1], [0, -y1], color='green')
m2_line1 = plt.Line2D([d, x1], [0, -y1], color='green')
pen_pos1 = plt.Circle([x1, -y1], 1.0, color='green', fill=False)

# set a wide range, since mpl is acting goofy about this
plt.axis([-d, 2*d, -d, 2*d])
ax = fig.gca()
ax.set_autoscale_on(False)


# pile all this onto our figure axis
ax.add_artist(m1)
ax.add_artist(m2)
ax.add_artist(m_line)

ax.add_artist(m1_line0)
ax.add_artist(m2_line0)
ax.add_artist(m1_line1)
ax.add_artist(m2_line1)
ax.add_artist(m1_circle0)
ax.add_artist(m2_circle0)
ax.add_artist(pen_pos0)
ax.add_artist(m1_circle1)
ax.add_artist(m2_circle1)
ax.add_artist(pen_pos1)


Out[111]:
<matplotlib.patches.Circle at 0x1230467d0>

In [ ]:

This is where we call our function to get our line movement:


In [112]:
path = get_line((x0, y0), (x1, y1))

In [113]:
path[:20]  # show the first 20 locations in path


Out[113]:
[(850, 400),
 (849, 400),
 (848, 400),
 (847, 400),
 (846, 400),
 (845, 400),
 (844, 400),
 (843, 399),
 (842, 399),
 (841, 399),
 (840, 399),
 (839, 399),
 (838, 399),
 (837, 399),
 (836, 399),
 (835, 399),
 (834, 399),
 (833, 399),
 (832, 399),
 (831, 398)]

In [ ]:


In [119]:
x0, y0 = (850, 400)
x1, y1 = (500, 810)

a0, b0 = cable_steps(x0, y0, d=d)
a1, b1 = cable_steps(x1, y1, d=d)

path = get_line((x0, y0), (x1, y1))

fig2 = plt.figure(figsize=(6, 6))

# the motors
m1 = plt.Circle((0, 0), 4, color='black')
m2 = plt.Circle((d, 0), 4, color='black')

# the line between the motors
m_line = plt.Line2D([0,d], [0, 0], color='black')

# the radius of the cables and pen position - start
m1_circle0 = plt.Circle((0, 0), a0, color='gray', fill=False)
m2_circle0 = plt.Circle((d, 0), b0, color='gray', fill=False)
m1_line0 = plt.Line2D([0, x0], [0, -y0], color='red')
m2_line0 = plt.Line2D([d, x0], [0, -y0], color='red')
pen_pos0 = plt.Circle([x0, -y0], 1.0, color='red', fill=False)

# the radius of the cables and pen position - end
m1_circle1 = plt.Circle((0, 0), a1, color='blue', fill=False)
m2_circle1 = plt.Circle((d, 0), b1, color='blue', fill=False)
m1_line1 = plt.Line2D([0, x1], [0, -y1], color='green')
m2_line1 = plt.Line2D([d, x1], [0, -y1], color='green')
pen_pos1 = plt.Circle([x1, -y1], 1.0, color='green', fill=False)

# set a wide range, since mpl is acting goofy about this
plt.axis([-d, 2*d, -d, 2*d])
ax = fig2.gca()
ax.set_autoscale_on(False)


# pile all this onto our figure axis
ax.add_artist(m1)
ax.add_artist(m2)
ax.add_artist(m_line)

ax.add_artist(m1_line0)
ax.add_artist(m2_line0)
ax.add_artist(m1_line1)
ax.add_artist(m2_line1)
ax.add_artist(m1_circle0)
ax.add_artist(m2_circle0)
ax.add_artist(pen_pos0)
ax.add_artist(m1_circle1)
ax.add_artist(m2_circle1)
ax.add_artist(pen_pos1)

curr_point = path[0]
for point in path[1:]:
    x0, y0 = curr_point
    x1, y1 = point
    
    a0, b0 = cable_steps(x0, y0)
    a1, b1 = cable_steps(x1, y1)
    
    line_segx = plt.Line2D([x0, x1], [-y0, -y0], color='black')
    line_segy = plt.Line2D([x1, x1], [-y0, -y1], color='black')
    ax.add_artist(line_segx)
    ax.add_artist(line_segy)
    
    curr_point = point



In [ ]:


In [9]:
# Import GPIO Libraries
import RPi.GPIO as GPIO
import time

In [10]:
class Motor(object):
    
    def __init__(self, pins, delay=4, initial_position=25):
        """ pins are provided in the sequence A1, A2, B1, B2
        
        """
        
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(pins[0], GPIO.OUT)
        GPIO.setup(pins[1], GPIO.OUT)
        GPIO.setup(pins[2], GPIO.OUT)
        GPIO.setup(pins[3], GPIO.OUT)
        self.pins = pins
        self.state = ''
        self.delay = delay/1000.0
        self.initial_position = initial_position
        self.current_position = initial_position
        self.reverse_seq = ['1001', '1010', '0110', '0101']
        self.forward_seq = ['0101', '0110', '1010', '1001']  
        
    def _set_step(self, step):
        self.state = step
        GPIO.output(self.pins[0], step[0] == '1')
        GPIO.output(self.pins[1], step[1] == '1')
        GPIO.output(self.pins[2], step[2] == '1')
        GPIO.output(self.pins[3], step[3] == '1')
        
    def _move_forward(self, steps, delay=None):
        if not delay:
            delay = self.delay

        for i in range(steps):
            for step in self.forward_seq:
                self._set_step(step)
                time.sleep(delay)

    def _move_backward(self, steps, delay=None):
        if not delay:
            delay = self.delay
            
        for i in range(steps):
            for step in self.reverse_seq:
                self._set_step(step)
                time.sleep(delay)
        
        
    def move(self, steps):
        if steps > 0:
            self._move_forward(abs(steps))
        else:
            self._move_backward(abs(steps))
        self.current_position += steps

In [11]:
del motor_x
del motor_y


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-11-d896b1ed215c> in <module>()
----> 1 del motor_x
      2 del motor_y

NameError: name 'motor_x' is not defined

In [12]:
motor_x = Motor(pins=(18, 23, 24, 17))
motor_y = Motor(pins=(16, 21, 12, 20))


/usr/local/lib/python2.7/dist-packages/ipykernel/__main__.py:9: RuntimeWarning: This channel is already in use, continuing anyway.  Use GPIO.setwarnings(False) to disable warnings.
/usr/local/lib/python2.7/dist-packages/ipykernel/__main__.py:10: RuntimeWarning: This channel is already in use, continuing anyway.  Use GPIO.setwarnings(False) to disable warnings.
/usr/local/lib/python2.7/dist-packages/ipykernel/__main__.py:11: RuntimeWarning: This channel is already in use, continuing anyway.  Use GPIO.setwarnings(False) to disable warnings.
/usr/local/lib/python2.7/dist-packages/ipykernel/__main__.py:12: RuntimeWarning: This channel is already in use, continuing anyway.  Use GPIO.setwarnings(False) to disable warnings.

In [13]:
motor_x._set_step('0000')
motor_y._set_step('0000')

In [14]:
svg_file_name = 'img/geneva_1.svg'

In [ ]:


In [ ]:


In [ ]:


In [ ]:


In [114]:
shelton_path = np.array([[ 1, 21],
       [ 4, 19],
       [ 6, 20],
       [ 7, 20],
       [ 8, 20],
       [ 9, 20],
       [10, 21],
       [13, 21],
       [14, 21],
       [15, 21],
       [15, 20],
       [18, 21],
       [19, 21],
       [20, 21],
       [21, 20],
       [20, 23],
       [20, 24],
       [21, 24],
       [21, 25],
       [22, 26],
       [22, 27],
       [23, 27],
       [24, 28],
       [24, 29],
       [25, 29],
       [25, 30],
       [26, 30],
       [26, 31],
       [27, 32],
       [27, 31],
       [26, 22],
       [28, 20],
       [23, 18],
       [26, 15],
       [28,  3],
       [27,  3],
       [26,  3],
       [26,  4],
       [25,  4],
       [25,  5],
       [24,  5],
       [24,  6],
       [23,  6],
       [23,  7],
       [23,  8],
       [22,  8],
       [22,  9],
       [22, 10],
       [23, 11],
       [24, 11],
       [24, 12],
       [23, 12],
       [24, 13],
       [25, 12],
       [26, 12],
       [26, 11],
       [25, 10],
       [26, 10],
       [27, 10],
       [28,  9],
       [27, 12],
       [28, 13],
       [27, 13],
       [27, 14],
       [25, 13],
       [24, 14],
       [23, 16],
       [21, 15],
       [21, 14],
       [21, 12],
       [20, 11],
       [21, 10],
       [20, 10],
       [19, 10],
       [19,  9],
       [18,  9],
       [17, 11],
       [17, 10],
       [17,  8],
       [16,  8],
       [16,  9],
       [15,  9],
       [15,  8],
       [14,  8],
       [14,  7],
       [13,  7],
       [12,  7],
       [12,  8],
       [12,  9],
       [11,  8],
       [11,  7],
       [10,  7],
       [11,  9],
       [10, 10],
       [10,  9],
       [ 9,  7],
       [ 8,  7],
       [ 7,  7],
       [ 7,  8],
       [ 6,  8],
       [ 5,  8],
       [ 5,  9],
       [ 4,  9],
       [ 3,  9],
       [ 3, 10],
       [ 2, 11],
       [ 2, 12],
       [ 2, 13],
       [ 2, 14],
       [ 2, 15],
       [ 2, 16],
       [ 2, 17],
       [ 3, 11],
       [ 4, 10],
       [10, 13],
       [11, 13],
       [12, 13],
       [12, 14],
       [10, 14],
       [10, 15],
       [12, 15],
       [21, 18],
       [17, 16],
       [16, 16],
       [17, 14],
       [16, 14],
       [16, 13],
       [16, 12],
       [15, 12],
       [14, 12],
       [14, 11],
       [14, 10],
       [13,  9],
       [13, 10],
       [11, 10],
       [11, 11],
       [12, 10],
       [12, 11],
       [13, 11],
       [14, 13],
       [14, 14],
       [14, 15],
       [14, 16],
       [12, 16],
       [11, 17],
       [ 6, 19],
       [ 5, 19],
       [ 4, 18],
       [ 3, 17],
       [ 3, 18]])

In [157]:
box_path = [(1, 1), (-20, 1), (-20, 20), (1, 20), (1, 1)]
work_path = shelton_path

In [159]:
current_point = work_path[0]
for point in work_path[1:]:
    x0, y0 = current_point
    x1, y1 = point
    
    # Get cable lengths (measured in steps) for our two points (measured in 'units'):
    a0, b0 = cable_steps(x0, y0, steps_per_unit=5)
    a1, b1 = cable_steps(x1, y1, steps_per_unit=5)
    
    #print "cable: (%s, %s) to (%s, %s)" % (a0, b0, a1, b1)

    path = get_line((a0, b0), (a1, b1))
    #print "path: %s" % path
    
    prev_position = list(path[0])
    for position in path[1:]:
        move_x = position[0]-prev_position[0]
        move_y = position[1]-prev_position[1]

        motor_x.move(move_x)
        motor_y.move(move_y)
        
        prev_position = position
    
    current_point = x1, y1

motor_x._set_step('0000')
motor_y._set_step('0000')


cable: (105, 107) to (97, 95)
path: [(105, 107), (104, 106), (104, 105), (103, 104), (102, 103), (102, 102), (101, 101), (100, 100), (100, 99), (99, 98), (98, 97), (98, 96), (97, 95)]
cable: (97, 95) to (104, 100)
path: [(97, 95), (98, 96), (99, 96), (100, 97), (101, 98), (102, 99), (103, 99), (104, 100)]
cable: (104, 100) to (105, 100)
path: [(104, 100), (105, 100)]
cable: (105, 100) to (107, 100)
path: [(105, 100), (106, 100), (107, 100)]
cable: (107, 100) to (109, 101)
path: [(107, 100), (108, 100), (109, 101)]
cable: (109, 101) to (116, 106)
path: [(109, 101), (110, 102), (111, 102), (112, 103), (113, 104), (114, 105), (115, 105), (116, 106)]
cable: (116, 106) to (123, 110)
path: [(116, 106), (117, 107), (118, 107), (119, 108), (120, 108), (121, 109), (122, 109), (123, 110)]
cable: (123, 110) to (126, 112)
path: [(123, 110), (124, 111), (125, 111), (126, 112)]
cable: (126, 112) to (129, 114)
path: [(126, 112), (127, 113), (128, 113), (129, 114)]
cable: (129, 114) to (125, 109)
path: [(129, 114), (128, 113), (127, 112), (127, 111), (126, 110), (125, 109)]
cable: (125, 109) to (138, 120)
path: [(125, 109), (126, 110), (127, 111), (128, 112), (129, 112), (130, 113), (131, 114), (132, 115), (133, 116), (134, 117), (135, 117), (136, 118), (137, 119), (138, 120)]
cable: (138, 120) to (141, 123)
path: [(138, 120), (139, 121), (140, 122), (141, 123)]
cable: (141, 123) to (145, 126)
path: [(141, 123), (142, 124), (143, 124), (144, 125), (145, 126)]
cable: (145, 126) to (145, 125)
path: [(145, 126), (145, 125)]
cable: (145, 125) to (152, 134)
path: [(145, 125), (146, 126), (147, 127), (147, 128), (148, 129), (149, 130), (150, 131), (150, 132), (151, 133), (152, 134)]
cable: (152, 134) to (156, 138)
path: [(152, 134), (153, 135), (154, 136), (155, 137), (156, 138)]
cable: (156, 138) to (159, 141)
path: [(156, 138), (157, 139), (158, 140), (159, 141)]
cable: (159, 141) to (163, 145)
path: [(159, 141), (160, 142), (161, 143), (162, 144), (163, 145)]
cable: (163, 145) to (170, 152)
path: [(163, 145), (164, 146), (165, 147), (166, 148), (167, 149), (168, 150), (169, 151), (170, 152)]
cable: (170, 152) to (174, 156)
path: [(170, 152), (171, 153), (172, 154), (173, 155), (174, 156)]
cable: (174, 156) to (177, 159)
path: [(174, 156), (175, 157), (176, 158), (177, 159)]
cable: (177, 159) to (184, 166)
path: [(177, 159), (178, 160), (179, 161), (180, 162), (181, 163), (182, 164), (183, 165), (184, 166)]
cable: (184, 166) to (188, 170)
path: [(184, 166), (185, 167), (186, 168), (187, 169), (188, 170)]
cable: (188, 170) to (191, 173)
path: [(188, 170), (189, 171), (190, 172), (191, 173)]
cable: (191, 173) to (195, 177)
path: [(191, 173), (192, 174), (193, 175), (194, 176), (195, 177)]
cable: (195, 177) to (198, 180)
path: [(195, 177), (196, 178), (197, 179), (198, 180)]
cable: (198, 180) to (202, 184)
path: [(198, 180), (199, 181), (200, 182), (201, 183), (202, 184)]
cable: (202, 184) to (209, 191)
path: [(202, 184), (203, 185), (204, 186), (205, 187), (206, 188), (207, 189), (208, 190), (209, 191)]
cable: (209, 191) to (205, 187)
path: [(209, 191), (208, 190), (207, 189), (206, 188), (205, 187)]
cable: (205, 187) to (170, 148)
path: [(205, 187), (204, 186), (203, 185), (202, 184), (201, 183), (201, 182), (200, 181), (199, 180), (198, 179), (197, 178), (196, 177), (195, 176), (194, 175), (193, 174), (192, 173), (192, 172), (191, 171), (190, 170), (189, 169), (188, 168), (187, 167), (186, 166), (185, 165), (184, 164), (183, 163), (183, 162), (182, 161), (181, 160), (180, 159), (179, 158), (178, 157), (177, 156), (176, 155), (175, 154), (174, 153), (174, 152), (173, 151), (172, 150), (171, 149), (170, 148)]
cable: (170, 148) to (172, 148)
path: [(170, 148), (171, 148), (172, 148)]
cable: (172, 148) to (146, 123)
path: [(172, 148), (171, 147), (170, 146), (169, 145), (168, 144), (167, 143), (166, 142), (165, 141), (164, 140), (163, 139), (162, 138), (161, 137), (160, 136), (159, 135), (158, 135), (157, 134), (156, 133), (155, 132), (154, 131), (153, 130), (152, 129), (151, 128), (150, 127), (149, 126), (148, 125), (147, 124), (146, 123)]
cable: (146, 123) to (150, 125)
path: [(146, 123), (147, 123), (148, 124), (149, 124), (150, 125)]
cable: (150, 125) to (140, 111)
path: [(150, 125), (149, 124), (149, 123), (148, 122), (147, 121), (146, 120), (146, 119), (145, 118), (144, 117), (144, 116), (143, 115), (142, 114), (141, 113), (141, 112), (140, 111)]
cable: (140, 111) to (135, 106)
path: [(140, 111), (139, 110), (138, 109), (137, 108), (136, 107), (135, 106)]
cable: (135, 106) to (130, 101)
path: [(135, 106), (134, 105), (133, 104), (132, 103), (131, 102), (130, 101)]
cable: (130, 101) to (131, 101)
path: [(130, 101), (131, 101)]
cable: (131, 101) to (126, 97)
path: [(131, 101), (130, 100), (129, 99), (128, 99), (127, 98), (126, 97)]
cable: (126, 97) to (127, 98)
path: [(126, 97), (127, 98)]
cable: (127, 98) to (122, 93)
path: [(127, 98), (126, 97), (125, 96), (124, 95), (123, 94), (122, 93)]
cable: (122, 93) to (123, 94)
path: [(122, 93), (123, 94)]
cable: (123, 94) to (118, 90)
path: [(123, 94), (122, 93), (121, 92), (120, 92), (119, 91), (118, 90)]
cable: (118, 90) to (120, 91)
path: [(118, 90), (119, 90), (120, 91)]
cable: (120, 91) to (121, 93)
path: [(120, 91), (120, 92), (121, 93)]
cable: (121, 93) to (117, 89)
path: [(121, 93), (120, 92), (119, 91), (118, 90), (117, 89)]
cable: (117, 89) to (118, 91)
path: [(117, 89), (117, 90), (118, 91)]
cable: (118, 91) to (120, 94)
path: [(118, 91), (119, 92), (119, 93), (120, 94)]
cable: (120, 94) to (127, 101)
path: [(120, 94), (121, 95), (122, 96), (123, 97), (124, 98), (125, 99), (126, 100), (127, 101)]
cable: (127, 101) to (132, 105)
path: [(127, 101), (128, 102), (129, 103), (130, 103), (131, 104), (132, 105)]
cable: (132, 105) to (134, 108)
path: [(132, 105), (133, 106), (133, 107), (134, 108)]
cable: (134, 108) to (129, 104)
path: [(134, 108), (133, 107), (132, 106), (131, 106), (130, 105), (129, 104)]
cable: (129, 104) to (136, 111)
path: [(129, 104), (130, 105), (131, 106), (132, 107), (133, 108), (134, 109), (135, 110), (136, 111)]
cable: (136, 111) to (138, 112)
path: [(136, 111), (137, 111), (138, 112)]
cable: (138, 112) to (143, 116)
path: [(138, 112), (139, 113), (140, 114), (141, 114), (142, 115), (143, 116)]
cable: (143, 116) to (141, 114)
path: [(143, 116), (142, 115), (141, 114)]
cable: (141, 114) to (134, 107)
path: [(141, 114), (140, 113), (139, 112), (138, 111), (137, 110), (136, 109), (135, 108), (134, 107)]
cable: (134, 107) to (139, 111)
path: [(134, 107), (135, 108), (136, 109), (137, 109), (138, 110), (139, 111)]
cable: (139, 111) to (143, 116)
path: [(139, 111), (140, 112), (141, 113), (141, 114), (142, 115), (143, 116)]
cable: (143, 116) to (147, 118)
path: [(143, 116), (144, 116), (145, 117), (146, 117), (147, 118)]
cable: (147, 118) to (147, 120)
path: [(147, 118), (147, 119), (147, 120)]
cable: (147, 120) to (154, 127)
path: [(147, 120), (148, 121), (149, 122), (150, 123), (151, 124), (152, 125), (153, 126), (154, 127)]
cable: (154, 127) to (149, 123)
path: [(154, 127), (153, 126), (152, 125), (151, 125), (150, 124), (149, 123)]
cable: (149, 123) to (152, 126)
path: [(149, 123), (150, 124), (151, 125), (152, 126)]
cable: (152, 126) to (140, 115)
path: [(152, 126), (151, 125), (150, 124), (149, 123), (148, 122), (147, 121), (146, 120), (145, 120), (144, 119), (143, 118), (142, 117), (141, 116), (140, 115)]
cable: (140, 115) to (138, 114)
path: [(140, 115), (139, 114), (138, 114)]
cable: (138, 114) to (140, 116)
path: [(138, 114), (139, 115), (140, 116)]
cable: (140, 116) to (129, 106)
path: [(140, 116), (139, 115), (138, 114), (137, 113), (136, 112), (135, 111), (134, 111), (133, 110), (132, 109), (131, 108), (130, 107), (129, 106)]
cable: (129, 106) to (126, 102)
path: [(129, 106), (128, 105), (127, 104), (127, 103), (126, 102)]
cable: (126, 102) to (120, 96)
path: [(126, 102), (125, 101), (124, 100), (123, 99), (122, 98), (121, 97), (120, 96)]
cable: (120, 96) to (114, 89)
path: [(120, 96), (119, 95), (118, 94), (117, 93), (117, 92), (116, 91), (115, 90), (114, 89)]
cable: (114, 89) to (116, 90)
path: [(114, 89), (115, 89), (116, 90)]
cable: (116, 90) to (111, 86)
path: [(116, 90), (115, 89), (114, 88), (113, 88), (112, 87), (111, 86)]
cable: (111, 86) to (107, 82)
path: [(111, 86), (110, 85), (109, 84), (108, 83), (107, 82)]
cable: (107, 82) to (105, 79)
path: [(107, 82), (106, 81), (106, 80), (105, 79)]
cable: (105, 79) to (100, 75)
path: [(105, 79), (104, 78), (103, 77), (102, 77), (101, 76), (100, 75)]
cable: (100, 75) to (101, 77)
path: [(100, 75), (100, 76), (101, 77)]
cable: (101, 77) to (98, 74)
path: [(101, 77), (100, 76), (99, 75), (98, 74)]
cable: (98, 74) to (93, 68)
path: [(98, 74), (97, 73), (96, 72), (95, 71), (95, 70), (94, 69), (93, 68)]
cable: (93, 68) to (89, 64)
path: [(93, 68), (92, 67), (91, 66), (90, 65), (89, 64)]
cable: (89, 64) to (91, 67)
path: [(89, 64), (90, 65), (90, 66), (91, 67)]
cable: (91, 67) to (87, 63)
path: [(91, 67), (90, 66), (89, 65), (88, 64), (87, 63)]
cable: (87, 63) to (85, 60)
path: [(87, 63), (86, 62), (86, 61), (85, 60)]
cable: (85, 60) to (80, 56)
path: [(85, 60), (84, 59), (83, 58), (82, 58), (81, 57), (80, 56)]
cable: (80, 56) to (78, 53)
path: [(80, 56), (79, 55), (79, 54), (78, 53)]
cable: (78, 53) to (73, 49)
path: [(78, 53), (77, 52), (76, 51), (75, 51), (74, 50), (73, 49)]
cable: (73, 49) to (69, 46)
path: [(73, 49), (72, 48), (71, 47), (70, 47), (69, 46)]
cable: (69, 46) to (72, 50)
path: [(69, 46), (70, 47), (70, 48), (71, 49), (72, 50)]
cable: (72, 50) to (75, 54)
path: [(72, 50), (73, 51), (73, 52), (74, 53), (75, 54)]
cable: (75, 54) to (68, 47)
path: [(75, 54), (74, 53), (73, 52), (72, 51), (71, 50), (70, 49), (69, 48), (68, 47)]
cable: (68, 47) to (65, 43)
path: [(68, 47), (67, 46), (66, 45), (66, 44), (65, 43)]
cable: (65, 43) to (61, 40)
path: [(65, 43), (64, 42), (63, 41), (62, 41), (61, 40)]
cable: (61, 40) to (71, 51)
path: [(61, 40), (62, 41), (63, 42), (64, 43), (65, 44), (66, 45), (66, 46), (67, 47), (68, 48), (69, 49), (70, 50), (71, 51)]
cable: (71, 51) to (70, 53)
path: [(71, 51), (71, 52), (70, 53)]
cable: (70, 53) to (67, 49)
path: [(70, 53), (69, 52), (68, 51), (68, 50), (67, 49)]
cable: (67, 49) to (57, 38)
path: [(67, 49), (66, 48), (65, 47), (64, 46), (63, 45), (62, 44), (62, 43), (61, 42), (60, 41), (59, 40), (58, 39), (57, 38)]
cable: (57, 38) to (53, 36)
path: [(57, 38), (56, 37), (55, 37), (54, 36), (53, 36)]
cable: (53, 36) to (49, 35)
path: [(53, 36), (52, 36), (51, 35), (50, 35), (49, 35)]
cable: (49, 35) to (53, 40)
path: [(49, 35), (50, 36), (51, 37), (51, 38), (52, 39), (53, 40)]
cable: (53, 40) to (50, 40)
path: [(53, 40), (52, 40), (51, 40), (50, 40)]
cable: (50, 40) to (47, 40)
path: [(50, 40), (49, 40), (48, 40), (47, 40)]
cable: (47, 40) to (51, 45)
path: [(47, 40), (48, 41), (49, 42), (49, 43), (50, 44), (51, 45)]
cable: (51, 45) to (49, 46)
path: [(51, 45), (50, 46), (49, 46)]
cable: (49, 46) to (47, 47)
path: [(49, 46), (48, 47), (47, 47)]
cable: (47, 47) to (52, 52)
path: [(47, 47), (48, 48), (49, 49), (50, 50), (51, 51), (52, 52)]
cable: (52, 52) to (55, 58)
path: [(52, 52), (52, 53), (53, 54), (53, 55), (54, 56), (54, 57), (55, 58)]
cable: (55, 58) to (60, 63)
path: [(55, 58), (56, 59), (57, 60), (58, 61), (59, 62), (60, 63)]
cable: (60, 63) to (65, 68)
path: [(60, 63), (61, 64), (62, 65), (63, 66), (64, 67), (65, 68)]
cable: (65, 68) to (70, 72)
path: [(65, 68), (66, 69), (67, 70), (68, 70), (69, 71), (70, 72)]
cable: (70, 72) to (75, 77)
path: [(70, 72), (71, 73), (72, 74), (73, 75), (74, 76), (75, 77)]
cable: (75, 77) to (80, 82)
path: [(75, 77), (76, 78), (77, 79), (78, 80), (79, 81), (80, 82)]
cable: (80, 82) to (85, 87)
path: [(80, 82), (81, 83), (82, 84), (83, 85), (84, 86), (85, 87)]
cable: (85, 87) to (57, 57)
path: [(85, 87), (84, 86), (83, 85), (82, 84), (81, 83), (80, 82), (79, 81), (78, 80), (78, 79), (77, 78), (76, 77), (75, 76), (74, 75), (73, 74), (72, 73), (71, 72), (70, 71), (69, 70), (68, 69), (67, 68), (66, 67), (65, 66), (64, 65), (64, 64), (63, 63), (62, 62), (61, 61), (60, 60), (59, 59), (58, 58), (57, 57)]
cable: (57, 57) to (53, 50)
path: [(57, 57), (56, 56), (56, 55), (55, 54), (55, 53), (54, 52), (54, 51), (53, 50)]
cable: (53, 50) to (82, 68)
path: [(53, 50), (54, 51), (55, 51), (56, 52), (57, 52), (58, 53), (59, 54), (60, 54), (61, 55), (62, 56), (63, 56), (64, 57), (65, 57), (66, 58), (67, 59), (68, 59), (69, 60), (70, 61), (71, 61), (72, 62), (73, 62), (74, 63), (75, 64), (76, 64), (77, 65), (78, 66), (79, 66), (80, 67), (81, 67), (82, 68)]
cable: (82, 68) to (85, 69)
path: [(82, 68), (83, 68), (84, 69), (85, 69)]
cable: (85, 69) to (88, 71)
path: [(85, 69), (86, 70), (87, 70), (88, 71)]
cable: (88, 71) to (92, 76)
path: [(88, 71), (89, 72), (90, 73), (90, 74), (91, 75), (92, 76)]
cable: (92, 76) to (86, 72)
path: [(92, 76), (91, 75), (90, 75), (89, 74), (88, 73), (87, 73), (86, 72)]
cable: (86, 72) to (90, 77)
path: [(86, 72), (87, 73), (88, 74), (88, 75), (89, 76), (90, 77)]
cable: (90, 77) to (96, 80)
path: [(90, 77), (91, 77), (92, 78), (93, 78), (94, 79), (95, 79), (96, 80)]
cable: (96, 80) to (138, 117)
path: [(96, 80), (97, 81), (98, 82), (99, 83), (100, 84), (101, 84), (102, 85), (103, 86), (104, 87), (105, 88), (106, 89), (107, 90), (108, 91), (109, 91), (110, 92), (111, 93), (112, 94), (113, 95), (114, 96), (115, 97), (116, 98), (117, 98), (118, 99), (119, 100), (120, 101), (121, 102), (122, 103), (123, 104), (124, 105), (125, 106), (126, 106), (127, 107), (128, 108), (129, 109), (130, 110), (131, 111), (132, 112), (133, 113), (134, 113), (135, 114), (136, 115), (137, 116), (138, 117)]
cable: (138, 117) to (116, 97)
path: [(138, 117), (137, 116), (136, 115), (135, 114), (134, 113), (133, 112), (132, 112), (131, 111), (130, 110), (129, 109), (128, 108), (127, 107), (126, 106), (125, 105), (124, 104), (123, 103), (122, 102), (121, 102), (120, 101), (119, 100), (118, 99), (117, 98), (116, 97)]
cable: (116, 97) to (113, 94)
path: [(116, 97), (115, 96), (114, 95), (113, 94)]
cable: (113, 94) to (110, 89)
path: [(113, 94), (112, 93), (112, 92), (111, 91), (111, 90), (110, 89)]
cable: (110, 89) to (106, 86)
path: [(110, 89), (109, 88), (108, 87), (107, 87), (106, 86)]
cable: (106, 86) to (103, 82)
path: [(106, 86), (105, 85), (104, 84), (104, 83), (103, 82)]
cable: (103, 82) to (100, 78)
path: [(103, 82), (102, 81), (101, 80), (101, 79), (100, 78)]
cable: (100, 78) to (96, 75)
path: [(100, 78), (99, 77), (98, 76), (97, 76), (96, 75)]
cable: (96, 75) to (92, 72)
path: [(96, 75), (95, 74), (94, 73), (93, 73), (92, 72)]
cable: (92, 72) to (89, 68)
path: [(92, 72), (91, 71), (90, 70), (90, 69), (89, 68)]
cable: (89, 68) to (86, 64)
path: [(89, 68), (88, 67), (87, 66), (87, 65), (86, 64)]
cable: (86, 64) to (79, 57)
path: [(86, 64), (85, 63), (84, 62), (83, 61), (82, 60), (81, 59), (80, 58), (79, 57)]
cable: (79, 57) to (82, 61)
path: [(79, 57), (80, 58), (80, 59), (81, 60), (82, 61)]
cable: (82, 61) to (74, 55)
path: [(82, 61), (81, 60), (80, 59), (79, 59), (78, 58), (77, 57), (76, 56), (75, 56), (74, 55)]
cable: (74, 55) to (77, 60)
path: [(74, 55), (75, 56), (75, 57), (76, 58), (76, 59), (77, 60)]
cable: (77, 60) to (78, 58)
path: [(77, 60), (78, 59), (78, 58)]
cable: (78, 58) to (81, 62)
path: [(78, 58), (79, 59), (79, 60), (80, 61), (81, 62)]
cable: (81, 62) to (85, 65)
path: [(81, 62), (82, 63), (83, 63), (84, 64), (85, 65)]
cable: (85, 65) to (95, 76)
path: [(85, 65), (86, 66), (87, 67), (88, 68), (89, 69), (90, 70), (90, 71), (91, 72), (92, 73), (93, 74), (94, 75), (95, 76)]
cable: (95, 76) to (98, 80)
path: [(95, 76), (96, 77), (96, 78), (97, 79), (98, 80)]
cable: (98, 80) to (102, 85)
path: [(98, 80), (99, 81), (100, 82), (100, 83), (101, 84), (102, 85)]
cable: (102, 85) to (106, 89)
path: [(102, 85), (103, 86), (104, 87), (105, 88), (106, 89)]
cable: (106, 89) to (100, 85)
path: [(106, 89), (105, 88), (104, 88), (103, 87), (102, 86), (101, 86), (100, 85)]
cable: (100, 85) to (101, 88)
path: [(100, 85), (100, 86), (101, 87), (101, 88)]
cable: (101, 88) to (99, 95)
path: [(101, 88), (101, 89), (100, 90), (100, 91), (100, 92), (100, 93), (99, 94), (99, 95)]
cable: (99, 95) to (98, 95)
path: [(99, 95), (98, 95)]
cable: (98, 95) to (92, 90)
path: [(98, 95), (97, 94), (96, 93), (95, 92), (94, 92), (93, 91), (92, 90)]
cable: (92, 90) to (86, 86)
path: [(92, 90), (91, 89), (90, 89), (89, 88), (88, 87), (87, 87), (86, 86)]
cable: (86, 86) to (91, 91)
path: [(86, 86), (87, 87), (88, 88), (89, 89), (90, 90), (91, 91)]

In [116]:



Out[116]:
[(2, 2), (1, 1)]

In [ ]: