1
+ function draw_coord_frame(origin ,varargin )
2
+ %% draw_coord_frame( origin , <opts> )
3
+ %
4
+ % Plots a 3D coordinate system origin.
5
+ % By default is aligned with the right-handed XYZ global coordinate system.
6
+ % Use optional arguments to customise:
7
+ %
8
+ % ------------------------------------------------------------------------
9
+ % KEY VALUE DESCRIPTION
10
+ % ------------------------------------------------------------------------
11
+ % 'rotate' [u v w] Rotate coordinates 'u' degrees around the X-axis,
12
+ % 'v' degrees around the Y-axis,
13
+ % 'w' degrees around the Z-axis.
14
+ % 'axes' ['x' and/or 'y' and/or 'z']
15
+ % Only plot axes listed
16
+ % 'labels' [true/false]
17
+ % Whether to print coordinate system labels
18
+ % 'index' str String index on labels (default str='1')
19
+ % ------------------------------------------------------------------------
20
+ % 'length' L Length of axes lines
21
+ % 'headlength' R Length of arrow head
22
+ % 'arrowangle' a Angle of arrowhead "quills"
23
+ % 'linecolour' [R G B] Red-Green-Blue colour of axis lines
24
+ % 'headcolour' [R G B] Red-Green-Blue colour of arrowhead faces
25
+ % 'headopacity' C Opacity of arrowhead faces
26
+ % ------------------------------------------------------------------------
27
+
28
+ %% Parse inputs:
29
+
30
+ p = inputParser ;
31
+ p .addRequired(' origin' );
32
+ p .addOptional(' rotate' ,[0 0 0 ]);
33
+ p .addOptional(' length' ,0.05 );
34
+ p .addOptional(' headlength' ,0.02 );
35
+ p .addOptional(' arrowangle' ,25 );
36
+ p .addOptional(' index' ,' 1' );
37
+ p .addOptional(' labels' ,true );
38
+ p .addOptional(' axes' ,' xyz' );
39
+ p .addParameter(' linecolour' ,[0 0 0 ]);
40
+ p .addParameter(' headcolour' ,0.5 *[1 1 1 ]);
41
+ p .addParameter(' headopacity' ,0.9 );
42
+ p .parse(origin ,varargin{: })
43
+
44
+ O = p .Results .origin ;
45
+ al = p .Results .length ;
46
+ hl = p .Results .headlength ;
47
+ ang = p .Results .arrowangle ;
48
+
49
+ r = p .Results .rotate ;
50
+ ni = p .Results .index ;
51
+ ecol = p .Results .linecolour ;
52
+ col = p .Results .headcolour ;
53
+ opac = p .Results .headopacity ;
54
+ labels_bool = p .Results .labels ;
55
+ plot_axes = p .Results .axes ;
56
+
57
+ % Constant that should actually be optional input
58
+ nameshift = 0.01 ;
59
+
60
+ %% Definition of a single axis (X)
61
+ %
62
+ % This is rotated to plot the other two in Y and Z.
63
+
64
+ ax = [al ; 0 ; 0 ]; % axis end point
65
+ pxy = ax - hl *[cosd(ang ); sind(ang ); 0 ]; % one point of the arrowhead
66
+ pxz = ax - hl *[cosd(ang ); 0 ; sind(ang )]; % one point of the other arrowhead
67
+ head1 = [ax pxy pxy .*[1 ; - 1 ; 1 ]]; % arrowhead points in XY plane
68
+ head2 = [ax pxz pxz .*[1 ; 1 ; - 1 ]]; % arrowhead points in XZ plane
69
+
70
+ % Rotation matrix (note inverse order of application)
71
+ if numel(r ) == 3
72
+ R = Rz(r(3 ))*Ry(r(2 ))*Rx(r(1 ));
73
+ elseif all(size(r )==[3 ,3 ])
74
+ R = r ;
75
+ else
76
+ error(' Rotation must be 3 cardan angles or a 3x3 rotation matrix.' )
77
+ end
78
+
79
+ %% Plot
80
+
81
+ hold on
82
+
83
+ if strcmp(plot_axes ,' xyz' )
84
+ if labels_bool
85
+ text(-nameshift + O(1 ),-nameshift + O(2 ),O(3 ),[' O_{' ,ni ,' }' ]);
86
+ end
87
+ end
88
+
89
+ for s = plot_axes
90
+ switch s
91
+ case ' x'
92
+ plot_one_coord(O ,R * ax , R * head1 , R * head2 , [' x_{' ,ni ,' }' ],[2 * nameshift ; 0 ; 0 ])
93
+ case ' y'
94
+ plot_one_coord(O ,R * Rz(+90 )*ax ,R * Rz(+90 )*head1 ,R * Rz(+90 )*head2 ,[' y_{' ,ni ,' }' ],[0 ; nameshift ; 0 ])
95
+ case ' z'
96
+ plot_one_coord(O ,R * Ry(-90 )*ax ,R * Ry(-90 )*head1 ,R * Ry(-90 )*head2 ,[' z_{' ,ni ,' }' ],[0 ; 0 ; nameshift ])
97
+ end
98
+ end
99
+
100
+ %% Nested functions
101
+
102
+ function plot_one_coord(O ,a ,head1 ,head2 ,name ,ns )
103
+ if labels_bool
104
+ text(ns(1 )+O(1 )+a(1 ),ns(2 )+O(2 )+a(2 ),ns(3 )+O(3 )+a(3 ),name );
105
+ end
106
+
107
+ plot3(O(1 )+[0 a(1 )],O(2 )+[0 a(2 )],O(3 )+[0 a(3 )],' color' ,ecol );
108
+ patch(O(1 )+head1(1 ,: ),O(2 )+head1(2 ,: ),O(3 )+head1(3 ,: ),ecol ,' facealpha' ,opac ,' facecolor' ,col );
109
+ patch(O(1 )+head2(1 ,: ),O(2 )+head2(2 ,: ),O(3 )+head2(3 ,: ),ecol ,' facealpha' ,opac ,' facecolor' ,col );
110
+ end
111
+
112
+ end
113
+
114
+ %% Rotation matrices
115
+ %
116
+ % These could all be anonymous functions if we wanted.
117
+
118
+ function R = Rz(t )
119
+ R = [cosd(t ) - sind(t ) 0 ;
120
+ sind(t ) cosd(t ) 0 ;
121
+ 0 0 1 ];
122
+ end
123
+
124
+ function R = Ry(t )
125
+ R = [ cosd(t ) 0 sind(t );
126
+ 0 1 0 ;
127
+ - sind(t ) 0 cosd(t )];
128
+ end
129
+
130
+ function R = Rx(t )
131
+ R = [1 0 0 ;
132
+ 0 cosd(t ) - sind(t );
133
+ 0 sind(t ) cosd(t )];
134
+ end
135
+
136
+ % assert( all( Rx(90)*[0;1;0]==[ 0; 0; 1]) )
137
+ % assert( all( Rz(90)*[0;1;0]==[-1; 0; 0]) )
138
+ % assert( all( Ry(90)*[1;0;0]==[ 0; 0;-1]) )
139
+ % assert( all( Rz(90)*[1;0;0]==[ 0; 1; 0]) )
140
+ % assert( all( Rx(90)*[0;0;1]==[ 0;-1; 0]) )
141
+ % assert( all( Ry(90)*[0;0;1]==[ 1; 0; 0]) )
0 commit comments