File Coverage

blib/lib/CAD/Drawing/Manipulate/Transform.pm
Criterion Covered Total %
statement 18 76 23.6
branch 0 20 0.0
condition n/a
subroutine 6 13 46.1
pod 7 7 100.0
total 31 116 26.7


line stmt bran cond sub pod time code
1             package CAD::Drawing::Manipulate::Transform;
2             our $VERSION = '0.02';
3              
4             # use CAD::Drawing;
5 3     3   16 use CAD::Drawing::Defined;
  3         4  
  3         545  
6              
7 3     3   2858 use Math::Vec qw(NewVec);
  3         17483  
  3         206  
8 3     3   5349 use Math::MatrixReal;
  3         111121  
  3         296  
9              
10             require Exporter;
11             @ISA = 'Exporter';
12             @EXPORT_OK = qw (
13             build_matrix
14             transform_pt
15             );
16              
17 3     3   36 use warnings;
  3         9  
  3         74  
18 3     3   61 use strict;
  3         7  
  3         111  
19 3     3   17 use Carp;
  3         5  
  3         3114  
20             ########################################################################
21              
22             =pod
23              
24             =head1 NAME
25              
26             CAD::Drawing::Manipulate::Transform - Matrix methods for CAD::Drawing
27              
28             =head1 DESCRIPTION
29              
30             Provides 3D transformation methods (based on traditional matrix
31             algorithms) for Drawing.pm objects.
32              
33             =head1 Coordinate System
34              
35             All of these methods assume a RIGHT-HANDED coordinate system. If you
36             are using a left-handed coordinate system, you are going to have
37             trouble, trouble, trouble. We aren't making video games here!
38              
39             =head1 AUTHOR
40              
41             Eric L. Wilhelm
42              
43             http://scratchcomputing.com
44              
45             =head1 COPYRIGHT
46              
47             This module is copyright (C) 2004-2006 by Eric L. Wilhelm. Portions
48             copyright (C) 2003 by Eric L. Wilhelm and A. Zahner Co.
49              
50             =head1 LICENSE
51              
52             This module is distributed under the same terms as Perl. See the Perl
53             source package for details.
54              
55             You may use this software under one of the following licenses:
56              
57             (1) GNU General Public License
58             (found at http://www.gnu.org/copyleft/gpl.html)
59             (2) Artistic License
60             (found at http://www.perl.com/pub/language/misc/Artistic.html)
61              
62             =head1 NO WARRANTY
63              
64             This software is distributed with ABSOLUTELY NO WARRANTY. The author,
65             his former employer, and any other contributors will in no way be held
66             liable for any loss or damages resulting from its use.
67              
68             =head1 Modifications
69              
70             The source code of this module is made freely available and
71             distributable under the GPL or Artistic License. Modifications to and
72             use of this software must adhere to one of these licenses. Changes to
73             the code should be noted as such and this notification (as well as the
74             above copyright information) must remain intact on all copies of the
75             code.
76              
77             Additionally, while the author is actively developing this code,
78             notification of any intended changes or extensions would be most helpful
79             in avoiding repeated work for all parties involved. Please contact the
80             author with any such development plans.
81              
82             =head1 SEE ALSO
83              
84             CAD::Drawing
85             CAD::Drawing::Calculate
86             CAD::Calc
87             Math::Vec
88             Math::Matrix
89              
90             =cut
91             ########################################################################
92              
93             =head1 Methods
94              
95             =cut
96             ########################################################################
97              
98             =head2 Transform
99              
100             $drw->Transform($addr, \%opts);
101              
102             Options:
103              
104             normal_ready => [@normal_vec] # no-questions-asked normal vector input
105              
106             =cut
107             sub Transform {
108 0     0 1   my $self = shift;
109 0           my ($addr, $opts) = @_;
110              
111             # option handling:
112 0           my $mat = build_matrix(%$opts);
113             # ocs vs wcs handling:
114 0           $self->to_wcs($addr);
115 0           my $obj = $self->getobj($addr);
116             # print "transforming\n";
117 0 0         unless(defined($opts->{normal_ready})) {
118             # normal data tag-along:
119 0           my $n = $obj->{normal};
120 0 0         $n || ($n = [0,0,1]);
121 0           @$n = transform_pt($n, $mat);
122 0           my $o = [0,0,0];
123 0           @$o = transform_pt($o, $mat);
124 0           $obj->{normal} = [NewVec(NewVec(@$n)->Minus($o))->UnitVector()];
125             }
126             else {
127             # print "over-ride: @{$opts->{normal_ready}}\n";exit;
128 0           $obj->{normal} = [@{$opts->{normal_ready}}];
  0            
129             }
130            
131             # pt vs pts:
132 0 0         if(my $pt = $obj->{pt}) {
    0          
133 0           @{$pt} = transform_pt($pt, $mat);
  0            
134             }
135             elsif(my $pts = $obj->{pts}) {
136 0           foreach my $pt (@{$pts}) {
  0            
137 0           @{$pt} = transform_pt($pt, $mat);
  0            
138             }
139             }
140             else {
141 0           croak("obj has no point or points!");
142             }
143              
144              
145             } # end subroutine Transform definition
146             ########################################################################
147             =head1 Non-OO Functions
148              
149             =cut
150             ########################################################################
151              
152             =head2 build_matrix
153              
154             Builds a linear transformation matrix according to %opts;
155              
156             $mat = build_matrix(%opts);
157              
158             =over
159              
160             =item Options:
161              
162             LTM => $ltm # pass-through for pre-built matrices
163             R => [$rX, $rY, $rZ] # rotation about each axis
164             T => [$tX, $tY, $tZ] # translation along each axis
165             S => [$sX, $sY, $sZ] # scaling along each axis
166              
167             =item Units:
168              
169             Scaling is in decimal (e.g. $sX = 0.9 will scale by 90%)
170              
171             =back
172              
173             =cut
174             sub build_matrix {
175 0     0 1   my (%opts) = @_;
176 0 0         $opts{LTM} && return($opts{LTM});
177 0           my $rotate = Math::MatrixReal->new_diag([1,1,1,1]);
178 0           my $translate = Math::MatrixReal->new_diag([1,1,1,1]);
179 0           my $scale = Math::MatrixReal->new_diag([1,1,1,1]);
180 0 0         if($opts{R}) {
181 0           my @r = rotation_matrices(@{$opts{R}});
  0            
182             # ORDER IS SIGNIFICANT!
183 0           $rotate = $r[0]*$r[1]*$r[2];
184             }
185 0 0         if($opts{T}) {
186 0           $translate = translation_matrix(@{$opts{T}});
  0            
187             }
188 0 0         if($opts{S}) {
189 0           $scale = scaling_matrix(@{$opts{S}});
  0            
190             }
191 0           return($rotate*$translate*$scale);
192             } # end subroutine build_matrix definition
193             ########################################################################
194              
195             =head2 NewMat
196              
197             Calls Math::MatrixReal->new_from_rows([@_]) see Math::MatrixReal for
198             methods which can be applied to the returned object.
199              
200             $mat = NewMat(@rows);
201              
202             =cut
203             sub NewMat {
204 0     0 1   return(Math::MatrixReal->new_from_rows([@_]));
205             } # end subroutine NewMat definition
206             ########################################################################
207              
208             =head2 transform_pt
209              
210             Applies matrix multiplication to linearly transform @pt by $mat. This
211             eliminates the tedium of making new matrices just to multiply one point.
212              
213             @pt = transform_pt(\@pt, $mat);
214              
215             =cut
216             sub transform_pt {
217 0     0 1   my ($point, $mat) = @_;
218 0           my @pt = @$point;
219 0 0         defined($pt[2]) || ($pt[2] = 0);
220 0           my $pt = Math::MatrixReal->new_from_cols([ [@pt[0..2], 1] ]);
221 0           $pt = $mat*$pt;
222             # print "now\n$pt\n";
223             # my @this = @{$pt};
224             # print "got @this\n";
225             # print join("\n", map({$_->[0]} @{$this[0]})), "\n";
226 0           return((map({$_->[0]} @{$pt->[0]}))[0..2]);
  0            
  0            
227             } # end subroutine transform_pt definition
228             ########################################################################
229              
230             =head2 rotation_matrices
231              
232             Returns a list of matrices corresponding to ($rX, $rY, $rZ)
233              
234             Rotation is in ccw radians about each axis (right-hand rule) except
235             that they may be specified in degrees with $angle . "d"
236              
237             @rotations = rotation_matrices($rX, $rY, $rZ);
238              
239             Resulting matrix will perform rotations in Z,Y,X order.
240              
241             =cut
242             sub rotation_matrices {
243 0     0 1   my(@R) = @_;
244 0           foreach my $ang (@R) {
245 0 0         if($ang =~ s/d$//) {
246 0           $ang *= $pi / 180;
247             }
248             }
249             return(
250 0           NewMat(
251             [1,0,0,0],
252             [0, cos($R[0]), -sin($R[0]), 0 ],
253             [0, sin($R[0]), cos($R[0]), 0 ],
254             [0, 0, 0, 1 ],
255             ),
256            
257             NewMat(
258             [ cos($R[1]), 0, sin($R[1]), 0],
259             [ 0, 1, 0, 0],
260             [ -sin($R[1]), 0, cos($R[1]), 0],
261             [0, 0, 0, 1 ],
262             ),
263            
264             NewMat(
265             [cos($R[2]), -sin($R[2]), 0, 0],
266             [sin($R[2]), cos($R[2]), 0, 0],
267             [0, 0, 1, 0],
268             [0, 0, 0, 1],
269             )
270             );
271             } # end subroutine rotation_matrices definition
272             ########################################################################
273              
274             =head2 translation_matrix
275              
276             Builds a linear transformation tranlation matrix from @trans, where
277             @trans = ($trX, $trY, $trZ).
278              
279             $mat = translation_matrix(@trans);
280              
281             =cut
282             sub translation_matrix {
283 0     0 1   my(@T) = @_;
284 0           my $mat = NewMat(
285             [1, 0, 0, $T[0]],
286             [0, 1, 0, $T[1]],
287             [0, 0, 1, $T[2]],
288             [0, 0, 0, 1]
289             );
290 0           return($mat);
291             } # end subroutine translation_matrix definition
292             ########################################################################
293              
294             =head2 scaling_matrix
295              
296             Builds a linear tranformation matrix from @scales, where @scales =
297             ($sX, $sY, $sZ).
298              
299             $mat = scaling_matrix(@scales);
300              
301             =cut
302             sub scaling_matrix {
303 0     0 1   my(@S) = @_;
304 0           my $mat = NewMat(
305             [$S[0], 0, 0, 0],
306             [0, $S[1], 0, 0],
307             [0, 0, $S[2], 0],
308             [0, 0, 0, 1]
309             );
310 0           return($mat);
311             } # end subroutine scaling_matrix definition
312             ########################################################################
313              
314              
315             1;