function pos = privateslfreespace(srcBlk, destSys)
%PRIVATESLFREESPACE Locates an area of free space in a Simulink model.
%   Pos = PRIVATESLFREESPACE(SrcBlk, DestSys) locates free space to copy SrcBlk to
%   DestSys. The search proceeds down, then across, the model.
%
%   This algorithm is biased towards making a model taller, rather than
%   wider. If the block cannot fit into the boundaries of the existing
%   blocks in the model, it is automatically placed underneath the lowest
%   block, on the left-hand side.

% Copyright 2007-2009 The MathWorks, Inc.
% $Revision: 1.1.6.1 $ $Date: 2010/03/04 16:32:30 $

confPos = get_param(srcBlk, 'Position');
confSize = confPos([3,4])-confPos([1,2]);
%%% Now find somewhere with that size plus 15px
% Start by getting the position of all blocks in the root
blks = find_system(destSys, 'SearchDepth',1, 'type', 'block');
% Get all the line objects too.
lns = find_system(destSys, 'FindAll', 'on', 'type', 'line');
if isempty(blks),
    % Easy one. No blocks, so return top left corner.
    pos = [15, 15, 15+confSize];
else
    blkPos = get_param(blks, 'Position');
    blkPos = vertcat(blkPos{:});
    maxX = max(blkPos(:,3));
    maxY = max(blkPos(:,4));
    % Now convert this into rectangles (x,y,width.height)
    blkRect = [blkPos(:,1:2), blkPos(:,3:4)-blkPos(:,1:2)];
    % Now for the lines, get all the points.
    if isempty(lns),
        lnVerts = [];
    else
        lnPts = get(lns, 'Points');
        lnVerts = [];
        if ~iscell(lnPts),
            lnPts = {lnPts};
        end
        for k=1:length(lnPts)
            numVerts = size(lnPts{k},1)-1;
            thisVerts = zeros(numVerts,4);
            for v=1:numVerts
                thisVerts(v,:)=[lnPts{k}(v,:), lnPts{k}(v+1,:)];
            end
            lnVerts(end+1:end+numVerts,1:4)=thisVerts;
        end
    end
    % TODO: For the text objects ('annotation'), what do we do?
    % Algorithm: Start at the top left, and go down, then along, in steps of
    % 15px
    curPos = [15, 0];  % Simulink wants to leave a 15px border.
    canPlace = false;
    while ~canPlace
        curPos(2)=curPos(2)+15;
        % If we're too low, start at the top of the next "column".
        if (curPos(2) > (maxY-confSize(2)))
            curPos(2) = 15;
            curPos(1) = curPos(1)+15;
            if (curPos(1) > (maxX-confSize(1)))
                curPos = [15, maxY+15];
                break;
            end
        end
        % Extend the boundary of thechecked rectangle by 15px.
        p = [curPos-[14,14], confSize+[28,28]];
        canPlace = all(rectint(p, blkRect)==0) && ~overline(lnVerts, p);
    end
    % We have to be able to place now
    pos = [curPos, curPos+confSize];
end

%% OVERLINE
function tf = overline(lnVerts, boxRect)
%   Returns true if the box points lie over a line segment.
bx1 = boxRect(1);
bx2 = boxRect(1)+boxRect(3);
by1 = boxRect(2);
by2 = boxRect(2)+boxRect(4);
tf = false;
for k=1:size(lnVerts,1)
    x1 = min(lnVerts(k,[1,3]));
    x2 = max(lnVerts(k,[1,3]));
    y1 = min(lnVerts(k,[2,4]));
    y2 = max(lnVerts(k,[2,4]));
    % Now we have strict ordering, so we've reduced the number of tests
    if      ((x1==x2) && (x1>bx1) && (x1<bx2)) && ((y1<=by1 && y2>=by1) || (y1<=by2 && y2>=by2)) || ...
            ((y1==y2) && (y1>by1) && (y1<by2)) && ((x1<=bx1 && x2>=bx1) || (x1<=bx2 && x2>=bx2))
        tf = true;
        break;
    end
end
