Is there a way to make the ends of a line created by imline snap to the nearest data point on a curve?
I'm trying to measure the slope between two points on a curve. The imline is great, but the ends of the line created by it do not snap to data points on a curve.
I'm wondering if I can drag the line around while both ends of the line remained on the curve.
It can be done using the 'PositionConstraintFcn' property of imline. Its value specifies a function handle that is called whenever the line is dragged using the mouse:
'PositionConstraintFcn': Function handle fcn that is called whenever the line is dragged using the mouse.
Whenever the object is moved because
of a mouse drag, the constraint function is called using
the syntax: constrained_position = fcn(new_position) where new_position is of the form [...]
The form of new_position is a 2x2 matrix, where each row is an endpoint of the line; and columns represent x and y respectively.
So all you have to do is specify a function that finds the closest point for each end and returns that constrained position. This can be done in two steps:
Create a function to do the actual job, using as inputs the new position (2x2 matrix) and the set of allowed positions (Nx2, where N represents the number of points of the curve). The output is a 2x2 matrix with the constrained position.
function constr_pos = imline_snap(new_pos, positions)
[~, ind1] = min(sum(bsxfun(#minus, new_pos(1,:), positions).^2, 2));
[~, ind2] = min(sum(bsxfun(#minus, new_pos(2,:), positions).^2, 2));
constr_pos = [positions(ind1,:); positions(ind2,:)];
Define this function in its own m-file (imline_snap.m) and place it where Matlab can find it, for example in the current folder.
Here's how it works. The function receives the two points selected by the mouse (new_pos) and the set of points defining the curve (positions). It computes the distance from the first mouse point to each point in the curve (sum(bsxfun(#minus, new_pos(1,:), positions).^2, 2)), and gets the index of the point in the curve with minimum distance (ind1). The same is done for the second point (giving the index ind2). Finally, those indices are used for selecting the appropriate curve points and building the ouput (constr_pos).
The above imline_snap function needs to be particularized with the allowed positions corresponding to the points of the curve. This is necessary because the PositionConstraintFcn must accept only one input, namely the first input of imline_snap. This can be done via an anonymous function (see the example below, line fcn = ...); whose handle is then passed to imline.
Example code:
h = plot(0:.01:1, (0:.01:1).^2); %// example curve. Get a handle to it
a = gca; %// handle to current axes
X = get(h,'XData'); %// x values of points from the curve
Y = get(h,'YData'); %// y values of points from the curve
fcn = #(pos) imline_snap(pos, [X(:) Y(:)]); %// particularize function using curve points
imline(a, 'PositionConstraintFcn', fcn) %// create imline with that PositionConstraintFcn
It should be noted that the code snaps to actual curve points. It doesn't for example interpolate between curve points. That could be done modifying the imline_snap function accordingly (but could result in sluggish movement if the number of operations is large).
Here's the above example at work (in Matlab R2010b). The righmost endpoint of the curve was dragged arbitrarily with the mouse, but it is seen to be snapped to the curve.
As a bonus, it would be easy to modify the function to display the slope in the figure title. Just add the following line at the end of the imline_snap function:
title(['Slope: ' num2str((constr_pos(2,2)-constr_pos(1,2))/(constr_pos(2,1)-constr_pos(1,1)))])
Example showing slope:
Related
Suppose we have quiver field i.e. we have a meshgrid and then we assign a vector to each point. Is it possible to plot only the quiver field within some polygon?
So in the figure below, we want everything outside the triangle to be cropped out.
Ideally the code will also be helpful for the next step of having multiple such polygons and cropping out everything on their complement.
Some approaches:
A direct way is to figure out the meshgrid for the particular polygon and then assign a vector to each point. But that will take a lot of time to figure out as polygons get more complicated. In other words, the regular meshgrid is the square polygon, so we must modify the meshgrid matrix depending on our polygon. A friend informed me of a mesh generator matlab code.
Use inpolygon. The input of inpolygon are points in (x,y). But in our case we only have the vector field assigned to a meshgrid. One idea is to solve the ode system to obtain concrete solution pairs (x,y) to plug into the polygon. But solving them takes a lot longer and the pictures are not as nice.
Here's some sample code which I think will generate the kind of plot you want. It uses inpolygon to "filter" out the points inside the polygon. The vector field is still evaluated at the original meshgrid points. It easily extends to multiple polygons too.
clear
clc
x = linspace(0, 1, 21);
[X,Y] = meshgrid(x,x);
U = -Y; %some velocity field
V = X;
hold off
quiver(X,Y,U,V); %quiver on all points
polygon = [0.2,0.2;
0.7,0.5;
0.5,0.8]; %polygon vertices
ind = inpolygon(X,Y,polygon(:,1),polygon(:,2)); %get indices of points inside polygon
hold on
quiver(X(ind),Y(ind),U(ind),V(ind)); %quiver of points inside polygon
I have a sequence with data and an offset. I'm asked to plot a stem() graph of the data, starting at the offset. I have figured out the data part (the easy part) and how to change the window to include the offset, but when I plot the graph, it shows the offset with a value of zero and zeros until 1 where the sequence.data will start and plot points.
methods
function s = sequence(data, offset)
s.data = data;
s.offset = offset;
end
function stem(x)
% STEM Display a Matlab sequence, x, using a stem plot.
stem(x.offset,x.data);
axis([x.offset x.offset+length(x.data) 'auto' 'auto']);
end
I need to figure out how to "move" my x.data to my x.offset and start stem plotting there.
I don't understand why you simply can't add an x offset while keeping the y data the same?
For instance, given your example in your comments above:
x = 0:4;
y = 1:5;
This is what the original graph looks like, as well as shifting the graph to the left by 3 (-3):
stem(x,y,'b');
hold on;
stem(x-3,y,'r');
This is what I get:
The blue data is the original, while the red is the shifted instance to the left by 3. As you can see, the y data is the same, but the x points move to the left by 3. What your code is actually doing is that it does not shift the actual data. You are only changing the display range of your stem plot. As such, you should really be doing this:
methods
function s = sequence(data, offset)
s.data = data;
s.offset = offset;
end
function stem(x)
% STEM Display a Matlab sequence, x, using a stem plot.
%// First define sequence from [0,N-1]
vals = 0:numel(x.data)-1;
%// Now use the above and manually shift the x coordinate
stem(vals+x.offset,x.data);
end
I'm going to assume that your data on the x-axis starts counting at 0, and so we will declare a sequence from 0 up to N-1 where N is the total number of elements that you have. Once we declare this sequence, when it's time to draw the stem plot, we simply add an offset to this sequence and use this as the x data. The y data should stay the same.
However, I would argue that creating a custom class for implementing this addition to stem is less readable than what I originally did above. If this is a requirement for whatever you're developing, then certainly go ahead and do it this way, but I don't really think it's necessary.
I'm currently plotting 2 separate 3-dimensional amorphous blobs which overlap each other. I have created the blobs by deforming a unit circle (as you can see in the code provided below). My question is: is there an easy way to isolate the overlapping region? I need to isolate the overlapping region and then color it differently (as in turn the region green, for example) to clearly show where the overlap is. My actual program has many shapes that overlap, however for the sake of simplicity, i have produced the following code to illustrate what i am trying to do:
% Create Sphere with 100 points
N = 100; % sphere grid points
[X,Y,Z] = sphere(N); % get x,y,z coordinates for sphere
num=size(X,1)*size(X,2); % get total amount of x-coordinates (m*n)
% Loop through every x-coordinate and apply scaling if applicable
for k=1:num % loop through every coordinate
value=X(k); % store original value of X(k) as value
if value<0 % compare value to 0
X(k)=0.3*value; % if < 0, scale value
end
end
% Loop through every z-coordinate and apply scaling if applicable
for k=1:num % loop through every coordinate
value=Z(k); % store original value of X(k) as value
if value>0 % compare value to 0
Z(k)=0.3*value; % if < 0, scale value
end
end
mesh(X,Y,Z,'facecolor','y','edgecolor','y','facealpha',...
0.2,'edgealpha',0.2);
hold on
mesh(-1*(X-1),Y,Z,'facecolor','r','edgecolor','r','facealpha',...
0.2,'edgealpha',0.2);
hold off
axis equal
I'm not necessarliy looking for code, just an effective algorithm or process to achieve the desired results as I need to adapt this result into the more sophisticated program I have.
Maintain an array (n-dimensional) of integers, as you draw your objects, increment each corresponding point in the array. When done, loop through the array, and each element > 1 has an overlap between two or more objects, use the array coordinate to color the objects based on the number of overlaps.
I have worked in 2D with the MATLAB builtin function inpolygon to find out overlapping areas. However, it does not natively support 3d. I would suggest you try the inhull function which you can find here at file exchange. Please note it only supports convex hulls.
If that doesn´t help you maybe you find some inspiration in this discussion.
I would like to create a function in Matlab that, given an image, will allow one to select a pixel by clicking on it in the image and return the coordinates of the pixel. Ideally, one would be able to click on several pixels in the image in succession, and the function would store all the respective coordinates in a matrix. Is there a way to do this in Matlab?
ginput
Graphical input from mouse or cursor
Syntax
[x,y] = ginput(n)
[x,y] = ginput
[x,y,button] = ginput(...)
Description
[x,y] = ginput(n) enables you to
identify n points from the current
axes and returns their x- and
y-coordinates in the x and y column
vectors. Press the Return key to
terminate the input before entering n
points.
I think this is what you want:
A=imread('filename.jpg');
image(A)
[x,y]=ginput()
I've got a series of XY point pairs in MATLAB. These pairs describe points around a shape in an image; they're not a function, meaning that two or more y points may exist for each x value.
I can plot these points individually using something like
plot(B(:,1),B(:,2),'b+');
I can also use plot to connect the points:
plot(B(:,1),B(:,2),'r');
What I'm trying to retrieve are my own point values I can use to connect the points so that I can use them for further analysis. I don't want a fully connected graph and I need something data-based, not just the graphic that plot() produces. I'd love to just have plot() generate these points (as it seems to do behind the scenes), but I've tried using the linseries returned by plot() and it either doesn't work as I understand it or just doesn't give me what I want.
I'd think this was an interpolation problem, but the points don't comprise a function; they describe a shape. Essentially, all I need are the points that plot() seems to calculate; straight lines connecting a series of points. A curve would be a bonus and would save me grief downstream.
How can I do this in MATLAB?
Thanks!
Edit: Yes, a picture would help :)
The blue points are the actual point values (x,y), plotted using the first plot() call above. The red outline is the result of calling plot() using the second approach above. I'm trying to get the point data of the red outline; in other words, the points connecting the blue points.
Adrien definitely has the right idea: define a parametric coordinate then perform linear interpolation on the x and y coordinates separately.
One thing I'd like to add is another way to define your parametric coordinate so you can create evenly-spaced interpolation points around the entire shape in one pass. The first thing you want to do, if you haven't already, is make sure the last coordinate point reconnects to the first by replicating the first point and adding it to the end:
B = [B; B(1,:)];
Next, by computing the total distance between subsequent points then taking the cumulative sum, you can get a parametric coordinate that makes small steps for points close together and larger steps for points far apart:
distance = sqrt(sum(diff(B,1,1).^2,2)); %# Distance between subsequent points
s = [0; cumsum(distance)]; %# Parametric coordinate
Now, you can interpolate a new set of points that are evenly spaced around the edge along the straight lines joining your points using the function INTERP1Q:
sNew = linspace(0,s(end),100).'; %'# 100 evenly spaced points from 0 to s(end)
xNew = interp1q(s,B(:,1),sNew); %# Interpolate new x values
yNew = interp1q(s,B(:,2),sNew); %# Interpolate new y values
These new sets of points won't necessarily include the original points, so if you want to be sure the original points also appear in the new set, you can do the following:
[sAll,sortIndex] = sort([s; sNew]); %# Sort all the parametric coordinates
xAll = [B(:,1); xNew]; %# Collect the x coordinates
xAll = xAll(sortIndex); %# Sort the x coordinates
yAll = [B(:,2); yNew]; %# Collect the y coordinate
yAll = yAll(sortIndex); %# Sort the y coordinates
EXAMPLE:
Here's an example to show how the above code performs (I use 11 pairs of x and y coordinates, one of which is repeated for the sake of a complete example):
B = [0.1371 0.1301; ... %# Sample data
0.0541 0.5687; ...
0.0541 0.5687; ... %# Repeated point
0.0588 0.5863; ...
0.3652 0.8670; ...
0.3906 0.8640; ...
0.4090 0.8640; ...
0.8283 0.7939; ...
0.7661 0.3874; ...
0.4804 0.1418; ...
0.4551 0.1418];
%# Run the above code...
plot(B(:,1),B(:,2),'b-*'); %# Plot the original points
hold on; %# Add to the plot
plot(xNew,yNew,'ro'); %# Plot xNew and yNew
I'd first define some parametric coordinate along the different segments (i.e. between the data points)
s = 1:size(B,1);
Then, just use interp1 to interpolate in s space. e.g. If you want to generate 10 values on the line between data point 5 and 6 :
s_interp = linspace(5,6,10); % parametric coordinate interpolation values
x_coord = interp1(s,B(:,1),s_interp,'linear');
y_coord = interp1(s,B(:,2),s_interp,'linear');
This should do the trick.
A.
Actually there is a MATLAB function "improfile", which might help you in your problem. Lets say these are the 4 coordinates which you want to find the locations between these coordinates.
xi=[15 30 20 10];
yi=[5 25 30 50];
figure;
plot(xi,yi,'r^-','MarkerSize',12)
grid on
Just generate a random image and run the function
n=50; % total number of points between initial coordinates
I=ones(max([xi(:);yi(:)]));
[cx,cy,c] = improfile(I,xi,yi,n);
hold on, plot(cx,cy,'bs-','MarkerSize',4)
Hope it helps