Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Widgets #38

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions src/accessvis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from . import _version
from .earth import *
from .utils import *
from .widgets import *

# Add current directory to sys.path because python is deranged
sys.path.append(os.path.dirname(__file__))
Expand Down
9 changes: 9 additions & 0 deletions src/accessvis/widgets/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from .calendar_widget import CalendarWidget
from .clock_widget import ClockWidget
from .image_widget import ImageWidget
from .season_widget import SeasonWidget
from .text_widget import TextWidget
from .widget_base import Widget, WidgetMPL, list_widgets

_ = (Widget, WidgetMPL, list_widgets, SeasonWidget) # to stop the linter complaining
_ = (CalendarWidget, ClockWidget, ImageWidget, TextWidget)
77 changes: 77 additions & 0 deletions src/accessvis/widgets/calendar_widget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import calendar
import datetime

import matplotlib.pyplot as plt
import numpy as np

from .widget_base import WidgetMPL


class CalendarWidget(WidgetMPL):
def __init__(self, lv, text_colour="black", **kwargs):
super().__init__(lv=lv, **kwargs)
self.text_colour = text_colour
self.arrow = None

def _make_mpl(self):

plt.rc("axes", linewidth=4)
plt.rc("font", weight="bold")
fig, ax = plt.subplots(subplot_kw={"projection": "polar"}, figsize=(5, 5))
fig.patch.set_facecolor((0, 0, 0, 0)) # make background transparent
ax.set_facecolor("white") # adds a white ring around edge

# Setting up grid
ax.set_rticks([])
ax.grid(False)
ax.set_theta_zero_location("NW")
ax.set_theta_direction(-1)

# Label Angles
MONTH = []
for i in range(1, 13):
MONTH.append(calendar.month_name[i][0])
MONTH = np.roll(MONTH, 2)
ANGLES = np.linspace(0.0, 2 * np.pi, 12, endpoint=False)
ax.tick_params(axis="x", which="major", pad=12, labelcolor=self.text_colour)
ax.set_xticks(ANGLES)
ax.set_xticklabels(MONTH, size=20)
ax.spines["polar"].set_color(self.text_colour)

# Make Colours:
ax.bar(x=0, height=10, width=np.pi * 2, color="black")
for i in range(12):
c = "darkorange" if i % 2 else "darkcyan"
ax.bar(x=i * np.pi / 6, height=10, width=np.pi / 6, color=c)

return fig, ax

def _update_mpl(self, fig, ax, date: datetime.datetime = None, show_year=True):
if show_year and date is not None:
title = str(date.year)
else:
title = ""
fig.suptitle(
title, fontsize=20, fontweight="bold", y=0.08, color=self.text_colour
)

if date is None:
return
else:
day_of_year = date.timetuple().tm_yday - 1
position = day_of_year / 365.0 * np.pi * 2.0
self.arrow = ax.arrow(
position,
0,
0,
8.5,
facecolor="#fff",
width=0.1,
head_length=2,
edgecolor="black",
) # , zorder=11, width=1)

def _reset_mpl(self, fig, ax, **kwargs):
fig.suptitle("")
if self.arrow is not None:
self.arrow.remove()
84 changes: 84 additions & 0 deletions src/accessvis/widgets/clock_widget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import datetime

import matplotlib.pyplot as plt
import numpy as np

from .widget_base import WidgetMPL


class ClockWidget(WidgetMPL):
def __init__(
self,
lv,
text_colour="white",
background="black",
show_seconds=False,
show_minutes=True,
show_hours=True,
**kwargs
):
super().__init__(lv=lv, **kwargs)
self.text_colour = text_colour
self.background = background
self.show_hours = show_hours
self.show_minutes = show_minutes
self.show_seconds = show_seconds
self.lines = []

# based on https://inprogrammer.com/analog-clock-python/
def _make_mpl(self):
fig = plt.figure(figsize=(2.5, 2.5), dpi=100)
fig.patch.set_facecolor((0, 0, 0, 0)) # make background transparent
ax = fig.add_subplot(111, polar=True)
plt.setp(ax.get_yticklabels(), visible=False)
ax.set_xticks(np.linspace(0, 2 * np.pi, 12, endpoint=False))
ax.set_xticklabels(range(1, 13))
ax.tick_params(axis="x", which="major", labelcolor=self.text_colour)
ax.set_theta_direction(-1)
ax.set_theta_offset(np.pi / 3.0)
ax.grid(False)
plt.ylim(0, 1)

ax.set_facecolor(self.background)
ax.spines["polar"].set_color(self.text_colour)

return fig, ax

def _update_mpl(self, fig, ax, time: datetime.time = None, **kwargs):
if time is None:
return

hour = time.hour
minute = time.minute
second = time.second
angles_h = (
2 * np.pi * hour / 12
+ 2 * np.pi * minute / (12 * 60)
+ 2 * second / (12 * 60 * 60)
- np.pi / 6.0
)
angles_m = (
2 * np.pi * minute / 60 + 2 * np.pi * second / (60 * 60) - np.pi / 6.0
)
angles_s = 2 * np.pi * second / 60 - np.pi / 6.0

if self.show_seconds:
lines = ax.plot(
[angles_s, angles_s], [0, 0.9], color=self.text_colour, linewidth=1
)
self.lines.extend(lines)
if self.show_minutes:
lines = ax.plot(
[angles_m, angles_m], [0, 0.7], color=self.text_colour, linewidth=2
)
self.lines.extend(lines)
if self.show_hours:
lines = ax.plot(
[angles_h, angles_h], [0, 0.3], color=self.text_colour, linewidth=4
)
self.lines.extend(lines)

def _reset_mpl(self, fig, ax, **kwargs):
for i in self.lines:
i.remove()
self.lines.clear()
12 changes: 12 additions & 0 deletions src/accessvis/widgets/image_widget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import matplotlib.pyplot as plt

from .widget_base import Widget


class ImageWidget(Widget):
def __init__(self, lv, file_path: str, **kwargs):
super().__init__(lv, **kwargs)
self.file_path = file_path

def _make_pixels(self, *args, **kwargs):
return plt.imread(self.file_path)
36 changes: 36 additions & 0 deletions src/accessvis/widgets/screen.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

in vec4 vColour;
in vec3 vVertex;
in vec2 vTexCoord; // scale 0 .. 1

//Custom uniform
uniform sampler2D uTexture;
uniform float widthToHeight = 1; // 1 means square, 2 means width is double height
uniform float scale = 0.5; // Scale down to half the height of the box
uniform vec2 offset = vec2(0,0);

out vec4 outColour;

void main(void)
{
vec2 size = vec2(scale*widthToHeight, scale);
vec2 texCoord = vTexCoord/size;
texCoord.y = 1-texCoord.y;

texCoord.x += (1 - 1/size.x) * offset.x;
texCoord.y += (1/size.y - 1) * offset.y;



if (texCoord.x >= 0.0 && texCoord.y >= 0.0 && texCoord.x <= 1.0 && texCoord.y <= 1.0)
outColour = texture(uTexture, texCoord);
else
discard;

//Discard transparent to skip depth write
//This fixes depth buffer output interfering with other objects
// in transparent areas
if (outColour.a <= 0.1)
discard;
}

29 changes: 29 additions & 0 deletions src/accessvis/widgets/screen.vert
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
in vec3 aVertexPosition;
in vec3 aVertexNormal;
in vec4 aVertexColour;
in vec2 aVertexTexCoord;

uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat4 uNMatrix;

uniform vec4 uColour;

out vec4 vColour;
out vec3 vVertex;
out vec3 vNormal;
out vec2 vTexCoord;
void main(void) {
gl_Position = vec4(aVertexPosition, 1.0);

vNormal = normalize(mat3(uNMatrix) * aVertexNormal);

if (uColour.a > 0.0)
vColour = uColour;
else
vColour = aVertexColour;

vTexCoord = aVertexTexCoord;
vVertex = aVertexPosition;
}

86 changes: 86 additions & 0 deletions src/accessvis/widgets/season_widget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import datetime

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import LinearSegmentedColormap

from .widget_base import WidgetMPL


class SeasonWidget(WidgetMPL):
def __init__(self, lv, text_colour="black", hemisphere="south", **kwargs):
super().__init__(lv=lv, **kwargs)
self.text_colour = text_colour
self.hemisphere = hemisphere
self.arrow = None

def _make_mpl(self):
plt.rc("axes", linewidth=4)
plt.rc("font", weight="bold")
fig, ax = plt.subplots(subplot_kw={"projection": "polar"}, figsize=(5, 5))
fig.patch.set_facecolor((0, 0, 0, 0)) # make background transparent
ax.set_facecolor("white") # adds a white ring around edge

# Setting up grid
ax.set_rticks([])
ax.grid(False)
ax.set_theta_zero_location("N")
ax.set_theta_direction(-1)

# Label Angles
if self.hemisphere == "south":
MONTH = ["Sum", "Aut", "Win", "Spr"]
cmap = LinearSegmentedColormap.from_list(
"custom_gradient", ["orange", "black", "blue", "black", "orange"]
)
else:
MONTH = ["Win", "Spr", "Sum", "Aut"]
cmap = LinearSegmentedColormap.from_list(
"custom_gradient", ["blue", "black", "orange", "black", "blue"]
)

ANGLES = np.linspace(np.pi / 4, 2 * np.pi + np.pi / 4, 4, endpoint=False)
ax.tick_params(axis="x", which="major", pad=12, labelcolor=self.text_colour)
ax.set_xticks(ANGLES)
ax.set_xticklabels(MONTH, size=20)
ax.spines["polar"].set_color(self.text_colour)

# Colour background based on time of year:
dec22_doy = datetime.date(2001, 12, 22).timetuple().tm_yday - 1
dec22 = np.pi * 2.0 * dec22_doy / 365 # summer solstice
r = np.linspace(0, 10, 5)
theta = np.linspace(dec22, dec22 + 2 * np.pi, 500)
R, T = np.meshgrid(r, theta)
ax.pcolormesh(T, R, T, cmap=cmap, shading="gouraud")

return fig, ax

def _update_mpl(self, fig, ax, date: datetime.datetime = None, show_year=True):
if show_year and date is not None:
title = str(date.year)
else:
title = ""
fig.suptitle(
title, fontsize=20, fontweight="bold", y=0.08, color=self.text_colour
)

if date is None:
return
else:
day_of_year = date.timetuple().tm_yday - 1
position = day_of_year / 365.0 * np.pi * 2.0
self.arrow = ax.arrow(
position,
0,
0,
8.5,
facecolor="#fff",
width=0.1,
head_length=2,
edgecolor="black",
) # , zorder=11, width=1)

def _reset_mpl(self, fig, ax, **kwargs):
fig.suptitle("")
if self.arrow is not None:
self.arrow.remove()
38 changes: 38 additions & 0 deletions src/accessvis/widgets/text_widget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import matplotlib.pyplot as plt

from .widget_base import WidgetMPL


class TextWidget(WidgetMPL):
def __init__(
self,
lv,
width=300,
height=50,
text_colour="black",
background=(0, 0, 0, 0),
**kwargs
):
super().__init__(lv, **kwargs)
self.width = width
self.height = height
self.text_colour = text_colour
self.background = background
self.text = None

def _make_mpl(self):
fig, ax = plt.subplots(figsize=(self.width / 100, self.height / 100), dpi=100)
fig.subplots_adjust(left=0, right=1, top=1, bottom=0)
ax.set_axis_off()
fig.patch.set_facecolor(self.background)
self.text = ax.text(
0.5, 0.5, "", ha="center", va="center", fontsize=20, color=self.text_colour
)

return fig, ax

def _update_mpl(self, fig, ax, text="", **kwargs):
self.text.set_text(text)

def _reset_mpl(self, fig, ax, **kwargs):
self.text.set_text("")
Loading
Loading