function [X,Y] = move(Constr,action,X,Y,X0,Y0)
%MOVE  Moves constraint
% 
%   [X0,Y0] = CONSTR.MOVE('init',X0,Y0) initialize the move. The 
%   pointer may be slightly moved to sit on the constraint edge
%   and thus eliminate distorsions due to patch thickness.
%
%   [X,Y] = CONSTR.MOVE('restrict',X,Y,X0,Y0) limits the displacement
%   to locations reachable by CONSTR.
%
%   STATUS = CONSTR.MOVE('update',X,Y,X0,Y0) moves the constraint.
%   For constraints indirectly driven by the mouse, the update is
%   based on a displacement (X0,Y0) -> (X,Y) for a similar constraint 
%   initially sitting at (X0,Y0).

%   Author(s): N. Hickey, P. Gahinet, A. Stothert
%   Copyright 1986-2004 The MathWorks, Inc. 
%   $Revision: 1.1.6.1.2.1 $  $Date: 2005/01/08 23:42:30 $

% RE: Incremental move is not practical because the constraint cannot
%     track arbitrary mouse motion (e.g., enter negative gain zone)

switch action
case 'init'
   % Initialization, model is
   %    xValue = xValue0 + (X-X0)
   %    yValue = yValue0 + (Y-Y0)
   Constr.AppData.xValue0 = unitconv(Constr.xCoords(Constr.SelectedEdge,:),...
      Constr.xUnits,Constr.getDisplayUnits('XUnits'));
   Constr.AppData.yValue0 = unitconv(Constr.yCoords(Constr.SelectedEdge,:),...
      Constr.yUnits,Constr.getDisplayUnits('YUnits'));
case 'restrict'
   
   %If part of a bound, check to prevent move beyond neighbours extremes.
   if size(Constr.xCoords,1)>1
      minSize  = eps;
      iElement = Constr.SelectedEdge;
      switch Constr.Orientation
         case 'horizontal'
            %Horizontal constraint, limit extent of x movement by neighbour
            if iElement < size(Constr.xCoords,1)
               Xright = unitconv(Constr.xCoords(iElement+1,2),...
                  Constr.xUnits,Constr.getDisplayUnits('xUnits'));
               X = min(X, X0-Constr.AppData.xValue0(2) + ...
                  Xright*(1-minSize*sign(Xright)));
            end
            if iElement > 1
               Xleft = unitconv(Constr.xCoords(iElement-1,1),...
                  Constr.xUnits,Constr.getDisplayUnits('xUnits'));
               X = max(X, X0-Constr.AppData.xValue0(1) + ...
                  Xleft*(1+minSize*sign(Xleft)));
            end
         case 'vertical'
            %Vertical constraint, limit extent of y movement by neighbour
            if iElement < size(Constr.xCoords,1)
               Yright = unitconv(Constr.yCoords(iElement+1,2), ...
                  Constr.yUnits,Constr.getDisplayUnits('yUnits'));
               Y = min(Y, Y0-Constr.AppData.yValue0(2) + ...
                  Yright*(1-minSize*sign(Yright)));
            end
            if iElement > 1
               Yleft = unitconv(Constr.yCoords(iElement-1,1),...
                  Constr.yUnits,Constr.getDisplayUnits('yUnits'));
               Y = max(Y, Y0-Constr.AppData.yValue0(1) + ...
                  Yleft*(1+minSize*sign(Yleft)));
            end
      end
   end
   
case 'update'
   iElement = Constr.SelectedEdge;
   
   % Update yCoord, turn off listeners.
   set(Constr.Listeners,'Enable','off');
   Constr.yCoords(iElement,:) = unitconv(Constr.AppData.yValue0 + (Y-Y0),...
      Constr.getDisplayUnits('YUnits'),Constr.yUnits);
   set(Constr.Listeners,'Enable','on');
   
   % Update xCoord, leave listeners on to fire updates and redraws.
   Constr.xCoords(iElement,:) = unitconv(Constr.AppData.xValue0 + (X-X0), ...
      Constr.getDisplayUnits('XUnits'),Constr.xUnits);
 
   % Status
   xLoc = unitconv(Constr.xCoords(iElement,:),Constr.xUnits,Constr.getDisplayUnits('XUnits'));
   LocStr = sprintf('Current location:  from %0.3g to %0.3g',...
      xLoc(1),xLoc(end));
   X = sprintf('Move constraint to desired location and release the mouse.\n%s',LocStr);
   
end

