Skip to content

Commit 3ee10c4

Browse files
committed
Merge branch 'main' into fixes
2 parents ee09adc + dfa811b commit 3ee10c4

11 files changed

+476
-1
lines changed

.github/workflows/CD.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ jobs:
5252
show-channel-urls: true
5353

5454
- name: Build and upload the conda package
55-
uses: uibcdf/action-build-and-upload-conda-packages@v1.3.0
55+
uses: uibcdf/action-build-and-upload-conda-packages@v1.4.0
5656
with:
5757
meta_yaml_dir: .conda
5858
python-version: 3.11

src/accessvis/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from . import _version
55
from .earth import *
66
from .utils import *
7+
from .widgets import *
78

89
# Add current directory to sys.path because python is deranged
910
sys.path.append(os.path.dirname(__file__))

src/accessvis/widgets/__init__.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from .calendar_widget import CalendarWidget # noqa: F401
2+
from .clock_widget import ClockWidget # noqa: F401
3+
from .image_widget import ImageWidget # noqa: F401
4+
from .season_widget import SeasonWidget # noqa: F401
5+
from .text_widget import TextWidget # noqa: F401
6+
from .widget_base import Widget, WidgetMPL, list_widgets # noqa: F401
+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import calendar
2+
import datetime
3+
4+
import matplotlib.pyplot as plt
5+
import numpy as np
6+
7+
from .widget_base import WidgetMPL
8+
9+
10+
class CalendarWidget(WidgetMPL):
11+
def __init__(self, lv, text_colour="black", **kwargs):
12+
super().__init__(lv=lv, **kwargs)
13+
self.text_colour = text_colour
14+
self.arrow = None
15+
16+
def _make_mpl(self):
17+
18+
plt.rc("axes", linewidth=4)
19+
plt.rc("font", weight="bold")
20+
fig, ax = plt.subplots(subplot_kw={"projection": "polar"}, figsize=(5, 5))
21+
fig.patch.set_facecolor((0, 0, 0, 0)) # make background transparent
22+
ax.set_facecolor("white") # adds a white ring around edge
23+
24+
# Setting up grid
25+
ax.set_rticks([])
26+
ax.grid(False)
27+
ax.set_theta_zero_location("NW")
28+
ax.set_theta_direction(-1)
29+
30+
# Label Angles
31+
MONTH = []
32+
for i in range(1, 13):
33+
MONTH.append(calendar.month_name[i][0])
34+
MONTH = np.roll(MONTH, 2)
35+
ANGLES = np.linspace(0.0, 2 * np.pi, 12, endpoint=False)
36+
ax.tick_params(axis="x", which="major", pad=12, labelcolor=self.text_colour)
37+
ax.set_xticks(ANGLES)
38+
ax.set_xticklabels(MONTH, size=20)
39+
ax.spines["polar"].set_color(self.text_colour)
40+
41+
# Make Colours:
42+
ax.bar(x=0, height=10, width=np.pi * 2, color="black")
43+
for i in range(12):
44+
c = "darkorange" if i % 2 else "darkcyan"
45+
ax.bar(x=i * np.pi / 6, height=10, width=np.pi / 6, color=c)
46+
47+
return fig, ax
48+
49+
def _update_mpl(self, fig, ax, date: datetime.datetime = None, show_year=True):
50+
if show_year and date is not None:
51+
title = str(date.year)
52+
else:
53+
title = ""
54+
fig.suptitle(
55+
title, fontsize=20, fontweight="bold", y=0.08, color=self.text_colour
56+
)
57+
58+
if date is None:
59+
return
60+
else:
61+
day_of_year = date.timetuple().tm_yday - 1
62+
position = day_of_year / 365.0 * np.pi * 2.0
63+
self.arrow = ax.arrow(
64+
position,
65+
0,
66+
0,
67+
8.5,
68+
facecolor="#fff",
69+
width=0.1,
70+
head_length=2,
71+
edgecolor="black",
72+
) # , zorder=11, width=1)
73+
74+
def _reset_mpl(self, fig, ax, **kwargs):
75+
fig.suptitle("")
76+
if self.arrow is not None:
77+
self.arrow.remove()

src/accessvis/widgets/clock_widget.py

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import datetime
2+
3+
import matplotlib.pyplot as plt
4+
import numpy as np
5+
6+
from .widget_base import WidgetMPL
7+
8+
9+
class ClockWidget(WidgetMPL):
10+
def __init__(
11+
self,
12+
lv,
13+
text_colour="white",
14+
background="black",
15+
show_seconds=False,
16+
show_minutes=True,
17+
show_hours=True,
18+
**kwargs
19+
):
20+
super().__init__(lv=lv, **kwargs)
21+
self.text_colour = text_colour
22+
self.background = background
23+
self.show_hours = show_hours
24+
self.show_minutes = show_minutes
25+
self.show_seconds = show_seconds
26+
self.lines = []
27+
28+
# based on https://inprogrammer.com/analog-clock-python/
29+
def _make_mpl(self):
30+
fig = plt.figure(figsize=(2.5, 2.5), dpi=100)
31+
fig.patch.set_facecolor((0, 0, 0, 0)) # make background transparent
32+
ax = fig.add_subplot(111, polar=True)
33+
plt.setp(ax.get_yticklabels(), visible=False)
34+
ax.set_xticks(np.linspace(0, 2 * np.pi, 12, endpoint=False))
35+
ax.set_xticklabels(range(1, 13))
36+
ax.tick_params(axis="x", which="major", labelcolor=self.text_colour)
37+
ax.set_theta_direction(-1)
38+
ax.set_theta_offset(np.pi / 3.0)
39+
ax.grid(False)
40+
plt.ylim(0, 1)
41+
42+
ax.set_facecolor(self.background)
43+
ax.spines["polar"].set_color(self.text_colour)
44+
45+
return fig, ax
46+
47+
def _update_mpl(self, fig, ax, time: datetime.time = None, **kwargs):
48+
if time is None:
49+
return
50+
51+
hour = time.hour
52+
minute = time.minute
53+
second = time.second
54+
angles_h = (
55+
2 * np.pi * hour / 12
56+
+ 2 * np.pi * minute / (12 * 60)
57+
+ 2 * second / (12 * 60 * 60)
58+
- np.pi / 6.0
59+
)
60+
angles_m = (
61+
2 * np.pi * minute / 60 + 2 * np.pi * second / (60 * 60) - np.pi / 6.0
62+
)
63+
angles_s = 2 * np.pi * second / 60 - np.pi / 6.0
64+
65+
if self.show_seconds:
66+
lines = ax.plot(
67+
[angles_s, angles_s], [0, 0.9], color=self.text_colour, linewidth=1
68+
)
69+
self.lines.extend(lines)
70+
if self.show_minutes:
71+
lines = ax.plot(
72+
[angles_m, angles_m], [0, 0.7], color=self.text_colour, linewidth=2
73+
)
74+
self.lines.extend(lines)
75+
if self.show_hours:
76+
lines = ax.plot(
77+
[angles_h, angles_h], [0, 0.3], color=self.text_colour, linewidth=4
78+
)
79+
self.lines.extend(lines)
80+
81+
def _reset_mpl(self, fig, ax, **kwargs):
82+
for i in self.lines:
83+
i.remove()
84+
self.lines.clear()

src/accessvis/widgets/image_widget.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import matplotlib.pyplot as plt
2+
3+
from .widget_base import Widget
4+
5+
6+
class ImageWidget(Widget):
7+
def __init__(self, lv, file_path: str, **kwargs):
8+
super().__init__(lv, **kwargs)
9+
self.file_path = file_path
10+
11+
def _make_pixels(self, *args, **kwargs):
12+
return plt.imread(self.file_path)

src/accessvis/widgets/screen.frag

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
in vec4 vColour;
3+
in vec3 vVertex;
4+
in vec2 vTexCoord; // scale 0 .. 1
5+
6+
//Custom uniform
7+
uniform sampler2D uTexture;
8+
uniform float widthToHeight = 1; // 1 means square, 2 means width is double height
9+
uniform float scale = 0.5; // Scale down to half the height of the box
10+
uniform vec2 offset = vec2(0,0);
11+
12+
out vec4 outColour;
13+
14+
void main(void)
15+
{
16+
vec2 size = vec2(scale*widthToHeight, scale);
17+
vec2 texCoord = vTexCoord/size;
18+
texCoord.y = 1-texCoord.y;
19+
20+
texCoord.x += (1 - 1/size.x) * offset.x;
21+
texCoord.y += (1/size.y - 1) * offset.y;
22+
23+
24+
25+
if (texCoord.x >= 0.0 && texCoord.y >= 0.0 && texCoord.x <= 1.0 && texCoord.y <= 1.0)
26+
outColour = texture(uTexture, texCoord);
27+
else
28+
discard;
29+
30+
//Discard transparent to skip depth write
31+
//This fixes depth buffer output interfering with other objects
32+
// in transparent areas
33+
if (outColour.a <= 0.1)
34+
discard;
35+
}
36+

src/accessvis/widgets/screen.vert

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
in vec3 aVertexPosition;
2+
in vec3 aVertexNormal;
3+
in vec4 aVertexColour;
4+
in vec2 aVertexTexCoord;
5+
6+
uniform mat4 uMVMatrix;
7+
uniform mat4 uPMatrix;
8+
uniform mat4 uNMatrix;
9+
10+
uniform vec4 uColour;
11+
12+
out vec4 vColour;
13+
out vec3 vVertex;
14+
out vec3 vNormal;
15+
out vec2 vTexCoord;
16+
void main(void) {
17+
gl_Position = vec4(aVertexPosition, 1.0);
18+
19+
vNormal = normalize(mat3(uNMatrix) * aVertexNormal);
20+
21+
if (uColour.a > 0.0)
22+
vColour = uColour;
23+
else
24+
vColour = aVertexColour;
25+
26+
vTexCoord = aVertexTexCoord;
27+
vVertex = aVertexPosition;
28+
}
29+
+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import datetime
2+
3+
import matplotlib.pyplot as plt
4+
import numpy as np
5+
from matplotlib.colors import LinearSegmentedColormap
6+
7+
from .widget_base import WidgetMPL
8+
9+
10+
class SeasonWidget(WidgetMPL):
11+
def __init__(self, lv, text_colour="black", hemisphere="south", **kwargs):
12+
super().__init__(lv=lv, **kwargs)
13+
self.text_colour = text_colour
14+
self.hemisphere = hemisphere
15+
self.arrow = None
16+
17+
def _make_mpl(self):
18+
plt.rc("axes", linewidth=4)
19+
plt.rc("font", weight="bold")
20+
fig, ax = plt.subplots(subplot_kw={"projection": "polar"}, figsize=(5, 5))
21+
fig.patch.set_facecolor((0, 0, 0, 0)) # make background transparent
22+
ax.set_facecolor("white") # adds a white ring around edge
23+
24+
# Setting up grid
25+
ax.set_rticks([])
26+
ax.grid(False)
27+
ax.set_theta_zero_location("N")
28+
ax.set_theta_direction(-1)
29+
30+
# Label Angles
31+
if self.hemisphere == "south":
32+
MONTH = ["Sum", "Aut", "Win", "Spr"]
33+
cmap = LinearSegmentedColormap.from_list(
34+
"custom_gradient", ["orange", "black", "blue", "black", "orange"]
35+
)
36+
else:
37+
MONTH = ["Win", "Spr", "Sum", "Aut"]
38+
cmap = LinearSegmentedColormap.from_list(
39+
"custom_gradient", ["blue", "black", "orange", "black", "blue"]
40+
)
41+
42+
ANGLES = np.linspace(np.pi / 4, 2 * np.pi + np.pi / 4, 4, endpoint=False)
43+
ax.tick_params(axis="x", which="major", pad=12, labelcolor=self.text_colour)
44+
ax.set_xticks(ANGLES)
45+
ax.set_xticklabels(MONTH, size=20)
46+
ax.spines["polar"].set_color(self.text_colour)
47+
48+
# Colour background based on time of year:
49+
dec22_doy = datetime.date(2001, 12, 22).timetuple().tm_yday - 1
50+
dec22 = np.pi * 2.0 * dec22_doy / 365 # summer solstice
51+
r = np.linspace(0, 10, 5)
52+
theta = np.linspace(dec22, dec22 + 2 * np.pi, 500)
53+
R, T = np.meshgrid(r, theta)
54+
ax.pcolormesh(T, R, T, cmap=cmap, shading="gouraud")
55+
56+
return fig, ax
57+
58+
def _update_mpl(self, fig, ax, date: datetime.datetime = None, show_year=True):
59+
if show_year and date is not None:
60+
title = str(date.year)
61+
else:
62+
title = ""
63+
fig.suptitle(
64+
title, fontsize=20, fontweight="bold", y=0.08, color=self.text_colour
65+
)
66+
67+
if date is None:
68+
return
69+
else:
70+
day_of_year = date.timetuple().tm_yday - 1
71+
position = day_of_year / 365.0 * np.pi * 2.0
72+
self.arrow = ax.arrow(
73+
position,
74+
0,
75+
0,
76+
8.5,
77+
facecolor="#fff",
78+
width=0.1,
79+
head_length=2,
80+
edgecolor="black",
81+
) # , zorder=11, width=1)
82+
83+
def _reset_mpl(self, fig, ax, **kwargs):
84+
fig.suptitle("")
85+
if self.arrow is not None:
86+
self.arrow.remove()

src/accessvis/widgets/text_widget.py

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import matplotlib.pyplot as plt
2+
3+
from .widget_base import WidgetMPL
4+
5+
6+
class TextWidget(WidgetMPL):
7+
def __init__(
8+
self,
9+
lv,
10+
width=300,
11+
height=50,
12+
text_colour="black",
13+
background=(0, 0, 0, 0),
14+
**kwargs
15+
):
16+
super().__init__(lv, **kwargs)
17+
self.width = width
18+
self.height = height
19+
self.text_colour = text_colour
20+
self.background = background
21+
self.text = None
22+
23+
def _make_mpl(self):
24+
fig, ax = plt.subplots(figsize=(self.width / 100, self.height / 100), dpi=100)
25+
fig.subplots_adjust(left=0, right=1, top=1, bottom=0)
26+
ax.set_axis_off()
27+
fig.patch.set_facecolor(self.background)
28+
self.text = ax.text(
29+
0.5, 0.5, "", ha="center", va="center", fontsize=20, color=self.text_colour
30+
)
31+
32+
return fig, ax
33+
34+
def _update_mpl(self, fig, ax, text="", **kwargs):
35+
self.text.set_text(text)
36+
37+
def _reset_mpl(self, fig, ax, **kwargs):
38+
self.text.set_text("")

0 commit comments

Comments
 (0)