#!/usr/bin/perl # # ./flypathmaker foo.config bar.flypath flypathname yo # # flypathmaker creates flight paths for partiview. # Author : Dinoj Surendran (dinoj@cs.uchicago.edu, dinojs@gmail.com) # as part of a project with Mark SubbaRao (Adler/UChicago), # Randy Landsberg (KICP/UChicago). Also thanks to n # Anne Rogers and Gina-Anne Levow and the UChicago CS Department. # Nods to Daniela Rosner, Oya Aran, Niamh O'Riordan, Vikas Sindhwani, # Deanna Barenboim, Rumbidzai Chidavaenzi, Murat Arsel, Credence Mtisi # and Daphne Robinson (and Mum and Dad) for tea and sympathy :) # # This documentation assumes you have already read fpath.html # This file requires tfm.pm (Stuart Levy's tfm.pl renamed) and # Dinoj's pvutils.pm and drsutils.pm use tfm; use pvutils; use drsutils; use POSIX qw(ceil floor); use FileHandle; use constant PI => 4 * atan2(1, 1); if ($#ARGV < 1 || $#ARGV>2 ){ die ("usage: perl flypathmaker.pl configfile flypathfile flightpath\n\n"); } our $configFile = $ARGV[0]; ($configFile,$pre,$ext,$anyOfProvidedExts,$errormessage) = drsutils::makeFileExtExists($configFile,"CONFIG"); die ($errormessage) if (! length($configFile)); our $FPfile = $ARGV[1]; ($FPfile,$pre,$ext,$anyOfProvidedExts,$errormessage) = drsutils::makeFileExtExists($FPfile,"FLYPATH"); die ($errormessage) if (! length($FPfile)); our $whichFP = ($#ARGV >= 2 ? $ARGV[2] : ""); $whichFP = "f_$whichFP" if ($whichFP !~ /^f_.*/); # # Types of files created # - *.wf : can be loaded in Partiview (readpath blah.wf, play) and viewed so you # can see where the camera is going. However, does not include any control over # fading, animation, labels, clipping, visibility, etc. # # - *snap : SNAP files should be made executable (chmod +x) and then asynced from # Partiview in GNU/Linux (OS X too?) to take pictures. Make sure all screensavers # are disabled and that nothing is in front of the Partiview window. SNAP files # can have commands for changing visibility and alpha (fading) of groups, animation, # clipping etc. # # - *jump : Just like SNAP but do not take pictures. Useful for testing fading etc - all those # things WF files can't do # # Files created (assume that BLAH is _) # BLAH.wf # BLAH/path.cf (and BLAH/m_*.speck) shows all the camera points generated # ############# default global parameters (modified by contents of config file ########### our $VERBOSE=1; our @UP = (0,1,0); # default up direction our $FPS = 24; # number of frames per second our $DOMEsize = 2200; # final size of dome master (need to generate 1100 x 1100 pictures for each of the five/six views our $Xsize = 640; # horizontal image size of regular (not dome) and stereo (each view) images our $Ysize = 480; # ditto, vertical our $FOV = 45; # field-of-view. This is overridden by any FOV tags in the flypath file. # It is ignored when generating anything concerning fulldome stuff since # the field of view then is 90. our $HLEN = POSIX::floor($FPS * 1.0); # smoothing parameter, specifies half-window size in frames. our $MAINTAINVERTICAL = 1; # UP is specified in INIT tag (if not it is 0 1 0 by default). # If MAINTAINVERTICAL is 1, the up direction is always assumed to be UP # If MAINTAINVERTICAL is 0, the up direction is always updated based on the previous timestep # You will want the 0 case if the data has no real updirection eg for sdss, # You will want the 1 case if the data is 'grounded' eg cosmic ray showers over earth our $DOSTEREO = 1; # 1 or 0, 1 (default) means files for creating stereo images will be generated our $DODOME = 1; # 1 or 0, 1 (default) means files for creating dome images will be generated our $STEREOSEP = -0.001; # eye separation, must be negative since partiview used cross-eyed stereo # (If a positive value is provided in the config file, it is negated.) our $initFrameOffset = 0; # the first image generated is normally called, say, blah0000.png. # If this is, say, 5, then the first image is blah0005.png our $outname = "snap"; # name of image file to be generated our $ext = "jpg"; # type of image rendered by partiview (choices are png, ppm, jpg, jpeg, tiff) our $numdec = 4; # number of decimal places in image files # with the defaults above, the image files created when running # BLAH_snap : snap0000.jpg snap0001.jpg snap0002.jpg ... # BLAH_stereo_snap : snap_left0000.jpg snap_right0000.jpg snap_left0001.jpg snap_right0001.jpg ... # BLAH_dome_snap : snapB0000.jpg snapL0000.jpg snapR0000.jpg snapU0000.jpg snapF0000.jpg snapB0001.jpg ... # # BLAH_stereo_snap goes to each position and takes two pictures. In case you want to do two runs, one for each eye view # we provide two other files # BLAH_left_snap : snap_left0000.jpg snap_left0001.jpg ... # BLAH_right_snap : snap_right0000.jpg snap_right0001.jpg ... # # As with stereo, you might prefer to do a different run for each dome view # BLAH_domeL_snap : snapL0000.jpg snapL0001.jpg snapL0002.jpg ... left # BLAH_domeR_snap : snapR0000.jpg snapR0001.jpg snapR0002.jpg ... right # BLAH_domeT_snap : snapT0000.jpg snapT0001.jpg snapT0002.jpg ... top # BLAH_domeF_snap : snapF0000.jpg snapF0001.jpg snapF0002.jpg ... front # BLAH_domeB_snap : snapB0000.jpg snapB0001.jpg snapB0002.jpg ... back our $SCALEFACTORPATH = 1; # scaling factor for path, used in makePVpath - not important our $XsizeMAX = 1600; # worry about this later - it's for tiling our $YsizeMAX = 1200; our $Xnumtiles = 1; our $Ynumtiles = 1; readConfig(); our $outfile = sprintf ("%s0%d.%s",$outname,$numdec,$ext); #outputConfig() if ($VERBOSE); ####################### now get dictionaries ready ########################### # flypathmaker allows users to specify relative times and positions # these are converted to absolute times and positions using dictionaries # e.g. if the .flypath file defines then # we say $pointDict{"p_blah"} = \(13 14 1) so that the user can refer to p_blah elsewhere in the .flypath file. our %pointDict = (); readPointTags(); # fills up %pointDict with p_0=(0,0,0) and anything in POINT tags our %groupDict = (); # or --> $groupDict{"g_blah"} = "g7" readGroupTags(); our %flypathDict = (); our %moveDict = (); our %initDict = (); our %effectDict = (); readOtherTagNames(); die ("No INIT tags in file $FPfile\n") if (! keys(%initDict)); die ("No MOVE tags in file $FPfile\n") if (! keys(%moveDict)); die ("No FLYPATH tags in file $FPfile\n") if (! keys(%flypathDict)); # die ("No EFFECT tags in file $FPfile\n") if (! keys(%effectDict)); ########### find our move name and durations, init, effect from correct FLYPATH tag ####################### our $initID = ""; our $effectID = ""; our @moveName = (); # names of moves (moveName[0] = "init", moveName[i]="m_..", ...,moveName[$numMoves]="m_...") our @moveDuration = (); # duration of moves in seconds (moveDuration[0]=0) our $numMoves=0; # moves are indexed from 0 to $numMoves, zeroth move is the initialization our $numFrames=0; # number of frames readFLYPATHtag(); # now initID, effectID, moveName, moveDuration, numMoves, numFrames all defined if (! exists ($initDict{$initID})){ die ("cannot find INIT tag for $m in FLYPATH for $initID"); } foreach $m(@moveName[1..$numMoves]){ if (! exists ($moveDict{$m})){ die ("cannot find MOVE tag for $m in FLYPATH $whichFP"); } } while ($numFrames >= 10**$numdec){ $numdec++; } ####################### our @moveFrame0 = $MP1 x (0); # $moveFrame0[$i] = index of frame at start of $i-th move our @moveFrame1 = $MP1 x (0); # $moveFrame1[$i] = index of frame at end of $i-th move our %timeDict = (); # e.g. $timeDict{m_id1_0} = 10.3, $timeDict{m_id1_100} = 12.5 our %frameDict = (); # like timeDict but returns integer frame fillTimeFrameDicts(); # now moveFrame0/1, %timeDict, %frameDict all defined ################################ our @pveffects = (); # @pveffects is a numFrames-element vector, # $pveffects[$i] = \@a, @a is variable-length vector with commands to be issued for the $i-th frame readEffects(); ################################ # a camera position is defined by an up direction (in @UP if MAINTAINING VERTICAL) and # camera = absolute position # lookat = some point where it is looking (there are infinitely many such points, one will do) # fov = field of view # roll = angle of tilt from the up vector # the user provides various tags in the .flypath file. # each move should, strictly speaking, provide all the above (and other info) but it does not. # In this case we must determine the missing parameters, usually from the previous move # therefore, we need to keep track of what tags the .flypath file actually provides, in these binary arrays: our $MP1 = 1+$numMoves; our @hasMoveTagCamera = (0) x $MP1; # $hasMoveTagCamera[$i] = 1 iff the $i-th move has a valid CAMERA tag # specifying where the camera should be at the end of the move our @hasMoveTagLookat = (0) x $MP1; # $hasMoveTagLookat[$i] = 1 iff the $i-th move has a valid LOOKAT tag our @hasMoveTagRoll = (0) x $MP1; # ditto, ROLL tag our @hasMoveTagFov = (0) x $MP1; # ditto, FOV tag our @hasMoveTagNumFullRot= (0) x $MP1; # ditto NUMFULLROT tag our @hasMoveTagSpeed = (0) x $MP1; # ditto, SPEED tag our @hasMoveTagCenter = (0) x $MP1; # ditto, CENTER tag our @hasMoveTagNormal = (0) x $MP1; # ditto, NORMAL tag, # $hasMoveTagNormal[$i] = 0 if no NORMAL tag supplied # $hasMoveTagNormal[$i] = 1 if NORMAL tag supplied without ONLINE # $hasMoveTagNormal[$i] = 2 if NORMAL tag supplied with ONLINE our @moveType = ("") x $MP1; # $moveType[$i] has a description of the type of the i-th move our @moveCamera = (0) x $MP1; # $moveCamera[$i] = (x,y,z) where x y z is camera position at end of move $i our @moveLookat = (0) x $MP1; # $moveLookat[$i] = (x,y,z) where x y z is lookat position at end of move $i (absolute values) our @moveRoll = (0) x $MP1; # $moveRoll[$i] = scalar angle of roll at end of move i our @moveFov = (0) x $MP1; # $moveFov[$i] = field-of-view in degrees at end of move i our @moveCenter = (0) x $MP1; # $moveCenter[$i] = (x,y,z) where x y z is center of of rotation at start of move i our @moveNormal = (0) x $MP1; # $moveNormal[$i] = (x,y,z) where x y z is other point of axis # (moveNormal[i]-moveCenter[i]) is orthog to circle being traced out our @moveNumFullRot= (0) x $MP1; # $moveRoll[$i] = number of full rotations done during move i (if 0 and things are collinear, converts to 1?) our @moveSpeedType = ("LINEAR") x $MP1; ################################# # in what follows, F:=$numFrames # note that frames are numbered from 0 to F-1 even if the final output images # are numbered from $initFrameOffset to $initFrameOffset+F-1 # the following arrays are F x 3 matrices our @rawCamera = (); # camera position before smoothing our @rawLookat = (); # lookat points before smoothing our @camera = (); # camera position after smoothing our @lookat = (); # lookat points after smoothing our @upvec = (); # up vector (part of orthog basis) our @prop = (); # 1+M-dim vector used to store proportinatly constants for speed ########### now read the INIT and MOVE tags and CAMERA, LOOKAT, FOV, ROLL, AXIS etc tags within each ############ our $CMOVE = -1; # current move number - needed when referring to m_curr, m_prev, m_next, etc readInitMoveTags(); # fills in @moveCamera, @moveRoll, etc, but only for those moves with appropriate tags # fills in @hasMoveTag* to keep track of which MOVEs provided which tags #for ($i=0; $i<=$#moveCamera; $i++){ printf "movecamera[%d] = %s\n", $i, $moveCamera[$i];}print "\n"; if (! $hasMoveTagCamera[0]) {die ("must supply CAMERA position in INIT tag");} for (my $i=1; $i<=$numMoves; $i++){ # if any clip tag is provided for a move, it must be provided for the initial move if (($moveFrame0[$i] == $moveFrame1[$i]) && (! drsutils::arrayEq(3,3,$moveCamera[$i-1],$moveCamera[$i]))) {die (sprintf("move %d (%s) has duration 0 but different start and end points\n",$i,$moveName[$i]));} } findAllAbsoluteCameraPos(); # fill in start and ending positions in %pointDict for each MOVE and INIT, # i.e. p_m_blah, p_M_0, .., p_M_$numMoves # fills missing values in @moveCamera # converst relative positions in @moveCamera to absolute ones fillMissingValuesInMoveArrays(); # fills @moveLookat, @moveFov, @moveRoll our @roll = (); # roll[$i] = value of roll at i-th frame, $i=0..$numFrames-1 our @fov = (); # fov[$i] = value of fov at i-th frame, $i=0..$numFrames-1 (ignored if snapping dome shots) findRollFov(); # @roll and @fov now filled with F values each processCentersAndNormals(); # determines which moves are lines, circles, 2d spirals, or 3d spirals # by this stage we never have to worry about $CMOVE again since all relative values are converted to absolute ones if ($VERBOSE) {printInitAndMoves();} ######### find roll and fov values for each frame ############### ######### find camera and lookat positions, find basic stats, do smoothing, visualize path ####### findRawCameraAndLookat(); our @MEAN=(), @STDV=(), @MAX=(); @MIN=(); getStats(0); doSmooth(); # converts @rawLookat and @rawCamera to @lookat and @camera our @jump = (); # jumps[$i] = string "x y z rx ry rz fov" for i-th frame - suitable for supplying to Partiview's jump command and WF pathfiles. findJumps(1); # 1 means use smoothed versions of camera and lookat ############ find x y z rx ry rz and place in @jump ################### my $outpath = $whichFP; $outpath =~ s/^f_//; open WF, ">$outname"."_$outpath".".wf"; for $f (0 .. $numFrames-1) { printf WF "%s\n", $jump[$f];} close WF; makeJumpSnapRegular ($outname."_$outpath"); makeJumpSnapStereo ($outname."_$outpath"."_stereo"); makeJumpSnapDome ($outname."_$outpath"."_dome"); makePVpath(1,$outname."_$outpath"."_path",$SCALEFACTORPATH); #makePVpath(0,$outpath."_rawpath",$SCALEFACTORPATH); makePartialJumpSnapRegular(10,$outname."_$outpath"); ######################################################################## ####################### now for all the subfunctions ################### ######################################################################## sub arr{ my $t = join(" ",@_); return "[$t"."]"; } # returns string with an array sub sAstr{ return drsutils::searchArraySTR(@_); } sub sAnum{ return drsutils::searchArrayNumeric(@_); } # given (x,@a) returns i>=0 if x is a[i] and -1 else sub sAic{ return drsutils::searchArrayIgnoreCase(@_); } # given (x,@a) returns i>=0 if lowercase(x) is lowecase(a[i]) and -1 else sub pA{ printf "%s", arr(@_); } # prints array with no newline sub pAn{ printf "%s\n", arr(@_); } # prints array with newline sub roundoff { return POSIX::floor( 0.5 + $_[0]); } # rounds off number sub n2s{ return drsutils::num2string(@_); } # convers number to string sub invMoveLookup{ return sAstr($_[0],@moveName); } # input is "m_blah", output is k if $moveName[$k] == "m_blah" sub a2s{ return drsutils::numarray2string(@_); } # prints numeric array sub addPVcommand{ # called by readEffects to add command $cmd to $pveffects[$fr], $fr=0..$numFrames-1 my ($fr,$cmd) = @_; while ($#pveffects <= $fr){ push (@pveffects,""); } $pveffects[$fr] = $pveffects[$fr]."echo $cmd\n"; } sub makePartialJumpSnapRegular{ # creates WF, JUMP and SNAP files (regular, stereo if $DOSTEREO, and dome if $DODOME) my ($EVERY,$ofn) = @_; # output every $EVERY-th frame open SNAP, ">$ofn"."_snap$EVERY"; open JUMP, ">$ofn"."_jump$EVERY"; printf JUMP "echo winsize %d %d\n",$Xsize,$Ysize; printf SNAP "echo winsize %d %d\n",$Xsize,$Ysize; for (my $f=0; $f<$numFrames; $f++){ if (($f % $EVERY) != 0){ my $fnextevery = ceil(1.0 * $f / $EVERY); my $cmd = $pveffects[$f]; if (length($cmd)){ printf JUMP "$cmd"; printf SNAP "$cmd"; } }else{ ############ (($f % $EVERY) == 0){ printf JUMP "echo jump %s\necho update\n", $jump[$f]; printf SNAP "echo jump %s\necho update\n", $jump[$f]; printf SNAP "echo snapshot %s%0$numdec"."d".".%s\n", $outname, $f+$initFrameOffset, $ext; } } close SNAP; close JUMP; } sub makeJumpSnapRegular{ # creates WF, JUMP and SNAP files (regular, stereo if $DOSTEREO, and dome if $DODOME) my ($ofn) = @_; open SNAP, ">$ofn"."_snap"; open JUMP, ">$ofn"."_jump"; printf JUMP "echo winsize %d %d\n",$Xsize,$Ysize; printf SNAP "echo winsize %d %d\n",$Xsize,$Ysize; for (my $f=0; $f<$numFrames; $f++){ my $cmd = $pveffects[$f]; printf JUMP "$cmd" if (length($cmd)); printf JUMP "echo jump %s\necho update\n", $jump[$f]; printf SNAP "$cmd" if (length($cmd)); printf SNAP "echo jump %s\necho update\n", $jump[$f]; printf SNAP "echo snapshot %s%0$numdec"."d".".%s\n", $outname, $f+$initFrameOffset, $ext; } close SNAP; close JUMP; } sub makeJumpSnapStereo{ # if $DOSTEREO is 1 (as is default) this function creates JUMP and SNAP files # suitable for asyncing by Partiview to generate stereo images of the flypath # my ($ofn) = @_; if ($DOSTEREO){ my @views = ("left","right"); for (my $t=0; $t<=$#views; $t++){ my $eye = $views[$t]; open SNAP, ">$ofn".$eye."_snap"; open JUMP, ">$ofn".$eye."_jump"; printf JUMP "echo winsize %d %d\n",$Xsize,$Ysize; printf JUMP "echo focalpoint on\n"; printf JUMP "echo stereo $STEREOSEP $eye\n"; printf SNAP "echo winsize %d %d\n",$Xsize,$Ysize; printf SNAP "echo focalpoint on\n"; printf SNAP "echo stereo $STEREOSEP $eye\n"; for (my $f=0; $f<$numFrames; $f++){ my $cmd = $pveffects[$f]; printf JUMP "$cmd" if (length($cmd)); printf JUMP "echo jump %s\necho update\n", $jump[$f]; printf SNAP "$cmd" if (length($cmd)); printf SNAP "echo jump %s\necho update\n", $jump[$f]; printf SNAP "echo snapshot %s%0$numdec"."d".".%s\n", $outname."_$eye" , $f+$initFrameOffset, $ext; } close SNAP; close JUMP; } open SNAP, ">$ofn"."_snap"; printf SNAP "echo winsize %d %d\n",$Xsize,$Ysize; printf SNAP "echo focalpoint on\n"; for (my $f=0; $f<$numFrames; $f++){ my $cmd = $pveffects[$f]; printf SNAP "$cmd" if (length($cmd)); printf SNAP "echo jump %s\necho update\n", $jump[$f]; for (my $t=0; $t<=$#views; $t++){ my $eye = $views[$t]; printf SNAP "echo stereo $STEREOSEP $eye\necho update\n"; printf SNAP "echo snapshot %s%0$numdec"."d".".%s\n", $outname."_$eye" , $f+$initFrameOffset, $ext; } } close SNAP; } } sub makeJumpSnapDome{ # creates WF, JUMP and SNAP files (regular, stereo if $DOSTEREO, and dome if $DODOME) my ($ofn) = @_; # see subcam documentation in # http://groups.google.com/group/partiview/browse_thread/thread/862423a27d208ffd/ef307f17e81658a9 if ($DODOME){ my @views = ("T","L","F","R","B","D"); # up/top, left, front, right, back(not bottom!), down my @subcams = ("T 0 90 0 45 45 45 45 1.0", "L 90 0 0 45 45 45 45 1.0", "F 0 0 0 45 45 45 45 1.0", "R 270 0 0 45 45 45 45 1.0", "B 180 0 0 45 45 45 45 1.0", "D 180 -90 0 45 45 45 45 1.0" ); for $t(0..$#subcams){ my $eye = $views[$t]; open SNAP, ">$ofn".$eye."_snap"; open JUMP, ">$ofn".$eye."_jump"; printf SNAP "echo winsize %d %d\n",$DOMEsize/2,$DOMEsize/2; printf JUMP "echo winsize %d %d\n",$DOMEsize/2,$DOMEsize/2; foreach $sc(@subcams){ printf SNAP "echo subcam $sc\n"; printf JUMP "echo subcam $sc\n"; } for $f (0.. $numFrames-1) { my $cmd = $pveffects[$f]; my @jmpbits = split /\s+/, $jump[$f]; $jmpbits[$#jmpbits] = "90"; my $jmp = join (" ",@jmpbits); printf JUMP "$cmd" if (length($cmd)); printf JUMP "echo jump %s\necho update\n", $jmp; printf JUMP "echo subcam %s\necho update\n", $views[$t]; printf SNAP "$cmd" if (length($cmd)); printf SNAP "echo jump %s\necho update\n", $jmp; printf SNAP "echo subcam %s\necho update\n", $views[$t]; printf SNAP "echo snapshot %s%0$numdec"."d".".%s\n", $outname.$eye , $f+$initFrameOffset, $ext; } close SNAP; close JUMP; } # create single snap file with all views combined open SNAP, ">$ofn"."_snap"; printf SNAP "echo winsize %d %d\n",$DOMEsize/2,$DOMEsize/2; foreach $sc(@subcams){ printf SNAP "echo subcam $sc\n"; } for $f (0.. $numFrames-1) { my @jmpbits = split /\s+/, $jump[$f]; $jmpbits[$#jmpbits] = "90"; my $jmp = join (" ",@jmpbits); my $cmd = $pveffects[$f]; printf SNAP "$cmd" if (length($cmd)); printf SNAP "echo jump %s\necho update\n", $jmp; for $t(0 .. $#subcams){ my $eye = $views[$t]; printf SNAP "echo subcam %s\necho update\n", $eye; printf SNAP "echo snapshot %s%0$numdec"."d".".%s\n", $outname.$eye , $f+$initFrameOffset, $ext; } } close SNAP; } } sub readEqInArrayOfTag{ # input is ($N,$blah1,...,$blahN,@a) where @a is array with some elements of the form TAG=x or TAG # if some TAG=x equals $blahn for some n=1..N then (x,1,1,@a0) is returned where @a0 is @a with TAG=x removed # if some TAG equals $blahn for some n=1..N then (0,0,1,@a0) is returned where @a0 is @a with TAG removed # Otherwise, (0,0,0,@a) is returned. # In other words the output is # (value of TAG if any, # did TAG come with a value?, # was TAG found with or without a value?, # array with any element TAG or TAG=x removed) my $N=shift(@_); my @tagnames = (); foreach (1..$N) { push (@tagnames, shift(@_)) ; push (@tagnames, lc($tagnames[$#tagnames])); } my $t; my $foundx=0; #did TAG come with or without a value my $foundval=0; #did TAG come with a value? my $val=0; # value of TAG if any my @tmp = (); # array to be returned foreach $t(@_){ my $tIsTag = 0; foreach $x(@tagnames){ if ($t =~ /^$x\s*=\s*(\S+)$/){ $foundx=1; $foundval=1; $val=$1; $tIsTag = 1; }elsif ($t =~ /^$x\s*$/){ $foundx=1; $tIsTag = 1; } } push (@tmp,$t) if ($tIsTag == 0); } return ($val,$foundval,$foundx,@tmp); } sub readEffects{ # determines, based on EFFECTS tag $effectID, the Partiview commands to be delivered for animation and turning groups on and off (with or without fading) # fills up @pveffects so that it is a numFrames-element vector, # $pveffects[$i] has a string with all commands for i-th frame separated by \n # open FLG, $FPfile || die("cant open $FPfile\n"); my $line,flightline; my $foundFlight = 0; my @a = (); my @fparts = (); my $inEffectTag = 0; while ($line = ) { $line = drsutils::stripComment($line,"\#","%"); @a = readTag($line); next if ($#a<0); if ($#a>=1 && ($a[0] eq "EFFECT") && (($a[1] eq $effectID) || ("e_".$a[1] eq $effectID))){ $inEffectTag = 1; }elsif ($a[0] eq "/EFFECT"){ $inEffectTag = 0; }elsif ($inEffectTag){ my $cmd = shift(@a); $anyinterpval = 0; $anyalphaval = 0; $anystepval = 0; $anyspeedval = 0; $anyfromval = 0; $anydurval = 0; $anytoval = 0; $anyendtimeval = 0; ($interp,$anyinterpval,$anyinterp,@a) = readEqInArrayOfTag(3,"INTERP","INTERPOLATION","INTERPOLATE",@a); ($alpha,$anyalphaval,$anyalpha,@a) = readEqInArrayOfTag(1,"ALPHA",@a); ($step,$anystepval,$anystep,@a) = readEqInArrayOfTag(1,"STEP",@a); ($speed,$anyspeedval,$anyspeed,@a) = readEqInArrayOfTag(1,"SPEED",@a); ($from,$anyfromval,$anyfrom,@a) = readEqInArrayOfTag(1,"FROM",@a); ($dur,$anydurval,$anydur,@a) = readEqInArrayOfTag(2,"DUR","DURATION",@a); ($to,$anytoval,$anyto,@a) = readEqInArrayOfTag(1,"TO",@a); ($endtime,$anyendtimeval,$anyendtime,@a) = readEqInArrayOfTag(2,"END","ENDTIME",@a); my @gps = (); my @rest = (); for (my $j=0; $j<=$#a; $j++){ my $x = groupLookup($a[$j]); if (length($x)>0){ push(@gps, $x); }else{ push(@rest,$a[$j]); } } if (@rest > 1){die (sprintf("unable to figure out option %s in line [%s]\n", $rest[1], $line)); } my $fr0 = (@rest>0 ? frameLookup($rest[0]) : -1); if ($cmd =~ /FADE/ && ! $anyalphaval) { die ("FADEON and FADEOFF must contain ALPHA values in line [ $line ]\n") ; } my $interpolationtype = ($anyinterp ? ($anyinterpval ? $interp : 1) : ( $cmd =~ /FADEON/ ? 0.5 : ($cmd =~ /FADEOFF/ ? 2 : 0))); my $duration = ($anydurval ? $dur : ( $cmd =~ /^FADE/ ? 5 : ($cmd =~ /CLIP$/ ? 5.0/$FPS : 0))); $from = ($anyfromval ? $from : ($cmd =~ /FADEON/ ? 0 : $alpha)); $to = ($anytoval ? $to : ($cmd =~ /FADEOFF/ ? 0 : $alpha)); my $fromto=$to-$from; my $fr1 = ($anyendtimeval ? frameLookup($endtime) : ($fr0 + roundoff($FPS*$duration))); if ($cmd eq "ANIM" && $anyendtimeval==0){ $fr1 = $numFrames - 1; } my $fr01 = $fr1-$fr0; my @interps=(); if ($fr0 < $fr1){ @interps = map { 1.0*$_/$fr01 } (0 .. $fr01) ; if ($interpolationtype eq "LOG"){ @interps = map { log(1+$_) / log(2) } @interps; }elsif ($interpolationtype eq "EXP"){ @interps = map { exp(1+$_) / exp(2) } @interps; }elsif ($interpolationtype != 1 && $interpolationtype != 0){ @interps = map { $_**$interpolationtype } @interps; } @interps = map { $from + $_*$fromto } @interps; } my $i=0; if ($cmd eq "ANIM"){ $speed = ($anyspeedval ? $speed : 10); $step = ($anystepval ? $speed : 0); foreach $i ($fr0 .. $fr1) { addPVcommand ($i, sprintf ("step %d",$step+roundoff((($i-$fr0)*$speed)/$FPS))); # CORRECT? } }elsif ($cmd eq "NEARCLIP"){ # foreach $i ($fr0 .. $fr1) { addPVcommand ($i, sprintf("clip %f", $interps[$i-$fr0])); } }elsif ($cmd eq "FARCLIP"){ # foreach $i ($fr0 .. $fr1) { addPVcommand ($i, sprintf("clip - %f", $interps[$i-$fr0])); } }elsif ($cmd eq "ON"){ foreach $x (@gps){ addPVcommand ($fr0, sprintf("%s alpha %f\n",$x,$alpha)) if ($anyalpha); addPVcommand ($fr0, sprintf ("%s on",$x)); } }elsif ($cmd eq "OFF"){ foreach $x (@gps){ addPVcommand ($fr0, sprintf ("%s off",$x)); } }elsif ($cmd eq "FADEON"){ foreach $x (@gps){ for (my $i=$fr0; $i<=$fr1; $i++) {addPVcommand ($i, sprintf("%s alpha %f",$x,$interps[$i-$fr0]));} addPVcommand ($fr0, sprintf ("%s on",$x)); } }elsif ($cmd eq "FADEOFF"){ foreach $x (@gps){ for (my $i=$fr0; $i<=$fr1; $i++) {addPVcommand ($i, sprintf("%s alpha %f",$x,$interps[$i-$fr0]));} addPVcommand ($fr1, sprintf ("%s off",$x)); } }elsif ($cmd eq "LABEL" || $cmd eq "LABELON" || $cmd eq "LABELS" || $cmd eq "LABELSON"){ foreach $x (@gps){ addPVcommand ($fr0, sprintf ("%s labels on",$x)); addPVcommand ($fr1, sprintf ("%s labels off",$x)) if ($fr1 < $numFrames); } }elsif ($cmd eq "LABELOFF" || $cmd eq "LABELSOFF"){ foreach $x (@gps){ addPVcommand ($fr0, sprintf ("%s labels off",$x)); } } } } close FLG; } sub getStats{ # determines min,max,mean,stdv values of x,y,z and radius(=mag([x y z]) using rawCamera my ($wherefrom) = @_; my $sum=(0,0,0,0); my $sum2=(0,0,0,0); if ($wherefrom == 0){ for (my $i=0; $i<$numFrames; $i++){ my $r = tfm::mag(@{$rawCamera[$i]}); $MAX[3] = ($i == 0 ? $r : ($MAX[3] > $r ? $MAX[3] : $r)); $MIN[3] = ($i == 0 ? $r : ($MIN[3] < $r ? $MIN[3] : $r)); $sum[3] += $r; $sum2[3] += ($r**2); for (my $j=0; $j<3; $j++){ my $t = $rawCamera[$i][$j]; $t = ($t > 0 ? $t : -1*$t); $sum[$j] += $t; $sum2[$j] += ($t**2); $MAX[$j] = ($i == 0 ? $t : ($MAX[$j] > $t ? $MAX[$j] : $t)); $MIN[$j] = ($i == 0 ? $t : ($MIN[$j] < $t ? $MIN[$j] : $t)); } } } for (my $j=0; $j<4; $j++){ $MEAN[$j] = $sum[$j] / $numFrames; $STDV[$j] = (($sum2[$j] / $numFrames) - ($mean[$j])**2)**.5; } } sub doSmooth{ @camera = (); @lookat = (); for (my $j=0; $j<3; $j++){ my @tmp = (); for (my $i=0; $i<$numFrames; $i++) { $tmp[$i] = $rawCamera[$i][$j]; } @tmp = smooth1dim(@tmp); $camera[$j] = (); for (my $i=0; $i<$numFrames; $i++) { $camera[$j][$i] = $tmp[$i];} @tmp = (); for (my $i=0; $i<$numFrames; $i++) { $tmp[$i] = $rawLookat[$i][$j]; } @tmp = smooth1dim(@tmp); $lookat[$j] = (); for (my $i=0; $i<$numFrames; $i++) { $lookat[$j][$i] = $tmp[$i];} } } sub smooth1dim{ # smooths L-dimensional vector a # also given B-dimensional vector of boundary indices my @a = @_; my @win = (); $smoothtype = "GAUSSIAN"; $HLEN = 1.0*$HLEN; if ($smoothtype eq "TRIANGLE"){ for (my $i=1; $i<=(2*$HLEN+1); $i++) {$win[$i-1] = ($i>$HLEN+1 ? (2*$HLEN+2-$i) : $i); } my $sum=0; for (my $i=0; $i<=$#win; $i++) {$sum+=$win[$i];} for (my $i=0; $i<=$#win; $i++) {$win[$i] /= $sum; } } if ($smoothtype eq "GAUSSIAN"){ # e.g. HLEN=4 => 4 3 2 1 0 1 2 3 4 for (my $i=0.0; $i<(2*$HLEN+1); $i+=1.0) {$win[$i] = ($i<=$HLEN ? $HLEN-1.0*$i : 1.0*$i-$HLEN); } my $Z = 0.5*$HLEN*$HLEN; for (my $i=0.0; $i<=$#win; $i++) { $win[$i] = exp(-(1.0/$Z)*$win[$i]*$win[$i]); } $sum=0; for (my $i=0; $i<=$#win; $i++) {$sum+=$win[$i];} for (my $i=0; $i<=$#win; $i++) {$win[$i] /= $sum; } } # pA(2*$HLEN+1,@win,1); # $sum=0; for (my $i=0; $i<=$#win; $i++) {$sum+=$win[$i];} # print "sum = $sum\n"; my @x = (); for (my $i=0 ; $i<=$HLEN; $i++) {$x[$i] = $a[$i]; } for (my $i=$#_-$HLEN; $i<=$#_ ; $i++) {$x[$i] = $a[$i]; } for (my $i=$HLEN+1; $i <= ($#_-$HLEN-1) ; $i++ ){ $x[$i] = 0; for (my $j=0; $j<=$#win; $j++){ $x[$i] = $x[$i] + ($win[$j] * $a[$i+$j-$HLEN]); } } return @x; } sub findJumps{ # $jump[$f] = "x y z rx ry rz theta" my ($opt) = @_; # opt = 0 : raw, opt = 1 : smoothed my @upn = @UP; my $f,$i; for $i (1.. $numMoves){ my $fr0 = $moveFrame0[$i]; my $fr1 = $moveFrame1[$i]; for $f ($fr0 .. $fr1){ my @fromvec = @{$rawCamera[$f]}; my @tovec = @{$rawLookat[$f]}; if ($opt){ @fromvec = ( $camera[0][$f], $camera[1][$f], $camera[2][$f] ); @tovec = ( $lookat[0][$f], $lookat[1][$f], $lookat[2][$f] ); } my @fmt = tfm::normalize(tfm::vsub(@fromvec,@tovec)); my @basis = drsutils::gramschmidt(3,@fmt,@upn,tfm::cross(@upn,@fmt)); if (! $MAINTAINVERTICAL) {@upn = @basis[3..5];} @basis = @basis[(6,7,8,3,4,5,0,1,2)]; my @w2c = tfm::m4(@basis); # convert from 3x3 matrix to 4x4 matrix if ($roll[$f]) { @w2c = tfm::tmul(tfm::tfm('z', $roll[$f]), @w2c) ; if (! $MAINTAINVERTICAL) {@upn = @w2c[4..6];} } @w2c[12..14] = @fromvec; my @vd = tfm::tfm2vd(@w2c); # my $jmp = ""; # for (my $a=0; $a<= $#vd; $a++){ # $jmp = sprintf ("%s %s",$jmp,n2s($vd[$a])); # } # $jump[$f] = sprintf ("%s %s",$jmp,n2s($fov[$f])); $jump[$f] = sprintf("%s %s",a2s(-1,@vd),n2s($fov[$f])); # if ($f<5){ printf("...%s %s\n",a2s(-1,@vd),n2s($fov[$f])); } } } } sub makePVpath{ # input : (option, name, scalefactor) # option is 0 if using raw camera path, 1 if using smooth path # name is name of directory created # scale factor (1 by default) is provided in case you want the camera path to be multiplied by it # - this is useful if you can't figure out the psize values to get points to show up. # Creates directory name or name_raw (if option is 0) and clears directory if already existent # Creates files, in directory name/name_raw, path.cf and m_blah1.speck/label and m_blah2.speck/label, etc. my ($fromwhere,$ofn,$scale) = @_; if (-d $ofn){ my @files2go = glob($ofn."/*"); foreach (@files2go) { unlink || die "Cannont delete file $_ !"; } }elsif (-e $ofn) { unlink($ofn) || die ("cannot delete file $ofn"); } if (! (-d $ofn)){ mkdir($ofn, 0755) || die "Cannot mkdir $ofn !"; } open CF,">$ofn"."/path.cf"; for (my $i=1; $i<=$numMoves; $i++){ my $LABELSTEP=10; my $psize = 10*$MAX[3]; my $lum = 10*$MAX[3]; printf CF "object g%d=%s\n include %s.speck\n include %s.label\n eval alpha 1\neval slum 1\neval lum const %f\neval lsize 1\neval laxes off\neval labels on\neval psize %f\neval ptsize 5 100\n", $i, $moveName[$i], $moveName[$i], $moveName[$i], $lum, $psize; points2speck($fromwhere, $ofn."/".$moveName[$i], $moveFrame0[$i] , $moveFrame1[$i], $LABELSTEP,$i,$scale); } printf CF "eval jump %s\n", $jump[($#jump > 50 ? 50 : 0)]; printf CF "eval clip 1 %f\n", 100 * $scale * $MAX[3]; close CF; } ######### sub outputConfig(){ printf ("printing $Xsize x $Ysize files (numbering starts at $initFrameOffset) at $FPS frames per second\n"); } sub readConfig(){ open CFG, $configFile || die("cant open $configFile\n"); my $line; my $i=0; # line number while ($line = ){ $i++; chomp $line; $line =~ s/^([^\#]*)\#.*/$1/; $line =~ s/^([^%]*)%.*/$1/; my @x = split /\s+/, $line; if ($#x >= 1){ if (0 <= sAic($x[0],"OUTNAME")) {$outname = $x[1];} elsif (0 <= sAic($x[0],"EXT","EXTENSION")) {$ext = $x[1];} elsif (0 <= sAic($x[0],"NUMDEC","NUMDP")) {$numdec = $x[1];} elsif (0 <= sAic($x[0],"UP")){ if ($#x != 3) {die ("'up x y z' requires 3 real-numbered arguments (x,y,z) : default x=0,y=1,z=0");} @UP = @x[1..3]; }elsif (0 <= sAic($x[0],"DOME")){ $DODOME = ($x[1]==0 ? 0 : 1); }elsif (0 <= sAic($x[0],"DOMESIZE")){ $DOMEsize = $x[1]; }elsif (0 <= sAic($x[0],"XSIZE")){ $Xsize = $x[1]; }elsif (0 <= sAic($x[0],"YSIZE")){ $Ysize = $x[1]; }elsif (0 <= sAic($x[0],"XSCREENMAX")){ $XsizeMAX = $x[1]; }elsif (0 <= sAic($x[0],"YSCREENMAX")){ $YsizeMAX = $x[1]; }elsif (0 <= sAic($x[0],"FRAMEOFFSET")){ $initFrameOffset = $x[1]; }elsif (0 <= sAic($x[0],"FPS")){ $FPS = $x[1]; }elsif (0 <= sAic($x[0],"STEREO")){ $DOSTEREO = ($x[1]==0 ? 0 : 1); }elsif (0 <= sAic($x[0],"STEREOSEP","EYESEP","STEREOSEPARATION","EYESEPARATION")){ $STEREOSEP = ($x[1] < 0 ? $x[1] : -1*$x[1]) if ($#x>=1); }elsif (0 <= sAic($x[0],"FOV")){ $FOV = $x[1]; }elsif (0 <= sAic($x[0],"MAINTAINVERTICAL", "VERTICAL")){ $MAINTAINVERTICAL = ($x[1]==0 ? 0 : 1); }elsif (0 <= sAic($x[0],"SMOOTH")){ $HLEN = roundoff($FPS * $x[1]); # x[1] has halfsize of smoothing window in seconds }elsif (0 <= sAic($x[0],"PATHSCALE","SCALEPATH")){ $SCALEFACTORPATH = $x[1]; }else{ printf ("Ignoring line $i of $cfgname ($line)\n"); } }elsif ($#x > -1){ printf ("Ignoring line $i of $cfgname ( $line )\n"); } } close CFG; } sub printInitAndMoves{ print "\n"; for (my $j=0; $j<=$numMoves; $j++){ printf "\nMove $j is %s with duration %f seconds \n %s\n",($j==0 ? "init ($initID".")" : $moveName[$j]),$moveDuration[$j], $moveType[$j]; printf " camera : "; pAn(@{$moveCamera[$j]}); printf " lookat : "; pAn(@{$moveLookat[$j]}); printf " fov : %.3f\n", $moveFov[$j]; printf " roll : %d\n", $moveRoll[$j]; if ($hasMoveTagCenter[$j]){ printf " center : "; pAn(@{$moveCenter[$j]}); } if ($hasMoveTagNormal[$j]){ printf " normal : "; pAn(@{$moveNormal[$j]}); } } print "\n"; } sub readFLYPATHtag { my @fpaths = keys (%frameDict); # known to be nonempty else would have died earlier if (length($whichFP) == 0){ # command line did not specify which flypath to use in FPfile - this is only valid if FPfile has exactly 1 file if (@fpaths == 1){ $whichFP = $fpaths[0]; }elsif (@fpaths > 1){ die ("no flypath specified in command line and more than 1 flypath tags available in $FPfile"); } }elsif (! exists($flypathDict{$whichFP})){ die ("could not find flypath $whichFP requested in command line\n"); } # now we can be sure that $flypathDict{$whichFP} exists. # open FLG, $FPfile || die("cant open $FPfile\n"); my $line,flightline; my $foundFlight = 0; my @fparts = (); my @tmp = (); $effectID = ""; $initID = ""; while ($line = ){ @fparts = readTag($line); if (($#fparts >= 2) && ($fparts[0] eq "FLYPATH") && (($fparts[1] eq $whichFP) || ("f_".$fparts[1] eq $whichFP))) { shift(@fparts); shift(@fparts); @tmp = @fparts; break; } } close(FLG); @fparts = @tmp; for (my $i=0; $i<2; $i++){ # searching for at most 2 elements (init,effect) before move-duration pairs if ($fparts[0] =~ /^e_.*/){ $effectID = $fparts[0]; shift(@fparts); }elsif ($fparts[0] =~ /^i_.*/){ $initID = $fparts[0]; shift(@fparts); } } if (length($effectID)){ if (! exists $effectDict{$effectID}){ die ("Cannot find EFFECT tag for effect $effectID mentioned in FLYPATH tag for $whichFP\n"); } }else{ # $effectID == "" ... only valid if there is only one EFFECT tag in flypath file my @tmp = keys (%effectDict); if (@tmp == 1){ $effectID = $tmp[0]; }elsif (@tmp > 1){ die ("No EFFECT tag specified in FLYPATH tag for $whichFP and there are multiple EFFECT tags in file $FPfile\n"); } } if (length($initID)){ if (! exists $initDict{$initID}){ die ("Cannot find INIT tag for initialization state $initID mentioned in FLYPATH tag for $whichFP\n"); } }else{ # $initID == "" ... only valid if there is only one INIT tag in flypath file my @tmp = keys (%initDict); if (@tmp == 1){ $initID = $tmp[0]; }elsif (@tmp > 1){ die ("No INIT tag specified in FLYPATH tag for $whichFP and there are multiple INIT tags in file $FPfile\n"); } } # now we can be sure that $initID = "i_..." and $initDict{$initID} exists die ("no moves in FLYPATH tag\n") if (@fparts == 0); $numMoves = roundoff (0.5 * @fparts); if (! drsutils::isPosInt($numMoves)){ die ("FLYPATH should have alternative MOVE DURATION values - no need to specify duration for INIT") } $moveName[0] = "init"; $moveDuration[0] = 0; for (my $i=1; $i<=$numMoves; $i++){ my $nam = $fparts[2*$i-2]; my $dur = $fparts[2*$i-1]; $dur =~ s/s$//; if ($nam !~ m/^m_/){ die (" failed to parse FLYPATH tag for $whichFP in $FPfile : $nam should be of the form m_* and specify a move\n"); }else{ if (! exists $moveDict{$nam}){ die ("cannot find MOVE tag for move $nam in FLYPATH tag for $whichFP in $FPfile\n"); }else{ if (! drsutils::isNumeric($dur)){ die ("duration $dur specified for move $nam in FLYPATH tag is not a number\n"); }elsif ($dur <= 0){ die ("duration $dur specified for move $nam in FLYPATH tag is not a positive real number\n"); }else{ $moveName[$i] = $nam; $moveDuration[$i] = $dur; if ($dur > 1000){ printf("WARNING : $dur is a very long duration - remember that durations are to be specified in seconds, not number of frames !\n"); } } } } } } sub fillTimeFrameDicts{ my $cumulativeTime = 0; $timeDict{"init"} = 0; $frameDict{"init"} = 0; $timeDict{"M_0_0"} = 0; $frameDict{"M_0_0"} = 0; $timeDict{"M_0_100"} = 0; $frameDict{"M_0_100"} = 0; $timeDict{"M_1_0"} = 0; $frameDict{"M_1_0"} = 0; $moveFrame0[0] = -1; $moveFrame1[0] = -1; $moveFrame0[1] = 0; for (my $i=1; $i<=$numMoves; $i++){ my $a = $moveName[$i]; # $a = m_... my $A = "M_$i"; $t = $cumulativeTime; $fr = roundoff ($t * $FPS ); $timeDict{$a."_0"} = $t; $timeDict{$A."_0"} = $t; $cumulativeTime += $moveDuration[$i]; $t = $cumulativeTime; $fr = POSIX::floor( 0.5 + $t * $FPS ); # rounds $timeDict{$a."_100"} = $t; $timeDict{$A."_100"} = $t; $moveFrame1[$i] = $fr; # ($i == $numMoves ? $fr : $fr -1); $moveFrame0[$i+1] = $fr+1 if ($i < $numMoves); $numFrames = $fr+1; $frameDict{$a."_0"} = $moveFrame0[$i]; $frameDict{$a."_100"} = $moveFrame1[$i]; $frameDict{$A."_0"} = $moveFrame0[$i]; $frameDict{$A."_100"} = $moveFrame1[$i]; } } ################# sub readOtherTagNames{ # reads names of all , , , tags in .flypath file # and places them in %moveDict, %initDict, %effectDict, %flyDict open FLG, $FPfile || die("cant open $FPfile\n"); while ($line = ) { my @a = readTag($line); if ($#a==1){ if ($a[0] eq "MOVE"){ $a[1] =~ s/^m_//; $moveDict{"m_".$a[1]} = 1; }elsif ($a[0] eq "EFFECT"){ $a[1] =~ s/^e_//; $effectDict{"e_".$a[1]} = 1; }elsif ($a[0] eq "INIT"){ $a[1] =~ s/^i_//; $initDict{"i_".$a[1]} = 1; } }elsif ($a[0] eq "FLYPATH"){ die if ($#a==0); $a[1] =~ s/^f_//; $flypathDict{"f_".$a[1]} = 1; } } close FLG; } sub readGroupTags{ open FLG, $FPfile || die("cant open $FPfile\n"); while ($line = ){ my @a = readTag($line); if ($#a >= 0 && $a[0] eq "GROUP"){ die if ($#a!=2); $a[1] =~ s/^g_//; if ($a[2] =~ /gd+/){ $groupDict{"g_".$a[1]} = $a[2]; }elsif ((drsutils::isInt($a[2])) && ($a[2]>0)){ $groupDict{"g_".$a[1]} = "g".$a[2]; }elsif ($a[2]=~/^g\d+/){ $groupDict{"g_".$a[1]} = $a[2]; }else{ print "IGNORING GROUP TAG in line : $line\n"; } } } } sub readPointTags{ $pointDict{"p_0"} = [(0,0,0)]; open FLG, $FPfile || die("cant open $FPfile\n"); while ($line = ){ my @a = readTag($line); if ($#a>=0 && $a[0] eq "POINT"){ die (sprintf("POINT tag in line '%s' has wrong number of arguments\n",$line)) if ($#a != 4); $a[1] =~ s/^p_//; my @xyz = (); for (my $j=0; $j<3; $j++){ if (drsutils::isNumeric($a[2+$j])){ $xyz[$j] = $a[2+$j]; }else{ die (sprintf("%d-th argument (%s) of POINT tag in line <%s> should be numeric\n",$j+2,$a[2+$j],$line)); } } $pointDict{"p_".$a[1]} = [ @xyz ]; } } close FLG; } sub readInitMoveTags { open FLG, $FPfile || die("cant open $FPfile\n"); # find each element of @hasMoveTags $CMOVE = -1; while ($line = ){ my @a = readTag($line); if ($#a>=0){ if (($a[0] eq "/INIT") || ($a[0] eq "/MOVE")){ $CMOVE = -1; }elsif ($#a>=1){ if ($CMOVE == -1){ if (($a[0] eq "INIT") && (($a[1] eq $initID) || ("i_".$a[1] eq $initID))){ $CMOVE = 0; }elsif ($a[0] eq "MOVE"){ $a =~ s/^m_//; # so that writing is equiv to my $mind = invMoveLookup("m_".$a[1]); # print "$a[0] $a[1] --> $mind\n"; if ($mind >= 1){ $CMOVE = $mind; } } }else{ # $CMOVE >= 0 if ($a[0] eq "CAMERA" && isPoint($a[1])){ $moveCamera[$CMOVE] = $a[1]; $hasMoveTagCamera[$CMOVE] = 1; }elsif ($a[0] eq "SPEED" && sAstr($a[0], "LINEAR","EXP")){ $moveSpeedType[$CMOVE] = $a[0]; $hasMoveTagSpeed[$CMOVE] = ($moveSpeedType[$CMOVE] eq "LINEAR" ? 0 : 1); }elsif ($a[0] eq "NEARCLIP" && isNumeric($a[1])){ $moveNearClip[$CMOVE] = $a[1]; $hasMoveTagNearClip[$CMOVE] = 1; }elsif ($a[0] eq "FARCLIP" && isNumeric($a[1])){ $moveFarClip[$CMOVE] = $a[1]; $hasMoveTagFarClip[$CMOVE] = 1; }elsif ($a[0] eq "LOOKAT" && isPoint($a[1])){ $moveLookat[$CMOVE] = $a[1]; $hasMoveTagLookat[$CMOVE] = 1; }elsif ($a[0] eq "ROLL" && drsutils::isNumeric($a[1])){ $hasMoveTagRoll[$CMOVE] = 1; $moveRoll[$CMOVE] = $a[1]; }elsif ($a[0] eq "FOV" && drsutils::isNumeric($a[1])){ $hasMoveTagFov[$CMOVE] = 1; $moveFov[$CMOVE] = $a[1]; }elsif ($a[0] eq "CENTER" && isPoint($a[1])){ $moveCenter[$CMOVE] = $a[1]; $hasMoveTagCenter[$CMOVE] = 1; }elsif ($a[0] eq "NORMAL" && isPoint($a[1])){ $moveNormal[$CMOVE] = $a[1]; $hasMoveTagNormal[$CMOVE] = 1; if ($#a>=2 && sAic($a[2],"ONLINE")){ $hasMoveTagNormal[$CMOVE] = 2; } }elsif (($a[0] eq "NUMFULLROT") && (drsutils::isNonNegInt($a[1]))){ $hasMoveTagNumFullRot[$CMOVE] = 1; $moveNumFullRot[$CMOVE] = $a[1]; } } } } } close FLG; $CMOVE=-1; } ## sub isPoint{ return 1 if ($_[0] =~ /^p_/); return 1 if ($_[0] =~ /add\((\S+,\S+)\)/); return 0; } sub timeLookup{ # if add, minus, negative, etc my $t = convertRelMoveName($_[0]); my $tnot_ = $t; $tnot_ =~ s/^t_//; if ($t =~ m/^-t_/){ $t =~ s/^-//; return -1 * timeLookup($t); }elsif ($t =~ m/^\s*add\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)\s*$/){ $t1 = (drsutils::isNumeric($1) ? $1 : timeLookup($1)); $t2 = (drsutils::isNumeric($2) ? $2 : timeLookup($2)); return ($t1+$t2); }elsif ($t =~ m/^\s*sub\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)\s*$/){ $t1 = (drsutils::isNumeric($1) ? $1 : timeLookup($1)); $t2 = (drsutils::isNumeric($2) ? $2 : timeLookup($2)); return ($t1-$t2); }elsif ($t =~ m/^t_(.*)s$/){ # if ($t =~ m/s$/){ $t =~ s/s$//; return $t; } return $1; }elsif (exists ($timeDict{$tnot_})){ return $timeDict{$tnot_}; }elsif ($t =~ /t_(.*)_([\.\d]+)$/){ # e.g. $t = t_m_blah_14 i.e. $tnot_ = m_blah_14 my $t0 = $1."_0"; my $t1 = $1."_100"; my $frac = $2 * 0.01; if ((exists($timeDict{$t0})) && (exists($timeDict{$t1}))){ $t0 = $timeDict{$t0}; $t1 = $timeDict{$t1}; return ($t0 + ($t1-$t0)*$frac); } }elsif ($t =~ /^t_(.*)$/){ return $1 if (drsutils::isNumeric($1)); } die ("cannot figure out time unit <$t>\n"); } sub groupLookup{ # example usage: # input : "g_blah" ... output is "g3" if $groupDict{"g_blah"} equals "g3" # input : "g_13" ... output is "g13" # input : "g13" ... output is "g13" # input : "gall" ... output is "gall" # input : "g_4u" and $groupDict{"g_4u"} does not exist, output is "" if (exists $groupDict{$_[0]}){ return $groupDict{$_[0]} }elsif ($_[0] =~ /^g_(\d+)$/){ return "g".$1; }elsif ($_[0] =~ /^g\d+$/){ return $_[0]; }elsif ($_[0] =~ /^gall$/){ return $_[0]; }else{ return ""; } } sub frameLookup { # like timeLookup but returns an integer between 0 and numFrames-1 inclusive (or -1 if not found) # examples of inputs # 12 (returns frame number corresponding to 12 seconds from start ie 12*$FPS ) # 12s (ditto, 12 seconds from start) # t_19.5 (19.5 seconds from start) # t_18.5s (18.5 seconds from start) # t_init_12.3s (12.3 seconds from start) # t_init_F (returns 0) # t_m_blah_F, 0<=F<=100 # t_m_blah_13s # t_m_blah_F_-2s # t_m_blah_F_-2 # t_M_i_F 0<=i<=numMoves # t_M_i_13s # t_M_i_F_-2s # t_M_i_F_-2 # # No add functions available # No unary minus (except in t_m_blah_F_-2s) my $t = convertRelMoveName($_[0]); if (($t =~ /^t_m_init$/) || ($t =~ /^t_m_init_/)) {$t =~ s/m_init/init/;} if ($t =~ /^t_init/){ $t =~ s/^t_init//; if ($t =~ /s$/){ $t =~ s/s$//; return roundoff ($t * $FPS); }elsif ($t =~ /\d+$/) {return 0;} }elsif ($t =~ /t_(m_[^_]+)/){ my $m = invMoveLookup($1); my $fr0 = $moveFrame0[$m]; my $fr1 = $moveFrame1[$m]; if ($t =~ /_(\d+)$/){ # t_m_blah_13 means 13% of the way into move m_blah return roundoff($fr0 + ($fr1-$fr0)*($1 * 0.01)); }elsif ($t =~ /_([^s]+)s$/){ # t_m_blah_13s means 13 seconds after start of move m_blah die if (! drsutils::isNumeric($1)); return $fr0 + roundoff($FPS * $1); } }else{ $t =~ s/^t_//; $t =~ s/s$//; if (drsutils::isNumeric($t)){ return roundoff ($t * $FPS ); } } die ("cannot figure out frame unit <$_[0]>\n"); } sub convertRelMoveName{ # input : string s # output : s with any occurence of m_next, m_curr, m_next # corresponding m_blah for some m_blah in @moveName. my ($tmp) = @_; if (($tmp !~ /m_prev/) && ($tmp !~ /m_next/) && ($tmp !~ /m_curr/) && ($tmp !~ /m_succ/)) { return $tmp;} if ($CMOVE>-1) { $mid = $moveName[$CMOVE]; $midprev = ($CMOVE <= 0 ? "init" : $moveName[$CMOVE-1]); $midnext = ($CMOVE > $numMoves ? "" : $moveName[$CMOVE+1]); $tmp =~ s/m_prev/$midprev/g; $tmp =~ s/m_curr/$mid/g; if (($tmp =~ m/m_succ/) || ($tmp =~ m/m_next/)){ if ($i == $#moveName){ die ("reference to $tmp is invalid in last move of flightpath\n"); }else{ $tmp =~ s/m_succ/$midnext/g; $tmp =~ s/m_next/$midnext/g; } } } return $tmp; } sub findAllAbsoluteCameraPos{ # # finds p_m_id_0 and p_m_id_1/100 for all moves m_id in the flightpath # and p_M_0, p_M_1,... p_M_$numMoves # places the above in pointDict # Also determines @moveCamera my @tmp = convertAbsPoint($moveCamera[0]); if ($#tmp==2){ $moveCamera[0] = [ @tmp ]; }elsif (exists($pointDict{$moveCamera[0]})){ $moveCamera[0] = [pointDict{$moveCamera[0]}]; }else{ die (sprintf("CAMERA position %s in INIT tag is not specified in a tag or in the form p_x:y:z\n",$moveCamera[0])); } $pointDict{"p_init"} = $moveCamera[0]; $pointDict{"p_init_0"} = $moveCamera[0]; $pointDict{"p_init_1"} = $moveCamera[0]; $pointDict{"p_init_100"} = $moveCamera[0]; my $midprev=""; my $mid="init"; my $midnext=$moveName[1]; $moveType[0] = "initialization state"; for (my $i=1; $i<=$numMoves; $i++){ $CMOVE = $i; $midprev = $mid; $mid = $midnext; $midnext = ($i == $numMoves ? "" : $moveName[$i+1]); $pointDict{"p_M_$i"."_0"} = $moveCamera[$i-1]; $pointDict{"p_$mid"."_0"} = $moveCamera[$i-1]; $moveCamera[$i] = ( $hasMoveTagCamera[$i] ? [pointLookup( $moveCamera[$i] )] : $moveCamera[$i-1]); if (drsutils::arrayEq(3,3,$moveCamera[$i-1],$moveCamera[$i])){ $moveType[$i] .= "Position unchanged. "; } $pointDict{"p_$mid"."_1"} = $moveCamera[$i]; $pointDict{"p_$mid"."_100"} = $moveCamera[$i]; $pointDict{"p_M_$i"."_1"} = $moveCamera[$i]; $pointDict{"p_M_$i"."_100"} = $moveCamera[$i]; } $CMOVE = -1; } sub fillMissingValuesInMoveArrays{ my @orig = (0,0,0); $moveRoll[0] = 0 if (! $hasMoveTagRoll[0]); $moveFov[0] = $FOV if (! $hasMoveTagFov[0]); $moveLookat[0] = ($hasMoveTagLookat[0] ? [pointLookup( $moveLookat[0])] : \@orig); for (my $i=1; $i<=$numMoves; $i++){ if ($hasMoveTagRoll[$i]){ $moveType[$i] .= sprintf("Rolling %.1f degrees %s. ",$moveRoll[$i],($moveRoll[$i]>0 ? "clockwise" : "anticlockwise")); } $moveRoll[$i] = $moveRoll[$i-1] + ($hasMoveTagRoll[$i] ? $moveRoll[$i] : 0); $moveFov[$i] = $moveFov[$i-1] if (! $hasMoveTagFov[$i]); if ($hasMoveTagFov[$i] && $moveFov[$i] != $moveFov[$i-1]){ $moveType[$i] .= sprintf("Changing field of view from %.1f to %.1f. ",$moveFov[$i-1],$moveFov[$i]); } $moveLookat[$i] = ($hasMoveTagLookat[$i] ? [pointLookup( $moveLookat[$i])] : $moveLookat[$i-1]); if (drsutils::arrayEq(3,3,$moveLookat[$i-1],$moveLookat[$i])){ $moveType[$i] .= "Looking at the same point. "; } } } sub processCentersAndNormals{ my $ND = 3; my @upn = @UP; my @upvec = @UP; for (my $i=1; $i<=$numMoves; $i++){ my $fr0 = $moveFrame0[$i]; my $fr1 = $moveFrame1[$i]; lineInterpolate(1, $moveLookat[$i-1], $moveLookat[$i], 1, $fr0,$fr1); # find raw lookat positions my $STILLCAM = ($moveType[$i] =~ /"Position unchanged"/); # STILLCAM is 1 iff camera stays in same position (its orientation can change) if (! $hasMoveTagCenter[$i]){ $hasMoveTagNormal[$i] = 0; $moveNormal[$i] = 0; $moveCenter[$i] = 0; $moveType[$i] .= "Straight Line Motion. " if ($STILLCAM); my $prop = 1; if ($hasMoveTagSpeed[$i]){ if ($moveSpeedType[$i] eq "EXP"){ $prop = "exp"; }else{ my $D = tfm::mag(tfm::vsub(@{$moveCamera[$i]},@{$moveCamera[$i-1]})); my $T = $fr1-$fr0; my $ep = 1e-6; my $v = ($i==1 ? $ep : tfm::mag(tfm::vsub(@{$rawCamera[$fr0-1]}, @{$rawCamera[$fr0-2]}))); my $v = ($v <= 0 ? $ep : $v); my $u = ($v / $D); # u = fraction of total dist travelled in first step. It equals (p-1)/(p^T-1) $prop = $u**(1.0/(1-$T)); } } lineInterpolate(0, $moveCamera[$i-1], $moveCamera[$i], $prop, $fr0, $fr1); }else{ # $hasMoveTagCenter[$i] is true $moveCenter[$i] = [pointLookup( $moveCenter[$i] )] ; my @center = @{$moveCenter[$i]}; my @p0 = @{$moveCamera[$i-1]}; my @p1 = @{$moveCamera[$i]}; my @p0c = tfm::vsub(@p0,@center); my @p1c = tfm::vsub(@p1,@center); my $r0 = tfm::mag(@p0c); die (sprintf("starting point for move $i (%s) equals center supplied\n",$moveName[$i])) if (! $r0); my $r1 = tfm::mag(@p1c); die (sprintf("ending point for move $i (%s) equals center supplied\n",$moveName[$i])) if (! $r1); @p0c = tfm::normalize(@p0c); @p1c = tfm::normalize(@p1c); my $P01COLLINEAR = ( drsutils::nearZero(drsutils::acos(tfm::dot(@p0c,@p1c))) ? 1 : 0); if (! $hasMoveTagNormal[$i]){ if ($STILLCAM || $P01COLLINEAR){ my @u = tfm::normalize(@upvec); my @e = drsutils::gramschmidt(3,@p0c,@u); printf "p0c(%s) u(%s) e(%s) e1(%s)\n", a2s(4,@p0c), a2s(4,@u), a2s(4,@e[0..2]), a2s(4,@e[3..5]); # $moveNormal[$i] = [ tfm::normalize(tfm::cross(@p0c,@u)) ]; # @UP x @p0c ? $moveNormal[$i] = @e[3..5]; }else{ # p0 and p1 and center are not collinear (so p0 != p1) and no normal is provided $moveNormal[$i] = [ tfm::normalize(tfm::cross(@p0c,@p1c)) ]; } $hasMoveTagNormal[$i] = 1; }else{ $moveNormal[$i] = [pointLookup( $moveNormal[$i] )] ; if ($hasMoveTagNormal[$i] == 2){ $moveNormal[$i] = [tfm::normalize(tfm::vsub (@{$moveNormal[$i]} , @{$moveCenter[$i]}))]; $hasMoveTagNormal[$i] = 1; } } my @normal = @{$moveNormal[$i]}; if (! drsutils::nearZero( tfm::dot(@p0c,@normal))){ die (sprintf("Error in parameters supplied for move $i (%s) : \n normal %s should be orthogonal to the difference %s\n between the initial camera position %s and the center %s\n but the angle between them is %.3f (%.3f degrees). ", $moveName[$i], a2s($ND,@normal), a2s($ND,@p0c), a2s($ND,@p0), a2s($ND,@center), $angle, $angle*180/$PI)); } $SPIRAL = (! drsutils::nearZero( tfm::dot(@p1c,@normal))); # SPIRAL = 0 or 1 if center-of-rotation is constant or changing-along-axis respectively # if moveNumFullRot[$i] is not supplied, it is 0 by default # but for some conditions, if no rotations happen, we stay in the same place or degenerate to a straight-line move # For those conditions, we redefine moveNumFullRot[$i] to be 1. my $DEGENERATE = ($STILLCAM || $P01COLLINEAR); if ($SPIRAL && ! $DEGENERATE){ # find real value d such that @center + d * @normal is closest point on line to p1 my $d = tfm::dot(tfm::vsub(@p0,@center),@normal); # Lengyel, Math for 3d Game Prog and Comp Graphics, page 80/81 my @center1 = tfm::vsadd($d,@normal,@center); $DEGENERATE = (! drsutils::nearZero(tfm::dot(tfm::normalize(tfm::vsub(@p1,@center1)),@p0c))); } $moveNumFullRot[$i] = 1 if ($DEGENERATE && (! $hasMoveNumFullRot[$i] || ! $moveNumFullRot[$i])); $moveType[$i] .= ($SPIRAL ? "3d Spiral. " : ($r0 == $r1 ? "2d Circle. " : "2d Spiral. ")); circleInterpolate(0, $moveCamera[$i-1], $moveCamera[$i], $moveCenter[$i], $moveNormal[$i], $moveNumFullRot[$i], 1.0, $fr0, $fr1); } # now must find 'up' positions for end of current move for $f ($fr0 .. $fr1){ my @fromvec = @{$rawCamera[$f]}; my @tovec = @{$rawLookat[$f]}; my @fmt = tfm::normalize(tfm::vsub(@fromvec,@tovec)); my @basis = drsutils::gramschmidt(3,@fmt,@upn,tfm::cross(@upn,@fmt)); # basis[3,4,5] are a close approximation to true 'up' direction for the next move and is thus stored in @upvec[$f] # If we are maintaining the vertical, we do not update the up direction @upn with them, but keep that at @UP # But if we dont have to maintain the vertical, @upn is refefined to be @upvec[$f] @upvec = @basis[3..5]; @basis = @basis[(6,7,8,3,4,5,0,1,2)]; my @w2c = tfm::m4(@basis); # convert from 3x3 matrix to 4x4 matrix if ($roll[$f]) { @w2c = tfm::tmul(tfm::tfm('z', $roll[$f]), @w2c) ; @upvec = @w2c[4..6]; } @w2c[12..14] = @fromvec; @upn = @upvec if (! $MAINTAINVERTICAL) ; } } } sub convertAbsPoint{ # returns () if not an absolute point and (x y z) otherwise where p_x:y:z or -p_x:y:z is input. my ($t) = @_; my @xyz = (); if ($t =~ m/^-p_.*/){ $t =~ s/^-p/p/; @xyz = convertAbsPoint($t); for (my $i=0; $i<=$#xyz; $i++){ $xyz[$i] = -1*$xyz[$i]; } }elsif ($t =~ m/^p_(\S+):(\S+):(\S+)\s*$/){ if (drsutils::isNumeric($1) && (drsutils::isNumeric($2) && drsutils::isNumeric($3))){ @xyz = ($1,$2,$3); } } return @xyz; } sub pointLookup{ # must have p_ prefix or be ...if add, minus, negative, etc my $t = convertRelMoveName($_[0]); my @xyz = (); if ($t =~ m/^add\((\S+),(\S+)\)$/){ @xyz = pointLookup($1); my @p = pointLookup($2); die ("points in $t are of different dimensions!") if ($#xyz != $#p); for (my $j=0; $j<=$#xyz; $j++){ $xyz[$j] += $p[$j]; } }elsif ($t =~ m/^-p_.*/){ $t =~ s/^-p/p/; @xyz = pointLookup($t); for (my $j=0; $j<=$#xyz; $j++){ $xyz[$j] *= -1; } }elsif (exists $pointDict{$t}){ @xyz = @{$pointDict{$t}}; }else{ @xyz = convertAbsPoint($t); } if ($#xyz < 0){ die ("cannot figure out what kind of point $t is\n"); }else{ return @xyz; } } sub findRawCameraAndLookat{ # find @rawCamera, @rawLookat using boundary conditions given for each move for (my $i=1; $i<=$numMoves; $i++){ my $fr0 = $moveFrame0[$i]; my $fr1 = $moveFrame1[$i]; # $upvec[$fr0-1] is defined except for first move since fr0 # recall that in processCentersAndNormals we had the following line # # $moveNormal[$i] = sprintf("p_%s:%s:%s UP",n2s(@p0c[0]),n2s(@p0c[1]),n2s(@p0c[2])); # @p0c is a unit vector # # since we had no user-provided normal, $STILLCAM || $P01COLLINEAR, and ! MAINTAINVERTICAL. # # Ok, so we will find a vector close to @upvec[$fr0-1] that is orthonormal to @p0-@center # That should result in a horizontal spin my $prop = 1; if ($hasMoveTagSpeed[$i]){ if ($moveSpeedType[$i] eq "EXP"){ $prop = "exp"; }else{ my $D = tfm::mag(tfm::vsub(@{$moveCamera[$i]},@{$moveCamera[$i-1]})); my $T = $fr1-$fr0; my $ep = 1e-6; my $v = ($i==1 ? $ep : tfm::mag(tfm::vsub(@{$rawCamera[$fr0-1]}, @{$rawCamera[$fr0-2]}))); my $v = ($v <= 0 ? $ep : $v); my $u = ($v / $D); # u = fraction of total dist travelled in first step. It equals (p-1)/(p^T-1) $prop = $u**(1.0/(1-$T)); # printf "prop = %0.20f (T=%d , u=%.20f , v=%.20f) \n", $prop, $T, $u, $v; } } lineInterpolate(1, $moveLookat[$i-1], $moveLookat[$i], 1, $fr0,$fr1); # find raw lookat positions # if ($moveType[$i] =~ /spiral/ || $moveType[$i] =~ /circle/){ if ($hasMoveTagNormal[$i]){ circleInterpolate(0, $moveCamera[$i-1], $moveCamera[$i], $moveCenter[$i], $moveNormal[$i], $moveNumFullRot[$i], $prop, $fr0, $fr1); }else{ lineInterpolate(0, $moveCamera[$i-1], $moveCamera[$i], $prop, $fr0, $fr1); } # now find up vector at end of move } } sub findRollFov{ # fills up @fov and @roll for all frames my $m; for $m(1 .. $numMoves){ my $ind0 = $moveFrame0[$m]; my $ind1 = $moveFrame1[$m]; my $tmp = 1.0/($ind1-$ind0); for (my $i=$ind0; $i<=$ind1 ; $i++){ $roll[$i] = $moveRoll[$m-1] + ($moveRoll[$m]-$moveRoll[$m-1]) * ($i-$ind0) * $tmp; $fov[$i] = $moveFov[$m-1] + ($moveFov[$m]-$moveFov[$m-1]) * ($i-$ind0) * $tmp; } } } sub readTag{ # input : " # this is a comment \n" # output : array ("AXIS","p_ere","p_are") my ($line) = @_; chomp $line; $line =~ s/^([^\#]*)\#.*/$1/; $line =~ s/^([^%]*)%.*/$1/; # $line = drsutils::stripComment($line,"\#","%"); if ($line =~ m/<\s*(\S+.*\S+)\s*>/){ @a = split /\s+/, $1; } return @a; } sub points2speck{ # creates a speck file uwing points in camera or rawcamera # used to create speck files of raw paths # outfilenoext does not end with .speck # # my ($fromwhere, $outfilenoext, $indstart, $indend, $labelstep, $g, $scale) = @_; $scale = ($scale ? $scale : 1); my $ofh = new FileHandle "> $outfilenoext".".speck"; # modify ofh options to set number of decimal places if (defined $ofh){ for (my $i=$indstart; $i<=$indend; $i++){ for (my $j=0; $j<3; $j++) {printf $ofh "%0.6f ", $scale * ($fromwhere ? $camera[$j][$i] : $rawCamera[$i][$j]);} printf $ofh " %d %d\n",$g,$i; } undef $ofh; } if ($labelstep > 0){ $ofh = new FileHandle "> $outfilenoext".".label"; # modify ofh options to set number of decimal places if (defined $ofh){ my $i=$indstart; while ($i<=$indend){ for (my $j=0; $j<3; $j++) {printf $ofh "%0.6f ", $scale * ($fromwhere ? $camera[$j][$i] : $rawCamera[$i][$j]);} printf $ofh "text %d\n", $i; $i+=$labelstep; $i = $indend if (($i > $indend) && ($i != ($indend+$labelstep))); } undef $ofh; } } } sub circleInterpolate{ my ($reswhere, $p0ref, $p1ref, $centerref, $normalref, $numfullrot, $prop, $ind0, $ind1) = @_; # finds raw positions for a move involving circular interpolation # Input: At frame number $ind0 we are at position @p0 = @{$p0ref} # At frame number $ind1 we are at position @p1 # the circle has center @center and normal @axis (@axis is @center + @normal where @normal is a directional vector) # $prop determines the kind of radius interpolation # $numfullrot is the number of full rotations in the circle (if @p0 == @p1 then this is at least 1) # # note that the circle may not be of constant radius. If r0 = dist(p0,center) and r1=dist(p1,center) # then the radius of the circle is interpolated depending on prop # # output : all the positions inbetween (and including) ind0 and ind1 - results placed depending on reswhere # reswhere = 0 : place result in rawCamera[ind0:ind1] # reswhere = 1 : place result in rawLookat[ind0:ind1] # reswhere = -1 : return ref to M x 3 array with interpolated values, where M=ind1-ind0+1 # (-1 option not tested yet) # # the plane of the circle has normal axis, where if p is any point on the circle other than p0 # (p-center) . ( (p0-center) x axis ) > 0 (==1?) # my @tbounds = (); if ($prop != 1) { if ($prop eq "exp"){ @tbounds = drsutils::makeExp($ind1-$ind0+1) ; }else{ @tbounds = drsutils::makeLog( $ind1-$ind0, $prop) ; } } # subtract center my @center = @{$centerref}; my @p0 = tfm::vsub( @{$p0ref}, @center); my @p1 = tfm::vsub( @{$p1ref}, @center); my @normal = tfm::normalize(@{$normalref}); my $r0 = tfm::mag(@p0); my $r1 = tfm::mag(@p1); die if (($ind0 == $ind1) && (! drsutils::arrayEq(3,3,\@p0,\@p1))); # if only one point is in this path, start and end points should be same die if ($reswhere != 0 && $reswhere != 1); my $angle = drsutils::acos((tfm::dot(@p0,@p1)/($r0*$r1))); $angle+=(2*PI) if ($angle < 0); # p90 is the position on the circle that is first passed through and # which is orthogonal to both @p0 and the @axis of rotation # # If p0,axis,p90 form a right hand coordinate system i.e. (p0 x axis) . p90 > 0 # p0n, normal, p90n -> unit X Y Z axes respectively # else # p0n, -normal, p90n -> unit X Y Z axes respectively # # To make life convenient we negate @axis if the latter case # # Then matrix ROT converts unit X Y Z axes to p0 axis p90 # i.e. we place the circle in the XZ plane and rotate # (clockwise?) around the positive/negative Y axis my @p0n = tfm::normalize(@p0); my @p1n = tfm::normalize(@p1); my @p90n = tfm::cross(@normal,@p0n); # my @ROT = tfm::transpose((@p0n,@normal,@p90n)); # which one? nontransposed seems more likely my @ROT = (@p0n,@normal,@p90n); my $finaltheta = 2*PI*$numfullrot + $angle; # angle at fraction t = ((t*finaltheta) % 360) my $M = ( $reswhere ? \@rawLookat : \@rawCamera); my $SPIRAL = (! drsutils::nearZero( tfm::dot(@p1n,@normal))); my $AXISLEN = tfm::dot(tfm::vsub(@p0,@center),@normal); # AXISLEN is smallest-magnitude real value d such that @center + d*@normal is closest point on axis # (axis is line passing through @center parallel to @normal) for (my $i=$ind0; $i<=$ind1; $i++){ my $ti = ($i-$ind0)/($ind1-$ind0); my $t = (@tbounds ? $tbounds[$i-$ind0] : $ti); my $ang = $finaltheta * $ti; my $r = $r0 + $t*($r1-$r0); my @p = ($r*(cos($ang)), 0, $r*(sin($ang))); @p = tfm::vsadd(1,@center,tfm::v3mmul(@p, @ROT)); if ($SPIRAL){ @p = tfm::vsadd($ti*$AXISLEN,@normal,@center); } $M->[$i] = [@p]; } return ($M ? 0 : \@Blah); } sub lineInterpolate{ my ($reswhere, $p0ref, $p1ref, $prop, $ind0, $ind1) = @_; # finds raw positions for a move involving linear interpolation # Input: At frame number $ind0 we are at position @p0 # At frame number $ind1 we are at position @p1 # output : all the positions inbetween (and including) ind0 and ind1 - results placed depending on reswhere # reswhere = 0 : place result in rawCamera[ind0:ind1] # reswhere = 1 : place result in rawLookat[ind0:ind1] # reswhere = -1 : return ref to M x 3 array with interpolated values, where M=ind1-ind0+1 # (-1 option not tested yet) # my $M = 0; $M = \@rawCamera if ($reswhere == 0); $M = \@rawLookat if ($reswhere == 1); my @p0 = @{$p0ref}; # my @p1 = @{$p1ref}; my @p0to1 = tfm::vsub(@{$p1ref},@p0); my @Blah = (); if ($ind0 == $ind1){ if ($M){ $M->[$ind0] = [@p0]; }else{ $Blah[0] = [@p0]; } }else{ my @tbounds = (); if ($prop != 1) { if ($prop eq "exp"){ @tbounds = drsutils::makeExp($ind1-$ind0+1) ; }else{ @tbounds = drsutils::makeLog( $ind1-$ind0, $prop) ; } } for (my $i=$ind0; $i<=$ind1; $i++){ my $t = (@tbounds ? $tbounds[$i-$ind0] : ($i-$ind0)/($ind1-$ind0) ); my @p = tfm::vsadd($t, @p0to1, @p0); if ($M){ $M->[$i] = [@p]; }else{ $Blah[($i-$ind0)] = [@p]; } } } return ($M ? 0 : \@Blah); } sub tilesnap{ # tile(xsize,ysize,blah,ext) # to be implemented # returns ($asynccmd,$stitchcmd) # want to create blah.ext # create blah.0m.0n.ppm, m=0:xsize, n=0:ysize # returns two strings # $asynccmd has echo commands to create png files using partiview's async command # $stitchcat has commands for calling pnmcat with to combine the png files into blah.ext later # }