complexvariables / conformalmapping

Conformal mapping toolkit for MATLAB
Other
11 stars 9 forks source link

Convert plot system to use new preference scheme. #25

Closed ehkropf closed 10 years ago

ehkropf commented 10 years ago

Move away from use of plotdef through use of a new plotset class which will subclass optset.

ehkropf commented 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.

ehkropf commented 10 years ago

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.

ehkropf commented 10 years ago

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.

ehkropf commented 10 years ago

Merge #31 gets this implementation.