File mplot/_axes3d.py added (mode: 100644) (index 0000000..1d61f6f) |
|
1 |
|
#!/usr/bin/env python |
|
2 |
|
# coding: utf-8 |
|
3 |
|
|
|
4 |
|
|
|
5 |
|
# Monkey patch module |
|
6 |
|
# Removes side panes on Axes3D plot |
|
7 |
|
# Just import it to make MPL unpredictable |
|
8 |
|
|
|
9 |
|
import numpy as np |
|
10 |
|
from matplotlib import artist |
|
11 |
|
from mpl_toolkits.mplot3d.axis3d import Axis |
|
12 |
|
from mpl_toolkits.mplot3d.axis3d import art3d, proj3d, move_from_center, tick_update_position |
|
13 |
|
|
|
14 |
|
from mpl_toolkits.mplot3d.axes3d import Axes3D |
|
15 |
|
from matplotlib.axes import Axes |
|
16 |
|
|
|
17 |
|
|
|
18 |
|
@artist.allow_rasterization |
|
19 |
|
def draw(self, renderer): |
|
20 |
|
self.label._transform = self.axes.transData |
|
21 |
|
renderer.open_group('axis3d', gid=self.get_gid()) |
|
22 |
|
|
|
23 |
|
ticks = self._update_ticks() |
|
24 |
|
|
|
25 |
|
info = self._axinfo |
|
26 |
|
index = info['i'] |
|
27 |
|
|
|
28 |
|
mins, maxs, centers, deltas, tc, highs = self._get_coord_info(renderer) |
|
29 |
|
|
|
30 |
|
# Determine grid lines |
|
31 |
|
minmax = np.where(highs, maxs, mins) |
|
32 |
|
maxmin = np.where(highs, mins, maxs) |
|
33 |
|
|
|
34 |
|
# Draw main axis line |
|
35 |
|
juggled = info['juggled'] |
|
36 |
|
edgep1 = minmax.copy() |
|
37 |
|
edgep1[juggled[0]] = maxmin[juggled[0]] |
|
38 |
|
|
|
39 |
|
edgep2 = edgep1.copy() |
|
40 |
|
edgep2[juggled[1]] = maxmin[juggled[1]] |
|
41 |
|
pep = np.asarray( |
|
42 |
|
proj3d.proj_trans_points([edgep1, edgep2], renderer.M)) |
|
43 |
|
centpt = proj3d.proj_transform(*centers, renderer.M) |
|
44 |
|
self.line.set_data(pep[0], pep[1]) |
|
45 |
|
self.line.draw(renderer) |
|
46 |
|
|
|
47 |
|
# Grid points where the planes meet |
|
48 |
|
xyz0 = np.tile(minmax, (len(ticks), 1)) |
|
49 |
|
xyz0[:, index] = [tick.get_loc() for tick in ticks] |
|
50 |
|
|
|
51 |
|
# Draw labels |
|
52 |
|
# The transAxes transform is used because the Text object |
|
53 |
|
# rotates the text relative to the display coordinate system. |
|
54 |
|
# Therefore, if we want the labels to remain parallel to the |
|
55 |
|
# axis regardless of the aspect ratio, we need to convert the |
|
56 |
|
# edge points of the plane to display coordinates and calculate |
|
57 |
|
# an angle from that. |
|
58 |
|
# TODO: Maybe Text objects should handle this themselves? |
|
59 |
|
dx, dy = (self.axes.transAxes.transform([pep[0:2, 1]]) - |
|
60 |
|
self.axes.transAxes.transform([pep[0:2, 0]]))[0] |
|
61 |
|
|
|
62 |
|
lxyz = 0.5 * (edgep1 + edgep2) |
|
63 |
|
|
|
64 |
|
# A rough estimate; points are ambiguous since 3D plots rotate |
|
65 |
|
ax_scale = self.axes.bbox.size / self.figure.bbox.size |
|
66 |
|
ax_inches = np.multiply(ax_scale, self.figure.get_size_inches()) |
|
67 |
|
ax_points_estimate = sum(72. * ax_inches) |
|
68 |
|
deltas_per_point = 48 / ax_points_estimate |
|
69 |
|
default_offset = 21. |
|
70 |
|
labeldeltas = ( |
|
71 |
|
(self.labelpad + default_offset) * deltas_per_point * deltas) |
|
72 |
|
axmask = [True, True, True] |
|
73 |
|
axmask[index] = False |
|
74 |
|
lxyz = move_from_center(lxyz, centers, labeldeltas, axmask) |
|
75 |
|
tlx, tly, tlz = proj3d.proj_transform(*lxyz, renderer.M) |
|
76 |
|
self.label.set_position((tlx, tly)) |
|
77 |
|
if self.get_rotate_label(self.label.get_text()): |
|
78 |
|
angle = art3d._norm_text_angle(np.rad2deg(np.arctan2(dy, dx))) |
|
79 |
|
self.label.set_rotation(angle) |
|
80 |
|
self.label.set_va(info['label']['va']) |
|
81 |
|
self.label.set_ha(info['label']['ha']) |
|
82 |
|
self.label.draw(renderer) |
|
83 |
|
|
|
84 |
|
# Draw Offset text |
|
85 |
|
|
|
86 |
|
# Which of the two edge points do we want to |
|
87 |
|
# use for locating the offset text? |
|
88 |
|
if juggled[2] == 2: |
|
89 |
|
outeredgep = edgep1 |
|
90 |
|
outerindex = 0 |
|
91 |
|
else: |
|
92 |
|
outeredgep = edgep2 |
|
93 |
|
outerindex = 1 |
|
94 |
|
|
|
95 |
|
pos = move_from_center(outeredgep, centers, labeldeltas, axmask) |
|
96 |
|
olx, oly, olz = proj3d.proj_transform(*pos, renderer.M) |
|
97 |
|
self.offsetText.set_text(self.major.formatter.get_offset()) |
|
98 |
|
self.offsetText.set_position((olx, oly)) |
|
99 |
|
angle = art3d._norm_text_angle(np.rad2deg(np.arctan2(dy, dx))) |
|
100 |
|
self.offsetText.set_rotation(angle) |
|
101 |
|
# Must set rotation mode to "anchor" so that |
|
102 |
|
# the alignment point is used as the "fulcrum" for rotation. |
|
103 |
|
self.offsetText.set_rotation_mode('anchor') |
|
104 |
|
|
|
105 |
|
#---------------------------------------------------------------------- |
|
106 |
|
# Note: the following statement for determining the proper alignment of |
|
107 |
|
# the offset text. This was determined entirely by trial-and-error |
|
108 |
|
# and should not be in any way considered as "the way". There are |
|
109 |
|
# still some edge cases where alignment is not quite right, but this |
|
110 |
|
# seems to be more of a geometry issue (in other words, I might be |
|
111 |
|
# using the wrong reference points). |
|
112 |
|
# |
|
113 |
|
# (TT, FF, TF, FT) are the shorthand for the tuple of |
|
114 |
|
# (centpt[info['tickdir']] <= pep[info['tickdir'], outerindex], |
|
115 |
|
# centpt[index] <= pep[index, outerindex]) |
|
116 |
|
# |
|
117 |
|
# Three-letters (e.g., TFT, FTT) are short-hand for the array of bools |
|
118 |
|
# from the variable 'highs'. |
|
119 |
|
# --------------------------------------------------------------------- |
|
120 |
|
if centpt[info['tickdir']] > pep[info['tickdir'], outerindex]: |
|
121 |
|
# if FT and if highs has an even number of Trues |
|
122 |
|
if (centpt[index] <= pep[index, outerindex] |
|
123 |
|
and np.count_nonzero(highs) % 2 == 0): |
|
124 |
|
# Usually, this means align right, except for the FTT case, |
|
125 |
|
# in which offset for axis 1 and 2 are aligned left. |
|
126 |
|
if highs.tolist() == [False, True, True] and index in (1, 2): |
|
127 |
|
align = 'left' |
|
128 |
|
else: |
|
129 |
|
align = 'right' |
|
130 |
|
else: |
|
131 |
|
# The FF case |
|
132 |
|
align = 'left' |
|
133 |
|
else: |
|
134 |
|
# if TF and if highs has an even number of Trues |
|
135 |
|
if (centpt[index] > pep[index, outerindex] |
|
136 |
|
and np.count_nonzero(highs) % 2 == 0): |
|
137 |
|
# Usually mean align left, except if it is axis 2 |
|
138 |
|
if index == 2: |
|
139 |
|
align = 'right' |
|
140 |
|
else: |
|
141 |
|
align = 'left' |
|
142 |
|
else: |
|
143 |
|
# The TT case |
|
144 |
|
align = 'right' |
|
145 |
|
|
|
146 |
|
self.offsetText.set_va('center') |
|
147 |
|
self.offsetText.set_ha(align) |
|
148 |
|
self.offsetText.draw(renderer) |
|
149 |
|
|
|
150 |
|
if self.axes._draw_grid and len(ticks): |
|
151 |
|
# Grid lines go from the end of one plane through the plane |
|
152 |
|
# intersection (at xyz0) to the end of the other plane. The first |
|
153 |
|
# point (0) differs along dimension index-2 and the last (2) along |
|
154 |
|
# dimension index-1. |
|
155 |
|
lines = np.stack([xyz0, xyz0, xyz0], axis=1) |
|
156 |
|
|
|
157 |
|
lines[:, 0, index - 2] = maxmin[index - 2] |
|
158 |
|
lines[:, 2, index - 1] = maxmin[index - 1] |
|
159 |
|
|
|
160 |
|
lines[:, :, 2] = np.min(lines[:, :, 2]) |
|
161 |
|
#print("lajny:", lines[:, :, 2]) |
|
162 |
|
self.gridlines.set_segments(lines) |
|
163 |
|
self.gridlines.set_color(info['grid']['color']) |
|
164 |
|
self.gridlines.set_linewidth(info['grid']['linewidth']) |
|
165 |
|
self.gridlines.set_linestyle(info['grid']['linestyle']) |
|
166 |
|
self.gridlines.draw(renderer, project=True) |
|
167 |
|
|
|
168 |
|
# Draw ticks |
|
169 |
|
tickdir = info['tickdir'] |
|
170 |
|
tickdelta = deltas[tickdir] |
|
171 |
|
if highs[tickdir]: |
|
172 |
|
ticksign = 1 |
|
173 |
|
else: |
|
174 |
|
ticksign = -1 |
|
175 |
|
|
|
176 |
|
for tick in ticks: |
|
177 |
|
# Get tick line positions |
|
178 |
|
pos = edgep1.copy() |
|
179 |
|
pos[index] = tick.get_loc() |
|
180 |
|
pos[tickdir] = ( |
|
181 |
|
edgep1[tickdir] |
|
182 |
|
+ info['tick']['outward_factor'] * ticksign * tickdelta) |
|
183 |
|
x1, y1, z1 = proj3d.proj_transform(*pos, renderer.M) |
|
184 |
|
pos[tickdir] = ( |
|
185 |
|
edgep1[tickdir] |
|
186 |
|
- info['tick']['inward_factor'] * ticksign * tickdelta) |
|
187 |
|
x2, y2, z2 = proj3d.proj_transform(*pos, renderer.M) |
|
188 |
|
|
|
189 |
|
# Get position of label |
|
190 |
|
default_offset = 8. # A rough estimate |
|
191 |
|
labeldeltas = ( |
|
192 |
|
(tick.get_pad() + default_offset) * deltas_per_point * deltas) |
|
193 |
|
|
|
194 |
|
axmask = [True, True, True] |
|
195 |
|
axmask[index] = False |
|
196 |
|
pos[tickdir] = edgep1[tickdir] |
|
197 |
|
pos = move_from_center(pos, centers, labeldeltas, axmask) |
|
198 |
|
lx, ly, lz = proj3d.proj_transform(*pos, renderer.M) |
|
199 |
|
|
|
200 |
|
tick_update_position(tick, (x1, x2), (y1, y2), (lx, ly)) |
|
201 |
|
tick.tick1line.set_linewidth( |
|
202 |
|
info['tick']['linewidth'][tick._major]) |
|
203 |
|
tick.draw(renderer) |
|
204 |
|
|
|
205 |
|
renderer.close_group('axis3d') |
|
206 |
|
self.stale = False |
|
207 |
|
|
|
208 |
|
Axis.draw = draw |
|
209 |
|
|
|
210 |
|
|
|
211 |
|
|
|
212 |
|
@artist.allow_rasterization |
|
213 |
|
def draw(self, renderer): |
|
214 |
|
# draw the background patch |
|
215 |
|
self.patch.draw(renderer) |
|
216 |
|
self._frameon = False |
|
217 |
|
|
|
218 |
|
# first, set the aspect |
|
219 |
|
# this is duplicated from `axes._base._AxesBase.draw` |
|
220 |
|
# but must be called before any of the artist are drawn as |
|
221 |
|
# it adjusts the view limits and the size of the bounding box |
|
222 |
|
# of the axes |
|
223 |
|
locator = self.get_axes_locator() |
|
224 |
|
if locator: |
|
225 |
|
pos = locator(self, renderer) |
|
226 |
|
self.apply_aspect(pos) |
|
227 |
|
else: |
|
228 |
|
self.apply_aspect() |
|
229 |
|
|
|
230 |
|
# add the projection matrix to the renderer |
|
231 |
|
self.M = self.get_proj() |
|
232 |
|
renderer.M = self.M |
|
233 |
|
renderer.vvec = self.vvec |
|
234 |
|
renderer.eye = self.eye |
|
235 |
|
renderer.get_axis_position = self.get_axis_position |
|
236 |
|
|
|
237 |
|
# Calculate projection of collections and patches and zorder them. |
|
238 |
|
# Make sure they are drawn above the grids. |
|
239 |
|
zorder_offset = max(axis.get_zorder() |
|
240 |
|
for axis in self._get_axis_list()) + 1 |
|
241 |
|
for i, col in enumerate( |
|
242 |
|
sorted(self.collections, |
|
243 |
|
key=lambda col: col.do_3d_projection(renderer), |
|
244 |
|
reverse=True)): |
|
245 |
|
col.zorder = zorder_offset + i |
|
246 |
|
for i, patch in enumerate( |
|
247 |
|
sorted(self.patches, |
|
248 |
|
key=lambda patch: patch.do_3d_projection(renderer), |
|
249 |
|
reverse=True)): |
|
250 |
|
patch.zorder = zorder_offset + i |
|
251 |
|
|
|
252 |
|
if self._axis3don: |
|
253 |
|
# Draw panes first |
|
254 |
|
#print(self._get_axis_list()) |
|
255 |
|
axis_list = self._get_axis_list() |
|
256 |
|
#axis_list[0].draw_pane(renderer) |
|
257 |
|
#axis_list[1].draw_pane(renderer) |
|
258 |
|
#for axis in self._get_axis_list(): |
|
259 |
|
# axis.draw_pane(renderer) |
|
260 |
|
|
|
261 |
|
# Then axes |
|
262 |
|
#print(self._get_axis_list()) |
|
263 |
|
#for axis in self._get_axis_list(): |
|
264 |
|
# axis.draw(renderer) |
|
265 |
|
axis_list[0].draw(renderer) |
|
266 |
|
axis_list[1].draw(renderer) |
|
267 |
|
|
|
268 |
|
|
|
269 |
|
# Then rest |
|
270 |
|
Axes.draw(self, renderer) |
|
271 |
|
|
|
272 |
|
Axes3D.draw = draw |
|
273 |
|
|
|
274 |
|
|
File mplot/misc.py changed (mode: 100644) (index fcf7b7e..647e2cb) |
5 |
5 |
# nazvy proměnných jsou v angličtině |
# nazvy proměnných jsou v angličtině |
6 |
6 |
# Ale komenty teda ne) |
# Ale komenty teda ne) |
7 |
7 |
|
|
|
8 |
|
__all__ = ['grid3d_patch'] |
8 |
9 |
|
|
9 |
|
import matplotlib as mpl |
|
|
10 |
|
def grid3d_patch(*args, **kwargs): |
|
11 |
|
from . import _axis3d_margins_patch, _axes3d |
10 |
12 |
|
|
11 |
|
|
|
12 |
|
|
|
13 |
|
__all__ = ['font'] |
|
14 |
|
|
|
15 |
|
def font(*args, **kwargs): |
|
16 |
|
mpl.rcParams['font.size'] = 22 |
|
17 |
|
|
|
18 |
|
|
|
19 |
|
def show2D(sample_box, space='R', drawing=None): |
|
20 |
|
fig = plt.figure() |
|
21 |
|
ax = fig.add_subplot(111) |
|
22 |
|
ax.space = space |
|
23 |
|
ax.sample_box = sample_box |
|
24 |
|
|
|
25 |
|
if drawing is None: |
|
26 |
|
maxes.base_drawing(ax) |
|
27 |
|
else: |
|
28 |
|
getattr(maxes, drawing)(ax) |
|
29 |
|
# specially for qt_plot. It already runs event loop |
|
30 |
|
return fig.show() |
|
31 |
|
|
|
32 |
|
|
|
33 |
|
|
|
34 |
|
def subplot3D(sample_box, ax3d, space='R'): |
|
35 |
|
Fails = getattr(sample_box.failure_samples, space) |
|
36 |
|
Succeses = getattr(sample_box.success_samples, space) |
|
37 |
|
xyz = [0,1,2] |
|
38 |
|
ax3d.scatter(Fails[:,xyz[0]], Fails[:,xyz[1]], Fails[:,xyz[2]], c='r', marker='.') |
|
39 |
|
ax3d.scatter(Succeses[:,xyz[0]], Succeses[:,xyz[1]], Succeses[:,xyz[2]], c='g', marker='.') |
|
40 |
|
|
|
41 |
|
ax3d.set_xlabel('X') |
|
42 |
|
ax3d.set_ylabel('Y') |
|
43 |
|
ax3d.set_zlabel('Z') |
|
44 |
|
|
|
45 |
|
|
|
46 |
|
|
|
47 |
|
def plot3D(sample_box, space='R', filename='', format='pdf'): |
|
48 |
|
if not filename: |
|
49 |
|
filename = 'store/%s_%s_%s_plot3D'%(sample_box.gm_signature, space, sample_box.nsim) |
|
50 |
|
fig = plt.figure() |
|
51 |
|
ax3d = fig.add_subplot(111, projection='3d') |
|
52 |
|
subplot3D(sample_box, ax3d, space) |
|
53 |
|
|
|
54 |
|
try: # jen pro formu zkusíme, vždyť musí funkce formálně něco dělat, žejo? |
|
55 |
|
fig.savefig(filename + "." + format)#, dpi=300) |
|
56 |
|
except: |
|
57 |
|
pass # nic se neděje |
|
58 |
|
|
|
59 |
|
# vracím plt místo figury, protože nechcem sa trapiť ivent lupem |
|
60 |
|
# return plt insted of just fig to bypass event loop issues |
|
61 |
|
return plt.show() |
|
62 |
|
|
|
63 |
|
|
|
64 |
|
|
|
65 |
|
# nikdo mi neuvěří, že by tohle postačílo a nebylo by nutné tohlensto furt úpravovat |
|
66 |
|
def plot2D(sample_box, space='R', candidates=False, filename=None, format='pdf'): |
|
67 |
|
if filename is None: |
|
68 |
|
try: |
|
69 |
|
filename = sample_box.filename |
|
70 |
|
except: |
|
71 |
|
filename = 'store/%s_%s_%s_plot2D'%(sample_box.gm_signature, space, sample_box.nsim) |
|
72 |
|
|
|
73 |
|
fig = plt.figure() |
|
74 |
|
ax = fig.add_subplot(111) |
|
75 |
|
ax.space = space |
|
76 |
|
ax.sample_box = sample_box |
|
77 |
|
|
|
78 |
|
mart.setup(ax) |
|
79 |
|
mart.curly(ax) |
|
80 |
|
mart.plot_boundaries(ax, linewidth=0.7) |
|
81 |
|
mart.scatter_points(ax) |
|
82 |
|
try: |
|
83 |
|
mart.triplot(ax, color="grey", linewidth=0.4) |
|
84 |
|
except: |
|
85 |
|
pass |
|
86 |
|
|
|
87 |
|
if candidates: |
|
88 |
|
#č ax.scatter posílá parameter cmap Collections třídě. |
|
89 |
|
#č Třída mimo jiného dědí cm.ScalarMappable, |
|
90 |
|
#č která inicializaci deleguje funkci cm.get_cmap() ve (svém) modulu cm. |
|
91 |
|
# cmap='viridis_r' #cmap='plasma', |
|
92 |
|
mart.scatter_candidates(ax, s=5, marker='.', cmap='plasma_r',\ |
|
93 |
|
alpha=None, linewidths=None, edgecolors=None, plotnonfinite=False) |
|
94 |
|
mart.plot_the_best_candidate(ax, "^", color='#3D0D5B') |
|
95 |
|
|
|
96 |
|
try: # jen pro formu zkusíme, vždyť musí funkce formálně něco dělat, žejo? |
|
97 |
|
fig.savefig(filename + "." + format, dpi=300) |
|
98 |
|
except: |
|
99 |
|
pass # nic se neděje |
|
100 |
|
|
|
101 |
|
# vracím plt místo figury, protože nechcem sa trapiť ivent lupem |
|
102 |
|
# return plt insted of just fig to bypass event loop issues |
|
103 |
|
return plt.show() |
|