Skip to content

Commit 418a9a1

Browse files
committed
Initial commit.
0 parents  commit 418a9a1

File tree

8 files changed

+476
-0
lines changed

8 files changed

+476
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*~
2+
*.asv

Example.mlx

332 KB
Binary file not shown.

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Colored Trajectory Plot
2+
3+
Version: 1.0
4+
5+
This chart creates a multi-color line based on a 2D trajectory with
6+
corresponding color data.
7+
8+
## Syntax
9+
* `colorTrajectoryPlot(x,y)` create a multi-color line plot following the 2D path specified by the coordinates x and y using the index of each coordinate to determine the color of the line. x and y must be numeric vectors of equal length.
10+
* `colorTrajectoryPlot(x,y,c)` create a multi-color line plot following the 2D path specified by the coordinates x and y using values from c to determine the color of the line at each coordinate. x, y, and c must be numeric vectors of equal length.
11+
* `colorTrajectoryPlot()` create a multi-color line plot using only name-value pairs.
12+
* `colorTrajectoryPlot(___,Name,Value)` specifies additional options for the multi-color line plot using one or more name-value pair arguments. Specify the options after all other input arguments.
13+
* `colorTrajectoryPlot(parent,___)` creates the multi-color line plot in the specified parent.
14+
* `h = colorTrajectoryPlot(___)` returns the colorTrajectoryPlot object. Use h to modify properties of the plot after creating it.
15+
16+
## Name-Value Pair Arguments/Properties
17+
* `XData` (n x 1 numeric vector) x-coordinates of the trajectory.
18+
* `YData` (n x 1 numeric vector) y-coordintaes of the trajectory.
19+
* `ColorData` (n x 1 numeric vector) data used to determine the color of the line.
20+
* `Colormap` (m x 3 matrix of RGB triplets) colormap used to convert `ColorData` into colors.
21+
* `ColorLimits` (1 x 2 numeric vector) limits used to convert `ColorData` into colors.
22+
* `ColorLimitsMode` (`'auto'` or '`manual'`) mode for the color limits.
23+
* `ColorbarVisible` (scalar `matlab.lang.OnOffSwitchState`) display the colorbar or not.
24+
* `LineWidth` (scalar positive double) line width of the trajectory.
25+
* `TitleText` (n x 1 string vector) title of the plot.
26+
* `SubtitleText` (n x 1 string vector) subtitle of the plot.
27+
* `ColorbarLabel` (n x 1 string vector) label on the colorbar.
28+
29+
## Example
30+
Create a multi-color trajectory plot using x, y, and color data generated by the helper script `randomWalk`.
31+
```
32+
[x,y,c] = randomWalk;
33+
c = colorTrajectoryPlot(x,y,c);
34+
c.ColorbarLabel = "Firing Rate (Hz)";
35+
colormap(hot(256))
36+
```

SECURITY.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Reporting Security Vulnerabilities
2+
3+
If you believe you have discovered a security vulnerability, please report it to
4+
[security@mathworks.com](mailto:security@mathworks.com). Please see
5+
[MathWorks Vulnerability Disclosure Policy for Security Researchers](https://www.mathworks.com/company/aboutus/policies_statements/vulnerability-disclosure-policy.html)
6+
for additional information.

colorTrajectoryPlot.m

Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
1+
classdef colorTrajectoryPlot < matlab.graphics.chartcontainer.ChartContainer & ...
2+
matlab.graphics.chartcontainer.mixin.Colorbar
3+
%colorTrajectoryPlot Create a multi-color trajectory plot
4+
% colorTrajectoryPlot(x,y) create a multi-color line plot following
5+
% the 2D path specified by the coordinates x and y using the index of
6+
% each coordinate to determine the color of the line. x and y must be
7+
% numeric vectors of equal length.
8+
%
9+
% colorTrajectoryPlot(x,y,c) create a multi-color line plot following
10+
% the 2D path specified by the coordinates x and y using values from
11+
% c to determine the color of the line at each coordinate. x, y, and
12+
% c must be numeric vectors of equal length.
13+
%
14+
% colorTrajectoryPlot() create a multi-color line plot using only
15+
% name-value pairs.
16+
%
17+
% colorTrajectoryPlot(___,Name,Value) specifies additional options
18+
% for the multi-color line plot using one or more name-value pair
19+
% arguments. Specify the options after all other input arguments.
20+
%
21+
% colorTrajectoryPlot(parent,___) creates the multi-color line plot
22+
% in the specified parent.
23+
%
24+
% h = colorTrajectoryPlot(___) returns the colorTrajectoryPlot
25+
% object. Use h to modify properties of the plot after creating it.
26+
27+
% Copyright 2020-2021 The MathWorks, Inc.
28+
29+
properties
30+
% x-coordinates of the trajectory.
31+
XData (:,1) double = []
32+
33+
% y-coordintaes of the trajectory.
34+
YData (:,1) double = []
35+
36+
% Data used to determine the color of the line.
37+
ColorData (:,1) double = []
38+
39+
% Title of the plot.
40+
TitleText (:,1) string = ""
41+
42+
% Subtitle of the plot.
43+
SubtitleText (:,1) string = ""
44+
45+
% Line width of the trajectory.
46+
LineWidth (1,1) {mustBeNumeric, mustBePositive} = 0.5
47+
48+
% Label on the colorbar.
49+
ColorbarLabel (:,1) string = ""
50+
end
51+
52+
properties (Dependent)
53+
% Colormap Colormap used to convert `ColorData` into colors.
54+
Colormap (:,3) double {mustBeNonempty, mustBeInRange(Colormap,0,1)} = get(groot, 'factoryFigureColormap')
55+
56+
% Limits used to convert `ColorData` into colors.
57+
ColorLimits (1,2) double {mustBeLimits} = [0 1]
58+
59+
% Mode for the color limits.
60+
ColorLimitsMode (1,:) char {mustBeAutoManual} = 'auto'
61+
end
62+
63+
properties (Access = protected)
64+
SaveAxesState struct = []
65+
end
66+
67+
properties (Access = private, Transient, NonCopyable)
68+
TrajectoryLine (:,1) matlab.graphics.primitive.Patch
69+
end
70+
71+
methods
72+
function obj = colorTrajectoryPlot(varargin)
73+
%
74+
75+
% Initialize list of arguments
76+
args = varargin;
77+
leadingArgs = cell(0);
78+
79+
% Check if the first input argument is a graphics object to use as parent.
80+
if ~isempty(args) && isa(args{1},'matlab.graphics.Graphics')
81+
% colorTrajectoryPlot(parent, ___)
82+
leadingArgs = args(1);
83+
args = args(2:end);
84+
end
85+
86+
% Check for optional positional arguments.
87+
if ~isempty(args) && isnumeric(args{1})
88+
if numel(args) >= 2 && mod(numel(args), 2) == 0 ...
89+
&& isnumeric(args{2})
90+
% colorTrajectoryPlot(x, y, Name, Value)
91+
x = args{1};
92+
y = args{2};
93+
94+
% Verify x and y are the same length.
95+
assert(numel(x) == numel(y), ...
96+
'colorTrajectoryPlot:DataLengthMismatch',...
97+
'y must be the same length as x.');
98+
99+
% Add x and y to the input arguments.
100+
leadingArgs = [leadingArgs {'XData', x, 'YData', y}];
101+
args = args(3:end);
102+
elseif numel(args) >= 3 && mod(numel(args), 2) == 1 ...
103+
&& isnumeric(args{2}) && isnumeric(args{3})
104+
% colorTrajectoryPlot(x, y, c, Name, Value)
105+
x = args{1};
106+
y = args{2};
107+
c = args{3};
108+
109+
% Verify x and y are the same length.
110+
assert(numel(x) == numel(y), ...
111+
'colorTrajectoryPlot:DataLengthMismatch',...
112+
'y must be the same length as x.');
113+
114+
% Verify x and c are the same length.
115+
assert(numel(c) == numel(y), ...
116+
'colorTrajectoryPlot:DataLengthMismatch',...
117+
'c must be the same length as x.');
118+
119+
% Add x, y, and c to the input arguments.
120+
leadingArgs = [leadingArgs {'XData', x, 'YData', y, 'ColorData', c}];
121+
args = args(4:end);
122+
else
123+
error('colorTrajectoryPlot:InvalidSyntax', 'Specify both x and y coordinates.');
124+
end
125+
end
126+
127+
% Combine positional arguments with name/value pairs.
128+
args = [leadingArgs args];
129+
130+
% Call superclass constructor method
131+
obj@matlab.graphics.chartcontainer.ChartContainer(args{:});
132+
133+
% Supress output unless it is requested.
134+
if nargout == 0
135+
clear obj
136+
end
137+
end
138+
end
139+
140+
methods (Access = protected)
141+
function setup(obj)
142+
% Configure the axes.
143+
ax = obj.getAxes();
144+
ax.NextPlot = 'replacechildren';
145+
ax.DataAspectRatio = [1 1 1];
146+
ax.Box = 'on';
147+
ax.XTick = [];
148+
ax.YTick = [];
149+
axis(ax,'tight')
150+
151+
% Create a patch to show the trajectory.
152+
obj.TrajectoryLine = patch(ax, 'Faces',[], 'Vertices', [], ...
153+
'FaceColor', 'none', 'EdgeColor', 'interp');
154+
155+
% Restore any saved axes state.
156+
loadAxesState(obj)
157+
end
158+
159+
function update(obj)
160+
% Verify that the data properties are consistent with one
161+
% another.
162+
showChart = verifyDataProperties(obj);
163+
obj.TrajectoryLine.Visible = showChart;
164+
165+
% Abort early if not visible due to invalid data.
166+
if ~showChart
167+
return
168+
end
169+
170+
% Update the vertices and faces on the patch.
171+
obj.TrajectoryLine.Vertices = [obj.XData obj.YData; NaN NaN];
172+
obj.TrajectoryLine.Faces = 1:numel(obj.XData)+1;
173+
174+
% Update the color of the patch.
175+
colorData = obj.ColorData;
176+
if isempty(colorData)
177+
colorData = (1:numel(obj.XData))';
178+
end
179+
obj.TrajectoryLine.FaceVertexCData = [colorData; NaN];
180+
181+
% Update the patch line width.
182+
obj.TrajectoryLine.LineWidth = obj.LineWidth;
183+
184+
% Update the title/subtitle and colorbar label.
185+
title(getAxes(obj), obj.TitleText, obj.SubtitleText);
186+
if obj.ColorbarVisible
187+
ylabel(getColorbar(obj), obj.ColorbarLabel);
188+
end
189+
end
190+
191+
function showChart = verifyDataProperties(obj)
192+
% XData and YData must be the same length.
193+
n = numel(obj.XData);
194+
showChart = numel(obj.YData) == n;
195+
if ~showChart
196+
warning('colorTrajectoryPlot:DataLengthMismatch',...
197+
'YData must be the same length as XData.');
198+
return
199+
end
200+
201+
% ColorData and must be empty or the same length as XData.
202+
showChart = isempty(obj.ColorData) || numel(obj.ColorData) == n;
203+
if ~showChart
204+
warning('colorTrajectoryPlot:DataLengthMismatch',...
205+
'ColorData must be empty or the same length as XData.');
206+
return
207+
end
208+
end
209+
210+
function loadAxesState(obj)
211+
state = obj.SaveAxesState;
212+
213+
if isfield(state, 'Colormap')
214+
obj.Colormap = state.Colormap;
215+
end
216+
217+
if isfield(state, 'ColorLimits')
218+
obj.ColorLimits = state.ColorLimits;
219+
end
220+
221+
if isfield(state, 'ColorbarVisible') && state.ColorbarVisible
222+
obj.ColorbarVisible = state.ColorbarVisible;
223+
end
224+
225+
% Reset the SaveAxesState for the next save.
226+
obj.SaveAxesState = [];
227+
end
228+
229+
function groups = getPropertyGroups(obj)
230+
if ~isscalar(obj)
231+
% List for array of objects
232+
groups = getPropertyGroups@matlab.mixin.CustomDisplay(obj);
233+
else
234+
% List for scalar object
235+
propList = cell(1,0);
236+
237+
% Add the title, if not empty or missing.
238+
nonEmptyTitle = obj.TitleText ~= "" & ~ismissing(obj.TitleText);
239+
if any(nonEmptyTitle)
240+
propList = {'TitleText'};
241+
end
242+
243+
% Add the subtitle, if not empty or missing.
244+
nonEmptySubtitle = obj.SubtitleText ~= "" & ~ismissing(obj.SubtitleText);
245+
if any(nonEmptySubtitle)
246+
propList{end+1} = 'SubtitleText';
247+
end
248+
249+
% Add either ColorData or XData and YData.
250+
if isempty(obj.ColorData)
251+
propList(end+1:end+2) = {'XData','YData'};
252+
else
253+
propList(end+1:end+3) = {'ColorData','ColorLimits','ColorbarLabel'};
254+
end
255+
256+
groups = matlab.mixin.util.PropertyGroup(propList);
257+
end
258+
end
259+
end
260+
261+
methods
262+
function title(obj, varargin)
263+
[t, st] = title(getAxes(obj), varargin{:});
264+
obj.TitleText = t.String;
265+
obj.SubtitleText = st.String;
266+
end
267+
268+
function subtitle(obj, varargin)
269+
ax = getAxes(obj);
270+
st = subtitle(ax, varargin{:});
271+
obj.SubtitleText = st.String;
272+
end
273+
end
274+
275+
methods
276+
function set.Colormap(obj, map)
277+
ax = getAxes(obj);
278+
ax.Colormap = map;
279+
end
280+
281+
function map = get.Colormap(obj)
282+
ax = getAxes(obj);
283+
map = ax.Colormap;
284+
end
285+
286+
function set.ColorLimits(obj, limits)
287+
ax = getAxes(obj);
288+
ax.CLim = limits;
289+
end
290+
291+
function limits = get.ColorLimits(obj)
292+
ax = getAxes(obj);
293+
limits = ax.CLim;
294+
end
295+
296+
function set.ColorLimitsMode(obj, mode)
297+
ax = getAxes(obj);
298+
ax.CLimMode = mode;
299+
end
300+
301+
function mode = get.ColorLimitsMode(obj)
302+
ax = getAxes(obj);
303+
mode = ax.CLimMode;
304+
end
305+
306+
function state = get.SaveAxesState(obj)
307+
state = obj.SaveAxesState;
308+
309+
if isempty(state)
310+
% Create a 1x1 struct.
311+
state = struct();
312+
313+
% Add fields to the struct for each axes property to store.
314+
ax = getAxes(obj);
315+
if ax.ColormapMode == "manual"
316+
state.Colormap = obj.Colormap;
317+
end
318+
319+
if obj.ColorLimitsMode == "manual"
320+
state.ColorLimits = obj.ColorLimits;
321+
end
322+
323+
state.ColorbarVisible = obj.ColorbarVisible;
324+
end
325+
end
326+
end
327+
end
328+
329+
function mustBeLimits(limits)
330+
331+
if numel(limits) ~= 2 || limits(2) <= limits(1)
332+
throwAsCaller(MException('colorTrajectoryPlot:InvalidLimits', 'Specify limits as two increasing values.'))
333+
end
334+
335+
end
336+
337+
function mustBeAutoManual(mode)
338+
339+
mustBeMember(mode, {'auto','manual'})
340+
341+
end

0 commit comments

Comments
 (0)