#!/usr/bin/perl # # vert2.pl -- vertex and strut calculation and reporting for the nose project # #--------------------------------------------------------------------------- # Copyright 2001 by Howard Cohen. All rights reserved worldwide. # You may use this program under the terms of the Gnu Public License, # given that you also keep this copyright notice intact. #--------------------------------------------------------------------------- # # Usage: vert2.pl ... # # Vert2 offers various services for its input files. It expects # vertexes.data to contain the vertex number and the X, Y, and Z # coordinates for each vertex. Columns are separated by whitespace, # newlines separate records. # # It expects struts.data to contain the strut number followed by the # two vertex numbers it connects, and then a 1 if only one strut is # needed (because it is on the centerline). If the last column is # empty or missing then "2" is assumed. Columns are separated by # whitespace, newlines separate records. # # The -dim argument causes vert2 to generate a report of the length # width and height of the nose. The program was instructed as to which # vertexes to consult for the size by observing which ones made sense in # the model. It generates a report like this: # # ---------------------------------------------------- # --- Desert Nose ----------------------------------- # ---------------------------------------------------- # Scale=1.7 # Struts=311 # Shortest (strut 071) = 0' 10" # Longest (strut 118) = 12' 7" # # Nose Width=21' 10" # Nose Length=25' 6" # Nose Height=14' 7" # Door Height=8' 5" # Deck Height=8' 0" # ---------------------------------------------------- # # The -cutsets argument causes vert2 to generate a report for each # length of conduit, listing which struts are to be cut from it, and # how long each strut is. Here is an example: # --------------------------------------------------------- # Conduit number 1 # Right strut 071: 5.500" = 0' 5 and 1/2" # Right strut 003: 7.575" = 0' 7 and 9/16" # Right strut 019: 7.862" = 0' 7 and 27/32" # Unique strut 074: 10.000" = 0' 10" # Right strut 073: 10.296" = 0' 10 and 9/32" # Unique strut 009: 11.000" = 0' 11" # Right strut 006: 12.273" = 1' 0 and 1/4" # Unique strut 011: 12.500" = 1' 0 and 1/2" # Right strut 027: 13.101" = 1' 1 and 3/32" # Right strut 164: 14.355" = 1' 2 and 11/32" # Right strut 084: 15.413" = 1' 3 and 13/32" # Total waste for conduit 1: 0' 0 and 1/8" # # The -labels argyment causes vert2 to generate a report suitable # for spooling to a printer loaded with standard 3 x 10 per sheet # avery labels ( 1" x 2 5/8" labels). It pagenates automatically. # It generates the labels in the order determined by the cutlist, # so that if you cut the conduits in numerical order, the labels # would be removed sequentially from each sheet. # # The -neighbors argument causes vert2 to generate a report of which # verticies are connected to it and via what struts. The report # looks like this (for each vertex): # # ---------------------------------------------------- # Vertex 1 has the following neighbors: # vertex 2 via strut 007 # vertex 43 via strut 008 # vertex 44 via strut 074 # vertex 47 via strut 073 # vertex 9 via strut 077 # # The -info argument provides additional information about how the # cutlists are determined. use GD; # access to graphics functions @fracs=('', '1/32', '1/16', '3/32', '1/8', '5/32', '3/16', '7/32', '1/4', '9/32', '5/16', '11/32', '3/8', '13/32', '7/16', '15/32', '1/2', '17/32', '9/16', '19/32', '5/8', '21/32', '11/16', '23/32', '3/4', '25/32', '13/16', '27/32', '7/8', '29/32', '15/16', '31/32'); $prog='vert2.pl'; $mode='face'; $cutlist=0; $infomode=0; $scale=1.7; $endbonus=2; # inches to add to a strut to leave room for hole at ends $labelwide=24; $pagelines=66; $cut_epsilon=0.03125; # 1/32 of an inch -- this is the margin for error $draw='wireframe'; $face='top'; # possible values: top, side, front $vtags=0; $stags=0; $slice=-1; $totslices=5; $onlystruts=''; $cgimode=0; $imgfile=''; @stronglist=(); $conduitlength=120; $max_usertime=60; $max_systime=$max_usertime; $audit=0; #$debug=1; $debug=0; #$infomode=1; $xbonus=0; $panelfile=''; $panelmargin=4; $kerf=1/16; # the kerf of a cut debug("ARGV=", @ARGV) if $debug; foreach $arg (@ARGV) { if ( $arg =~ /-info/ ) { $infomode=1; } elsif ( $arg =~ /-neighbors?/ ) { $mode='neighbors'; } elsif ( $arg =~ /-strutlengths?/ ) { $mode='strutlengths'; } elsif ( $arg =~ /-strutcoords?/ ) { $mode='strutcoords'; } elsif ( $arg =~ /-panels?=([\w\.]+)/ ) { $panelfile=$1; } elsif ( $arg =~ /-panelmargin=([\d\.]+)/ ) { $panelmargin=$1 if $1 > 0; } elsif ( $arg =~ /-gif=(\w+)/ ) { $mode='gif'; $draw=$1 if $1 ne ''; } elsif ( $arg =~ /-gif/ ) { $mode='gif'; } elsif ( $arg =~ /-slice=(\d+)/ ) { $slice=$1; } elsif ( $arg =~ /-xbonus=([\d\.]+)/ ) { $xbonus=$1; } elsif ( $arg =~ /-audit=(\d+)/ ) { $audit=$1; } elsif ( $arg =~ /-conduitlength=([\.\d]+)/ ) { $conduitlength=$1; } elsif ( $arg =~ /-scale=([\.\d]+)/ ) { $scale=$1; } elsif ( $arg =~ /-toslice=(\d+)/ ) { $toslice=$1; } elsif ( $arg =~ /-filename=([-\w\.\/]+)/ ) { $imgfile=$1; } elsif ( $arg =~ /-slices?=(\d+)/ ) { $totslices=$1; } elsif ( $arg =~ /-maxcpu=(\d+)/ ) { $max_systime=$max_usertime=$1; } elsif ( $arg =~ /-epsilon=([\d\.]+)/ ) { $cut_epsilon=$1; } elsif ( $arg =~ /-endbonus=([\d\.]+)/ ) { $endbonus=$1; } elsif ( $arg =~ /-kerfwidth=([\d\.]+)/ ) { $kerf=$1; } elsif ( $arg =~ /-struts?=([\d,]+)/ ) { $onlystruts=$1; } elsif ( $arg =~ /-strong=([\d,]+)/ ) { @stronglist=split(/\s,/, $1); } elsif ( $arg =~ /-face=(\w+)/ ) { $face=lc $1; } elsif ( $arg =~ /-cutsets/ ) { $mode='cutsets'; # determine which struts to cut together } elsif ( $arg =~ /-labels?/ ) { $mode='labels'; } elsif ( $arg =~ /-dim/ ) { $mode='dim'; } elsif ( $arg =~ /-cgi/ ) { $cgimode=1; } elsif ( $arg =~ /-vtags?/ ) { $vtags=1; } elsif ( $arg =~ /-stags?/ ) { $stags=1; } else { error("unrecognized argument: '$arg'"); } } $toslice=$slice if ($slice > 0 && $toslice < $slice); $havedata=1; $vertcoords='vertexes.data' if $vertcoords eq '' && !$cgimode; # the default vertex data file $strutdata='struts.data' if $strutdata eq '' && !$cgimode; # the default strut data file #print STDERR "loading $vertcoords\n"; if ( $vertcoords eq '' ) { $havedata=0; } elsif ( !open(DF, $vertcoords)) { error("Cannot open '$vertcoords'. $!"); $havedata=0; } #$debug=1; if ( $havedata ) { while() { next if /^#|^\s*$/; ($v, $x, $y, $z) = split; if ( $x > 0 ) { $x+=$xbonus; } elsif ( $x < 0 ) { $x-=$xbonus; } if ( defined($vertex_X{$v}) ) { usrerror("vertex $v defined twice!"); next; } elsif ( $v eq '' ) { usrerror("vertex data file format error (missing vertex number)"); last; } elsif ( $x eq '' ) { usrerror("vertex data file format error (missing X coordinate)"); last; } elsif ( $y eq '' ) { usrerror("vertex data file format error (missing Y coordinate)"); last; } elsif ( $z eq '' ) { usrerror("vertex data file format error (missing Z coordinate)"); last; } $vertex_X{$v}=$x * $scale; $vertex_Y{$v}=$y * $scale; $vertex_Z{$v}=$z * $scale; debug("VERTEXES: v=$v, x=$vertex_X{$v}, y=$vertex_Y{$v}," . " z=$vertex_Z{$v}") if $debug; } close(DF); } #$debug=0; if ( !$havedata ) { ; } elsif ( !open(SD, $strutdata)) { error("Cannot open '$strutdata'. $!\n"); $havedata=0; } if ( $havedata ) { $shortest=0; $longest=0; $minY=9999999; while() { next if /^#|^\s*$/; ($strutnum, $v1, $v2, $need) = split; #debug("STRUTS: strutnum=$strutnum, v1=$v1, v2=$v2") if $strutnum == 5; if ( defined($strutname{$strutnum} ) ) { usrerror("strut $strutnum defined twice!"); next; } elsif ( $strutnum <= 0 || $v1 <= 0 || $v2 <= 0 ) { usrerror("strut data file format error"); last; } $need=2 if $need < 1; $strutnum=sprintf("%3.3d", $strutnum); $strutname=sprintf("%2.2d-to-%2.2d", $v1, $v2); $strutnum{$strutname}=$strutnum; $strutname{$strutnum}=$strutname; $fromv{$strutnum}=$v1; $tov{$strutnum}=$v2; push(@{"neighbors_$v1"}, "$v2,$strutnum"); push(@{"neighbors_$v2"}, "$v1,$strutnum"); #push(@{"panel_adjacent_$v1"}, $strutnum); #push(@{"panel_adjacent_$v2"}, $strutnum); $x1=$vertex_X{$v1}; $y1=$vertex_Y{$v1}; $z1=$vertex_Z{$v1}; $x2=$vertex_X{$v2}; $y2=$vertex_Y{$v2}; $z2=$vertex_Z{$v2}; $strutlen=strutlength($x1, $x2, $y1, $y2, $z1, $z2, $strutnum); $strutlen{$strutnum}=$strutlen; $need{$strutnum}=$need; $totstruts+=$need; if ( $need == 2 ) { push(@needstruts, "$strutnum,Left"); push(@needstruts, "$strutnum,Right"); } elsif ( $need == 1 ) { push(@needstruts, "$strutnum,Unique"); } $maxX=$x1 if $maxX < $x1; $maxX=$x2 if $maxX < $x2; $maxY=$y1 if $maxY < $y1; $maxY=$y2 if $maxY < $y2; $maxZ=$z1 if $maxZ < $z1; $maxZ=$z2 if $maxZ < $z2; $minY=$y1 if $minY > $y1; $minY=$y2 if $minY > $y2; if ( $shortest == 0 || $shortest > $strutlen ) { $shortest=$strutlen; $shortstrut=$strutnum; } if ( $longest < $strutlen ) { $longest=$strutlen; $longstrut=$strutnum; } } close(SD); } if ( !$havedata || $panelfile eq '' ) { ; } elsif ( !open(PD, $panelfile)) { error("Cannot open '$panelfile'. $!\n"); $havedata=0; } if ( $havedata && $panelfile ne '' ) { while() { next if /^#|^\s*$/; ($panelnum, $usv1, $usv2, $usv3, $need) = split; ($v1, $v2, $v3) = sort numerically ( $usv1, $usv2, $usv3 ); #debug("PANELS: ------------------------------------------") if debug; #debug("PANELS: panelnum=$panelnum, v1=$v1, v2=$v2, v3=$v3") if debug; if ( defined($panelname{$panelnum} ) ) { usrerror("panel $panelnum defined twice!"); next; } elsif ( $panelnum <= 0 || $v1 <= 0 || $v2 <= 0 || $v3 <= 0 ) { usrerror("panel data file format error"); last; } $need=2 if $need < 1; $panelnum=sprintf("%3.3d", $panelnum); $panelname=sprintf("%2.2d-to-%2.2d-to-%2.2d", $v1, $v2, $v3); $panelnum{$panelname}=$panelnum; $panelname{$panelnum}=$panelname; $panelv1{$panelnum}=$v1; $panelv2{$panelnum}=$v2; $panelv3{$panelnum}=$v3; $x1=$vertex_X{$v1}; $y1=$vertex_Y{$v1}; $z1=$vertex_Z{$v1}; $x2=$vertex_X{$v2}; $y2=$vertex_Y{$v2}; $z2=$vertex_Z{$v2}; $x3=$vertex_X{$v3}; $y3=$vertex_Y{$v3}; $z3=$vertex_Z{$v3}; $panelstrutlen1{$panelnum}= strutlength($x1, $x2, $y1, $y2, $z1, $z2, "$panelnum:1-2"); $panelstrutlen3{$panelnum}= strutlength($x2, $x3, $y2, $y3, $z2, $z3, "$panelnum:2-3"); $panelstrutlen2{$panelnum}= strutlength($x3, $x1, $y3, $y1, $z3, $z1, "$panelnum:3-1"); # now do a reverse-lookup to determine the strut numbers # They could be "fromN - to - toN" or "toN - to - fromN". my %strutlist=(); #debug("PANELS: neighbors of V1=$v1") if debug; foreach $neighbor (@{"neighbors_$v1"}) { #debug("PANELS: neighbor=$neighbor") if debug; ($vN, $strutnum)=split(/,/, $neighbor); next if $strutlist{$strutnum}; if ( $vN == $v2 ) { $strutlist{$strutnum}=1; #debug("PANELS: matched V2 ($vN)") if debug; } elsif ( $vN == $v3 ) { $strutlist{$strutnum}=1; #debug("PANELS: matched V3 ($vN)") if debug; } } #debug("PANELS: neighbors of V2=$v2") if debug; foreach $neighbor (@{"neighbors_$v2"}) { #debug("PANELS: neighbor=$neighbor") if debug; ($vN, $strutnum)=split(/,/, $neighbor); next if $strutlist{$strutnum}; if ( $vN == $v1 ) { $strutlist{$strutnum}=1; #debug("PANELS: matched V1 ($vN)") if debug; } elsif ( $vN == $v3 ) { $strutlist{$strutnum}=1; #debug("PANELS: matched V3 ($vN)") if debug; } } #debug("PANELS: neighbors of V3=$v3") if debug; foreach $neighbor (@{"neighbors_$v3"}) { ($vN, $strutnum)=split(/,/, $neighbor); #debug("PANELS: neighbor=$neighbor") if debug; next if $strutlist{$strutnum}; if ( $vN == $v1 ) { $strutlist{$strutnum}=1; #debug("PANELS: matched V1 ($vN)") if debug; } elsif ( $vN == $v2 ) { $strutlist{$strutnum}=1; #debug("PANELS: matched V2 ($vN)") if debug; } } my $panelstrutarr="panelstruts_$panelnum"; push(@{"panelstruts_$panelnum"}, sort numerically keys %strutlist); $panelstrutname=sprintf("%2.2d-to-%2.2d-to-%2.2d", $$panelstrutarr[0], $$panelstrutarr[1], $$panelstrutarr[2]); $panelstrutname{$panelnum}=$panelstrutname; #debug("PANELS: panelstrutname=$panelstrutname") if debug; $need{$panelnum}=$need; $totpanels+=$need; if ( $need == 2 ) { push(@needpanels, "$panelnum,Left"); push(@needpanels, "$panelnum,Right"); } elsif ( $need == 1 ) { push(@needpanels, "$panelnum,Unique"); } } close(PD); } #$shortestd=$shortest + $endbonus; #$longestd=$longest + $endbonus; $shortestd=$shortest; $longestd=$longest; &pagetop if $cgimode; debug("mode=$mode") if $debug; debug("havedata=$havedata") if $debug; if ( $mode ne 'gif' && $cgimode && $havedata ) { print("
\n");
}

