Graphics-Penplotter-GcodeXY

 view release on metacpan or  search on metacpan

lib/Graphics/Penplotter/GcodeXY/Geometry3D.pm  view on Meta::CPAN

# SECTION: SVG projection output
# ==========================================================================

sub project_to_svg ($self, $obj, %opts) {
    my $polylines;
    if (ref $obj eq 'HASH' && $obj->{verts}) {
        $polylines = $self->occlusion_clip($obj, %opts);
    } else {
        $polylines = $obj;
    }
    my ($mnx,$mny,$mxx,$mxy) = (1e99,1e99,-1e99,-1e99);
    for my $seg (@$polylines) {
        for my $p (@$seg) {
            $mnx=$p->[0] if $p->[0]<$mnx; $mxx=$p->[0] if $p->[0]>$mxx;
            $mny=$p->[1] if $p->[1]<$mny; $mxy=$p->[1] if $p->[1]>$mxy;
        }
    }
    $mnx=$mny=0 unless $mnx<$mxx;
    $mxx=$mxy=100 unless $mny<$mxy;
    my $svg = sprintf(
        "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"%.4f %.4f %.4f %.4f\">\n",
        $mnx, $mny, $mxx-$mnx, $mxy-$mny
    );
    for my $seg (@$polylines) {
        my ($x1,$y1) = @{$seg->[0]};
        my ($x2,$y2) = @{$seg->[1]};
        $svg .= sprintf(
            "<line x1=\"%.4f\" y1=\"%.4f\" x2=\"%.4f\" y2=\"%.4f\" "
           ."stroke=\"black\" stroke-width=\"0.5\"/>\n",
            $x1,$y1,$x2,$y2
        );
    }
    $svg .= "</svg>\n";
    return $svg;
}

# ==========================================================================
# SECTION: Mesh I/O -- OBJ
# ==========================================================================

sub mesh_to_obj ($self, $mesh, $name='object') {
    my $out = "# Generated by Graphics::Penplotter::GcodeXY::Geometry3D\n";
    $out .= "o $name\n";
    for my $v (@{ $mesh->{verts} }) {
        $out .= sprintf("v %.6f %.6f %.6f\n", @$v);
    }
    for my $f (@{ $mesh->{faces} }) {
        $out .= sprintf("f %d %d %d\n", $f->[0]+1, $f->[1]+1, $f->[2]+1);
    }
    return $out;
}

sub mesh_from_obj ($self, $str) {
    my (@verts, @faces);
    for my $line (split /\n/, $str) {
        $line =~ s/^\s+//;
        if ($line =~ /^v\s+([\d.eE+\-]+)\s+([\d.eE+\-]+)\s+([\d.eE+\-]+)/) {
            push @verts, [$1+0, $2+0, $3+0];
        }
        elsif ($line =~ /^f\s+(\S+)\s+(\S+)\s+(\S+)/) {
            my @idx = map { (split m{/}, $_)[0] - 1 } ($1,$2,$3);
            push @faces, \@idx;
        }
    }
    return $self->mesh(\@verts, \@faces);
}

# ==========================================================================
# SECTION: Mesh I/O -- STL (ASCII)
# ==========================================================================

sub mesh_to_stl ($self, $mesh, $name='solid') {
    my $out = "solid $name\n";
    my @v   = @{ $mesh->{verts} };
    for my $f (@{ $mesh->{faces} }) {
        my ($a,$b,$c) = @$f;
        my ($ux,$uy,$uz) = map { $v[$b][$_]-$v[$a][$_] } 0..2;
        my ($wx,$wy,$wz) = map { $v[$c][$_]-$v[$a][$_] } 0..2;
        my ($nx,$ny,$nz) = ($uy*$wz-$uz*$wy, $uz*$wx-$ux*$wz, $ux*$wy-$uy*$wx);
        my $l = sqrt($nx*$nx+$ny*$ny+$nz*$nz) || 1;
        $out .= sprintf("  facet normal %.6f %.6f %.6f\n", $nx/$l,$ny/$l,$nz/$l);
        $out .= "    outer loop\n";
        $out .= sprintf("      vertex %.6f %.6f %.6f\n", @{$v[$a]});
        $out .= sprintf("      vertex %.6f %.6f %.6f\n", @{$v[$b]});
        $out .= sprintf("      vertex %.6f %.6f %.6f\n", @{$v[$c]});
        $out .= "    endloop\n  endfacet\n";
    }
    $out .= "endsolid $name\n";
    return $out;
}

sub mesh_from_stl ($self, $str) {
    my (@verts, @faces, %seen, @pending);
    my $vi = sub {
        my ($x,$y,$z) = @_;
        my $key = sprintf("%.9f,%.9f,%.9f", $x,$y,$z);
        unless (exists $seen{$key}) {
            $seen{$key} = scalar @verts;
            push @verts, [$x,$y,$z];
        }
        return $seen{$key};
    };
    for my $line (split /\n/, $str) {
        $line =~ s/^\s+//;
        if ($line =~ /^vertex\s+([\d.eE+\-]+)\s+([\d.eE+\-]+)\s+([\d.eE+\-]+)/) {
            push @pending, $vi->($1+0,$2+0,$3+0);
            if (@pending == 3) { push @faces, [@pending]; @pending = () }
        }
    }
    return $self->mesh(\@verts, \@faces);
}

# ==========================================================================
# SECTION: Numeric configuration
# ==========================================================================

sub set_tolerance ($self, $eps) {
    $eps > 0 or $self->_croak('set_tolerance: eps must be positive');
    $self->{_g3_tolerance} = $eps;
    return 1;
}



( run in 1.604 second using v1.01-cache-2.11-cpan-71847e10f99 )