You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
232 lines
9.7 KiB
232 lines
9.7 KiB
"""Auxillary classes for kplot.py which we do not want the user to see."""
|
|
|
|
__version__ = 0.3
|
|
__author__ = "Martin Wiechert <martin.wiechert@gmx.de>"
|
|
__date__ = "December 19, 2001"
|
|
|
|
import KMatplot as kmp
|
|
import Numeric
|
|
import kplot
|
|
|
|
class _kplane: # curve [/ bar graph / arrows]
|
|
"""Represents 2d data like a curve or bar graph or plane arrow set"""
|
|
|
|
def __init__ (self, subplot, mask, data):
|
|
self.parent = subplot
|
|
self.id = self.parent.parent.proc.add_dataset (self.parent.id,
|
|
kmp.PlotCurve)
|
|
for i in range (len (data)): # (len gives number of rows.)
|
|
self.parent.parent.proc.set_channel (self.parent.id, self.id,
|
|
'XYxy'.find (mask [i]),
|
|
data [i, :])
|
|
|
|
class _kmap: # surface / contour / pixmap
|
|
"""Represents a scalar valued map from the plane.
|
|
(I.e. a surface or contour or pixmap.)"""
|
|
|
|
def __init__ (self, subplot, type, data):
|
|
self.parent = subplot
|
|
self.id = self.parent.parent.proc.add_dataset (self.parent.id, type)
|
|
for i in range (len (data)):
|
|
if data [i]:
|
|
self.parent.parent.proc.set_channel (self.parent.id, self.id,
|
|
(i - 1) % 3, data [i])
|
|
|
|
class _kpoly: # polygons
|
|
"""Represents a polygon set in space."""
|
|
|
|
def __init__ (self, subplot, X, Y, Z):
|
|
self.parent = subplot
|
|
self.id = self.parent.parent.proc.add_dataset (self.parent.id,
|
|
kmp.PlotFigure)
|
|
for i in range (3):
|
|
self.parent.parent.proc.set_channel (self.parent.id, self.id,
|
|
i, [X, Y, Z] [i])
|
|
|
|
class _ksubplot:
|
|
"""Maintains one set of axes."""
|
|
|
|
def __init__ (self, plot):
|
|
self.parent = plot
|
|
self.hold = 1
|
|
self._3d = None
|
|
self.sets = []
|
|
# Attributes 'id' and 'current' are created with first 'plot' call.
|
|
|
|
def _shrink (self, kw):
|
|
"""Replaces dX, dY by x, y (to simplify parsing).
|
|
Check keyword legality."""
|
|
|
|
kw = kw.replace ('dX', 'x')
|
|
kw = kw.replace ('dY', 'y')
|
|
# Check legality.
|
|
X = kw.count ('X')
|
|
Y = kw.count ('Y')
|
|
x = kw.count ('x')
|
|
y = kw.count ('y')
|
|
# Any of the above letters at most once, no other letters,
|
|
# composite keywords must contain Y.
|
|
if X > 1 or Y > 1 or x > 1 or y > 1 or X + Y + x + y != len (kw) or \
|
|
(Y == 0 and len (kw) > 1):
|
|
raise kplot.Error, 'Unknown keyword.'
|
|
return kw
|
|
|
|
def _plot_curve (self, kw, data):
|
|
"""Plots one curve."""
|
|
|
|
if len (data) % len (kw):
|
|
raise kplot.Error, 'Mismatched number of rows.'
|
|
for i in range (0, len (data), len (kw)):
|
|
self.sets.append (_kplane (self, kw, data [i : i + len (kw), :]))
|
|
|
|
def plot (self, args, kwargs):
|
|
"""Plots one or more curves"""
|
|
|
|
# Most of the code is for sorting out data according to keywords.
|
|
# See kplot.kplot.plot doc string for keyword semantics.
|
|
|
|
# First handle hold and axes status.
|
|
if self._3d != 0: # wrong axes or no axes at all
|
|
if self._3d == 1: # wrong axes - erase all
|
|
self.parent.proc.remove_all_datasets (self.id)
|
|
self.sets = []
|
|
self.parent.proc.remove_axes (self.id)
|
|
self._3d = 0
|
|
self.id = self.parent.proc.add_axes (self._3d)
|
|
elif not self.hold:
|
|
self.parent.proc.remove_all_datasets (self.id)
|
|
self.sets = []
|
|
|
|
# Now tackle arguments.
|
|
# Composite keywords are independent (as they carry complete data
|
|
# for one (or even multiple) plot(s)). So they are passed instantly.
|
|
# All others (positional and one-letter keywords) are collected and
|
|
# then plotted in one go.
|
|
# Compatibility of positional and one-letter keyword args is checked
|
|
# via args_given.
|
|
coll_args = {} # Collect arguments here.
|
|
if args: # First look for positional arguments.
|
|
n = len (args)
|
|
if n == 1:
|
|
synth_kw = 'Y'
|
|
else:
|
|
synth_kw = 'XYxy' [:n]
|
|
for i in range (n):
|
|
coll_args [synth_kw [i]] = args [i]
|
|
if n > 2:
|
|
args_given = 2 # error bars ...
|
|
else:
|
|
args_given = 1 # ... only coordinates ...
|
|
else:
|
|
args_given = 0 # ... nothing
|
|
synth_kw = ''
|
|
for kw in kwargs.keys ():
|
|
kws = self._shrink (kw)
|
|
if len (kws) > 1: # Mixed data - plot instantly.
|
|
if not len (kwargs [kw].shape) == 2:
|
|
raise Error, 'Matrix expected.'
|
|
self._plot_curve (kws, kwargs [kw])
|
|
# Data given separately for X, Y, dX, dY - keep track, but check
|
|
# for compatibility with positional arguments first.
|
|
elif args_given == 2 or (args_given == 1 and kws in 'XY'):
|
|
raise kplot.Error, 'Too many keyword arguments.'
|
|
else:
|
|
synth_kw = synth_kw + kws
|
|
coll_args [kws] = kwargs [kw]
|
|
|
|
# Finally plot collected arguments if there are any.
|
|
if coll_args:
|
|
if not 'Y' in synth_kw:
|
|
raise kplot.Error, 'Y data missing.'
|
|
# All data must have the same shape as Y-data.
|
|
# Exception: If multiple curves are given, X may still be just one
|
|
# row, because a common X-axis is feasible.
|
|
sh = coll_args ['Y'].shape
|
|
if len (sh) > 2:
|
|
raise Error, 'Matrix or vector expected'
|
|
if ('x' in synth_kw and coll_args ['x'].shape != sh) or \
|
|
('y' in synth_kw and coll_args ['y'].shape != sh) or \
|
|
('X' in synth_kw and not (coll_args ['X'].shape == sh or
|
|
(len (coll_args ['X'].shape) == 1 and
|
|
len (coll_args ['X']) == sh [-1]))):
|
|
raise kplot.Error, 'Mismatched shapes.'
|
|
n = len (synth_kw)
|
|
data = Numeric.zeros ((n, sh [-1]), 'd') # container for data mix
|
|
if len (sh) == 1: # just one curve
|
|
for i in range (n): # mix data ...
|
|
data [i, :] = coll_args [synth_kw [i]]
|
|
self._plot_curve (synth_kw, data) # ... and send
|
|
else: # multiple curves
|
|
if 'X' in synth_kw and len (coll_args ['X'].shape) == 1:
|
|
# Common X-axis
|
|
# Put 'X' to the end, set it once and for all and trick
|
|
# the subsequent loop into overlooking it.
|
|
synth_kw = synth_kw.replace ('X', '') + 'X'
|
|
n -= 1
|
|
data [n, :] = coll_args ['X']
|
|
for j in range (sh [0]):
|
|
for i in range (n): # mix data ...
|
|
data [i, :] = coll_args [synth_kw [i]] [j, :]
|
|
self._plot_curve (synth_kw, data) # ... and send
|
|
self.current = len (self.sets) - 1
|
|
|
|
def plane_map (self, args, type):
|
|
"""Plots a surface or contour or pixmap."""
|
|
|
|
# First handle hold and axes status.
|
|
_3d = type == kmp.PlotSurface # Surfaces are 3d, which contours and
|
|
# pixmaps are not.
|
|
if self._3d != _3d: # wrong axes or no axes at all
|
|
if self._3d == (not _3d): # wrong axes - erase all
|
|
self.parent.proc.remove_all_datasets (self.id)
|
|
self.sets = []
|
|
self.parent.proc.remove_axes (self.id)
|
|
self._3d = _3d
|
|
self.id = self.parent.proc.add_axes (self._3d)
|
|
elif not self.hold:
|
|
self.parent.proc.remove_all_datasets (self.id)
|
|
self.sets = []
|
|
|
|
if not len (args [0].shape) == 2:
|
|
raise kplot.Error, 'Matrix expected.'
|
|
# Check if scaling vectors are ok if given.
|
|
# Note that for pixmaps they must be one cell larger,
|
|
# because they denote pixel edges.
|
|
if not args [-2] or (len (args [-2].shape) == 1 and
|
|
len (args [-2]) == args [0].shape [1] +
|
|
(type == kmp.PlotImage)) and \
|
|
not args [-1] or (len (args [-1].shape) == 1 and
|
|
len (args [-1]) == args [0].shape [0] +
|
|
(type == kmp.PlotImage)):
|
|
if args [-1]: # KMatplot wants a column vector here.
|
|
args [-1] = args [-1] [:, Numeric.NewAxis]
|
|
self.sets.append (_kmap (self, type, args)) # Send.
|
|
else:
|
|
raise kplot.Error, 'Shapes do not match.'
|
|
self.current = len (self.sets) - 1
|
|
|
|
def polygon (self, X, Y, Z):
|
|
"""Plots a set of polygons."""
|
|
|
|
# First handle hold and axes status.
|
|
if self._3d != 1: # wrong axes or no axes at all
|
|
if self._3d == 0: # wrong axes - erase all
|
|
self.parent.proc.remove_all_datasets (self.id)
|
|
self.sets = []
|
|
self.parent.proc.remove_axes (self.id)
|
|
self._3d = 1
|
|
self.id = self.parent.proc.add_axes (self._3d)
|
|
elif not self.hold:
|
|
self.parent.proc.remove_all_datasets (self.id)
|
|
self.sets = []
|
|
|
|
if len (X.shape) == 1:
|
|
X, Y, Z = [i [:, Numeric.NewAxis] for i in [X, Y, Z]]
|
|
elif len (X.shape) == 2:
|
|
X, Y, Z = [Numeric.transpose (i) for i in [X, Y, Z]]
|
|
else:
|
|
raise kplot.Error, 'Matrix or vector expected.'
|
|
self.sets.append (_kpoly (self, X, Y, Z))
|
|
self.current = len (self.sets) - 1
|
|
|