diff --git a/pyramid/fielddata.py b/pyramid/fielddata.py index c32ccf9022ded53ef41b56c8b36806e9a5309ba0..bb2dae6a37d85e52ccef4c37c1397fcaab6017b0 100644 --- a/pyramid/fielddata.py +++ b/pyramid/fielddata.py @@ -725,8 +725,9 @@ class VectorData(FieldData): from .file_io.io_vectordata import save_vectordata save_vectordata(self, filename, **kwargs) - def plot_field(self, title='Vector Field', axis=None, proj_axis='z', figsize=(9, 8), - ax_slice=None, show_mask=True, bgcolor='white', hue_mode='triadic'): + def plot_quiver(self, title='Vector Field', axis=None, proj_axis='z', figsize=(9, 8), + coloring='angle', ar_dens=1, ax_slice=None, log=False, scaled=True, + scale=1., show_mask=True, bgcolor='white', hue_mode='triadic'): """Plot a slice of the vector field as a quiver plot. Parameters @@ -739,9 +740,23 @@ class VectorData(FieldData): The axis, from which a slice is plotted. The default is 'z'. figsize : tuple of floats (N=2) Size of the plot figure. + coloring : {'angle', 'amplitude', 'uniform', matplotlib color} + Color coding mode of the arrows. Use 'full' (default), 'angle', 'amplitude', 'uniform' + (black or white, depending on `bgcolor`), or a matplotlib color keyword. + ar_dens: int, optional + Number defining the arrow density which is plotted. A higher ar_dens number skips more + arrows (a number of 2 plots every second arrow). Default is 1. ax_slice : int, optional The slice-index of the axis specified in `proj_axis`. Is set to the center of `proj_axis` if not specified. + log : boolean, optional + The loratihm of the arrow length is plotted instead. This is helpful if only the + direction of the arrows is important and the amplitude varies a lot. Default is False. + scaled : boolean, optional + Normalizes the plotted arrows in respect to the highest one. Default is True. + scale: float, optional + Additional multiplicative factor scaling the arrow length. Default is 1 + (no further scaling). show_mask: boolean Default is True. Shows the outlines of the mask slice if available. bgcolor: {'black', 'white'}, optional @@ -756,7 +771,7 @@ class VectorData(FieldData): The axis on which the graph is plotted. """ - self._log.debug('Calling plot_field') + self._log.debug('Calling plot_quiver') assert proj_axis == 'z' or proj_axis == 'y' or proj_axis == 'x', \ 'Axis has to be x, y or z (as string).' if ax_slice is None: @@ -776,24 +791,70 @@ class VectorData(FieldData): submask = self.get_mask()[..., ax_slice] else: raise ValueError('{} is not a valid argument (use x, y or z)'.format(proj_axis)) + # Prepare quiver (select only used arrows if ar_dens is specified): + dim_uv = u_mag.shape + vv, uu = np.indices(dim_uv) + 0.5 # shift to center of pixel + uu = uu[::ar_dens, ::ar_dens] + vv = vv[::ar_dens, ::ar_dens] + u_mag = u_mag[::ar_dens, ::ar_dens] + v_mag = v_mag[::ar_dens, ::ar_dens] + amplitudes = np.hypot(u_mag, v_mag) + angles = np.angle(u_mag + 1j * v_mag, deg=True).tolist() + # Calculate the arrow colors: + if coloring == 'angle': + self._log.debug('Encoding angles') + hue = np.arctan2(v_mag, u_mag) / (2 * np.pi) + hue[hue < 0] += 1 + if hue_mode == 'triadic': + cmap = colors.hls_triadic_cmap + elif hue_mode == 'tetradic': + cmap = colors.hls_tetradic_cmap + else: + raise ValueError('Hue mode {} not understood!'.format(hue_mode)) + elif coloring == 'amplitude': + self._log.debug('Encoding amplitude') + hue = amplitudes / amplitudes.max() + cmap = 'jet' + elif coloring == 'uniform': + self._log.debug('Automatic uniform color encoding') + hue = np.zeros_like(u_mag) # use black arrows! + cmap = 'gray' if bgcolor == 'white' else 'Greys' + else: + self._log.debug('Automatic uniform color encoding') + hue = np.zeros_like(u_mag) # use black arrows! + cmap = ListedColormap([coloring]) # If no axis is specified, a new figure is created: if axis is None: self._log.debug('axis is None') fig = plt.figure(figsize=figsize) axis = fig.add_subplot(1, 1, 1) axis.set_aspect('equal') - # Plot the field: - dim_uv = u_mag.shape - hue = np.arctan2(v_mag, u_mag) / (2 * np.pi) # Hue according to angle! - hue[hue < 0] += 1 # Shift negative values! - luminance = 0.5 * submask # Luminance according to mask! - if bgcolor == 'white': # Invert luminance: - luminance = 1 - luminance - saturation = np.hypot(u_mag, v_mag) # Saturation according to amplitude! - saturation /= saturation.max() - rgb = colors.rgb_from_hls(hue, luminance, saturation, mode=hue_mode) - axis.imshow(Image.fromarray(rgb), origin='lower', interpolation='none', - extent=(0, dim_uv[1], 0, dim_uv[0])) + # Take the logarithm of the arrows to clearly show directions (if specified): + if log and np.any(amplitudes): # If the slice is empty, skip! + cutoff = 10 + amp = np.round(amplitudes, decimals=cutoff) + min_value = amp[np.nonzero(amp)].min() + u_mag = np.round(u_mag, decimals=cutoff) / min_value + u_mag = np.log10(np.abs(u_mag) + 1) * np.sign(u_mag) + v_mag = np.round(v_mag, decimals=cutoff) / min_value + v_mag = np.log10(np.abs(v_mag) + 1) * np.sign(v_mag) + amplitudes = np.hypot(u_mag, v_mag) # Recalculate (used if scaled)! + # Scale the amplitude of the arrows to the highest one (if specified): + if scaled: + u_mag /= amplitudes.max() + 1E-30 + v_mag /= amplitudes.max() + 1E-30 + im = axis.quiver(uu, vv, u_mag, v_mag, hue, cmap=cmap, clim=(0, 1), angles=angles, + pivot='middle', units='xy', scale_units='xy', scale=scale / ar_dens, + minlength=0.05, width=1*ar_dens, headlength=2, headaxislength=2, + headwidth=2, minshaft=2) + if coloring == 'amplitude': + fig = plt.gcf() + fig.subplots_adjust(right=0.8) + cbar_ax = fig.add_axes([0.82, 0.15, 0.02, 0.7]) + cbar = fig.colorbar(im, cax=cbar_ax) + cbar.ax.tick_params(labelsize=14) + cbar_title = u'amplitude' + cbar.set_label(cbar_title, fontsize=15) # Change background color: axis.set_axis_bgcolor(bgcolor) # Show mask: @@ -820,9 +881,8 @@ class VectorData(FieldData): # Return plotting axis: return axis - def plot_streamline(self, title='Vector Field', axis=None, proj_axis='z', figsize=(9, 8), - coloring='angle', ax_slice=None, density=2, linewidth=2, - show_mask=True, bgcolor='white', hue_mode='triadic'): + def plot_field(self, title='Vector Field', axis=None, proj_axis='z', figsize=(9, 8), + ax_slice=None, show_mask=True, bgcolor='white', hue_mode='triadic'): """Plot a slice of the vector field as a quiver plot. Parameters @@ -835,19 +895,9 @@ class VectorData(FieldData): The axis, from which a slice is plotted. The default is 'z'. figsize : tuple of floats (N=2) Size of the plot figure. - coloring : {'angle', 'amplitude', 'uniform'} - Color coding mode of the arrows. Use 'full' (default), 'angle', 'amplitude' or - 'uniform'. ax_slice : int, optional The slice-index of the axis specified in `proj_axis`. Is set to the center of `proj_axis` if not specified. - density : float or 2-tuple, optional - Controls the closeness of streamlines. When density = 1, the domain is divided into a - 30x30 grid—density linearly scales this grid. Each cebll in the grid can have, at most, - one traversing streamline. For different densities in each direction, use - [density_x, density_y]. - linewidth : numeric or 2d array, optional - Vary linewidth when given a 2d array with the same shape as velocities. show_mask: boolean Default is True. Shows the outlines of the mask slice if available. bgcolor: {'black', 'white'}, optional @@ -862,7 +912,7 @@ class VectorData(FieldData): The axis on which the graph is plotted. """ - self._log.debug('Calling plot_quiver') + self._log.debug('Calling plot_field') assert proj_axis == 'z' or proj_axis == 'y' or proj_axis == 'x', \ 'Axis has to be x, y or z (as string).' if ax_slice is None: @@ -882,51 +932,24 @@ class VectorData(FieldData): submask = self.get_mask()[..., ax_slice] else: raise ValueError('{} is not a valid argument (use x, y or z)'.format(proj_axis)) - # Prepare quiver (select only used arrows if ar_dens is specified): - dim_uv = u_mag.shape - uu = np.arange(dim_uv[1]) + 0.5 # shift to center of pixel - vv = np.arange(dim_uv[0]) + 0.5 # shift to center of pixel - u_mag, v_mag = self.get_slice(ax_slice, proj_axis) - # v_mag = np.ma.array(v_mag, mask=submask) - amplitudes = np.hypot(u_mag, v_mag) - # Calculate the arrow colors: - if coloring == 'angle': - self._log.debug('Encoding angles') - color = np.arctan2(v_mag, u_mag) / (2 * np.pi) - color[color < 0] += 1 - if hue_mode == 'triadic': - cmap = colors.hls_triadic_cmap - elif hue_mode == 'tetradic': - cmap = colors.hls_tetradic_cmap - else: - raise ValueError('Hue mode {} not understood!'.format(hue_mode)) - elif coloring == 'amplitude': - self._log.debug('Encoding amplitude') - color = amplitudes / amplitudes.max() - cmap = 'jet' - elif coloring == 'uniform': - self._log.debug('No color encoding') - color = np.zeros_like(u_mag) # use black arrows! - cmap = 'gray' if bgcolor == 'white' else 'Greys' - else: - raise AttributeError("Invalid coloring mode! Use 'angles', 'amplitude' or 'uniform'!") # If no axis is specified, a new figure is created: if axis is None: self._log.debug('axis is None') fig = plt.figure(figsize=figsize) axis = fig.add_subplot(1, 1, 1) axis.set_aspect('equal') - # Plot the streamlines: - im = plt.streamplot(uu, vv, u_mag, v_mag, density=density, linewidth=linewidth, - color=color, cmap=cmap) - if coloring == 'amplitude': - fig = plt.gcf() - fig.subplots_adjust(right=0.8) - cbar_ax = fig.add_axes([0.82, 0.15, 0.02, 0.7]) - cbar = fig.colorbar(im.lines, cax=cbar_ax) - cbar.ax.tick_params(labelsize=14) - cbar_title = u'amplitude' - cbar.set_label(cbar_title, fontsize=15) + # Plot the field: + dim_uv = u_mag.shape + hue = np.arctan2(v_mag, u_mag) / (2 * np.pi) # Hue according to angle! + hue[hue < 0] += 1 # Shift negative values! + luminance = 0.5 * submask # Luminance according to mask! + if bgcolor == 'white': # Invert luminance: + luminance = 1 - luminance + saturation = np.hypot(u_mag, v_mag) # Saturation according to amplitude! + saturation /= saturation.max() + rgb = colors.rgb_from_hls(hue, luminance, saturation, mode=hue_mode) + axis.imshow(Image.fromarray(rgb), origin='lower', interpolation='none', + extent=(0, dim_uv[1], 0, dim_uv[0])) # Change background color: axis.set_axis_bgcolor(bgcolor) # Show mask: @@ -953,10 +976,10 @@ class VectorData(FieldData): # Return plotting axis: return axis - def plot_quiver(self, title='Vector Field', axis=None, proj_axis='z', figsize=(9, 8), - coloring='angle', ar_dens=1, ax_slice=None, log=False, scaled=True, - scale=1., show_mask=True, bgcolor='white', hue_mode='triadic'): - """Plot a slice of the vector field as a quiver plot. + def plot_quiver_field(self, title='Vector Field', axis=None, proj_axis='z', + figsize=(9, 8), ar_dens=1, ax_slice=None, show_mask=True, + bgcolor='white', hue_mode='triadic'): + """Plot the vector field as a field plot with uniformly colored arrows overlayed. Parameters ---------- @@ -968,23 +991,61 @@ class VectorData(FieldData): The axis, from which a slice is plotted. The default is 'z'. figsize : tuple of floats (N=2) Size of the plot figure. - coloring : {'angle', 'amplitude', 'uniform', matplotlib color} - Color coding mode of the arrows. Use 'full' (default), 'angle', 'amplitude', 'uniform' - (black or white, depending on `bgcolor`), or a matplotlib color keyword. ar_dens: int, optional Number defining the arrow density which is plotted. A higher ar_dens number skips more arrows (a number of 2 plots every second arrow). Default is 1. ax_slice : int, optional The slice-index of the axis specified in `proj_axis`. Is set to the center of `proj_axis` if not specified. - log : boolean, optional - The loratihm of the arrow length is plotted instead. This is helpful if only the - direction of the arrows is important and the amplitude varies a lot. Default is False. - scaled : boolean, optional - Normalizes the plotted arrows in respect to the highest one. Default is True. - scale: float, optional - Additional multiplicative factor scaling the arrow length. Default is 1 - (no further scaling). + show_mask: boolean + Default is True. Shows the outlines of the mask slice if available. + bgcolor: {'black', 'white'}, optional + Determines the background color of the plot. + hue_mode : {'triadic', 'tetradic'} + Optional string for determining the hue scheme. Use either a triadic or tetradic + scheme (see the according colormaps for more information). + + Returns + ------- + axis: :class:`~matplotlib.axes.AxesSubplot` + The axis on which the graph is plotted. + + """ + axis = self.plot_field(title=title, axis=axis, proj_axis=proj_axis, figsize=figsize, + ax_slice=ax_slice, show_mask=show_mask, bgcolor=bgcolor, + hue_mode=hue_mode) + self.plot_quiver(axis=axis, proj_axis=proj_axis, figsize=figsize, coloring='uniform', + ar_dens=ar_dens, ax_slice=ax_slice, show_mask=show_mask, bgcolor=bgcolor) + return axis + + def plot_streamline(self, title='Vector Field', axis=None, proj_axis='z', figsize=(9, 8), + coloring='angle', ax_slice=None, density=2, linewidth=2, + show_mask=True, bgcolor='white', hue_mode='triadic'): + """Plot a slice of the vector field as a quiver plot. + + Parameters + ---------- + title : string, optional + The title for the plot. + axis : :class:`~matplotlib.axes.AxesSubplot`, optional + Axis on which the graph is plotted. Creates a new figure if none is specified. + proj_axis : {'z', 'y', 'x'}, optional + The axis, from which a slice is plotted. The default is 'z'. + figsize : tuple of floats (N=2) + Size of the plot figure. + coloring : {'angle', 'amplitude', 'uniform'} + Color coding mode of the arrows. Use 'full' (default), 'angle', 'amplitude' or + 'uniform'. + ax_slice : int, optional + The slice-index of the axis specified in `proj_axis`. Is set to the center of + `proj_axis` if not specified. + density : float or 2-tuple, optional + Controls the closeness of streamlines. When density = 1, the domain is divided into a + 30x30 grid—density linearly scales this grid. Each cebll in the grid can have, at most, + one traversing streamline. For different densities in each direction, use + [density_x, density_y]. + linewidth : numeric or 2d array, optional + Vary linewidth when given a 2d array with the same shape as velocities. show_mask: boolean Default is True. Shows the outlines of the mask slice if available. bgcolor: {'black', 'white'}, optional @@ -1021,18 +1082,16 @@ class VectorData(FieldData): raise ValueError('{} is not a valid argument (use x, y or z)'.format(proj_axis)) # Prepare quiver (select only used arrows if ar_dens is specified): dim_uv = u_mag.shape - vv, uu = np.indices(dim_uv) + 0.5 # shift to center of pixel - uu = uu[::ar_dens, ::ar_dens] - vv = vv[::ar_dens, ::ar_dens] - u_mag = u_mag[::ar_dens, ::ar_dens] - v_mag = v_mag[::ar_dens, ::ar_dens] + uu = np.arange(dim_uv[1]) + 0.5 # shift to center of pixel + vv = np.arange(dim_uv[0]) + 0.5 # shift to center of pixel + u_mag, v_mag = self.get_slice(ax_slice, proj_axis) + # v_mag = np.ma.array(v_mag, mask=submask) amplitudes = np.hypot(u_mag, v_mag) - angles = np.angle(u_mag + 1j * v_mag, deg=True).tolist() # Calculate the arrow colors: if coloring == 'angle': self._log.debug('Encoding angles') - hue = np.arctan2(v_mag, u_mag) / (2 * np.pi) - hue[hue < 0] += 1 + color = np.arctan2(v_mag, u_mag) / (2 * np.pi) + color[color < 0] += 1 if hue_mode == 'triadic': cmap = colors.hls_triadic_cmap elif hue_mode == 'tetradic': @@ -1041,45 +1100,28 @@ class VectorData(FieldData): raise ValueError('Hue mode {} not understood!'.format(hue_mode)) elif coloring == 'amplitude': self._log.debug('Encoding amplitude') - hue = amplitudes / amplitudes.max() + color = amplitudes / amplitudes.max() cmap = 'jet' elif coloring == 'uniform': - self._log.debug('Automatic uniform color encoding') - hue = np.zeros_like(u_mag) # use black arrows! + self._log.debug('No color encoding') + color = np.zeros_like(u_mag) # use black arrows! cmap = 'gray' if bgcolor == 'white' else 'Greys' else: - self._log.debug('Automatic uniform color encoding') - hue = np.zeros_like(u_mag) # use black arrows! - cmap = ListedColormap([coloring]) + raise AttributeError("Invalid coloring mode! Use 'angles', 'amplitude' or 'uniform'!") # If no axis is specified, a new figure is created: if axis is None: self._log.debug('axis is None') fig = plt.figure(figsize=figsize) axis = fig.add_subplot(1, 1, 1) axis.set_aspect('equal') - # Take the logarithm of the arrows to clearly show directions (if specified): - if log and np.any(amplitudes): # If the slice is empty, skip! - cutoff = 10 - amp = np.round(amplitudes, decimals=cutoff) - min_value = amp[np.nonzero(amp)].min() - u_mag = np.round(u_mag, decimals=cutoff) / min_value - u_mag = np.log10(np.abs(u_mag) + 1) * np.sign(u_mag) - v_mag = np.round(v_mag, decimals=cutoff) / min_value - v_mag = np.log10(np.abs(v_mag) + 1) * np.sign(v_mag) - amplitudes = np.hypot(u_mag, v_mag) # Recalculate (used if scaled)! - # Scale the amplitude of the arrows to the highest one (if specified): - if scaled: - u_mag /= amplitudes.max() + 1E-30 - v_mag /= amplitudes.max() + 1E-30 - im = axis.quiver(uu, vv, u_mag, v_mag, hue, cmap=cmap, clim=(0, 1), angles=angles, - pivot='middle', units='xy', scale_units='xy', scale=scale / ar_dens, - minlength=0.05, width=1*ar_dens, headlength=2, headaxislength=2, - headwidth=2, minshaft=2) + # Plot the streamlines: + im = plt.streamplot(uu, vv, u_mag, v_mag, density=density, linewidth=linewidth, + color=color, cmap=cmap) if coloring == 'amplitude': fig = plt.gcf() fig.subplots_adjust(right=0.8) cbar_ax = fig.add_axes([0.82, 0.15, 0.02, 0.7]) - cbar = fig.colorbar(im, cax=cbar_ax) + cbar = fig.colorbar(im.lines, cax=cbar_ax) cbar.ax.tick_params(labelsize=14) cbar_title = u'amplitude' cbar.set_label(cbar_title, fontsize=15) @@ -1109,48 +1151,6 @@ class VectorData(FieldData): # Return plotting axis: return axis - def plot_quiver_field(self, title='Vector Field', axis=None, proj_axis='z', - figsize=(9, 8), ar_dens=1, ax_slice=None, show_mask=True, - bgcolor='white', hue_mode='triadic'): - """Plot the vector field as a field plot with uniformly colored arrows overlayed. - - Parameters - ---------- - title : string, optional - The title for the plot. - axis : :class:`~matplotlib.axes.AxesSubplot`, optional - Axis on which the graph is plotted. Creates a new figure if none is specified. - proj_axis : {'z', 'y', 'x'}, optional - The axis, from which a slice is plotted. The default is 'z'. - figsize : tuple of floats (N=2) - Size of the plot figure. - ar_dens: int, optional - Number defining the arrow density which is plotted. A higher ar_dens number skips more - arrows (a number of 2 plots every second arrow). Default is 1. - ax_slice : int, optional - The slice-index of the axis specified in `proj_axis`. Is set to the center of - `proj_axis` if not specified. - show_mask: boolean - Default is True. Shows the outlines of the mask slice if available. - bgcolor: {'black', 'white'}, optional - Determines the background color of the plot. - hue_mode : {'triadic', 'tetradic'} - Optional string for determining the hue scheme. Use either a triadic or tetradic - scheme (see the according colormaps for more information). - - Returns - ------- - axis: :class:`~matplotlib.axes.AxesSubplot` - The axis on which the graph is plotted. - - """ - axis = self.plot_field(title=title, axis=axis, proj_axis=proj_axis, figsize=figsize, - ax_slice=ax_slice, show_mask=show_mask, bgcolor=bgcolor, - hue_mode=hue_mode) - self.plot_quiver(axis=axis, proj_axis=proj_axis, figsize=figsize, coloring='uniform', - ar_dens=ar_dens, ax_slice=ax_slice, show_mask=show_mask, bgcolor=bgcolor) - return axis - def plot_quiver3d(self, title='Vector Field', limit=None, cmap='jet', mode='2darrow', coloring='full', ar_dens=1, opacity=1.0, hue_mode='triadic'): """Plot the vector field as 3D-vectors in a quiverplot. diff --git a/pyramid/phasemap.py b/pyramid/phasemap.py index e9e193940efba5e754c451e1bdb3016a2fe82115..b7860d715bd82beb5a0b003d38490081030d600d 100644 --- a/pyramid/phasemap.py +++ b/pyramid/phasemap.py @@ -658,50 +658,6 @@ class PhaseMap(object): # Return plotting axis: return axis - def plot_phase3d(self, title='Phase Map', unit='rad', cmap='RdBu'): - """Display the phasemap as a 3D surface with contourplots. - - Parameters - ---------- - title : string, optional - The title of the plot. The default is 'Phase Map'. - unit: {'rad', 'mrad', 'µrad'}, optional - The plotting unit of the phase map. The phase is scaled accordingly before plotting. - cmap : string, optional - The :class:`~matplotlib.colors.Colormap` which is used for the plot as a string. - The default is 'RdBu'. - - Returns - ------- - axis: :class:`~matplotlib.axes.AxesSubplot` - The axis on which the graph is plotted. - - """ - self._log.debug('Calling plot_phase3d') - # Take units into consideration: - phase = self.phase * self.UNITDICT[unit] - # Create figure and axis: - fig = plt.figure() - axis = Axes3D(fig) - # Plot surface and contours: - vv, uu = np.indices(self.dim_uv) - axis.plot_surface(uu, vv, phase, rstride=4, cstride=4, alpha=0.7, cmap=cmap, - linewidth=0, antialiased=False) - axis.contourf(uu, vv, phase, 15, zdir='z', offset=np.min(phase), cmap=cmap) - axis.set_title(title) - axis.view_init(45, -135) - axis.set_xlabel('u-axis [px]') - axis.set_ylabel('v-axis [px]') - axis.set_zlabel('phase shift [{}]'.format(unit)) - if self.dim_uv[0] >= self.dim_uv[1]: - u_bin, v_bin = np.max((2, np.floor(9 * self.dim_uv[1] / self.dim_uv[0]))), 9 - else: - u_bin, v_bin = 9, np.max((2, np.floor(9 * self.dim_uv[0] / self.dim_uv[1]))) - axis.xaxis.set_major_locator(MaxNLocator(nbins=u_bin, integer=True)) - axis.yaxis.set_major_locator(MaxNLocator(nbins=v_bin, integer=True)) - # Return plotting axis: - return axis - def plot_holo(self, title=None, gain='auto', axis=None, hue_mode='triadic', interpolation='none', figsize=(8, 8)): """Display the color coded holography image. @@ -862,3 +818,48 @@ class PhaseMap(object): # Return the plotting axes: return phase_axis, holo_axis + + + def plot_phase3d(self, title='Phase Map', unit='rad', cmap='RdBu'): + """Display the phasemap as a 3D surface with contourplots. + + Parameters + ---------- + title : string, optional + The title of the plot. The default is 'Phase Map'. + unit: {'rad', 'mrad', 'µrad'}, optional + The plotting unit of the phase map. The phase is scaled accordingly before plotting. + cmap : string, optional + The :class:`~matplotlib.colors.Colormap` which is used for the plot as a string. + The default is 'RdBu'. + + Returns + ------- + axis: :class:`~matplotlib.axes.AxesSubplot` + The axis on which the graph is plotted. + + """ + self._log.debug('Calling plot_phase3d') + # Take units into consideration: + phase = self.phase * self.UNITDICT[unit] + # Create figure and axis: + fig = plt.figure() + axis = Axes3D(fig) + # Plot surface and contours: + vv, uu = np.indices(self.dim_uv) + axis.plot_surface(uu, vv, phase, rstride=4, cstride=4, alpha=0.7, cmap=cmap, + linewidth=0, antialiased=False) + axis.contourf(uu, vv, phase, 15, zdir='z', offset=np.min(phase), cmap=cmap) + axis.set_title(title) + axis.view_init(45, -135) + axis.set_xlabel('u-axis [px]') + axis.set_ylabel('v-axis [px]') + axis.set_zlabel('phase shift [{}]'.format(unit)) + if self.dim_uv[0] >= self.dim_uv[1]: + u_bin, v_bin = np.max((2, np.floor(9 * self.dim_uv[1] / self.dim_uv[0]))), 9 + else: + u_bin, v_bin = 9, np.max((2, np.floor(9 * self.dim_uv[0] / self.dim_uv[1]))) + axis.xaxis.set_major_locator(MaxNLocator(nbins=u_bin, integer=True)) + axis.yaxis.set_major_locator(MaxNLocator(nbins=v_bin, integer=True)) + # Return plotting axis: + return axis