# Constraining imline to points of an existing imellipse?

3 views (last 30 days)
Dominik Mattioli on 1 Aug 2018
Answered: Tim Jackman on 27 Sep 2018
I'd like to draw a line and then be able to adjust the endpoints of that line while restricting them to the vertices of an ellipse. I don't understand how to use the setPositionConstraintFcn though.
function drawStuff
% Draw ellipse.
figure;
e = imellipse;
global ePos
ePos = wait(e)';
hold on;
plot(ePos(:,1),ePos(:,2),'color','r'); % Replace imroi with a plotted line object.
hold off;
delete(e);
% Draw a line constrained by points of ellipse.
iFirst2Pts = randi(size(ePos,1),2,1);
while iFirst2Pts(1) == iFirst2Pts(2) % Two unique initial points for line.
iFirst2Pts(2) = randi(size(ePos,1));
end
l = imline(gca,ePos(iFirst2Pts,1),ePos(iFirst2Pts,2));
l.setColor('g');
l.setPositionConstraintFcn(@constrainNewPos);
lPos = l.wait;
plot(lPos(:,1),lPos(:,2),'color','g');
delete(l);
% Create constraint function
function constrainNewPos
for idx = 1:100
% Check if both points are within polygon of ellipse.
linePos = l.getPosition;
for jdx = 1:2
in = inpolygon(linePos(jdx,1),linePos(jdx,2),ePos(1,:),ePos(2,:));
% If point is not in polygon, shift to nearest point on ellipse.
if ~in
% Compute Euclidean distances.
distances = sqrt(sum(bsxfun(@minus,ePos',linePos(jdx,:)).^2,2));
% Find smallest distance, use to index into ellipse points.
linePos(jdx,:) = ePos(:,distances == min(distances))';
l.setPosition = linePos;
end
end
pause(.0001);
end
end
end
-------------------------
Error using drawStuff/constrainNewPos Too many input arguments.
Error in imline>imlineAPI/endPointMotion (line 590) new_position=position_constraint_function(candidate_position);
Error in imline>@(varargin)endPointMotion(h_hit) (line 525) @(varargin) endPointMotion(h_hit) );
Error in iptaddcallback/callbackProcessor (line 149) fun(varargin{:});
Error while evaluating Figure WindowButtonMotionFcn. fun(varargin{:});
-------------------------

Tim Jackman on 27 Sep 2018
This is the approach that I would take. First, beginning in 18b the Image Processing Toolbox has a new suite of ROI tools. I would suggest using those. The basic work flow to do this would be as follows:
1. Draw an ellipse and turn off all interactions so it is static after being drawn
2. Create a line ROI object
3. Add a listener to listen for any line ROI motion
4. In the listener callback, check if the line is outside the ellipse and set the previous position if true.
Here is an example to accomplish this task:
function lineInsideEllipse
% Draw an ellipse and then turn off all interactions
hEllipse = drawellipse('InteractionsAllowed','none');
% Create an empty line ROI
hLine = images.roi.Line('Color','red');
% Listen to any line movement
% Begin interactive placement of the line starting from the ellipse center
beginDrawingFromPoint(hLine,hEllipse.Center);
end
function movingCallback(hLine,evt,hEllipse)
xCandidates = evt.CurrentPosition(:,1);
yCandidates = evt.CurrentPosition(:,2);
% Check if the line current position is now outside the ellipse
TF = inROI(hEllipse, xCandidates, yCandidates);
if ~all(TF)
% Line is at least partially outside the ellipse, reset the line
% position to be the previous position
hLine.Position = evt.PreviousPosition;
end
end