-
Notifications
You must be signed in to change notification settings - Fork 21
/
Bench.m
333 lines (314 loc) · 13.7 KB
/
Bench.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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
classdef Bench < handle
% Bench class implements a system of optical elements
% A complex optical system can be stored and manipulated as a whole by
% making it a Bench instance.
%
% Member functions:
%
% b = Bench( obj ) - constructor function
% INPUT:
% obj - an optical element, cell array of elements, or another bench
% OUTPUT:
% b - bench object
%
% b.display() - displays bench b's information
%
% b.draw( rays, draw_fl, alpha, scale, new_figure_fl ) - draws bench b in the current axes
% INPUT:
% rays - array of rays objects comprising full or partial light path
% draw_fl - display rays as 'arrows' (default), 'lines', or 'rays'
% alpha - opacity of optical surfaces from 0 to 1, default .33
% scale - scale of the arrow heads for the 'arrows' draw_fl
% new_figure_fl - 0, do not open, or 1, open (default)
%
% a = b.copy() - copies bench b to bench a
%
% b.append( a, n ) - appends element a to bench b n times. n > 1
% corresponds to multiple possible interactions (internal reflections
% and such).
%
% b.prepend( a, n ) - prepends element a to bench b n times
%
% b.replace( ind, a ) - replaces an element with index ind on bench b with element a
%
% b.remove( inds ) - removes elements located at inds on bench b
%
% b.rotate( rot_axis, rot_angle, rot_fl ) - rotate the bench b with all its elements
% INPUT:
% rot_axis - 1x3 vector defining the rotation axis
% rot_angle - rotation angle (radians)
% rot_fl - (0, default) rotation of the bench elements wrt to the
% global origin, (1) rotation wrt to the bench geometric center
%
% b.translate( tr_vec ) - translate the bench b with all its elements
% INPUT:
% tr_vec - 1x3 translation vector
%
% rays_through = b.trace( rays_in, out_fl ) - trace rays through optical elements
% on the bench b
% INPUT:
% rays_in - incoming rays, e.g., created by the Rays() function
% out_fl - 0 include even rays that missed some elements on the
% bench, - 1 (default) exlude such rays
% OUTPUT:
% rays_through - a cell array of refracted/reflected rays of the same
% length as the number of optical elements on the bench.
%
% Copyright: Yury Petrov, 2016
%
properties
elem = {}; % cell array of optical elements
cnt = 0; % counter of elements in the system
end
methods
function self = Bench( obj )
% b = Bench( obj ) - constructor function
% INPUT:
% obj - an optical element, cell array of elements, or another bench
% OUTPUT:
% b - bench object
if nargin == 0
return;
end
if isa( obj, 'Bench' ) % if another Bench
obj = obj.elem; % extract elements
end
% append object(s) to the optical system
nobj = length( obj );
for i = 1 : nobj
self.cnt = self.cnt + 1;
if nobj == 1
self.elem{ self.cnt } = obj;
elseif iscell( obj ) % other benches or cell arrays of Surfaces
self.elem{ self.cnt } = obj{ i };
elseif isvector( obj ) % Rays
self.elem{ self.cnt } = obj( i );
end
end
end
function display( self )
% b.display() - displays bench b's information
for i = 1 : self.cnt
obj = self.elem{ i };
fprintf( '\n%s:\n', class( obj ) );
obj.display;
end
end
function draw( self, rays, draw_fl, alpha, scale, new_figure_fl )
% b.draw( rays, draw_fl, alpha, scale, new_figure_fl ) - draws bench b in the current axes
% INPUT:
% rays - array of rays objects comprising full or partial light path
% draw_fl - display rays as 'arrows' (default), 'lines', or 'rays'
% alpha - opacity of optical surfaces from 0 to 1, default .33
% scale - scale of the arrow heads for the 'arrows' draw_fl
% new_figure_fl - 0, do not open, or 1, open (default)
if nargin < 6 || isempty( new_figure_fl )
new_figure_fl = 1; % open a new figure by default
end
if nargin < 5 || isempty( scale )
if nargin > 1
scale = ones( 1, length( rays ) );
else
scale = 1;
end
else
if length( scale ) == 1
scale = repmat( scale, 1, length( rays ) ); % make all ones
elseif length( scale ) < length( rays )
if size( scale, 1 ) > size( scale, 2 )
scale = scale';
end
scale = [ scale ones( 1, length( rays ) - length( scale ) ) ]; % append ones
end
end
if nargin < 4 || isempty( alpha )
alpha = 0.33;
end
if nargin < 3 || isempty( draw_fl )
draw_fl = 'arrows';
end
if nargin < 2 || isempty( rays )
rays = [];
end
if new_figure_fl == 1
fname = dbstack; % get debugging info
[ ~, fname ] = fname.name; % get the second (original) call function name
figure( 'Name', [ 'OPTOMETRIKA: ' fname ], 'NumberTitle', 'Off', ...
'Position', [ 0 0 1024 1024 ], ...
'Color', 'k' );
end
hold on;
for i = 1 : self.cnt
obj = self.elem{ i };
if isprop( obj, 'glass' ) && ( strcmp( obj.glass{1}, 'soot' ) || strcmp( obj.glass{2}, 'soot' ) )
color = [ .25 .25 .25 1 ];
obj.draw( color );
else
color = [ 1 1 1 alpha ];
obj.draw( color );
end
end
if ~isempty( rays )
if strcmp( draw_fl, 'lines' ) || strcmp( draw_fl, 'clines' ) || strcmp( draw_fl, 'rays' ) % draw ray bundles as lines
if strcmp( draw_fl, 'lines' )
sym = '-';
else
sym = '*:';
end
for i = 1 : length( rays ) - 1
vis = ( rays( i ).I ~= 0 ) & ...
isfinite( sum( rays( i ).r.^2, 2 ) ) & ...
isfinite( sum( rays( i + 1 ).r.^2, 2 ) ); % visible rays
real = dot( rays( i + 1 ).r - rays( i ).r, rays( i ).n, 2 ) > 0; % real rays (vs. virtual for virtual image)
[ unique_colors, ~, ic ] = unique( rays( i ).color, 'rows' );
for j = 1 : size( unique_colors, 1 )
cvis = vis & real & ( ic == j );
plot3( [ rays( i ).r( cvis, 1 )'; rays( i + 1 ).r( cvis, 1 )' ], ...
[ rays( i ).r( cvis, 2 )'; rays( i + 1 ).r( cvis, 2 )' ], ...
[ rays( i ).r( cvis, 3 )'; rays( i + 1 ).r( cvis, 3 )' ], sym, 'Color', unique_colors( j, : ) );
end
end
elseif strcmp( draw_fl, 'arrows' )
for i = 1 : length( rays )
rays( i ).draw( scale( i ) );
end
end
end
%if new_figure_fl == 1
gca.Clipping = 'off';
axis equal vis3d off;
%grid on;
camlight( 'left' );
camlight( 'right' );
camlight( 'headlight' );
view( -54, 54 );
lighting phong;
rotate3d on;
%end
end
function b = copy( self )
% a = b.copy() - copies bench b to bench a
b = feval( class( self ) );
b.cnt = self.cnt;
for i = 1 : length( self.elem )
b.elem{ i } = self.elem{ i }.copy;
end
end
function append( self, obj, mult )
% b.append( a, n ) - appends element a to bench b n times. n > 1
% corresponds to multiple possible interactions (internal reflections
% and such).
if nargin < 3
mult = 1;
end
if isa( obj, 'Bench' ) % if another Bench
obj = obj.elem; % extract elements
end
% append object(s) to the optical system
nobj = length( obj );
for m = 1 : mult
for i = 1 : nobj
self.cnt = self.cnt + 1;
if nobj == 1
self.elem{ self.cnt } = obj;
elseif iscell( obj ) % other benches or cell arrays of Surfaces
self.elem{ self.cnt } = obj{ i };
elseif isvector( obj ) % Rays
self.elem{ self.cnt } = obj( i );
end
end
end
end
function prepend( self, obj, mult )
% b.prepend( a, n ) - prepends element a to bench b n times
if nargin < 3
mult = 1;
end
if isa( obj, 'Bench' ) % if another Bench
obj = obj.elem; % extract elements
end
self.elem = fliplr( self.elem ); % reverse element direction temporarily
% prepend object(s) to the optical system
nobj = length( obj );
for m = 1 : mult
for i = nobj : -1 : 1 % append in the opposite order
self.cnt = self.cnt + 1;
if nobj == 1
self.elem{ self.cnt } = obj;
elseif iscell( obj ) % other benches or cell arrays of Surfaces
self.elem{ self.cnt } = obj{ i };
elseif isvector( obj ) % Rays
self.elem{ self.cnt } = obj( i );
end
end
end
self.elem = fliplr( self.elem ); % restitute the original order
end
function replace( self, ind, obj )
% b.replace( ind, a ) - replaces an element with index ind on bench b with element a
self.elem{ ind } = obj;
end
function remove( self, inds )
% b.remove( inds ) - removes elements located at inds on bench b
if self.cnt == 0
error( 'The bench is already empty!' );
else
self.elem( inds ) = [];
self.cnt = self.cnt - length( inds );
end
end
function rotate( self, rot_axis, rot_angle, rot_fl )
% b.rotate( rot_axis, rot_angle, rot_fl ) - rotate the bench b with all its elements
% INPUT:
% rot_axis - 1x3 vector defining the rotation axis
% rot_angle - rotation angle (radians)
% rot_fl - (0, default) rotation of the bench elements wrt to the
% global origin, (1) rotation wrt to the bench geometric center
if nargin < 4
rot_fl = 0;
end
cntr = [ 0 0 0 ];
if rot_fl == 1 % rotate around the geometric center of the bench
for i = 1 : self.cnt % loop through the optic system
cntr = cntr + self.elem{ i }.r;
end
cntr = cntr / self.cnt;
end
% rotate bench elements
for i = 1 : self.cnt % loop through the optic system
self.elem{ i }.rotate( rot_axis, rot_angle ); % rotate normal
self.elem{ i }.r = cntr + rodrigues_rot( self.elem{ i }.r - cntr, rot_axis, rot_angle ); % rotate position
end
if abs( rot_angle ) > pi/2 % reverse order in which the elements are encountered by rays
self.elem = fliplr( self.elem );
end
end
function translate( self, tr_vec )
% b.translate( tr_vec ) - translate the bench b with all its elements
% INPUT:
% tr_vec - 1x3 translation vector
for i = 1 : self.cnt % loop through the optic system
self.elem{ i }.r = self.elem{ i }.r + tr_vec; % translate position
end
end
function rays = trace( self, rays_in, out_fl )
% rays_through = b.trace( rays_in, out_fl ) - trace rays through optical elements
% on the bench b
% INPUT:
% rays_in - incoming rays, e.g., created by the Rays() function
% out_fl - 0 include even rays that missed some elements on the
% bench, - 1 (default) exlude such rays
% OUTPUT:
% rays_through - a cell array of refracted/reflected rays of the same
% length as the number of optical elements on the bench.
if nargin < 3
out_fl = 1; % exclude rays which miss elements of the bench
end
rays( 1, self.cnt + 1 ) = Rays; % allocate cnt instances of Rays
rays( 1 ) = rays_in;
for i = 1 : self.cnt % loop through the optic system
rays( i + 1 ) = rays( i ).interaction( self.elem{ i }, out_fl );
end
end
end
end