5.0

5.0 | 1 rating Rate this file 13 Downloads (last 30 days) File Size: 4.47 KB File ID: #30369
image thumbnail

LineManipulator

by

 

11 Feb 2011 (Updated )

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

| Watch this File

File Information
Description

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

MATLAB release MATLAB 7.14 (R2012a)
Other requirements The function imports 'java.awt.Robot' - sadly I do not know if this is basic functionality or not.
Tags for This File   Please login to tag files.
Please login to add a comment or rating.
Comments and Ratings (5)
09 Aug 2012 Sebastian Holmqvist

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.

08 Aug 2012 Sebastian Holmqvist

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.

16 Jul 2012 Thomas Otterstaetter

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 ;)

03 Jul 2012 Sebastian Holmqvist  
03 Jul 2012 Sebastian Holmqvist

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
16 Jul 2012

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

Contact us