File Exchange

image thumbnail

LineManipulator

version 1.1 (4.47 KB) by

Allows mouse-based manipulation of the data of a line object.

1 Download

Updated

View License

The originally intended use is to give the user of a gui the opportunity to create his own function/dataset which can be fetched from the X/YData of the line object after it has been changed.

The function adds a few callbackfunctions to the figure that contains the line object. These implement the following functionalities:
-adding points to the line
-removing points from the line
-moving existing points under different restrictions

You basically just have to call 'LineManipulator(lineHandle,controlMode)'
with lineHandle being a valid handle to a line object and controlMode being one of the following strings:
'nocheck'
everything is allowed
'stopx','stopy','stopxy'
blocks the moving data point when it would pass the neigbouring points on the x/y-axis
'pushx','pushy','pushxy'
pushes all other data points with the moving data point if it would pass them on the x/y-axis
'off'
deactivates the functionality

Comments and Ratings (5)

Also, if you happen to have very uneven axis ranges (one small, one very big), the behaviour can be somewhat odd with the cursor jumping between points seemingly at random.

Try

h = plot(linspace(1,7000,5),linspace(10,30,5)); pathManipulator(h, 'nocheck')

with one axis at 7000 ticks and the other at 5. As you can see, when you try to move one marker past another left/right, the cursor switches to the other marker even though the distance seems fairly large. This does not happen when passing the marker up/down.

My solution is to take the axis ranges into account and scale the vertical and the horizontal distances accordingly before calculating the pythagoras distance.

So swap out all lines

[~,minDistIndex]=min((XData-currentPoint(1,1)).^2+(YData-currentPoint(1,2)).^2);

with

cAxis=[get(axHandle,'XLim') get(axHandle,'YLim')];
xScale = cAxis(2)-cAxis(1);
yScale = cAxis(4)-cAxis(3);
[~,minDistIndex]=min(((XData-currentPoint(1,1))*yScale).^2+((YData-currentPoint(1,2))*xScale).^2);

and it should do the trick.

I believe I found a bug in your latest version.

First off, if a line is initiated (with this script) twice in a row, it's old styling is not recoverable since the Userdata already has been overwritten. That's a minor issue and could be prevented by checking if a line has been initiated and simply do LineManipulator(lineHandle, 'off') before initiating it once more.

The bug however, is when using either 'pushx' or 'pushy'. The pushing part works fine, but the points are unable to move freely in the other direction. So for e.g 'pushx' I simply added

YData(minDistIndex)=currentPoint(1,2);

on line 195. Similarly you add the corresponding piece of code on line 205.

Thanks for the feedback :)
I incorporated the turning off feature - admittedly not very beautiful anymore by loading the Userdata of the line....but it works ;)

Beautiful piece!

Would be nice with a feature that allows turning the whole thing on and off and restoring the line properties. E.g LineManipulator(h, 'off') or similar.

Also, I modified your script to check the closest distance for all line objects, not just the most recent one.

%% ClosestPoint
% Find the closest point in all available line objects
function [lineHandle, minDistIndex] = ClosestPoint(currentPoint)
lineHandles = get(axHandle, 'Children');

% Init output etc.
minDist = Inf;
minDistIndex = 0;
lineHandle = 0;

% Iterate through all children of the current axes
for i = 1:length(lineHandles)
objType = get(lineHandles(i), 'Type');
objTag = get(lineHandles(i), 'Tag');

% Only check line objects
if strcmp(objType, 'line') && ~strcmp(objTag, 'Ghost')
XData = get(lineHandles(i), 'XData');
YData = get(lineHandles(i), 'YData');
[dist, distIndex] = min((XData - currentPoint(1,1)).^2 + (YData - currentPoint(1,2)).^2);

if dist < minDist
minDist = dist;
minDistIndex = distIndex;
lineHandle = lineHandles(i);
end
end
end
end

Just comment out all lines
[~,minDistIndex]=min((XData-currentPoint(1,1)).^2+(YData-currentPoint(1,2)).^2);
and insert
[lineHandle, minDistIndex] = ClosestPoint(currentPoint);
before lines
XData=get(lineHandle,'XData');
YData=get(lineHandle,'YData');

I just disabled the ghost lines from plotting, but you can easily modify the script to tag those lines with e.g 'Ghost' to exclude them. Example:
plot(axHandle,get(lineHandle,'XData'),get(lineHandle,'YData'),'Color',get(lineHandle,'Color')...
,'Linewidth',get(lineHandle,'LineWidth')*0.5,'LineStyle',':','Tag','Ghost')

Updates

1.1

Added the functionality to turn everything off/switch back to normal by calling:
LineManipulator(lineHandle,'off')

MATLAB Release
MATLAB 7.14 (R2012a)

Download apps, toolboxes, and other File Exchange content using Add-On Explorer in MATLAB.

» Watch video