#! /usr/bin/gawk -f # Last edited on 2019-03-15 19:41:38 by stolfilocal BEGIN { usage = ( ARGV[0] " [ -v junk=JUNKFILE ] < INFILE > OUTFILE" ); abort = -1; # Reads a ".pov" or ".inc" file. # Removes all camera and light_source commands, # replacing them by a call to the "camlight" macro. # Writes the deleted text to "JUNKFILE" (default "junk.inc") if (junk == "") { junk = "junk.inc"; } cmtstate = 0; # 0 = plain, 1 = inside "/*..*/" brastate = 0; # 0 = plain, 1 = waiting opeing "{". povstate = 0; # 0 = plain, 1 = inside "camera", 2 = "light source" hadcam = 0; # TRUE if any "camera" command was found. reset_camera_params(); eols = 2; # Number of consecutive "end-of-line" characters just written. # Default values: default_cam_ctr = "<0,0,0>"; default_cam_vec = "<10,10,10>"; default_cam_dst = "20.0"; default_cam_rad = "10.0"; # Output standard header: output_string("// Last edited on DATE TIME by USER\n"); output_string("// Processed by remove-cam-lights\n"); } function reset_camera_params() { # Resets the camera parameters {cam_{ctr,loc,dir,sky,rot}}: cam_ctr = "??"; # camera's "look_at" cam_loc = "??"; # camera's "location" cam_sky = "??"; # camera's "cam_sky" cam_dir = "??"; # camera's "direction" cam_rot = "??"; # camera's "rotation" } (abort >= 0) { exit abort; } // { lin = $0; # Line cleanup: gsub(/[\011]/, " ", lin); gsub(/[\015]/, "", lin); # Process line up to but not including the final "\n". # However we write "\n" to {junk} after each "*/", # at the end of each "//" comment, and at the end # of each camera/light specification. while (lin != "") { if (cmtstate == 1) { # inside "/*...*/" comment: if (match(lin, /[*][\/]/)) { # Split {lin} just after the "*/": bef = substr(lin,1,RSTART+RLENGTH-1); lin = substr(lin,RSTART+RLENGTH); # Part before "*/" is junk: printf "%s\n", bef > junk; cmtstate = 0; } else { printf "%s", lin > junk; lin = ""; } } else if (brastate == 1) { # Expecting a "{" after "camera" or "light_source": if (match(lin, /^[ ]+/)) { # Split blanks off, remain in same state: bef = substr(lin,1,RSTART+RLENGTH-1); lin = substr(lin,RSTART+RLENGTH); output_string(bef); } else if (match(lin, /^([\/][\/]|[\/][*]|[{])/)) { # Get the matched string {key} and split {lin} at it: key = substr(lin,RSTART,RLENGTH); lin = substr(lin,RSTART+RLENGTH); if (key == "{") { printf "%s", key > junk; brastate = 0; } else if (key == "//") { printf "//%s\n", lin > junk; lin = ""; } else if (key == "/*") { printf "/*" > junk; cmtstate = 1; } else { prog_error("bad key"); } } else { data_error("missing \"{\" after camera or light_source"); } } else if (povstate == 1) { # Inside "camera" command if (match(lin, /([\/][\/]|[\/][*]|[{}]|location|sky|look_at|rotate|direction)/)) { # Get the matched string {key} and split {lin} at it: bef = substr(lin,1,RSTART-1); key = substr(lin,RSTART,RLENGTH); lin = substr(lin,RSTART+RLENGTH); # Part before {key} is always junk: printf "%s", bef > junk; if (key == "location") { printf "%s", key > junk; cam_loc = get_camera_param(); } else if (key == "sky") { printf "%s", key > junk; cam_sky = get_camera_param(); } else if (key == "look_at") { printf "%s", key > junk; cam_ctr = get_camera_param(); } else if (key == "rotate") { printf "%s", key > junk; cam_rot = get_camera_param(); } else if (key == "direction") { printf "%s", key > junk; cam_dir = get_camera_param(); } else if (key == "//") { printf "//%s\n", lin > junk; lin = ""; } else if (key == "/*") { printf "/*" > junk; cmtstate = 1; } else if (key == "}") { printf "}\n" > junk; povstate = 0; } else if (key == "{") { data_error("found \"{\" inside camera spec"); } else { prog_error("bad key"); } } else { printf "%s", lin > junk; lin = ""; } } else if (povstate == 2) { # Inside "light_source" directive if (match(lin, /([\/][\/]|[\/][*]|[{}])/)) { # Get the matched string {key} and split {lin} at it: bef = substr(lin,1,RSTART-1); key = substr(lin,RSTART,RLENGTH); lin = substr(lin,RSTART+RLENGTH); # Part before {key} is always junk: printf "%s", bef > junk; if (key == "//") { printf "//%s\n", lin > junk; lin = ""; } else if (key == "/*") { printf "/*" > junk; cmtstate = 1; } else if (key == "}") { printf "}\n" > junk; povstate = 0; } else if (key == "{") { data_error("found \"{\" inside light source spec"); } else { prog_error("bad key"); } } else { printf "%s", lin > junk; lin = ""; } } else { # Normal state ({povstate == 0, cmtstate == 0}) if (match(lin, /^[ ]+/)) { # Grab blanks and write them out: spc = substr(lin,RSTART,RLENGTH); lin = substr(lin,RSTART+RLENGTH); output_string(spc); printf "%s", spc > junk; # Retain current state } else if (match(lin, /^([\/][\/]|[\/][*]|camera|light_source)/)) { # Get the matched string {key} and split {lin} at it: bef = substr(lin,1,RSTART-1); key = substr(lin,RSTART,RLENGTH); lin = substr(lin,RSTART+RLENGTH); # Print part before {key}: output_string(bef); # Decide next state if (key == "camera") { povstate = 1; brastate = 1; if (hadcam) { data_warning("multiple cameras"); } hadcam = 1; reset_camera_params(); printf "%s", key > junk; } else if (key == "light_source") { povstate = 2; brastate = 1; printf "%s", key > junk; } else if (key == "//") { printf "//%s\n", lin > junk; lin = ""; } else if (key == "/*") { cmtstate = 1; printf "/*" > junk; } else { prog_error("bad key"); } } else { output_string(lin); lin = ""; } } } # Process end-of-line: if ((povstate == 0) && (cmtstate == 0)) { output_string("\n"); } else { printf "\n" > junk; } } function get_camera_param( par,tag) { # Extracts from the beginning of {lin} a camera parameter, # up to the next parameter keyword ("up", "right", etc.), comment, # or "}". Returns the parameter as result, writes the parsed # portion of {lin} to {junk}, and leaves the remainder in {lin}. # For now, require parameter to be on the same line as the keyword, # with no intervening comments. if (match(lin, /([\/][\/]|[\/][*]|[{}]|location|cam_sky|look_at|right|up|angle|rotate|direction)/)) { # Get the matched string {tag} and split {lin} at it: par = substr(lin,1,RSTART-1); tag = substr(lin,RSTART,RLENGTH); lin = substr(lin,RSTART); if (tag == "{") { data_error("found \"{\" inside camera spec"); } } else { par = lin; lin = ""; } # The {par} string is junk: printf "%s", par > junk; # Cleanup the {par} string: gsub(/^[ ]+/, "", par); gsub(/[ ]+$/, "", par); gsub(/[ ][ ]+/, " ", par); gsub(/[ ][,]/, ",", par); gsub(/[,][ ]/, ",", par); gsub(/[ ][>]/, ">", par); gsub(/[<][ ]/, "<", par); # Check for missing parameter value: if (par == "") { data_error("missing camera parameter value"); return "??"; } else { return par; } } END { if (abort >= 0) { exit abort; } # Check for proper closing of things: if (cmtstate != 0) { param_error("unterminated \"/*...*/\" comment"); } else if (brastate != 0) { param_error("missing \"{\" after \"camera\" or \"light_source\""); } else if (povstate != 0) { param_error("unterminated camera or light source spec"); } if (hadcam == 0) { param_warning("no POV-Ray 'camera' spec found"); } else { # Cleanup camera parameters, generate standard macro call: # Require {cam_sky} to be Y or Z: if (cam_sky == "??") { param_warning("missing camera \"sky\" parameter"); cam_sky = "y"; } else if (cam_sky !~ /^[yz]$/) { param_warning(("non-standard cam_sky \"" cam_sky "\"")); cam_sky = ( "(" cam_sky ")" ); } # Require {cam_ctr}, protect it: if (cam_ctr == "??") { param_warning("missing camera \"look_at\" parameter"); cam_ctr = default_cam_ctr; } else if (is_zero_vector(cam_ctr)) { cam_ctr = "<0,0,0>"; } else if (! is_plain_vector(cam_ctr)) { cam_ctr = ( "(" cam_ctr ")" ); } # Build the camera vector {cam_vec}: if (cam_loc != "??") { # Protect the camera location {cam_loc}: if (! is_plain_vector(cam_loc)) { cam_loc = ( "(" cam_loc ")" ); } # Build the camera vector as {cam_loc - cam_ctr}: if (is_zero_vector(cam_ctr)) { cam_vec = cam_loc; } else { cam_vec = ( "(" cam_loc "-" cam_ctr ")" ); } } else if (cam_dir != "??") { # Take the camera direction as the camera vector: cam_vec = cam_dir; } else { param_warning("missing camera \"location\" and \"direction\" parameter"); cam_vec = "??"; } # Rotate the camera if so specified: if (cam_rot != "??") { cam_vec = ( "vrotate(" cam_vec "," cam_rot ")" ); } # Print original camera parameters as comments: output_string("\n"); output_string("// Original camera parameters:\n"); output_string(("// #local cam_ctr = " cam_ctr "\n")); output_string(("// #local cam_loc = " cam_loc "\n")); output_string(("// #local cam_vec = " cam_vec "\n")); output_string(("// #local cam_sky = " cam_sky "\n")); # Add standard camera spec: cam_vec = default_cam_vec; cam_dst = estimate_cam_dst(unpov_vector(cam_ctr),unpov_vector(cam_loc)); cam_rad = estimate_cam_rad(cam_dst); output_string("\n"); output_string("#include \"camlight.inc\"\n"); output_string(sprintf("camlight(%s,%s,%s,%s,%s,1.2)\n",cam_ctr,cam_rad,cam_vec,cam_dst,cam_sky)); } close(junk); } function is_plain_vector(vec) { # TRUE iff {vec} is a plain zero POV-Ray vector. vec = cleanup_vector(vec); return (vec ~ /^[<][-+]?[0-9]+([.][0-9]*)?[,][-+]?[0-9]+([.][0-9]*)?[,][-+]?[0-9]+([.][0-9]*)?[>]$/); } function is_zero_vector(vec) { # TRUE iff {vec} is a plain zero POV-Ray vector. return is_plain_vector(vec) && (vec !~ /[1-9]/); } function cleanup_vector(vec) { # Removes some superflouous spaces from a POV-Ray vector. # If the vector is plain, all spaces are removed and # numbers have at least one integer digit. # Remove allowed spaces: gsub(/^[ ]+[<]/,"<",vec); gsub(/[>][ ]+$/,">",vec); gsub(/[<][ ]+/,"<",vec); gsub(/[-][ ]+/,"-",vec); gsub(/[+][ ]+/,"+",vec); gsub(/[,][ ]+/,",",vec); gsub(/[ ]+[>]/,">",vec); gsub(/[ ]+[-]/,"-",vec); gsub(/[ ]+[+]/,"+",vec); gsub(/[ ]+[,]/,",",vec); # Make sure that numbers have a leading digit: gsub(/[-][.]/,"-0.",vec); gsub(/[+][.]/,"+0.",vec); gsub(/[,][.]/,",0.",vec); gsub(/[<][.]/,"<0.",vec); return vec; } function unpov_vector(vec) { # Converts a POV-ray vector from POV-ray format "<{X},{Y},{Z}>" # (plus spaces) to plain "{X} {Y} {Z}" (single infix spaces). # Returns "??" if {vec} is not in that format, or if the coords # are not explicit numbers. # Works only on plain vec = cleanup_vector(vec); if (! is_plain_vector(vec)) { param_warning(("invalid pov vector[" vec "]")); return "??"; } # Replace POV-Ray demimiters by infix spaces: gsub(/[<>, ]+/, " ", vec); gsub(/^[ ]+/, "", vec); gsub(/[ ]+$/, "", vec); # Sanity check: if (vec !~ /^[-+]?[0-9]+([.][0-9]*)?[ ][-+]?[0-9]+([.][0-9]*)?[ ][-+]?[0-9]+([.][0-9]*)?$/) { prog_error(("unpov [" vec "]")); } return vec; } function estimate_cam_dst(cam_ctr,cam_loc, n,ctr,loc,dx,xy,dz,d) { # Tries to estimate the camera distance parameter from the final # camera interest point {cam_ctr} and location {cam_loc}. In case of # failure, returns a canventional positive distance. if (cam_ctr == "??") { d = 0; } else if (cam_loc == "??") { d = 0; } else { n = split(cam_ctr, ctr); if (n != 3) { prog_error(("bad ctr [" cam_ctr "]")); } n = split(cam_loc, loc); if (n != 3) { prog_error(("bad loc [" cam_loc "]")); } dx = ctr[1] - loc[1]; dy = ctr[2] - loc[2]; dz = ctr[3] - loc[3]; d = sqrt(dx*dx + dy*dy + dz*dz); } if (d == 0) { return default_cam_dst; } else { return sprintf("%.3f", d); } } function estimate_cam_rad(cam_dst, r) { # Tries to estimate the scene radius of interest from the final # camera distance parameter [cam_dst}. In case of # failure, returns a canventional positive distance. if (cam_dst == "??") { return "12.0"; } r = 0.55*(cam_dst + 0.0); if (r == 0) { return default_cam_rad; } else { return sprintf("%.3f", r); } } function output_string(str) { # Writes {str} to {stdout}, supressing excess blank lines. # Assumes that {str} contains at most one "\n", at the end. if (str == "") { return; } if (str ~ /^[ ]*[\n]$/) { # Print "\n" only if prev line was not blank: if (eols < 2) { printf "\n"; eols++ } } else { if (str !~ /^[^\n]+[\n]?$/) { prog_error("bad str") } # Remove any trailing blanks: gsub(/[ ]+[\n]/, "\n", str); # Print and update {eols}: printf "%s", str; eols = (substr(str, length(str), 1) == "\n" ? 1 : 0); } } function arg_error(msg) { printf "** %s\n", msg > "/dev/stderr"; abort = 1; exit abort; } function data_error(msg) { printf "%s:%d: «%s»\n", FILENAME, FNR, $0 > "/dev/stderr"; printf "%s:%d: ** %s\n", FILENAME, FNR, msg > "/dev/stderr"; abort = 1; exit abort; } function data_warning(msg) { printf "%s:%d: «%s»\n", FILENAME, FNR, $0 > "/dev/stderr"; printf "%s:%d: warning: %s\n", FILENAME, FNR, msg > "/dev/stderr"; } function param_error(msg) { printf "%s:%d: ** %s\n", FILENAME, FNR, msg > "/dev/stderr"; abort = 1; exit abort; } function param_warning(msg) { printf "%s:%d: warning: %s\n", FILENAME, FNR, msg > "/dev/stderr"; } function prog_error(msg) { printf "%s:%d: ** program error: %s\n", FILENAME, FNR, msg > "/dev/stderr"; abort = 1; exit abort; }