Streaks while using color indexing with fill
Show older comments
I am trying to fill the area between two curves with a color that depends on a value at each point along one of the curves, but I am getting variations in color that should not be there. I have two (100x1) vectors: y1 and y2 which I plot against a third vector x (also 100x1) such that every point in y2 is directly above (or below or at the same location as) a point on y1. I have a fourth vector v that is the same length as x, y1, and y2, which I would like to use as a color index to fill the area between the curves defined by (x,y1) and (x,y2). (The datasets are attached below, for reference.) The command fill needs a closed loop to define the area to be filled, so I have created vectors X and Y to form the loop:
X=[x;flipud(x)];
Y=[y1;flipud(y2)];
If I type
fill(X,Y,'b')
the area between y1 and y2 will be filled in with blue. I want to fill this area with colors defined by the third variable, so I created C to serve as the color index (per the helpfile on fill):
C=[v;flipud(v)];
fill(X,Y,C)
I therefore expect the area between y1 and y2 to be filled with a color that varies horizontally (from one x value to the next), but not vertically, since each value of C corresponds to a single value in X. I get substantial vertical variation, however:

The command fill works as described in the documentation when I do not use a color index, so I believe that I am using it correctly. I have verified that the color index C has the same value at both the upper and lower points in x, since that would account for at least some variation in C between the curves at the same x position. There was no error in the color index, and it would not account for the degree of value variation in any event.
Is this a known issue while working with fill? Does anyone have suggestions as to 1) why this is happening and/or 2) how I can fix it?
Accepted Answer
More Answers (1)
Star Strider
on 16 Feb 2016
I don’t have your data, but from my experience with fill, patch, and their friends, you may want to horizontally concatenate these rather than vertically concatenate them:
X=[x flipud(x)];
Y=[y1 flipud(y2)];
9 Comments
Sarah
on 16 Feb 2016
Sarah
on 16 Feb 2016
Star Strider
on 16 Feb 2016
Without at least a sample of your data to work with, it is difficult to experiment to see what works with it. The fliplr is correct, but for my example to work, the vectors both need to be row vectors.
Change my code to:
X=[x(:)' flipud(x(:)')];
Y=[y1(:)' flipud(y2(:)')];
and see if that works.
Mike Garrity
on 16 Feb 2016
Edited: Mike Garrity
on 16 Feb 2016
What version of MATLAB are you using?
When I wrote that blog post about polygon interpolation, I was using the graphics system that was introduced in R2014b. The earlier one had a number of bugs in this area. You may have hit one of those. I'm guessing from the 'jet' colormap that you may be using an earlier version.
Here's my attempt to replicate your example with R2015b:
x = linspace(0,2*pi,100)';
y1 = sin(x);
y2 = 1+sin(x);
xp = [x;flipud(x)];
yp = [y1;flipud(y2)];
cp = xp;
fill(xp,yp,cp)

xp = [x;flipud(x)];
yp = [y1;flipud(y2)];
cp = yp;
fill(xp,yp,cp)

But both of those cases appear to work in R2014a:


So I expect I haven't reproduced your example correctly.
Star Strider
on 16 Feb 2016
The only other possibility (that just now occurred to me) is that your vectors aren’t sorted by ‘x’. Concatenate your data into a single (Nx2) matrix, sort them (using the sortrows function, sorting by the column with the ‘x’ data) and see if doing that helps. I have no idea what your data are, so I can’t test this idea.
Mike Garrity
on 17 Feb 2016
Edited: Mike Garrity
on 17 Feb 2016
@Mike Garrity The version I am using is indeed R2014a. Your blog
post indicates that color interpolation goes all right as long as
you indexing to a linear function of X and/or Y-- but my color
indexing vector is not linearly related to either.
Well then, as I explained in that blog post, the patch object is not going to be able to do this for you.
somewhat hampered by the program insisting that it has the pde
toolbox and a licesnse for it ...
I was just using pdetool to illustrate the approach because it takes care of a lot of the grunt work for you. The basic idea is the same regardless of how you do it.
If the function describing your colors is not a linear combination of your X & Y coordinates, then you are going to have to subdivide your shape into enough triangles that the linear interpolation within each triangle is a reasonably accurate representation of your function. There are lots of ways to do this. If we knew more about the function you have, we might be able to make some suggestions.
The most general purpose approach is probably the incremental delaunay construction that Damian Sheehy describes in this post on Loren's blog. Basically, you would convert your polygon into a constrained delaunayTriangulation, and then keep inserting additional points and retriangulating until you have small enough triangles.
That approach might look something like this.
Starting with my original example:
x = linspace(0,2*pi,100)';
y1 = sin(x);
y2 = 1+sin(x);
xp = [x;flipud(x)];
yp = [y1;flipud(y2)];
Create a constrained delaunayTriangulation. There are two tricky bits here. We need to pass in the edges of the polygon (so no triangles cross it), and we need to throw out any triangles which are outside the polygon:
edges = 1:numel(xp);
edges(2,:) = circshift(edges(1,:),[0 -1]);
dt = delaunayTriangulation([xp, yp],edges');
tin = isInterior(dt);
triplot(dt(tin,:),dt.Points(:,1),dt.Points(:,2))

Now we need to add more vertices. You'll want to figure out a way to do this that takes advantage of what you know about the function behind your colors. I'll just use a random number generator.

dt.Points(end+(1:numel(xn)),:) = [xn', yn'];
tin = isInterior(dt);
triplot(dt(tin,:),dt.Points(:,1),dt.Points(:,2))

Now we can compute the colors for the points which are on or in the polygon:
intris = dt(tin,:);
inpoints = unique(intris(:));
xp = dt.Points(:,1);
yp = dt.Points(:,2);
cp = nan(size(xp));
cp(inpoints) = cos(xp(inpoints)) .* cos(yp(inpoints));
And then plot them with patch (which is the low-level command that fill was calling for you):
patch('Vertices',[xp,yp], ... 'Faces',dt(tin,:), ... 'FaceVertexCData',cp, ... 'FaceColor','interp','EdgeColor','none')

So that's the basic idea, but I can't help you with the step of inserting the new points because I don't know anything about the function underlying your colors, or about your polygon.
What makes this very, very strange is that the interpolation is
darn-near perfect if I plot my X values as y's and viceversa:
I would expect that's just random luck. Sometimes the triangulation just happens to occur in a way that looks "reasonable". You can see that in some of the examples in that blog post. For example, the one where color is a function of x^2 looks pretty reasonable, but if you look closely you'll notice that the zero is offset to the right.
Mike Garrity
on 17 Feb 2016
Those two versions use different polygon triangulators, so the way they subdivide the polygon can be completely different.
But, I would caution against considering it a "fix". It's the sort of thing that can cause you a lot of heartburn. Cases where results appear reasonable, but aren't accurate can be even more dangerous than cases where you clearly get a wrong answer. In release R2014b, we strongly considered making patch error out if the color data wasn't a linear function of the coordinates, so that users would no they were in danger, but there were complaints about it erroring in cases where it "seemed to work".
Categories
Find more on Surface and Mesh Plots in Help Center and File Exchange
Products
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!