if ( $mode eq 'strutlengths' && $havedata ) {
    foreach $strutnum (sort keys %strutlen) {
	$strutlen=$strutlen{$strutnum};
	$strutlenstr=sprintf("%.3f", $strutlen);
        print "strut $strutnum: $strutlenstr = " . toenglish($strutlen) . "\n";
    }

} elsif ( $panelfile ne '' && $havedata ) {
    foreach $panelid ( @needpanels ) {
	($panelnum, $label)=split(/,/, $panelid);
	$panelname=$panelname{$panelnum};
	$panelstrutname=$panelstrutname{$panelnum};

	$len1=toenglish($panelstrutlen1{$panelnum});
	$len2=toenglish($panelstrutlen2{$panelnum});
	$len3=toenglish($panelstrutlen3{$panelnum});

	$strutlenstr=sprintf("%.3f", $strutlen);

	if ( $mode eq 'labels' ) {
	    $labelstr="$label panel $panelnum";
	    push(@labels1, centerstr($labelstr, $labelwide));

	    $labelstr="Vertices $panelname";
	    push(@labels2, centerstr($labelstr, $labelwide));

	    $panelstrutname = $panelstrutname{$panelnum};

	    $labelstr="Struts $panelstrutname";
	    push(@labels3, centerstr($labelstr, $labelwide));

	    my $labelnum=1;
	    foreach $strutnum (@{"panelstruts_$panelnum"}) {
		$labelstr="$panelnum: " .
			toenglish($strutlen{$strutnum});

		push(@{"labels$labelnum"}, centerstr($labelstr, $labelwide));
		$labelnum++;
	    }
	}
    }

} elsif ( $mode eq 'strutcoords' && $havedata ) {
    my $num=1;
    foreach $strutnum (sort keys %strutlen) {
	$v1=$fromv{$strutnum};
	$v2=$tov{$strutnum};

	$x1=$vertex_X{$v1};
	$y1=$vertex_Y{$v1};
	$z1=$vertex_Z{$v1};

	$x2=$vertex_X{$v2};
	$y2=$vertex_Y{$v2};
	$z2=$vertex_Z{$v2};
	$strutlen=$strutlen{$strutnum};
	$strutlenstr=sprintf("%.3f", $strutlen);
	print "=====================================================\n";
        print "strut $strutnum: $strutlenstr = " . toenglish($strutlen) . "\n";
        print "     from: $v1 \tx=", toenglish($x1) . "\n";
        print "               \ty=", toenglish($y1) . "\n";
        print "               \tz=", toenglish($z1) . "\n";
        print "               \t---------------------\n";
        print "       to: $v2 \tx=", toenglish($x2) . "\n";
        print "               \ty=", toenglish($y2) . "\n";
        print "               \tz=", toenglish($z2) . "\n";

	if ( $num % 6 == 0 ) {
	    print "\f\n";
	} else {
	    print "\n";
	}

	$num++;
    }


} elsif ( $mode eq 'dim' && $havedata ) {
    print "----------------------------------------------------\n";
    print "---  Desert Nose -----------------------------------\n";
    print "----------------------------------------------------\n";
    print "Scale=$scale\n";
    print "Struts=$totstruts\n";
    print "Endbonus=$endbonus\n";
    print "Kerf=$kerfwidth\n";
    print "X-Bonus=$xbonus\n";

    $dimstr=toenglish($shortestd);
    print "Shortest (strut $shortstrut) = $dimstr\n";

    $dimstr=toenglish($longestd);
    print "Longest (strut $longstrut) = $dimstr\n";

    print "\n";

    $leftx=$vertex_X{'12'};
    $width=2 * $leftx;
    $dimstr=toenglish($width);
    print "Nose Width=$dimstr\n";

    $length=$vertex_Y{'61'} - $vertex_Y{'1'};
    $dimstr=toenglish($length);
    print "Nose Length=$dimstr\n";

    $height=$vertex_Z{'28'};
    $dimstr=toenglish($height);
    print "Nose Height=$dimstr\n";

    $doorhigh=$vertex_Z{'6'};
    $dimstr=toenglish($doorhigh);
    print "Door Height=$dimstr\n";

    $deckhigh=$vertex_Z{'38'};
    $dimstr=toenglish($deckhigh);
    print "Deck Height=$dimstr\n";
    print "----------------------------------------------------\n";

    exit;
} elsif ( $mode eq 'gif' && $havedata ) {
    drawnose($imgfile);
    exit;
} elsif ( $mode eq 'neighbors' && $havedata ) {
    foreach $vertex ( sort numerically keys %vertex_X ) {
	print "----------------------------------------------------\n";
	print "Vertex $vertex has the following neighbors:\n";
	foreach $neigh ( @{"neighbors_$vertex"} ) {
	    ($vertex, $strutnum)=split(/,/, $neigh);
	    print "\tvertex $vertex via strut $strutnum\n";
	}
	print "\n";
    }
}

