#!/usr/bin/perl -w # # usage: # ./distort-stl OUTPUT DISTORTION [PARAMS...] ... # # DISTORTIONs: # # project-cylinder RADIUS # projects the X-Z plane onto the cylinder of # radius RADIUS with axis [0, 0, t] # origin becomes [0, -RADIUS, 0] # other planes of the input are projected onto smaller # or larger cylinders accordingly # probably a bad idea if # object has any Y > RADIUS # object has any |X| > tau / RADIUS # technically, treats input as if it were # polar-rectangular coords: # Z' = Z; R' = Y + RADIUS; theta' = X / RADIUS # and then converts back into cartesian # honours fa but not fs or fn # # set-fa $FA use strict; use autodie; use List::Util; use POSIX; use File::Temp (); use Data::Dumper; sub TAU () { M_PI * 2; } our $fa = 10; our $triangles; our $output; sub shift_arg () { die unless @ARGV; scalar shift @ARGV; } #no warnings qw(recursion); sub subdivide_triangle ($$) { my ($t, $fn) = @_; #print STDERR 'SUBDIV', Dumper($t, $fn); my @mids; foreach my $ix (0..2) { my $jx = ($ix+1) % 3; my @midp; foreach my $ci (0..2) { push @midp, 0.5 * ($t->[$ix][$ci] + $t->[$jx][$ci]); } push @mids, \@midp; } foreach my $ix (0..2) { #print STDERR 'SUBDIV IX ', $ix, "\n"; my $kx = ($ix+2) % 3; $fn->([ $t->[$ix], $mids[$ix], $mids[$kx] ]); } #print STDERR 'SUBDIV MID\n'; $fn->(\@mids); } sub append_triangle ($) { my ($t) = @_; push @$output, $t; } #---------- project-cylinder ---------- our $project_cylinder_radius; our $project_cylinder_max_d_theta; sub project_cylinder_need_subdivide () { foreach my $t (@$triangles) { my @thetas = map { $_->[0] / $project_cylinder_radius } @$t; foreach my $ix (0..2) { if (abs($thetas[$ix] - $thetas[($ix+1)%3]) > $project_cylinder_max_d_theta) { return 1; } } } return 0; } sub project_cylinder_tri { my ($t) = @_; #print STDERR 'PROJECT', Dumper($t); my $radius = $project_cylinder_radius; my @ot; foreach my $p (@$t) { my ($x,$y,$z) = @$p; my $r = $radius - $y; my $theta = $x / $radius; push @ot, [ $r * sin($theta), -$r * cos($theta), $z ]; } append_triangle \@ot; } sub op__project_cylinder () { $project_cylinder_radius = shift_arg; $project_cylinder_max_d_theta = $fa * TAU/360; while (project_cylinder_need_subdivide()) { $output = []; foreach my $t (@$triangles) { subdivide_triangle $t, \&append_triangle; } $triangles = $output; } $output = []; foreach my $t (@$triangles) { project_cylinder_tri $t; } $triangles = $output; } #---------- main program ---------- our $raw; while (@ARGV && $ARGV[0] =~ m/^-/) { $_ = shift @ARGV; last if m/^--$/; if (s/^--raw$//) { $raw = 1; } else { die "$_ ?"; } } my $itmp; my $otmp; my $admesh_stdout = '--write-ascii-stl /dev/fd/3 3>&1 >/dev/null'; if ($raw) { open I, "<& STDIN"; $otmp = *STDOUT; } else { $itmp = new File::Temp; $otmp = new File::Temp; system "cat >$itmp"; open I, "admesh $admesh_stdout $itmp |"; } my $triangle; while () { s/^\s*//; if (m/^outer\s+loop/) { die if $triangle; $triangle = []; } elsif (s/^vertex\s+//) { my $lhs = $&; s/\s+$//; my @xyz = split /\s+/, $_; die unless $triangle; push @$triangle, \@xyz; } elsif (m/^endloop/) { die unless @$triangle == 3; push @$triangles, $triangle; undef $triangle; } elsif (m/^(?:solid|facet\s+normal|endfacet|endsolid)\s/) { } else { die "$_ ?"; } } close I; if 0; # suppresses Name "main::I" used only once while (@ARGV) { my $op = shift_arg; $op =~ y/-/_/; &{ ${*::}{"op__$op"} }; } select $otmp; print "solid distort-stl\n"; foreach my $t (@$triangles) { print " facet normal 0 0 0\n"; print " outer loop\n"; die unless @$t==3; foreach my $p (@$t) { die unless @$p==3; print " vertex"; printf " %.18g", $_ foreach @$p; print "\n"; } print " endloop\n"; print " endfacet\n"; } print "endsolid distort-stl\n"; flush $otmp; if (!$raw) { system "admesh --normal-values $admesh_stdout $otmp"; }