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