Closed ehkropf closed 10 years ago
The issues/#25-plotprefs branch has a start on this change, but there are some problems.
The goal was to be able to pass a conformal map object to a plot call and have things just magically happen. So the plot call in
f = szmap(ellipse(2, 1));
plot(f)
creates a grid for the domain of f
, in this case a disk
region, and applies f
to the grid, plotting this result, and finishes by plotting the boundary of the range of f
. One can also do things like
plot(f, 'numRadialLines', 40, 'numCircularLines', 10)
or
plot(f, 'gridType', 'carleson')
to get different results. The problem is that something that should work like
plot(f, 'gridColor', 'g')
will actually fail. Where
set(cmtplot, 'gridColor', 'g')
plot(f)
works. This issue is that grid construction options are actually stored by disk
and diskex
using gridset
, while the grid colour comes from the plotset
class. And the parsing logic for this isn't up to separating these correctly.
There obviously needs to be a cleaner way to do this. It feels wrong to have disk
storing its own options, but I'm not sure what the alternative should be.
Ideally one should be able to have
plot(f, 'gridColor', 'g', 'lineColor', 'b', 'gridType', 'carleson')
work as you think it would.
Idea: Each level of plot "pulls" its relevant preferences from a given varargin
list. For example, say we start the plot call with
plot(f, 'gridColor', 'g', 'lineColor', 'b', 'gridType', 'carleson')
The conformalmap
class provides the default behaviour, which is to first plot the grid. The call to construct the grid strips the 'gridType', 'carleson'
from the list, and the remaining two pairs are then passed to the call to plot the grid. The grid plot function strips the 'gridColor', 'g'
from the list and plots the grid. Finally, the leftover arguments are passed to the routine which plots the boundary and strips the 'lineColor', 'b'
from the list.
Granted this requires getting return arguments from plot, which are typically handles, so not such a good idea. Instead: an appropriately defined pullArgs
method. For instance the disk
class might have a static pullGridArgs
method that separates anything it might use for grid construction out of a list. Then the cmtplot
would have a static pullGridArgs
method that separates any preferences for grid plotting. For plotting curves, the cmtplot
would have a pullCurveArgs
which would grab any options for plotting curves if needed. And so on. Example code in conformalmap.plot
:
[gargs, args] = disk.pullGridArgs(varargin{:});
[gpargs, args] = cmtplot.pullGridArgs(args{:});
plot(apply(f, grid(f.domain_, gargs{:})), gpargs{:})
plot(f.range_, args{:})
Of course here we just pass all remaining args to the curve plot, which would error on anything not used.
New strategy.
Default plot behaviour for closedcurve
objects is as follows. Let C
be a closed curve, then plot(C, args{:})
where args
is a cell array of name/value pairs does (with things like return values stripped for the sake of example)
function plot(C, varargin)
...
plot_(C); % actual plot function, subclasses may redefine this
[cargs, pargs] = cmtplot.closedcurveArgs(varargin{:});
set(h, pargs{:}, cargs{:})
...
The closedcurveArgs
function separates pairs recognised by plotset
and then translates these to pairs which can be passed to matlab's handle graphics. Subsequently putting the resulting argument pairs in the order shown gives the behaviour that plot(C, 'lineColor', 'g', 'color', 'k')
and plot(C, 'color', 'k', 'lineColor', 'g')
both plot a green curve (CMT preferences take precedence), but I don't think this is a bad thing.
A similar thing happens when calling plot(gd, args{:})
where gd
is a gridcurves
object. That is,
function plot(gd, varargin)
...
[gargs, pargs] = cmtplot.gridArgs(varargin{:});
for k = 1:numel(gd)
plot(gd{k}, pargs{:}, gargs{:})
end
...
And similar behaviour for other base plot behaviour. (Note plot behaviour for region
is to plot each of its boundary curves, so we ignore it for this discussion.)
Finally we get to the plot
routine in conformalmap
. Here we do
function plot(f, varargin)
...
[gargs, pargs] = separateArgs(get(f.domain_), varargin{:});
plot(apply(f, grid(f.domain_, gargs{:})), pargs{:});
plot(f.range_, pargs{:});
...
The separateArgs
method belongs to optset
, so in this case we use get(f.domain_)
to determine which optset
subclass is in use (gridargs
by default), and then gargs
is a cell array of name/value pairs which applies only to grid construction, and we just pass the remaining pairs in pargs
to the plot routines.
The upshot is a command, for a conformal map f
, like
plot(f, 'gridColor', 'g', 'lineColor', 'm', 'gridType', 'polar')
dispatches the name/value pairs to the right components.
Merge #31 gets this implementation.
Move away from use of
plotdef
through use of a newplotset
class which will subclassoptset
.