Created
November 9, 2020 16:49
-
-
Save atif-hassan/0494f0d5d25b907efd260f46ec6aea4c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
=========== | |
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