-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSimpleScriptGen.m
244 lines (226 loc) · 8.4 KB
/
SimpleScriptGen.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
classdef SimpleScriptGen < handle
% Class for generating scripts that compute geometry and draw graphics.
%
% These scripts are designed for use with creating bits of art
% that are previewed in app designer.
%
% To support this case the script is divided into these 3 parts:
% geom script - part that computs geometry to use
% gfx script - part that creates graphics objects
% color script - part that creates and sets colormaps
%
% This lets the application generate geometry script and run it to get the
% desired geometry without creating graphics. Alternately, the entire script
% can be run to create a graphic directly.
%
% See associated meta script with this app for sample use case.
% Copyright 2024 The MathWorks, Inc.
properties (SetAccess=private)
% Script Part
geomscript (:,1) string = []
gfxscript (:,1) string = []
% Modes
constantfolding (1,1) logical = false
% Constant Tracking
constants (:,1) struct
end
properties (SetAccess=private)
mode = "geom"
end
methods
function SSG = SimpleScriptGen(headercomment, opts)
arguments
headercomment = []
opts.constantfolding = false
end
SSG.constantfolding = opts.constantfolding;
SSG.addcomment(headercomment,Header=true);
end
function nextsection(SSG)
% Switch script-gen to next section of the script.
switch SSG.mode
case "geom"
SSG.mode = "gfx";
case "gfx"
SSG.mode = "color";
otherwise
error('no next mode');
end
% Each section separated by a blank line.
SSG.addlines("");
end
function addcomment(SSG, comment, opts)
arguments
SSG
comment (:,1) string
opts.Header (1,1) logical = false
end
if opts.Header
cc = "%% ";
else
cc = "% ";
end
SSG.addlines(cc + comment, Comment=true);
end
function addlines(SSG, lines, opts)
% Add various lines of code to current script
arguments
SSG
lines (:,1) string
opts.Comment (1,1) logical = false
end
switch SSG.mode
case "geom"
SSG.geomscript = [ SSG.geomscript
lines ];
case "gfx"
SSG.gfxscript = [ SSG.gfxscript
lines ];
case "color"
if lines == "" || opts.Comment
SSG.gfxscript = [ SSG.gfxscript
lines ];
else
error("Don't add lines to color section, Use colormap method instead.");
end
otherwise
error('no next mode');
end
end
function constant(SSG, varname, value, comment)
% Add a constant into this script.
% If constant folding is off, add line to script.
% If constant folding is on, add no script parts, just remember the constant.
if SSG.constantfolding
else
if nargin >= 4
cc = " %" + comment;
else
cc = "";
end
SSG.addlines(varname + "=" + SSG.toStr(value) + ";" + cc);
end
CS.name = string(varname);
CS.value = value;
if isempty(SSG.constants)
SSG.constants = CS;
else
SSG.constants(end+1) = CS;
end
end
function str = ref (SSG, const, subsref)
% Return script text referencing the constant variable CONST.
% If constant folding is off, return variable name.
% If constant folding is on, return the value as a string.
%
% Optional SUBSREF indicates if a refernce index is needed
% when accessing the variable or value.
arguments
SSG
const
subsref (:,1) double = []
end
if SSG.constantfolding
CS = SSG.conststruct(const);
if ~isempty(subsref)
str = SSG.toStr(CS.value(subsref));
else
str = SSG.toStr(CS.value);
end
else
if ~isempty(subsref)
str = const + "(" + subsref + ")";
else
str = const;
end
end
end
end
%% Handle Colormp specification.
%
% Support either returning generated script, or computed colormap values.
properties
colormapscript
colormapvaluescript
namedmap
end
methods
function colormap(SSG, colormap_specifier, cmapsize)
% Handle script generation of a custom colormap.
% A colormap_specifier can be a string, the name of a colormap like parula,
% or it can be a pair of RGB colors.
% If colors are passed in, the script fabricates a linearly interpolated colormap.
%
% Optional cmapsize controls how big the generated colormap should be.
if ischar(colormap_specifier) || isstring(colormap_specifier)
SSG.colormapscript = "colormap(gca," + colormap_specifier + "(" + cmapsize + "));";
SSG.colormapvaluescript = "map = " + colormap_specifier + "(" + cmapsize + ");";
SSG.namedmap = true;
else
vc = validatecolor(colormap_specifier,'multiple');
if size(vc,1) == 2
SSG.colormapvaluescript = [ "map = [ linspace(" + vc(1,1) + "," + vc(2,1) + "," + cmapsize + ");"
" linspace(" + vc(1,2) + "," + vc(2,2) + "," + cmapsize + ");"
" linspace(" + vc(1,3) + "," + vc(2,3) + "," + cmapsize + ")]';"];
SSG.colormapscript = "colormap(gca,map);";
SSG.namedmap = false;
else
error('Colormap can be either a colormap name, or 2 colors');
end
end
end
end
%% Value Generation
methods (Access=private)
function str = toStr(~, value)
% Convert some VALUE into a string to be inserted into a script.
if ischar(value) || isstring(value)
str = """" + string(value) + """";
elseif isnumeric(value)
if isscalar(value)
str = "" + value;
elseif isvector(value)
str = "[" + join(string(value), " ") + "]";
else
error("Don't know how to stringify value numbers of size %s,%s", size(value));
end
else
error("Don't know how to stringify value of class %s", class(value));
end
end
function CS = conststruct(SSG, name)
cn = [ SSG.constants.name ];
mask = strcmp(cn, name);
CS = SSG.constants(mask);
end
end
%% Final script generation OR execution
methods
function eval(SSG)
% Evaluate the generated script.
eval(join(SSG.geomscript,newline));
eval(join(SSG.gfxscript,newline));
if ~SSG.namedmap
eval(join(SSG.colormapvaluescript,newline));
end
eval(join(SSG.colormapscript,newline));
end
function SCRIPT = generatescript(SSG)
% Return the generated script.
geom = join(SSG.geomscript,newline);
gfx = join(SSG.gfxscript,newline);
if ~SSG.namedmap
cmapv = join(SSG.colormapvaluescript,newline) + newline;
else
cmapv = "";
end
cmap = join(SSG.colormapscript,newline);
SCRIPT = geom + newline + gfx + newline + cmapv + cmap;
end
function map = generatemap(SSG)
% Evaluate the color part of the script, and return the colormap.
eval(join(SSG.colormapvaluescript,newline));
map = map; %#ok
end
end
end