1 //******************************************************************************
2 ///
3 /// @file core/material/warp.cpp
4 ///
5 /// Implementation of warps.
6 ///
7 /// The code in this file implements functions that warp or modify the point at
8 /// which a texture pattern is evaluated.
9 ///
10 /// @copyright
11 /// @parblock
12 ///
13 /// Persistence of Vision Ray Tracer ('POV-Ray') version 3.8.
14 /// Copyright 1991-2017 Persistence of Vision Raytracer Pty. Ltd.
15 ///
16 /// POV-Ray is free software: you can redistribute it and/or modify
17 /// it under the terms of the GNU Affero General Public License as
18 /// published by the Free Software Foundation, either version 3 of the
19 /// License, or (at your option) any later version.
20 ///
21 /// POV-Ray is distributed in the hope that it will be useful,
22 /// but WITHOUT ANY WARRANTY; without even the implied warranty of
23 /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 /// GNU Affero General Public License for more details.
25 ///
26 /// You should have received a copy of the GNU Affero General Public License
27 /// along with this program. If not, see <http://www.gnu.org/licenses/>.
28 ///
29 /// ----------------------------------------------------------------------------
30 ///
31 /// POV-Ray is based on the popular DKB raytracer version 2.12.
32 /// DKBTrace was originally written by David K. Buck.
33 /// DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
34 ///
35 /// @endparblock
36 ///
37 //******************************************************************************
38
39 // Unit header file must be the first file included within POV-Ray *.cpp files (pulls in config)
40 #include "core/material/warp.h"
41
42 #include "base/pov_err.h"
43
44 #include "core/material/noise.h"
45 #include "core/material/pattern.h"
46 #include "core/material/texture.h"
47 #include "core/math/matrix.h"
48 #include "core/math/randomsequence.h"
49
50 // this must be the last file included
51 #include "base/povdebug.h"
52
53 namespace pov
54 {
55
56 /*****************************************************************************
57 * Local preprocessor defines
58 ******************************************************************************/
59
60 const DBL COORDINATE_LIMIT = 1.0e17;
61
62 /*****************************************************************************
63 * Static functions
64 ******************************************************************************/
65
66 static RandomDoubleSequence WarpRands(0.0, 1.0, 32768);
67
68 /*****************************************************************************
69 *
70 * FUNCTION
71 *
72 * Warp_EPoint
73 *
74 * INPUT
75 *
76 * EPoint -- The original point in 3d space at which a pattern
77 * is evaluated.
78 * TPat -- Texture pattern struct
79 *
80 * OUTPUT
81 *
82 * TPoint -- Point after turbulence and transform
83 * have been applied
84 *
85 * RETURNS
86 *
87 * AUTHOR
88 *
89 * POV-Ray Team
90 *
91 * DESCRIPTION
92 *
93 * CHANGES
94 *
95 ******************************************************************************/
96
Warp_EPoint(Vector3d & TPoint,const Vector3d & EPoint,const TPATTERN * TPat)97 void Warp_EPoint (Vector3d& TPoint, const Vector3d& EPoint, const TPATTERN *TPat)
98 {
99 WarpList& warps=TPat->pattern->warps;
100
101 TPoint = EPoint;
102
103 for (WarpList::reverse_iterator iWarp = warps.rbegin(); iWarp != warps.rend(); iWarp ++)
104 {
105 WarpPtr& warp = *iWarp;
106 warp->WarpPoint(TPoint);
107 }
108
109 for (int i=X; i<=Z; i++)
110 {
111 if (TPoint[i] > COORDINATE_LIMIT)
112 TPoint[i]= COORDINATE_LIMIT;
113 else if (TPoint[i] < -COORDINATE_LIMIT)
114 TPoint[i] = -COORDINATE_LIMIT;
115 }
116 }
117
WarpPoint(Vector3d & TPoint) const118 bool BlackHoleWarp::WarpPoint(Vector3d& TPoint) const
119 {
120 Vector3d C = Center;
121
122 if (Repeat)
123 {
124 int blockX = 0, blockY = 0, blockZ = 0;
125
126 /* first, get the block number we're in for each dimension */
127 /* block numbers are (currently) calculated relative to 0 */
128 /* we use floor () since it correctly returns -1 for the
129 first block below 0 in each axis */
130 /* one final point - we could run into overflow problems if
131 the repeat vector was small and the scene very large. */
132 if (Repeat_Vector[X] >= EPSILON)
133 blockX = (int) floor (TPoint[X] / Repeat_Vector[X]);
134
135 if (Repeat_Vector[Y] >= EPSILON)
136 blockY = (int) floor (TPoint[Y] / Repeat_Vector[Y]);
137
138 if (Repeat_Vector[Z] >= EPSILON)
139 blockZ = (int) floor (TPoint[Z] / Repeat_Vector[Z]);
140
141 if (Uncertain)
142 {
143 /* if the position is uncertain calculate the new one first */
144 /* this will allow the same numbers to be returned by frand */
145
146 int seed = Hash3d (blockX, blockY, blockZ);
147 C[X] += WarpRands(seed) * Uncertainty_Vector[X];
148 C[Y] += WarpRands(seed + 1) * Uncertainty_Vector[Y];
149 C[Z] += WarpRands(seed + 2) * Uncertainty_Vector[Z];
150 }
151
152 C[X] += Repeat_Vector[X] * blockX;
153 C[Y] += Repeat_Vector[Y] * blockY;
154 C[Z] += Repeat_Vector[Z] * blockZ;
155 }
156
157 Vector3d Delta = TPoint - C;
158 DBL Length = Delta.length();
159
160 /* Length is the distance from the centre of the black hole */
161 if (Length >= Radius)
162 return true;
163
164 if (Type == 0)
165 {
166 /* now convert the length to a proportion (0 to 1) that the point
167 is from the edge of the black hole. a point on the perimeter
168 of the black hole will be 0.0; a point at the centre will be
169 1.0; a point exactly halfway will be 0.5, and so forth. */
170 Length = (Radius - Length) / Radius;
171
172 /* Strength is the magnitude of the transformation effect. firstly,
173 apply the Power variable to Length. this is meant to provide a
174 means of controlling how fast the power of the Black Hole falls
175 off from its centre. if Power is 2.0, then the effect is inverse
176 square. increasing power will cause the Black Hole to be a lot
177 weaker in its effect towards its perimeter.
178
179 finally we multiply Strength with the Black Hole's Strength
180 variable. if the resultant value exceeds 1.0 we clip it to 1.0.
181 this means a point will never be transformed by more than its
182 original distance from the centre. the result of this clipping
183 is that you will have an 'exclusion' area near the centre of
184 the black hole where all points whose final value exceeded or
185 equalled 1.0 were moved by a fixed amount. this only happens
186 if the Strength value of the Black Hole was greater than one. */
187
188 DBL S = pow (Length, Power) * Strength;
189 if (S > 1.0) S = 1.0;
190
191 /* if the Black Hole is inverted, it gives the impression of 'push-
192 ing' the pattern away from its centre. otherwise it sucks. */
193 Delta *= (Inverted ? -S : S);
194
195 /* add the scaled Delta to the input point to end up with TPoint. */
196 TPoint += Delta;
197 }
198
199 return true;
200 }
201
WarpPoint(Vector3d & EPoint) const202 bool CubicWarp::WarpPoint(Vector3d& EPoint) const
203 {
204 DBL x = EPoint[X], y = EPoint[Y], z = EPoint[Z];
205 const DBL ax = fabs(x), ay = fabs(y), az = fabs(z);
206
207 if(x >= 0 && x >= ay && x >= az)
208 {
209 EPoint[X] = 0.75 - 0.25*(z/x+1.0)/2.0;
210 EPoint[Y] = 1.0/3.0 + (1.0/3.0)*(y/x+1.0)/2.0;
211 EPoint[Z] = x;
212 }
213 else if(y >= 0 && y >= ax && y >= az)
214 {
215 EPoint[X] = 0.25 + 0.25*(x/y+1.0)/2.0;
216 EPoint[Y] = 1.0 - (1.0/3.0)*(z/y+1.0)/2.0;
217 EPoint[Z] = y;
218 }
219 else if(z >= 0 && z >= ax && z >= ay)
220 {
221 EPoint[X] = 0.25 + 0.25*(x/z+1.0)/2.0;
222 EPoint[Y] = 1.0/3.0 + (1.0/3.0)*(y/z+1.0)/2.0;
223 EPoint[Z] = z;
224 }
225 else if(x < 0 && x <= -ay && x <= -az)
226 {
227 x = -x;
228 EPoint[X] = 0.25*(z/x+1.0)/2.0;
229 EPoint[Y] = 1.0/3.0 + (1.0/3.0)*(y/x+1.0)/2.0;
230 EPoint[Z] = x;
231 }
232 else if(y < 0 && y <= -ax && y <= -az)
233 {
234 y = -y;
235 EPoint[X] = 0.25 + 0.25*(x/y+1.0)/2.0;
236 EPoint[Y] = (1.0/3.0)*(z/y+1.0)/2.0;
237 EPoint[Z] = y;
238 }
239 else
240 {
241 z = -z;
242 EPoint[X] = 1.0 - 0.25*(x/z+1.0)/2.0;
243 EPoint[Y] = 1.0/3.0 + (1.0/3.0)*(y/z+1.0)/2.0;
244 EPoint[Z] = z;
245 }
246
247 return 1;
248 }
249
WarpPoint(Vector3d & EPoint) const250 bool CylindricalWarp::WarpPoint(Vector3d& EPoint) const
251 {
252 DBL len, theta;
253 DBL x = EPoint[X];
254 DBL y = EPoint[Y];
255 DBL z = EPoint[Z];
256
257 // Determine its angle from the point (1, 0, 0) in the x-z plane.
258 len = sqrt(x * x + z * z);
259
260 if(len == 0.0)
261 return false;
262 else
263 {
264 if(z == 0.0)
265 {
266 if(x > 0)
267 theta = 0.0;
268 else
269 theta = M_PI;
270 }
271 else
272 {
273 theta = acos(x / len);
274 if(z < 0.0)
275 theta = TWO_M_PI - theta;
276 }
277
278 theta /= TWO_M_PI; // This will be from 0 to 1
279 }
280
281 if(DistExp == 1.0)
282 theta *= len;
283 else if (DistExp != 0.0)
284 theta *= pow(len, DistExp);
285
286 x = theta;
287 z = len;
288
289 if((Orientation_Vector[X] == 0.0) &&
290 (Orientation_Vector[Y] == 0.0) &&
291 (Orientation_Vector[Z] == 1.0))
292 {
293 EPoint[X] = x;
294 EPoint[Y] = y;
295 EPoint[Z] = z;
296 }
297 else
298 {
299 EPoint[X] = (Orientation_Vector[X] * z) +
300 (Orientation_Vector[Y] * x) +
301 (Orientation_Vector[Z] * x);
302 EPoint[Y] = (Orientation_Vector[X] * y) +
303 (Orientation_Vector[Y] * -z) +
304 (Orientation_Vector[Z] * y);
305 EPoint[Z] = (Orientation_Vector[X] * -x) +
306 (Orientation_Vector[Y] * y) +
307 (Orientation_Vector[Z] * z);
308 }
309
310 return true;
311 }
312
WarpPoint(Vector3d & TPoint) const313 bool IdentityWarp::WarpPoint(Vector3d& TPoint) const
314 {
315 return true;
316 }
317
WarpPoint(Vector3d & EPoint) const318 bool PlanarWarp::WarpPoint(Vector3d& EPoint) const
319 {
320 DBL x = EPoint[X];
321 DBL z = OffSet;
322 DBL y = EPoint[Y];
323
324 if((Orientation_Vector[X] == 0.0) &&
325 (Orientation_Vector[Y] == 0.0) &&
326 (Orientation_Vector[Z] == 1.0))
327 {
328 EPoint[X] = x;
329 EPoint[Y] = y;
330 EPoint[Z] = z;
331 }
332 else
333 {
334 EPoint[X] = (Orientation_Vector[X] * z) +
335 (Orientation_Vector[Y] * x) +
336 (Orientation_Vector[Z] * x);
337 EPoint[Y] = (Orientation_Vector[X] * y) +
338 (Orientation_Vector[Y] * -z) +
339 (Orientation_Vector[Z] * y);
340 EPoint[Z] = (Orientation_Vector[X] * -x) +
341 (Orientation_Vector[Y] * y) +
342 (Orientation_Vector[Z] * z);
343 }
344
345 return true;
346 }
347
WarpPoint(Vector3d & TPoint) const348 bool RepeatWarp::WarpPoint(Vector3d& TPoint) const
349 {
350 SNGL BlkNum = (SNGL)floor(TPoint[Axis]/Width);
351
352 TPoint[Axis] -= BlkNum*Width;
353
354 if (((int)BlkNum) & 1)
355 {
356 TPoint *= Flip;
357 if ( Flip[Axis] < 0 )
358 TPoint[Axis] += Width;
359 }
360
361 TPoint += (DBL)BlkNum * Offset;
362
363 return true;
364 }
365
WarpPoint(Vector3d & EPoint) const366 bool SphericalWarp::WarpPoint(Vector3d& EPoint) const
367 {
368 DBL len, phi, theta,dist;
369 DBL x = EPoint[X];
370 DBL y = EPoint[Y];
371 DBL z = EPoint[Z];
372
373 // Make sure this vector is on the unit sphere.
374
375 dist = sqrt(x * x + y * y + z * z);
376
377 if(dist == 0.0)
378 return 0;
379 else
380 {
381 x /= dist;
382 y /= dist;
383 z /= dist;
384 }
385
386 // Determine its angle from the x-z plane.
387 phi = 0.5 + asin(y) / M_PI; // This will be from 0 to 1
388
389 // Determine its angle from the point (1, 0, 0) in the x-z plane.
390 len = sqrt(x * x + z * z);
391 if(len == 0.0)
392 {
393 // This point is at one of the poles. Any value of xcoord will be ok...
394 theta = 0;
395 }
396 else
397 {
398 if(z == 0.0)
399 {
400 if(x > 0)
401 theta = 0.0;
402 else
403 theta = M_PI;
404 }
405 else
406 {
407 theta = acos(x / len);
408 if (z < 0.0)
409 theta = TWO_M_PI - theta;
410 }
411 theta /= TWO_M_PI; /* This will be from 0 to 1 */
412 }
413
414 if(DistExp == 1.0)
415 {
416 theta *= dist;
417 phi *= dist;
418 }
419 else if(DistExp != 0.0)
420 {
421 theta *= pow(dist, DistExp);
422 phi *= pow(dist, DistExp);
423 }
424
425 x = theta;
426 z = dist;
427 y = phi;
428
429 if((Orientation_Vector[X] == 0.0) &&
430 (Orientation_Vector[Y] == 0.0) &&
431 (Orientation_Vector[Z] == 1.0))
432 {
433 EPoint[X] = x;
434 EPoint[Y] = y;
435 EPoint[Z] = z;
436 }
437 else
438 {
439 EPoint[X] = (Orientation_Vector[X] * z) +
440 (Orientation_Vector[Y] * x) +
441 (Orientation_Vector[Z] * x);
442 EPoint[Y] = (Orientation_Vector[X] * y) +
443 (Orientation_Vector[Y] * -z) +
444 (Orientation_Vector[Z] * y);
445 EPoint[Z] = (Orientation_Vector[X] * -x) +
446 (Orientation_Vector[Y] * y) +
447 (Orientation_Vector[Z] * z);
448 }
449
450 return 1;
451 }
452
WarpPoint(Vector3d & EPoint) const453 bool ToroidalWarp::WarpPoint(Vector3d& EPoint) const
454 {
455 DBL len, phi, theta;
456 DBL r0;
457 DBL x = EPoint[X];
458 DBL y = EPoint[Y];
459 DBL z = EPoint[Z];
460
461 r0 = MajorRadius;
462
463 // Determine its angle from the x-axis.
464
465 len = sqrt(x * x + z * z);
466
467 if(len == 0.0)
468 return false;
469 else
470 {
471 if(z == 0.0)
472 {
473 if(x > 0)
474 theta = 0.0;
475 else
476 theta = M_PI;
477 }
478 else
479 {
480 theta = acos(x / len);
481 if(z < 0.0)
482 theta = TWO_M_PI - theta;
483 }
484 }
485
486 theta = 0.0 - theta;
487
488 // Now rotate about the y-axis to get the point (x, y, z) into the x-y plane.
489
490 x = len - r0;
491 len = sqrt(x * x + y * y);
492 phi = acos(-x / len);
493 if (y > 0.0)
494 phi = TWO_M_PI - phi;
495
496 // Determine the parametric coordinates.
497
498 theta /= (-TWO_M_PI);
499
500 phi /= TWO_M_PI;
501
502 if (DistExp == 1.0)
503 {
504 theta *= len;
505 phi *= len;
506 }
507 else if (DistExp != 0.0)
508 {
509 theta *= pow(len, DistExp);
510 phi *= pow(len, DistExp);
511 }
512
513 x = theta;
514 z = len;
515 y = phi;
516
517 if((Orientation_Vector[X] == 0.0) &&
518 (Orientation_Vector[Y] == 0.0) &&
519 (Orientation_Vector[Z] == 1.0))
520 {
521 EPoint[X] = x;
522 EPoint[Y] = y;
523 EPoint[Z] = z;
524 }
525 else
526 {
527 EPoint[X] = (Orientation_Vector[X] * z) +
528 (Orientation_Vector[Y] * x) +
529 (Orientation_Vector[Z] * x);
530 EPoint[Y] = (Orientation_Vector[X] * y) +
531 (Orientation_Vector[Y] * -z) +
532 (Orientation_Vector[Z] * y);
533 EPoint[Z] = (Orientation_Vector[X] * -x) +
534 (Orientation_Vector[Y] * y) +
535 (Orientation_Vector[Z] * z);
536 }
537
538 return true;
539 }
540
WarpPoint(Vector3d & TPoint) const541 bool TransformWarp::WarpPoint(Vector3d& TPoint) const
542 {
543 MInvTransPoint(TPoint, TPoint, &Trans);
544 return true;
545 }
546
WarpPoint(Vector3d & TPoint) const547 bool GenericTurbulenceWarp::WarpPoint(Vector3d& TPoint) const
548 {
549 Vector3d PTurbulence;
550 DTurbulence (PTurbulence, TPoint, this);
551 TPoint += PTurbulence * Turbulence;
552 return true;
553 }
554
555
556
Warp_Normal(Vector3d & TNorm,const Vector3d & ENorm,const TPATTERN * TPat,bool DontScaleBumps)557 void Warp_Normal (Vector3d& TNorm, const Vector3d& ENorm, const TPATTERN *TPat, bool DontScaleBumps)
558 {
559 const WarpList& warps = TPat->pattern->warps;
560
561 if(!DontScaleBumps)
562 TNorm = ENorm.normalized();
563 else
564 TNorm = ENorm;
565
566 for (WarpList::const_reverse_iterator iWarp = warps.rbegin(); iWarp != warps.rend(); iWarp ++)
567 {
568 const WarpPtr& warp = *iWarp;
569 warp->WarpNormal(TNorm);
570 }
571
572 if(!DontScaleBumps)
573 TNorm.normalize();
574 }
575
WarpNormal(Vector3d & TNorm) const576 bool IdentityWarp::WarpNormal(Vector3d& TNorm) const
577 {
578 return true;
579 }
580
WarpNormal(Vector3d & TNorm) const581 bool TransformWarp::WarpNormal(Vector3d& TNorm) const
582 {
583 MInvTransNormal(TNorm, TNorm, &Trans);
584 return true;
585 }
586
WarpNormal(Vector3d & TNorm) const587 bool BlackHoleWarp::WarpNormal(Vector3d& TNorm) const { /* Error("Black Hole Warp not yet implemented for normals"); */ return false; }
WarpNormal(Vector3d & TNorm) const588 bool CubicWarp::WarpNormal(Vector3d& TNorm) const { /* Error("Cubic Warp not yet implemented for normals"); */ return false; }
WarpNormal(Vector3d & TNorm) const589 bool CylindricalWarp::WarpNormal(Vector3d& TNorm) const { /* Error("Cylindrical Warp not yet implemented for normals"); */ return false; }
WarpNormal(Vector3d & TNorm) const590 bool PlanarWarp::WarpNormal(Vector3d& TNorm) const { /* Error("Planar Warp not yet implemented for normals"); */ return false; }
WarpNormal(Vector3d & TNorm) const591 bool RepeatWarp::WarpNormal(Vector3d& TNorm) const { /* Error("Repeat Warp not yet implemented for normals"); */ return false; }
WarpNormal(Vector3d & TNorm) const592 bool SphericalWarp::WarpNormal(Vector3d& TNorm) const { /* Error("Spherical Warp not yet implemented for normals"); */ return false; }
WarpNormal(Vector3d & TNorm) const593 bool ToroidalWarp::WarpNormal(Vector3d& TNorm) const { /* Error("Toroidal Warp not yet implemented for normals"); */ return false; }
WarpNormal(Vector3d & TNorm) const594 bool GenericTurbulenceWarp::WarpNormal(Vector3d& TNorm) const { /* Error("Turbulence Warp not yet implemented for normals"); */ return false; }
595
596
UnWarp_Normal(Vector3d & TNorm,const Vector3d & ENorm,const TPATTERN * TPat,bool DontScaleBumps)597 void UnWarp_Normal (Vector3d& TNorm, const Vector3d& ENorm, const TPATTERN *TPat, bool DontScaleBumps)
598 {
599 const WarpList& warps = TPat->pattern->warps;
600
601 if(!DontScaleBumps)
602 TNorm = ENorm.normalized();
603 else
604 TNorm = ENorm;
605
606 for (WarpList::const_iterator iWarp = warps.begin(); iWarp != warps.end(); iWarp ++)
607 {
608 const WarpPtr& warp = *iWarp;
609 warp->UnwarpNormal(TNorm);
610 }
611
612 if(!DontScaleBumps)
613 TNorm.normalize();
614 }
615
UnwarpNormal(Vector3d & TNorm) const616 bool IdentityWarp::UnwarpNormal(Vector3d& TNorm) const
617 {
618 return true;
619 }
620
UnwarpNormal(Vector3d & TNorm) const621 bool TransformWarp::UnwarpNormal(Vector3d& TNorm) const
622 {
623 MTransNormal(TNorm, TNorm, &Trans);
624 return true;
625 }
626
UnwarpNormal(Vector3d & TNorm) const627 bool BlackHoleWarp::UnwarpNormal(Vector3d& TNorm) const { /* Error("Black Hole Warp not yet implemented for normals"); */ return false; }
UnwarpNormal(Vector3d & TNorm) const628 bool CubicWarp::UnwarpNormal(Vector3d& TNorm) const { /* Error("Cubic Warp not yet implemented for normals"); */ return false; }
UnwarpNormal(Vector3d & TNorm) const629 bool CylindricalWarp::UnwarpNormal(Vector3d& TNorm) const { /* Error("Cylindrical Warp not yet implemented for normals"); */ return false; }
UnwarpNormal(Vector3d & TNorm) const630 bool PlanarWarp::UnwarpNormal(Vector3d& TNorm) const { /* Error("Planar Warp not yet implemented for normals"); */ return false; }
UnwarpNormal(Vector3d & TNorm) const631 bool RepeatWarp::UnwarpNormal(Vector3d& TNorm) const { /* Error("Repeat Warp not yet implemented for normals"); */ return false; }
UnwarpNormal(Vector3d & TNorm) const632 bool SphericalWarp::UnwarpNormal(Vector3d& TNorm) const { /* Error("Spherical Warp not yet implemented for normals"); */ return false; }
UnwarpNormal(Vector3d & TNorm) const633 bool ToroidalWarp::UnwarpNormal(Vector3d& TNorm) const { /* Error("Toroidal Warp not yet implemented for normals"); */ return false; }
UnwarpNormal(Vector3d & TNorm) const634 bool GenericTurbulenceWarp::UnwarpNormal(Vector3d& TNorm) const { /* Error("Turbulence Warp not yet implemented for normals"); */ return false; }
635
636
637 /*****************************************************************************
638 *
639 * FUNCTION
640 *
641 * INPUT
642 *
643 * OUTPUT
644 *
645 * RETURNS
646 *
647 * AUTHOR
648 *
649 * DESCRIPTION
650 *
651 * CHANGES
652 *
653 ******************************************************************************/
654
Destroy_Warps(WarpList & warps)655 void Destroy_Warps (WarpList& warps)
656 {
657 for (WarpList::iterator iWarp = warps.begin(); iWarp != warps.end(); iWarp ++)
658 delete *iWarp;
659 warps.clear();
660 }
661
662
663
664 /*****************************************************************************
665 *
666 * FUNCTION
667 *
668 * INPUT
669 *
670 * OUTPUT
671 *
672 * RETURNS
673 *
674 * AUTHOR
675 *
676 * DESCRIPTION
677 *
678 * CHANGES
679 *
680 ******************************************************************************/
681
Copy_Warps(WarpList & rNew,const WarpList & old)682 void Copy_Warps (WarpList& rNew, const WarpList& old)
683 {
684 rNew.reserve(old.size());
685 for (WarpList::const_iterator i = old.begin(); i != old.end(); i ++)
686 rNew.push_back((*i)->Clone());
687 }
688
689 }
690