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:
everything is allowed
blocks the moving data point when it would pass the neigbouring points on the x/y-axis
pushes all other data points with the moving data point if it would pass them on the x/y-axis
deactivates the functionality
Thomas Otterstaetter (2020). LineManipulator (https://www.mathworks.com/matlabcentral/fileexchange/30369-linemanipulator), MATLAB Central File Exchange. Retrieved .
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.
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
xScale = cAxis(2)-cAxis(1);
yScale = cAxis(4)-cAxis(3);
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
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 ;)
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.
% 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);
Just comment out all lines
[lineHandle, minDistIndex] = ClosestPoint(currentPoint);
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:
Added the functionality to turn everything off/switch back to normal by calling: