Code covered by the BSD License  

Highlights from
Better line crossings in Simulink models

image thumbnail

Better line crossings in Simulink models

by

 

Improves readability of Simulink models by appending "line steps" to lines where they cross.

Editor's Notes:

This file was selected as MATLAB Central Pick of the Week

betterCrossings(sub_h, fun_s)
function out = betterCrossings(sub_h, fun_s)
% betterCrossings(sub_h, fun_s)
% Will update line crossings in Simulink model to improve readability of the model.
% argument #1: subsystem handle
% argument #2: one of "on", "off", "check"
%                 on: will add line steps at line crossings where applicable
%                off: will remove line steps previously added
%              check: will return true/false if there are (are not) any line crossings
%                     appropriate for update. No model change is performed.
%
% This script uses the fact that default Simulink grid is 5 pixels, but the height of the line
% step appended by this script is 4 (or any other value CONST_HEIGHT, see m-file). This way,
% line steps can be also identified during removal.
%
% Author: Josef Rieger, 2012

out = logical(0);

if strcmp(fun_s, 'on')
	removeOnly_l = logical(0);
	checkCrossing_l = logical(0);
elseif strcmp(fun_s, 'off')
	removeOnly_l = logical(1);
	checkCrossing_l = logical(0);
elseif strcmp(fun_s, 'check')
	removeOnly_l = logical(0);
	checkCrossing_l = logical(1);
else
	error('Usage: betterCrossings(sub_h, fun_s) where fun_s is one of "on", "off", "check".');
end

% disadvantage: will remove all "CONST_HEIGHT" (default 4 pixels) line steps in subsystem

CONST_HEIGHT = 4; % line step height. Is used during automated removal as well during adding.
CONST_WIDTH = 10; % line step width

% get this subsystem's line handles
lines_h = find_system(sub_h, 'SearchDepth', 1, 'FindAll', 'on', 'Type', 'line');

% get line segments coordinates
segmentPoints_n = get_param(lines_h, 'Points')';

if ~checkCrossing_l
	
	% 1)
	
	% remove steps if already present
	for i = 1 : length(lines_h)
		thisSegmentPoints_n = segmentPoints_n{i};
		if size(thisSegmentPoints_n, 1) < 5 % less then 5 segments, we need at least _|-|_
			continue;
		end
		% step can be present
		thisSegmentPointsDiff_n = diff(thisSegmentPoints_n);
		newSegmentPoints_n = thisSegmentPoints_n(1:2,:); % preserve first line segment
		changed_l = logical(0);
		o = 2;
		while o <= size(thisSegmentPointsDiff_n, 1) - 2
			if (thisSegmentPointsDiff_n(o,1) == 0 & thisSegmentPointsDiff_n(o,2) == -CONST_HEIGHT) && ...
					(thisSegmentPointsDiff_n(o+2,1) == 0 & thisSegmentPointsDiff_n(o+2,2) == +CONST_HEIGHT)
				% remove this step
				newSegmentPoints_n(end+1,:) = thisSegmentPoints_n(o+3,:);
				changed_l = logical(1);
				o = o + 3;
			else
				newSegmentPoints_n(end+1,:) = thisSegmentPoints_n(o+1,:);
				o = o + 1;
			end
		end
		if changed_l
			newSegmentPoints_n = [newSegmentPoints_n; thisSegmentPoints_n(o+1:end,:)];
			% lets remove duplicated horizontal segments
			newSegmentPointsDiff_n = diff(newSegmentPoints_n);
			newSegmentPoints2_n = newSegmentPoints_n(1,:); % preserve first node
			o = 1;
			while o < size(newSegmentPointsDiff_n,1)-1
				skipped_l = logical(0);
				while newSegmentPointsDiff_n(o,2) == 0 & newSegmentPointsDiff_n(o+1,2) == 0
					skipped_l = logical(1);
					o = o + 1;
					if o == size(newSegmentPointsDiff_n,1)-1
						break;
					end
				end
				if skipped_l
					if o < size(newSegmentPointsDiff_n,1)-1
						newSegmentPoints2_n(end+1,:) = newSegmentPoints_n(o+1,:);
						o = o + 1;
					else
						o = o + 1;
					end
				else
					o = o + 1;
					newSegmentPoints2_n(end+1,:) = newSegmentPoints_n(o,:);
				end
			end
			newSegmentPoints2_n = [newSegmentPoints2_n ; newSegmentPoints_n(o+1:end,:)];
			if ~all(all(newSegmentPoints2_n([1 end],:) == thisSegmentPoints_n([1 end],:)))
				fprintf('FATAL ERROR #1: first and last nodes doesnt correspond!?\n');
				keyboard;
			else
				set(lines_h(i), 'Points', newSegmentPoints2_n);
			end
		end
	end
	
	if removeOnly_l
		return;
	end
	
end

% 2)

% get line segments coordinates
segmentPoints_n = get_param(lines_h, 'Points')';

% how many segments does each of the lines have?
segmentsCount_n = cellfun('length', segmentPoints_n) - 1;
% total number of lines (segments) we will be analyzing
n = sum(segmentsCount_n);
% get segment start/end point coordinates
points_n = zeros(n, 4);
tmp_idx = [0 cumsum(segmentsCount_n)];
for i = 1 : length(segmentPoints_n)
	points_n(tmp_idx(i)+1:tmp_idx(i+1),:) = [segmentPoints_n{i}(1:end-1,:) segmentPoints_n{i}(2:end,:)];
end

% keep only vertical line segments
isVert_l = points_n(:,1) == points_n(:,3);
xVert_n = points_n(isVert_l, 1);
yMin_n = min(points_n(isVert_l, [2 4])')';
yMax_n = max(points_n(isVert_l, [2 4])')';

% 3) iterate through all lines
for i = 1 : length(lines_h)
	thisSegmentPoints_n = segmentPoints_n{i};
	thisSegmentPointsDiff_n = diff(thisSegmentPoints_n);
	newSegmentPoints_n = thisSegmentPoints_n(1,:);
	isLeftToRight_l = thisSegmentPointsDiff_n(:,1) > 0;
	changed_l = logical(0);
	for o = 1 : size(thisSegmentPoints_n,1)-1
		if thisSegmentPointsDiff_n(o,2) == 0 % segment is horizontal
			% are there any vertical line segments crossing it?
			if isLeftToRight_l(o)
				l1 = (thisSegmentPoints_n(o,1) < xVert_n) & (thisSegmentPoints_n(o+1,1) > xVert_n); % left to right
			else
				l1 = (thisSegmentPoints_n(o,1) > xVert_n) & (thisSegmentPoints_n(o+1,1) < xVert_n); % right to left
			end
			l2 = (thisSegmentPoints_n(o,2) > yMin_n) & (thisSegmentPoints_n(o,2) < yMax_n);
			l = l1 & l2;
			if any(l)
				if checkCrossing_l
					out = logical(1);
					return;
				end
				% append steps to thisXVert_n locations
				if isLeftToRight_l(o)
					thisXVert_n = sort(xVert_n(l));
					p = 1;
					while p <= length(thisXVert_n)
						% go up
						newSegmentPoints_n(end+1,:) = [(thisXVert_n(p)-CONST_WIDTH/2) thisSegmentPoints_n(o,2)];
						newSegmentPoints_n(end+1,:) = [(thisXVert_n(p)-CONST_WIDTH/2) thisSegmentPoints_n(o,2)-CONST_HEIGHT];
						if p < length(thisXVert_n)
							while thisXVert_n(p+1) < (thisXVert_n(p) + CONST_WIDTH)
								p = p + 1;
								if p == length(thisXVert_n)
									break;
								end
							end
						end
						% go down
						newSegmentPoints_n(end+1,:) = [(thisXVert_n(p)+CONST_WIDTH/2) thisSegmentPoints_n(o,2)-CONST_HEIGHT];
						newSegmentPoints_n(end+1,:) = [(thisXVert_n(p)+CONST_WIDTH/2) thisSegmentPoints_n(o,2)];
						p = p + 1;
					end
				else
					thisXVert_n = -sort(-xVert_n(l));
					p = 1;
					while p <= length(thisXVert_n)
						% go up
						newSegmentPoints_n(end+1,:) = [(thisXVert_n(p)+CONST_WIDTH/2) thisSegmentPoints_n(o,2)];
						newSegmentPoints_n(end+1,:) = [(thisXVert_n(p)+CONST_WIDTH/2) thisSegmentPoints_n(o,2)-CONST_HEIGHT];
						if p < length(thisXVert_n)
							while thisXVert_n(p+1) > (thisXVert_n(p) - CONST_WIDTH)
								p = p + 1;
								if p == length(thisXVert_n)
									break;
								end
							end
						end
						% go down
						newSegmentPoints_n(end+1,:) = [(thisXVert_n(p)-CONST_WIDTH/2) thisSegmentPoints_n(o,2)-CONST_HEIGHT];
						newSegmentPoints_n(end+1,:) = [(thisXVert_n(p)-CONST_WIDTH/2) thisSegmentPoints_n(o,2)];
						p = p + 1;
					end
				end
				newSegmentPoints_n(end+1,:) = thisSegmentPoints_n(o+1,:);
				changed_l = logical(1);
			else
				newSegmentPoints_n(end+1,:) = thisSegmentPoints_n(o+1,:);
			end
		else
			newSegmentPoints_n(end+1,:) = thisSegmentPoints_n(o+1,:);
		end
	end
	if changed_l
		if ~all(all(newSegmentPoints_n([1 end],:) == thisSegmentPoints_n([1 end],:)))
			fprintf('FATAL ERROR #2: first and last nodes doesnt correspond!?\n');
			keyboard;
		else
			set(lines_h(i), 'Points', newSegmentPoints_n);
		end
	end
end

Contact us