Skip to content

Instantly share code, notes, and snippets.

@atif-hassan
Created November 9, 2020 16:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save atif-hassan/0494f0d5d25b907efd260f46ec6aea4c to your computer and use it in GitHub Desktop.
Save atif-hassan/0494f0d5d25b907efd260f46ec6aea4c to your computer and use it in GitHub Desktop.
"""
===========
Poly Editor
===========
This is an example to show how to build cross-GUI applications using
Matplotlib event handling to interact with objects on the canvas.
**This code was taken from an awesome stackoverflow answer and modified to suit our needs.**
"""
import numpy as np
from matplotlib.lines import Line2D
from matplotlib.artist import Artist
from Farm_Evaluator_Vec_Part1 import fitness_func
from Farm_Evaluator_Vec_Part2 import driver
import pandas as pd
import os
import sys
from creator_and_destroyer import god
from constraint_corrector import corrector
# Custom function provided by the organizers to evaluate a windfarm layout
farm_evaluator_info = driver()
def dist(x, y):
"""
Return the distance between two points.
"""
d = x - y
return np.sqrt(np.dot(d, d))
def dist_point_to_segment(p, s0, s1):
"""
Get the distance of a point to a segment.
*p*, *s0*, *s1* are *xy* sequences
This algorithm from
http://geomalgorithms.com/a02-_lines.html
"""
v = s1 - s0
w = p - s0
c1 = np.dot(w, v)
if c1 <= 0:
return dist(p, s0)
c2 = np.dot(v, v)
if c2 <= c1:
return dist(p, s1)
b = c1 / c2
pb = s0 + b * v
return dist(p, pb)
class PolygonInteractor(object):
showverts = True
epsilon = 5 # max pixel distance to count as a vertex hit
def __init__(self, ax, poly, turbines, load_and_save_path):
self.load_and_save_path = load_and_save_path
self.turbine_index = -1
self.prev_x, self.prev_y = -1, -1
self.turbines = turbines
if poly.figure is None:
raise RuntimeError('You must first add the polygon to a figure '
'or canvas before defining the interactor')
self.ax = ax
canvas = poly.figure.canvas
self.poly = poly
x, y = zip(*self.poly.xy)
self.line = Line2D(x, y,linewidth=0,
marker='o', markerfacecolor='r',
animated=True)
self.ax.add_line(self.line)
self.cid = self.poly.add_callback(self.poly_changed)
self._ind = None # the active vert
canvas.mpl_connect('draw_event', self.draw_callback)
canvas.mpl_connect('button_press_event', self.button_press_callback)
canvas.mpl_connect('key_press_event', self.key_press_callback)
canvas.mpl_connect('button_release_event', self.button_release_callback)
canvas.mpl_connect('motion_notify_event', self.motion_notify_callback)
self.canvas = canvas
def draw_callback(self, event):
self.background = self.canvas.copy_from_bbox(self.ax.bbox)
#self.ax.draw_artist(self.poly)
self.ax.draw_artist(self.line)
# do not need to blit here, this will fire before the screen is
# updated
def poly_changed(self, poly):
'this method is called whenever the polygon object is called'
# only copy the artist props to the line (except visibility)
vis = self.line.get_visible()
Artist.update_from(self.line, poly)
self.line.set_visible(vis) # don't use the poly visibility state
def get_ind_under_point(self, event):
'get the index of the vertex under point if within epsilon tolerance'
# display coords
xy = np.asarray(self.poly.xy)
xyt = self.poly.get_transform().transform(xy)
xt, yt = xyt[:, 0], xyt[:, 1]
d = np.hypot(xt - event.x, yt - event.y)
indseq, = np.nonzero(d == d.min())
ind = indseq[0]
if d[ind] >= self.epsilon:
ind = None
return ind
def button_press_callback(self, event):
'whenever a mouse button is pressed'
if not self.showverts:
return
if event.inaxes is None:
return
if event.button != 1:
return
self._ind = self.get_ind_under_point(event)
self.turbine_index = self._ind
def button_release_callback(self, event):
if event.button == 3 and not (self.turbine_index is None):
self.turbines[self.turbine_index, 0], self.turbines[self.turbine_index, 1] = self.prev_x, self.prev_y
df = pd.DataFrame(self.turbines, columns = ['x', 'y'])
df.to_csv(self.load_and_save_path, index=False)
god()
'whenever a mouse button is released'
if not self.showverts:
return
if event.button != 1:
return
self._ind = None
if not (self.turbine_index is None):
self.prev_x, self.prev_y = self.turbines[self.turbine_index, 0], self.turbines[self.turbine_index, 1]
self.turbines[self.turbine_index, 0] = event.xdata
self.turbines[self.turbine_index, 1] = event.ydata
score = fitness_func(self.turbines, farm_evaluator_info)
if not (score is None):
print(score)
#else:
#print("Constraints violated. Restarting.")
# god()
def key_press_callback(self, event):
'whenever a key is pressed'
if not event.inaxes:
return
if event.key == 't':
self.showverts = not self.showverts
self.line.set_visible(self.showverts)
if not self.showverts:
self._ind = None
elif event.key == 'd':
ind = self.get_ind_under_point(event)
if ind is not None:
self.poly.xy = np.delete(self.poly.xy,
ind, axis=0)
self.line.set_data(zip(*self.poly.xy))
elif event.key == 'i':
xys = self.poly.get_transform().transform(self.poly.xy)
p = event.x, event.y # display coords
for i in range(len(xys) - 1):
s0 = xys[i]
s1 = xys[i + 1]
d = dist_point_to_segment(p, s0, s1)
if d <= self.epsilon:
self.poly.xy = np.insert(
self.poly.xy, i+1,
[event.xdata, event.ydata],
axis=0)
self.line.set_data(zip(*self.poly.xy))
break
if self.line.stale:
self.canvas.draw_idle()
def motion_notify_callback(self, event):
'on mouse movement'
if not self.showverts:
return
if self._ind is None:
return
if event.inaxes is None:
return
if event.button != 1:
return
x, y = event.xdata, event.ydata
self.poly.xy[self._ind] = x, y
if self._ind == 0:
self.poly.xy[-1] = x, y
elif self._ind == len(self.poly.xy) - 1:
self.poly.xy[0] = x, y
self.line.set_data(zip(*self.poly.xy))
self.canvas.restore_region(self.background)
self.ax.draw_artist(self.line)
self.canvas.blit(self.ax.bbox)
if __name__ == '__main__':
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
load_and_save_path = "preds.csv"
df = pd.read_csv(load_and_save_path)
turbines = df.values
score = fitness_func(turbines, farm_evaluator_info)
if not (score is None):
print("The original score: ", score)
poly = Polygon(turbines, animated=True)
fig, ax = plt.subplots()
ax.add_patch(poly)
p = PolygonInteractor(ax, poly, turbines, load_and_save_path)
ax.set_title('Click and drag a point to move it')
ax.set_xlim((50, 4000))
ax.set_ylim((50, 4000))
plt.show()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment