-
Notifications
You must be signed in to change notification settings - Fork 64
Expand file tree
/
Copy pathunit_circle.py
More file actions
145 lines (109 loc) · 4.16 KB
/
unit_circle.py
File metadata and controls
145 lines (109 loc) · 4.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
"""
Unit circle
===========
Example with linear selectors on a sine and cosine function that demonstrates the unit circle.
This shows how fastplotlib supports bidirectional events, drag the linear selector on the sine
or cosine function and they will both move together.
Click on the sine or cosine function to set the colormap transform to illustrate the sine or
cosine function output values on the unit circle.
"""
# test_example = false
# sphinx_gallery_pygfx_docs = 'screenshot'
import numpy as np
import fastplotlib as fpl
# helper function to make a cirlce
def make_circle(center, radius: float, n_points: int) -> np.ndarray:
theta = np.linspace(0, 2 * np.pi, n_points)
xs = radius * np.cos(theta)
ys = radius * np.sin(theta)
return np.column_stack([xs, ys]) + center
# We will have 3 subplots in a layout like this:
"""
|========|========|
| | |
| | sine |
| | |
| circle |========|
| | |
| | cosine |
| | |
|========|========|
"""
# we can define this layout using "extents", i.e. min and max ranges on the canvas
# (x_min, x_max, y_min, y_max)
# extents can be defined as fractions as shown here
extents = [
(0, 0.5, 0, 1), # circle subplot
(0.5, 1, 0, 0.5), # sine subplot
(0.5, 1, 0.5, 1), # cosine subplot
]
# create a figure with 3 subplots
figure = fpl.Figure(
extents=extents,
names=["unit circle", "sin(x)", "cos(x)"],
size=(700, 560)
)
# set the axes to intersect at (0, 0, 0) to better illustrate the unit circle
for subplot in figure:
subplot.axes.intersection = (0, 0, 0)
subplot.toolbar = False # reduce clutter
figure["sin(x)"].camera.maintain_aspect = False
figure["cos(x)"].camera.maintain_aspect = False
# create sine and cosine data
xs = np.linspace(0, 2 * np.pi, 360)
sine_data = np.sin(xs)
cosine_data = np.cos(xs)
# circle data
circle_data = make_circle(center=(0, 0), radius=1, n_points=360)
# make the circle line graphic, set the cmap transform using the sine function
circle = figure["unit circle"].add_line(
circle_data, thickness=4, cmap="bwr", cmap_transform=sine_data
)
# line to show the circle radius
# use it to indicate the current position of the sine and cosine selctors (below)
radius_data = np.array([[0, 0, 0], [*circle_data[0], 0]])
circle_radius = figure["unit circle"].add_line(
radius_data, thickness=6, colors="magenta"
)
# sine line graphic, cmap transform set from the sine function
sine = figure["sin(x)"].add_line(
sine_data, thickness=10, cmap="bwr", cmap_transform=sine_data
)
# cosine line graphic, cmap transform set from the sine function
# illustrates the sine function values on the cosine graphic
cosine = figure["cos(x)"].add_line(
cosine_data, thickness=10, cmap="bwr", cmap_transform=sine_data
)
# add linear selectors to the sine and cosine line graphics
sine_selector = sine.add_linear_selector()
cosine_selector = cosine.add_linear_selector()
def set_circle_cmap(ev):
# sets the cmap transforms
cmap_transform = ev.graphic.data[:, 1] # y-val data of the sine or cosine graphic
for g in [sine, cosine]:
g.cmap.transform = cmap_transform
# set circle cmap transform
circle.cmap.transform = cmap_transform
# when the sine or cosine graphic is clicked, the cmap_transform
# of the sine, cosine and circle line graphics are all set from
# the y-values of the clicked line
sine.add_event_handler(set_circle_cmap, "click")
cosine.add_event_handler(set_circle_cmap, "click")
def set_x_val(ev):
# used to sync the two selectors
value = ev.info["value"]
index = ev.get_selected_index()
sine_selector.selection = value
cosine_selector.selection = value
circle_radius.data[1, :-1] = circle_data[index]
# add same event handler to both graphics
sine_selector.add_event_handler(set_x_val, "selection")
cosine_selector.add_event_handler(set_x_val, "selection")
# set initial position of the selector so it's not just overlapping the y-axis
sine_selector.selection = 100
figure.show()
# NOTE: fpl.loop.run() should not be used for interactive sessions
# See the "JupyterLab and IPython" section in the user guide
if __name__ == "__main__":
print(__doc__)
fpl.loop.run()