# The purpose of this mode is to determine which struts should be
# cut from the same piece of conduit, which is 10 feet long, to
# minimize the overall waste.
# 
# Waste is defined as the length of conduit which cannot be used.
# This would have to be less than the length of the shortest strut.

if ( !$havedata ) {
    ;

} elsif ( $mode eq 'cutsets' || ( $mode eq 'labels'  && $panelfile eq '' ) ) {
    # first build a hash called  strutsneeded to track how many struts
    # still need to be made of each size.  While, we're at it, build
    # a sort tag so that the struts can be processed efficiently.

    debug("processing cutsets data") if $debug;
    my @unsorted_struts=();
    my @long_sorted_struts=();		# sorted longest to shortest
    my @short_sorted_struts=();		# sorted shortest to longest
    foreach $strutid ( @needstruts ) {
	($strutnum,$id)=split(/,/, $strutid);
	$strutlen=$strutlen{$strutnum};
	$strutsavail{$strutnum}=$need{$strutnum};

	$sorttag="$strutlen\n$strutid";
	push(@unsorted_struts, $sorttag);
    }
    debug("there are", $#unsorted_struts, "struts") if $debug;

    foreach $comb ( sort numerically @unsorted_struts ) {
	($strutlen, $strutid)=split(/\n/, $comb);
	push(@long_sorted_struts, $strutid);
	unshift(@short_sorted_struts, $strutid);
    }

    # Now, group the struts to minimize waste by allocating a set of
    # largest unused lengths that will still fit within the single 10'
    # length of EMT.

    $totalwaste=0;	# track the total waste

    @unsorted_bywaste=();	# accumulates conduit numbers by waste length
    %hogs=();			# stores $sid by $strutid

    # Each cnum is one length of electrical conduit
    for($cnum=1; !$done; $cnum++) {
        $remain=$conduitlength;
	my @strutnum_list=();

	@strutwaste=();	 # initialize the array that tracks waste by strut
	$skipped=0;
	$analyzed=0;

	($usertime, $systime)=times;
	if ( $usertime > $max_usertime || $systime > $max_systime) {
	    if ( $cgimode ) {
		print ("
\n" . "

Sorry, but this data is" . " taking too much CPU time to crunch. If the number of" . " struts is not too large, try using longer conduits or" . " a smaller scale so that pieces fit easier.

\n" . "

\n"); } else { print "Cumulative CPU time limit exceeded\n"; } $done=1; $havedata=0; last; } if ( $#long_sorted_struts == -1 ) { infomsg("no more struts to cut!"); last; } for ($sid=0; $sid <= $#long_sorted_struts; $sid++) { $strutid=$long_sorted_struts[$sid]; ($strutnum,$label)=split(/,/, $strutid); $wouldremain=$remain - $strutlen{$strutnum} - $kerf; if ( $wouldremain < $cut_epsilon ) { # this strut is too large $skipped++; next; } # can $wouldremain be absorbed by any other piece? my $found=0; $nextskipped=0; for ($nextsid=0; $nextsid <= $#long_sorted_struts; $nextsid++) { $nextstrutid=$long_sorted_struts[$nextsid]; ($nextstrutnum,$nextlabel)=split(/,/, $nextstrutid); # skip the strut we are trying to place; next if $nextstrutid eq $strutid; # skip if not available $nextwouldremain=$wouldremain - $strutlen{$nextstrutnum} -$kerf; if ( $nextwouldremain < $cut_epsilon ) { # this strut is too large $nextskipped++; next; } else { # we found the largest strut which would fit # it is the largest because it is the first found # in a list sorted largest to smallest. $found=1; last; } } if ( $found ) { # We take the first one, because we at least know something # will fit. But we'll let the algoritthm pick a possibly # better choice next time, with the hope that yet one more # piece will fit. push(@strutnum_list, $strutid); splice(@long_sorted_struts, $sid, 1); # remove from list $remain=$remain - $strutlen{$strutnum} - $kerf; infomsg("$cnum." . "Taking strut $strutid as a reasonable choice."); } else { if ( $nextskipped == 0 ) { push(@strutnum_list, $strutid); splice(@long_sorted_struts, $sid, 1); # remove from list $remain=$remain - $strutlen{$strutnum} - $kerf; # none were skipped, so we're out of struts! infomsg("It's done! -- a total of $cnum lengths", "needed"); $done=1; infomsg("$cnum." . "Taking strut $strutid as the only choice."); last; } else { # this is a questionable combination. There may # be a better combination out there. if ( !$analyzed ) { infomsg("analyzing remaining struts for best fit..."); } $waste=$nextwouldremain; push(@strutwaste, "$waste\n$strutid"); $analyzed++; } } } if ( $#strutwaste == -1 && !$done ) { # If we get here it is because if we took the current # strut $strutid, no more struts would fit at all. # so, we'll see allocate it in the next piece and try # for a better fit. # # This can't be a stopping case because we would have # detected that above with the $nextskipped test. # # Skipping it means that it remains in @long_sorted_struts. infomsg("$cnum. skipping strut $strutnum because it's a hog"); } else { foreach $comb (sort numerically @strutwaste) { ($waste, $bestid)=split(/\n/, $comb); local($bestchoice, $bestlabel)=split(/,/, $bestid); push(@strutnum_list, $bestid); $remain=$remain - $strutlen{$bestchoice} - $kerf; # We really don't know where it is anymore. # intermediate ones could have been removed. # So we'll look for it and delete it when we find it. for($i=0; $i <= $#long_sorted_struts; $i++) { if ( $long_sorted_struts[$i] eq $bestid ) { # remove from list splice(@long_sorted_struts, $i, 1); infomsg("$cnum. deleting strut $bestid at index $i"); last; } } infomsg("$cnum." . "Taking strut $bestid as best choice with waste" . " of $waste."); last; } } @{"conduit_$cnum"}=@strutnum_list; push(@unsorted_bywaste, "$remain\n$cnum"); $wastelen{$cnum}=$remain; $totlow=0; foreach $strutid (@strutnum_list) { local($strutnum, $a_label)=split(/,/, $strutid); $v1=$fromv{$strutnum}; $v2=$tov{$strutnum}; $z1=$vertex_Z{$v1}; $z2=$vertex_Z{$v2}; $lowness=$z1; $lowness=$z2 if $lowness > $z2; # take the lowest z vertex $lowness{$strutnum}=$lowness; # left and right have equal lowness $lowness=0; $lowness++ if $z1 == 0 || $z2 == 0; $totlow+=$lowness; } push(@unsorted_bylow, "$totlow\n$cnum"); } $total_lengths=$cnum; } %seenstruts=(); if ( $mode eq 'cutsets' && $havedata ) { #for($cnum=1; $cnum <= $total_lengths; $cnum++) { # } #($waste, $cnum)=split(/\n/, $comb); $total_conduits=0; $total_struts=0; $num_conduits=$#unsorted_bylow + 1; foreach $comb (reverse sort numerically @unsorted_bylow) { ($totlow, $cnum)=split(/\n/, $comb); $total_conduits++; $waste=$wastelen{$cnum}; print "---------------------------------------------------------\n"; print "Conduit number $total_conduits of $num_conduits\n"; foreach $strutid (@{"conduit_$cnum"}) { local($strutnum, $label)=split(/,/, $strutid); $total_struts++; $seenstruts{$strutnum}++; $lowstr=sprintf("%d", int($lowness{$strutnum})); $len=sprintf("%3.3f", $strutlen{$strutnum}); print "\t$label strut $strutnum: $len\" = ", toenglish($strutlen{$strutnum}); $tab=''; if ( !$hasfrac ) { $tab="\t"; } print "$tab\t[lowness=$lowstr]\n"; } print "Total waste for conduit $cnum: ", toenglish(0 + $waste), "\n"; printf("Total lowness rating: %d\n", int($totlow)); print "\n"; $totalwaste+=$waste; if ( $worstwaste < $waste ) { $secondworst=$worstwaste; $worstwaste=$waste; } elsif ( $secondworst < $waste ) { $secondworst=$waste; } } print "=========================================================\n"; print "Total waste across all $total_conduits lengths: ", toenglish($totalwaste), "\n"; # we remove the worst case because it was most likely due to # incomplete use of the last strut. Any way, we won't let the # worst case influence the averages. $total_length=$total_conduits * $conduitlength; $totalwaste-=$worstwaste; $waste_percent=int(100 * $totalwaste / $total_length); $waste_pct_str=sprintf("%.2f", $waste_percent); print "Total waste percentage: $waste_pct_str\%\n"; print "Total number of struts: $total_struts\n"; print "Worst case waste length: ", toenglish($worstwaste), "\n"; print "Second Worst case waste length: ", toenglish($secondworst), "\n"; print "Average waste length: ", toenglish(($totalwaste)/$total_conduits), "\n"; foreach $strutid (@long_sorted_struts) { local($strutnum,$label)=split(/,/, $strutid); print "--> Warning: still need $strutid\n"; } } @labels=(); %seenstruts=(); if ( $mode eq 'labels' && $havedata && $panelfile eq '' ) { foreach $comb (reverse sort numerically @unsorted_bylow) { ($totlow, $cnum)=split(/\n/, $comb); $total_conduits++; $waste=$waste{$cnum}; $waste=$wastelen{$cnum}; foreach $strutid (@{"conduit_$cnum"}) { local($strutnum, $label)=split(/,/, $strutid); $strutname=$strutname{$strutnum}; $dinches=$strutlen{$strutnum}; $dimstr=toenglish($dinches); $labelstr="$label strut $strutnum"; push(@labels1, centerstr($labelstr, $labelwide)); $labelstr="vertices $strutname"; push(@labels2, centerstr($labelstr, $labelwide)); $labelstr="length: $dimstr"; push(@labels3, centerstr($labelstr, $labelwide)); } } } %skiprows=( 0,1, 1,1, 2,2, 8,8, 14,14, 15,15, 21,21, 27,27, 33,33, 34,34, 40,40, 46,46, 52,52, 53,53, 59,59, 65,65, 66,66 ); if ( $mode eq 'labels' && $havedata ) { $line=1; $done=0; $label=0; for($physrow=1; !$done ; ) { if ( $physrow == $pagelines + 1 ) { $physrow = 1; } if ( $skiprows{$physrow} > 0 ) { #print "$physrow ----------------------------------------------" . # "------------------------\n"; print "\n"; $physrow++; } else { for ( $line = 1; $line <=3 ; $line++ ) { for($col=0; $col<=2 && !$done; $col++) { $lblnum=$label + $col; if ( $line == 1 ) { print $labels1[$lblnum]; } elsif ( $line == 2 ) { print $labels2[$lblnum]; } elsif ( $line == 3 ) { print $labels3[$lblnum]; } if ( $col == 2 ) { print "\n"; $physrow++; } else { print " "; } } print "\n"; $physrow++; if ( $labels1[$lblnum - 2] eq '' ) { $done++; next; } } $label+=3; } } } # The nose is drawn in three dimensions by plotting third dimension # in color gradient as well as an offset into the other two dimensions. # The direction of this offset is based on two ratios: an X ratio and a # Y ratio. These ratios are each multiplied by the Z coordinate and # added to the X and Y coordinates respectively to provide an offset in # X and Y which reflects the value of Z. #@colorset=('0000cc', '006666', '009966', '00ff99', '33cc66', '66ff66', # '99cc66', 'cccc99', 'ffff99', 'ffff66', 'ffcc33', 'ff9933', # 'ff6600'); sub drawnose { local($filename)=@_; $size=700; #$scaleup=3; $midline=$size/2; $baseline=$size-int($size/10); # 10% up from the bottom $im = new GD::Image($size,$size); $black = $im->colorAllocate(0, 0, 0); $red=$im->colorAllocate(255,0,0); $white = $im->colorAllocate(255, 255, 255); $ylabelscale=2; $im->filledRectangle(0,0,$size,$size,$white); # a black rectangle if ( 0 ) { $Xratio=0.25; $Yratio=0.50; } else { $Xratio=0; $Yratio=0; } %busyarea=(); $busydivisor=30; $busyxcount=int($size/$busydivisor); $busyycount=int($size/$busydivisor); $pixels_per_block=$size / $busyxcount; if ( $stags ) { $im->string(gdSmallFont,10,10,"Strut numbers are noted",$black); } elsif ( $vtags ) { $im->string(gdSmallFont,10,10,"Vertex numbers are noted",$black); } @colorset=('0000cc', '006666', '009966', '00ff99', '33cc66', '66ff66', '99cc66', 'cccc99', 'ffff99', 'ffff66', 'ffcc33', 'ff9933', 'ff6600'); @colorset=( '0000cc', '006666', '009999', '339966', '339900', '660099', '663366', '993366', '996633', 'cc6666', 'ff6666', 'ff0099', 'ff00ff'); #$maxZ=$vertex_Z{'28'}; # vertex 28 is the highest in the nose if ($#colorset == -1 ) { print STDERR "$prog: no colorset array was defined\n"; print STDERR "$prog: it should contain RRGGBB hex values\n"; } $totcolors=0; foreach $color (@colorset) { if ( $color =~ /^(\w\w)(\w\w)(\w\w)$/ ) { $totcolors++; infomsg("adding color $color"); $decr=0 + hex $1; # these are hex values $decg=0 + hex $2; # these are hex values $decb=0 + hex $3; # these are hex values push(@colorsR, $decr); # force them to be numbers push(@colorsG, $decg); # force them to be numbers push(@colorsB, $decb); # force them to be numbers } else { print STDERR "$prog: colors must be RRGGBB hex values\n"; print STDERR "$prog: '$color' is not in that format\n"; } } # allocate colors as defined in the arrays @colorsR, @colorsG, @colorsB for( $col=0; $col <= $#colorsR; $col++) { $colors[$col]=$im->colorAllocate($colorsR[$col], $colorsG[$col], $colorsB[$col]); } # This factor gets applied to Z values to determine which color to use. if ( $face eq 'top' ) { if ( $maxZ == 0 ) { $z_color_factor = 1; } else { $z_color_factor = ($totcolors) / $maxZ; } $len=1; $maxmax=$maxY; $maxmax=$maxX if $maxmax < $maxX; $scaleup=$size / ($maxmax * 1.2); } elsif ( $face eq 'side' ) { $z_color_factor = ($totcolors) / $maxX; $len=1; $maxmax=$maxZ; $maxmax=$maxY if $maxmax < $maxY; $scaleup=$size / ($maxmax * 1.2); } elsif ( $face eq 'front' ) { $z_color_factor = ($totcolors) / ($maxY - $miny); $len=1; $maxmax=$maxZ; $maxmax=$maxX if $maxmax < $maxX; $scaleup=$size / ($maxmax * 2.2); } debug("scaleup=$scaleup") if $debug; if ( $slice > 0 ) { $toty=$maxY - $minY; $slicewide=$toty/$totslices; $slicemin=$slicewide * ($slice - 1); $slicemax=$slicewide * $toslice; } if ( $draw eq 'wireframe' ) { if ( $onlystruts eq '' ) { @showstruts=reverse sort keys %strutlen; } else { @showstruts=split(/,/, $onlystruts); if ( $#showstruts == -1 ) { print STDERR "$prog: no strut numbers to display\n"; exit; } } #print STDERR "showstruts=" . join(" ", @showstruts) . "\n"; foreach $strutnum ( @showstruts ) { $strutnum=sprintf("%3.3d", 0+$strutnum); #if ( $strutnum == $audit && $audit > 0) { # $infomode=1; #} else { # $infomode=0; # #next; #} $v1=$fromv{$strutnum}; $v2=$tov{$strutnum}; infomsg("strut $strutnum: v1=$v1, v2=$v2"); if ( $slice > 0 ) { if ( $vertex_Y{$v1} + 1 < $slicemin || $vertex_Y{$v1} - 1 > $slicemax ) { # This one is in the wrong slice; infomsg("strut $strutnum: v1=$v1, v2=$v2 -- wrong slice"); next; } } if ( $face eq 'top' ) { $z1=$vertex_Z{$v1}; $x1=$vertex_X{$v1}; $y1=$vertex_Y{$v1}; $z2=$vertex_Z{$v2}; $x2=$vertex_X{$v2}; $y2=$vertex_Y{$v2}; $tagoffx=100; $tagoffy=100; $textoffx=0; $textoffy=0; $themax=$maxY; $maxbusyx=$busyxcount; } elsif ( $face eq 'side' ) { # In the side view we pick a line toward the bottom of the # gif and build the image so that: # Z in the model becomes Y in the drawing # Y in the model becomes X in the drawing # X in the model becomes Z in the drawing $z1=$vertex_X{$v1}; $x1=$vertex_Y{$v1}; $y1=$vertex_Z{$v1}; $z2=$vertex_X{$v2}; $x2=$vertex_Y{$v2}; $y2=$vertex_Z{$v2}; $tagoffx=100; $tagoffy=-100; $textoffx=0; $textoffy=-12; $themax=$maxY; $maxbusyx=$busyxcount; } elsif ( $face eq 'front' ) { # In the front view we pick a line toward the bottom of the # gif and build the image so that: # Z in the model becomes X in the drawing # Y in the model becomes Z in the drawing # X in the model becomes Y in the drawing $x1=$vertex_X{$v1}; $z1=$vertex_Y{$v1}; $y1=$vertex_Z{$v1}; $x2=$vertex_X{$v2}; $z2=$vertex_Y{$v2}; $y2=$vertex_Z{$v2}; $tagoffx=-100; $tagoffy=-100; $textoffx=-12; $textoffy=-12; $themax=$maxZ; $maxbusyx=$busyxcount / 2; } infomsg("point 1: x1=$x1,y1=$y1 -- x2=$x2,y2=$y2"); $x1+=$z1 * $Xratio; $y1+=$z1 * $Yratio; $x2+=$z2 * $Xratio; $y2+=$z2 * $Yratio; $x1*=$scaleup; $y1*=$scaleup; $x2*=$scaleup; $y2*=$scaleup; infomsg("point 2: x1=$x1,y1=$y1 -- x2=$x2,y2=$y2"); $col=$z1; $col=$z2 if $col < $z2; # take the highest value if ( $face eq 'top' ) { $col=int(0.5 + $col * $z_color_factor); } elsif ( $face eq 'side' ) { $col=int(0.5 + $col * $z_color_factor); } elsif ( $face eq 'front' ) { $col=$col - $minY; $acol=$col; $col=int($totcolors - (0.5 + $col * $z_color_factor)); } infomsg("line: x1=$x1,y1=$y1 -- x2=$x2,y2=$y2 in color $col"); #$im->line($x1,$y1,$x2,$y2,$colors[$col]); $col = 0 if $col < 0; $col = $totcolors-1 if $col > $totcolors; $color=$colors[$col]; #$color=$white if $col == 0; #$color=$white; $color=$red if !defined($color); #$color=$red if $strutnum == $audit && $audit > 0; if ( $draw eq 'wireframe' ) { if ( $face eq 'top' ) { $im->line($x1 + $midline,$y1,$x2 + $midline,$y2,$color); $im->line($midline - $x1,$y1,$midline - $x2,$y2,$color) unless $need{$strutnum} == 1; $tagx=$x1 + $midline; $tagy=$y1; $tag2x=$x2 + $midline; $tag2y=$y2; } elsif ( $face eq 'side' ) { $y1=$baseline-$y1; $y2=$baseline-$y2; $tagy=$y1; $tagx=$x1; $tag2y=$y2; $tag2x=$x2; $im->line($x1,$y1,$x2,$y2,$color); } elsif ( $face eq 'front' ) { $im->line($midline - $x1,$baseline-$y1,$midline - $x2, $baseline-$y2,$color); $im->line($x1 + $midline, $baseline-$y1, $x2 + $midline, $baseline-$y2,$color) unless $need{$strutnum} == 1; $tagx=$midline-$x1; $tagy=$baseline-$y1; $tag2x=$midline-$x2; $tag2y=$baseline-$y2; } if ( $stags ) { $tagx=int(($tagx+$tag2x)/2); $tagy=int(($tagy+$tag2y)/2); } if ( $stags || $vtags ) { $totagx=$tagx+($len * $tagoffx); #$totagy=$tagy - ($len * $y1) if $face eq 'front'; #$totagy=$tagy + ($len * $y1 ) if $face eq 'top'; #$totagy=$tagy + $len * $tagoffy if $face eq 'side'; $totagy=$tagy + ($len * $tagoffy); $busyx=int($totagx / $pixels_per_block); $busyy=int($totagy / $pixels_per_block); $busyx=1 if $busyx<1; my $done=0; $initialx=$busyx; my $remap=0; while ( $busyarea{"$busyx,$busyy"} > 0 ) { if ( $busyx + 1 >= $maxbusyx ) { $busyx=$initialx; $busyy++; } else { $busyx++; } } $totagx=$busyx * $pixels_per_block; $totagy=$busyy * $pixels_per_block; } if ( $stags ) { $im->line($tagx,$tagy,$totagx,$totagy,$black); $im->string(gdSmallFont,$totagx+$textoffx,$totagy+$textoffy, $strutnum,$color); $busyarea{"$busyx,$busyy"}=$strutnum; } elsif ( $vtags ) { #$totagx=$tagx+($len * $tagoffx); #$totagy=$tagy - ($len * $y1) if $face eq 'front'; #$totagy=$tagy - ($len * $y1 / 5) if $face eq 'top'; #$totagy=$tagy + $len * $tagoffy if $face eq 'side'; if ( !$seen{$v1} ) { $seen{$v1}=1; $im->line($tagx,$tagy,$totagx,$totagy,$black); $im->string(gdSmallFont,$totagx+$textoffx, $totagy+$textoffy, $v1,$color); $busyarea{"$busyx,$busyy"}=$v1; } } } } } elsif ( $draw eq 'solid' ) { # for each main vertex, consider all its neighbor vertexes %didtriangle=(); $coli=0; foreach $v (keys %vertex_Z) { # for each neighbor vertex we want to find if the neighbor # shares a common neighbor with the main vertex. If so, this # is a trio which can be rendered as a triangle. Once we render # a triangle we need to record the fact that we've rendered that # triangle. We do that by sorting the vertex numbers and creating # a string from the three vertex numbers. Then we store that # string in a hash. foreach $nv ( @{"neighbors_$v"}) { $nv=~s/,.*//; foreach $nnv ( @{"neighbors_$nv"}) { $nnv=~s/,.*//; next if $nnv eq $nv; next if $nnv eq $v; foreach $nnnv ( @{"neighbors_$nnv"}) { $nnnv=~s/,.*//; next if $nnnv eq $nnv; next if $nnnv eq $nv; if ( $nnnv == $v ) { # the neighbor's neighbor's neighbor has the # main vertex as a neighbor, so this is a trio. ($a,$b,$c)=sort numerically($v, $nv, $nnv); $hashstring="$a.$b.$c"; if ( $didtriangle{$hashstring} ) { } elsif ( !$didtriangle{$hashstring} ) { $didtriangle{$hashstring}=1; $col=$vertex_Z[$v]; if ( $face eq 'top' ) { $col=int(0.5 + $col * $z_color_factor); } elsif ( $face eq 'side' ) { $col=int(0.5 + $col * $z_color_factor); } elsif ( $face eq 'front' ) { $col=$col - $minY; $acol=$col; $col=int($totcolors - (0.5 + $col * $z_color_factor)); } $col = 0 if $col < 0; $col = $totcolors-1 if $col > $totcolors; $color=$colors[$col]; $color=$red if !defined($color); if ( $coli >= $totcolors ) { $coli=0; } $poly = new GD::Polygon; $x=$vertex_X{$v} * $scaleup; $y=$vertex_Y{$v} * $scaleup; $z=$vertex_Z{$v} * $scaleup; $poly->addPt($x, $y, $z); $x=$vertex_X{$nv} * $scaleup; $y=$vertex_Y{$nv} * $scaleup; $z=$vertex_Z{$nv} * $scaleup; $poly->addPt($x, $y, $z); $x=$vertex_X{$nnv} * $scaleup; $y=$vertex_Y{$nnv} * $scaleup; $z=$vertex_Z{$nnv} * $scaleup; $poly->addPt($x, $y, $z); $im->filledPolygon($poly, $colors[$coli]); undef($poly); $coli++; } } } } } } } #cropimg($im,$size,$size); if ( $filename ne '' ) { if ( $cgimode) { $openfile="$tmpdir/$filename"; } else { $openfile="$filename"; } if ( !open(IMF, ">$openfile")) { usrerror("could not create image file. $!"); error("could not create image file. $!"); } else { print IMF $im->gif; close(IMF); if ( $cgimode ) { print "

" . "" . "View Image ($filename)

\n"; } else { print "Image saved in $filename"; } } } else { binmode STDOUT; print $im->gif; } } if ( $mode ne 'gif' && $cgimode ) { print("

\n"); } &pagebottom if $cgimode; # cropimg determines how many rows from the top i sub cropimg { local($im, $wide, $high)=@_; } sub centerstr { local($str, $total_len)=@_; $remain=$total_len - length($str); $halfremain=$remain/2; $newstr=(' ' x $halfremain) . $str . (' ' x $halfremain); $newlen=length($newstr); if ( $newlen < $total_len ) { $diff = $total_len - $newlen; if ( $diff > 0 ) { $newstr .= ' ' x $diff ; } } return($newstr); } # The length is determined by the following formula (for a pair of # vertexes): # _____________________________________ # length= _ / # \/ (x2-x1)**2 + (y2-y1)**2 + (z2-z1)**2 # # sub strutlength { local($x1, $x2, $y1, $y2, $z1, $z2, $strutnum) = @_; if ( 0 ) { $dx=$x2 - $x1; $dy=$y2 - $y1; $len1=sqrt($dx**2 + $dy**2); $dz=$z2 - $z1; $len=sqrt($len1**2 + $dz**2); } elsif ( 0 ) { $dx=$x2 - $x1; $dx*=-1 if $dx < 0; $dy=$y2 - $y1; $dy*=-1 if $dy < 0; $dz=$z2 - $z1; $dz*=-1 if $dz < 0; my $sum=$dx**2 + $dy**2 + $dz**2; $len=sqrt($sum); } else { if ( $x1 < $x2 ) { $dx=$x2 - $x1; } elsif ( $x2 < $x1 ) { $dx=$x1 - $x2; } else { $dx=0; } if ( $y1 < $y2 ) { $dy=$y2 - $y1; } elsif ( $y2 < $y1 ) { $dy=$y1 - $y2; } else { $dy=0; } if ( $z1 < $z2 ) { $dz=$z2 - $z1; } elsif ( $z2 < $z1 ) { $dz=$z1 - $z2; } else { $dz=0; } my $sum=$dx**2 + $dy**2 + $dz**2; $len=sqrt($sum); } my $len=$len+$endbonus; return($len); } sub toenglish { local($dinches)=@_; $feet=int($dinches / 12); $finches=$dinches - ($feet * 12); $inches=int($finches); $fracs=$finches - $inches; $fracs = int($fracs * 32); $fracstr=$fracs[$fracs]; if ( $fracstr ne '' ) { $fracstring=" and $fracstr\""; $hasfrac=1; # global, used for formatting } else { $hasfrac=0; # global, used for formatting $fracstring="\""; } return("$feet\' $inches$fracstring"); } sub infomsg { print STDERR join(' ', @_) . "\n" if $infomode; } sub numerically { $zzaa = $a; $zzaa =~ s@/.*@@; $zzbb = $b; $zzbb =~ s@/.*@@; $zzaa <=> $zzbb }; sub debug { print STDERR "Debug: " . join(' ', @_), "\n"; } sub error { print STDERR "Error: " . join(' ', @_), "\n"; } sub usrerror { push(@usrerrors, "Error: " . join(' ', @_)); } 1;