1pub const PYTHON_HEADER: &str = "### file generated by the 'plotpy' Rust crate
27
28import numpy as np
29import matplotlib.pyplot as plt
30import matplotlib.ticker as tck
31import matplotlib.patches as pat
32import matplotlib.path as pth
33import matplotlib.patheffects as pff
34import matplotlib.lines as lns
35import matplotlib.transforms as tra
36import mpl_toolkits.mplot3d
37
38# Variable to handle NaN values coming from Rust
39NaN = np.nan
40
41# List of additional objects to calculate bounding boxes
42EXTRA_ARTISTS = []
43
44# Adds an entity to the EXTRA_ARTISTS list to calculate bounding boxes
45def add_to_ea(obj):
46 global EXTRA_ARTISTS
47 if obj!=None: EXTRA_ARTISTS.append(obj)
48
49# Is a dictionary of mplot3d objects (one for each subplot_3d)
50THREE_D = dict()
51
52# Is a tuple holding the key to the current THREE_D object (defines the subplot_3d)
53THREE_D_ACTIVE = (1,1,1)
54
55# Creates or returns the mplot3d object with the current subplot_3d definition specified by THREE_D_ACTIVE
56def ax3d():
57 global THREE_D
58 global THREE_D_ACTIVE
59 if not THREE_D_ACTIVE in THREE_D:
60 a, b, c = THREE_D_ACTIVE
61 THREE_D[THREE_D_ACTIVE] = plt.gcf().add_subplot(a,b,c,projection='3d')
62 THREE_D[THREE_D_ACTIVE].set_xlabel('x')
63 THREE_D[THREE_D_ACTIVE].set_ylabel('y')
64 THREE_D[THREE_D_ACTIVE].set_zlabel('z')
65 add_to_ea(THREE_D[THREE_D_ACTIVE])
66 return THREE_D[THREE_D_ACTIVE]
67
68# Specifies the THREE_D_ACTIVE parameters to define a subplot_3d
69def subplot_3d(a,b,c):
70 global THREE_D_ACTIVE
71 THREE_D_ACTIVE = (a,b,c)
72 ax3d()
73
74# Transforms data limits to axis limits
75def data_to_axis(coords):
76 plt.axis() # must call this first
77 return plt.gca().transLimits.transform(coords)
78
79# Transforms axis limits to data limits
80def axis_to_data(coords):
81 plt.axis() # must call this first
82 return plt.gca().transLimits.inverted().transform(coords)
83
84# Configures the aspect of axes with a same scaling from data to plot units for x, y and z.
85def set_equal_axes():
86 global THREE_D
87 if len(THREE_D) == 0:
88 ax = plt.gca()
89 ax.axes.set_aspect('equal')
90 return
91 try:
92 ax = ax3d()
93 ax.set_box_aspect([1,1,1])
94 limits = np.array([ax.get_xlim3d(), ax.get_ylim3d(), ax.get_zlim3d()])
95 origin = np.mean(limits, axis=1)
96 radius = 0.5 * np.max(np.abs(limits[:, 1] - limits[:, 0]))
97 x, y, z = origin
98 ax.set_xlim3d([x - radius, x + radius])
99 ax.set_ylim3d([y - radius, y + radius])
100 ax.set_zlim3d([z - radius, z + radius])
101 except:
102 import matplotlib
103 print('VERSION of MATPLOTLIB = {}'.format(matplotlib.__version__))
104 print('ERROR: set_box_aspect is missing in this version of Matplotlib')
105
106# Function to ignore calls to plt such as the colorbar in an inset Axes
107def ignore_this(*args, **kwargs):
108 pass
109
110################## plotting commands follow after this line ############################
111
112";
113
114const PY_NUM_MARKERS: [&str; 12] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"];
115
116pub(crate) fn quote_marker(maker_style: &str) -> String {
138 if PY_NUM_MARKERS.contains(&maker_style) {
139 String::from(maker_style)
140 } else {
141 format!("'{}'", maker_style)
142 }
143}
144
145#[cfg(test)]
148mod tests {
149 use super::PYTHON_HEADER;
150
151 #[test]
152 fn constants_are_correct() {
153 assert_eq!(PYTHON_HEADER.len(), 2886);
154 }
155}