#!/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 File::Temp; use List::Util; our $fa = 10; our @triangles; sub shift_arg () { die unless @ARGV; scalar shift @ARGV; } sub subdivide_triangle ($$) { my ($it, $fn) = @_; my @mids; foreach my $ix (0..2) { my $jx = ($ix+1) % 3; my @midp; foreach my $ci (0..2) { push @midp, 0.5 * ($it->[$ix][$ci] + $it->[$jx][$ci]); } push @mids, @midp; } foreach my $ix (0..2) { my $kx = ($ix+2) % 3; $fn->([ $it->[$ix], $mids[$ix], $it->[$kx] ]); } $fn->(\@mids); } #---------- project-cylinder ---------- our $project_cylinder_radius; our $project_cylinder_max_d_theta; sub project_cylinder_tri { my ($it) = @_; my $radius = project_cylinder_radius; my @thetas = map { $_->[0] / $radius } @$it; foreach my $ix (0..2) { if (abs($thetas[$ix] - $thetas[($ix+1)%3]) > $project_cylinder_max_d_theta) { subdivide_triangle $it, \&project_cylinder_tri; return; } } my @ot; foreach my $p (@it) { my ($x,$y,$z) = @$p; my $r = $radius - $y; my $theta = $x / $radius; push @ot, [ $r * sin($theta), -$r * cos($theta), $z ]; } push @triangles, \@ot; } sub op__project_cylinder () { $project_cylinder_radius = shift_arg; $project_cylinder_max_d_theta = $fa * TAU/360; my @input = (@triangles); @triangles = (); project_cylinder_tri $_ foreach @input; } #---------- main program ---------- if (@ARGV && $ARGV[0] =~ m/^-/) { die "no options supported\n"; } my $itmp = new File::Temp; my $otmp = new File::Temp; system "cat >$itmp"; my $admesh_stdout = '--write-ascii-stl /dev/fd/3 3>&1 >/dev/null'; open I, "admesh $admesh_stdout $itmp |"; my $triangle; while () { if (m/^\s+outer\s+loop/) { die if $triangle; $triangle = []; } elsif (s/^\s+vertex\s+//) { my $lhs = $&; s/\s+$//; my @xyz = split /\s+/, $_; die unless $triangle; push @$triangle, \@xya; } elsif (m/^\s+endloop/) { die unless @$triangle == 3; push @triangles, $triangle; } elsif (m/^\s+(?: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"; print " vertex"; printf " %.18g", $_ foreach @$t; print "\n"; print " endloop\n"; print " endfacet\n"; } print "endsolid distort-stl\n"; flush $otmp; system "admesh --normal-values $admesh_stdout $otmp";