Death Star

From Rosetta Code
Revision as of 17:01, 20 August 2011 by rosettacode>TimToady (→‎{{header|Perl 6}}: minor correction to handle case of small sphere eating back of large sphere too)
Task
Death Star
You are encouraged to solve this task according to the task description, using any language you may know.

Death Star is a task to display a region that consists of a large sphere with part of a smaller sphere removed from it as a result of geometric subtraction. (This will basically produce a shape like a "death star".)

Brlcad

<lang brlcad># We need a database to hold the objects opendb deathstar.g y

  1. We will be measuring in kilometers

units km

  1. Create a sphere of radius 60km centred at the origin

in sph1.s sph 0 0 0 60

  1. We will be subtracting an overlapping sphere with a radius of 40km
  2. The resultant hole will be smaller than this, because we only
  3. only catch the edge

in sph2.s sph 0 90 0 40

  1. Create a region named deathstar.r which consists of big minus small sphere

r deathstar.r u sph1.s - sph2.s

  1. We will use a plastic material texture with rgb colour 224,224,224
  2. with specular lighting value of 0.1 and no inheritance

mater deathstar.r "plastic sp=0.1" 224 224 224 0

  1. Clear the wireframe display and draw the deathstar

B deathstar.r

  1. We now trigger the raytracer to see our finished product

rt</lang>

C

Primitive ray tracing. <lang c>#include <stdio.h>

  1. include <math.h>

char shades[] = ".:!*oe&#%@";

double light[3] = { -50, 30, 50 }; void normalize(double * v) { double len = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); v[0] /= len; v[1] /= len; v[2] /= len; }

double dot(double *x, double *y) { double d = x[0]*y[0] + x[1]*y[1] + x[2]*y[2]; return d < 0 ? -d : 0; }

typedef struct { double cx, cy, cz, r; } sphere_t; sphere_t big = { 20, 20, 0, 20 }, small = { 7, 7, -10, 15 };

/* check if a ray (x,y, -inf)->(x, y, inf) hits a sphere; if so, return

  the intersecting z values.  z1 is closer to the eye */

int hit_sphere(sphere_t *sph, double x, double y, double *z1, double *z2) { double zsq; x -= sph->cx; y -= sph->cy; zsq = sph->r * sph->r - (x * x + y * y); if (zsq < 0) return 0; zsq = sqrt(zsq); *z1 = sph->cz - zsq; *z2 = sph->cz + zsq; return 1; }

void draw_sphere(double k, double ambient) { int i, j, intensity, hit_result; double b; double vec[3], x, y, zb1, zb2, zs1, zs2; for (i = floor(big.cy - big.r); i <= ceil(big.cy + big.r); i++) { y = i + .5; for (j = floor(big.cx - 2 * big.r); j <= ceil(big.cx + 2 * big.r); j++) { x = (j - big.cx) / 2. + .5 + big.cx;

/* ray lands in blank space, draw bg */ if (!hit_sphere(&big, x, y, &zb1, &zb2)) hit_result = 0;

/* ray hits big sphere but not small one, draw big sphere surface */ else if (!hit_sphere(&small, x, y, &zs1, &zs2)) hit_result = 1;

/* ray hits both, but small surface if obscured by big */ else if (zs1 > zb1) hit_result = 1;

/* if deeper intersection on small shpere is inside big sphere, only place where small sphere surface will be shown */ else if (zs2 > zb1) hit_result = 2; else hit_result = 1;

switch(hit_result) { case 0: putchar(' '); continue; case 1: vec[0] = x - big.cx; vec[1] = y - big.cy; vec[2] = zb1 - big.cz; break; default: vec[0] = small.cx - x; vec[1] = small.cy - y; vec[2] = small.cz - zs2; }

normalize(vec); b = pow(dot(light, vec), k) + ambient; intensity = (1 - b) * (sizeof(shades) - 1); if (intensity < 0) intensity = 0; if (intensity >= sizeof(shades) - 1) intensity = sizeof(shades) - 2; putchar(shades[intensity]); } putchar('\n'); } }

int main() { normalize(light);

draw_sphere(2, .5); return 0; }</lang>output<lang> eeeeeee!!!:::::::

                       ooooooeeeeeeeeeee..............
                   *ooooooooooeeeeeeeeeee.................
                !****ooooooooooeeeeeeeeeee...................
             :!!!!*****oooooooooeeeeeeeeee......................
           ::::!!!!*****oooooooooeeeeeeee.........................
         ....:::!!!!*****ooooooooeeeeeeee...........................
       .......:::!!!!*****ooooooooeeeeee..............................
      .........:::!!!!****ooooooooeeeeee...............................
    ............:::!!!!****ooooooooeeee..................................
   .............::::!!!****ooooooooee.....................................
  ...............:::!!!!****oooooooe.......................................
 ................::::!!!****ooooooo.........................................
 .................:::!!!****ooooo...........................................
..................:::!!!****ooo::............................................
..................:::!!!****oo:::............................................

..................:::!!!!***!!!::::............................................ ..................:::!!!**!!!!!:::::........................................... .................:::!!!****!!!!!:::::.......................................... e...............:::!oo******!!!!!:::::......................................... ee............:::oooooo******!!!!!:::::........................................ eeeeee::::eeoooooooooooo******!!!!!:::::....................................... eeeeeeeeeeeeeoooooooooooo******!!!!!!:::::..................................... eeeeeeeeeeeeeeoooooooooooo******!!!!!!:::::....................................

eeeeeeeeeeeeeeoooooooooooo*******!!!!!!:::::.................................
eeeeeeeeeeeeeeeooooooooooooo******!!!!!!::::::..............................:
 eeeeeeeeeeeeeeeooooooooooooo*******!!!!!!:::::::..........................:
 eeeeeeeeeeeeeeeeoooooooooooooo*******!!!!!!!:::::::.....................::!
  eeeeeeeeeeeeeeeeeooooooooooooo********!!!!!!!:::::::::..............::::!
   eeeeeeeeeeeeeeeeeoooooooooooooo********!!!!!!!!::::::::::::::::::::::!*
    eeeeeeeeeeeeeeeeeeooooooooooooooo********!!!!!!!!!!:::::::::::::!!!!*
      eeeeeeeeeeeeeeeeeoooooooooooooooo**********!!!!!!!!!!!!!!!!!!!!!*
       eeeeeeeeeeeeeeeeeeooooooooooooooooo************!!!!!!!!!!!!****
         eeeeeeeeeeeeeeeeeeoooooooooooooooooo**********************o
           eeeeeeeeeeeeeeeeeeeooooooooooooooooooooo************ooo
             eeeeeeeeeeeeeeeeeeeeooooooooooooooooooooooooooooooo
                eeeeeeeeeeeeeeeeeeeeooooooooooooooooooooooooo
                   eeeeeeeeeeeeeeeeeeeeeoooooooooooooooooo
                       eeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
                              eeeeeeeeeeeeeeeee</lang>

DWScript

Translation of C version.

<lang delphi>const cShades = '.:!*oe&#%@';

type TVector = array [0..2] of Float;

var light : TVector = [-50.0, 30, 50];

procedure Normalize(var v : TVector); begin

  var len := Sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
  v[0] /= len; v[1] /= len; v[2] /= len;

end;

function Dot(x, y : TVector) : Float; begin

  var d :=x[0]*y[0] + x[1]*y[1] + x[2]*y[2];
  if d<0 then 
     Result:=-d 
  else Result:=0;

end;

type

  TSphere = record
     cx, cy, cz, r : Float;
  end;
  

const big : TSphere = (cx: 20; cy: 20; cz: 0; r: 20); const small : TSphere = (cx: 7; cy: 7; cz: -10; r: 15);

function HitSphere(sph : TSphere; x, y : Float; var z1, z2 : Float) : Boolean; begin

  x -= sph.cx;
  y -= sph.cy;
  var zsq = sph.r * sph.r - (x * x + y * y);
  if (zsq < 0) then Exit False;
  zsq := Sqrt(zsq);
  z1 := sph.cz - zsq;
  z2 := sph.cz + zsq;
  Result:=True;

end;

procedure DrawSphere(k, ambient : Float); var

  i, j, intensity : Integer;
  b : Float;
  x, y, zb1, zb2, zs1, zs2 : Float;
  vec : TVector;

begin

  for i:=Trunc(big.cy-big.r) to Trunc(big.cy+big.r)+1 do begin
     y := i + 0.5;
     for j := Trunc(big.cx-2*big.r) to Trunc(big.cx+2*big.r) do begin
        x := (j-big.cx)/2 + 0.5 + big.cx;

        if not HitSphere(big, x, y, zb1, zb2) then begin
           Print(' ');
           continue;
        end;
        if not HitSphere(small, x, y, zs1, zs2) then begin
           vec[0] := x - big.cx;
           vec[1] := y - big.cy;
           vec[2] := zb1 - big.cz;
        end else begin
           if zs1 < zb1 then begin
              if zs2 > zb2 then begin
                 Print(' ');
                 continue;
              end;
              if zs2 > zb1 then begin
                 vec[0] := small.cx - x;
                 vec[1] := small.cy - y;
                 vec[2] := small.cz - zs2;
              end else begin
                 vec[0] := x - big.cx;
                 vec[1] := y - big.cy;
                 vec[2] := zb1 - big.cz;
              end;
           end else begin
              vec[0] := x - big.cx;
              vec[1] := y - big.cy;
              vec[2] := zb1 - big.cz;
           end;
        end;

        Normalize(vec);
        b := Power(Dot(light, vec), k) + ambient;
        intensity := Round((1 - b) * Length(cShades));
        Print(cShades[ClampInt(intensity+1, 1, Length(cShades))]);
     end;
     PrintLn();
  end;

end;

Normalize(light);

DrawSphere(2, 0.3);</lang>

Perl

Writes a PGM to stdout. <lang perl>use strict;

sub sq { my $s = 0; $s += $_ ** 2 for @_; $s; }

sub hit { my ($sph, $x, $y) = @_; $x -= $sph->[0]; $y -= $sph->[1];

my $z = sq($sph->[3]) - sq($x, $y); return if $z < 0;

$z = sqrt $z; return $sph->[2] - $z, $sph->[2] + $z; }

sub normalize { my $v = shift; my $n = sqrt sq(@$v); $_ /= $n for @$v; $v; }

sub dot { my ($x, $y) = @_; my $s = $x->[0] * $y->[0] + $x->[1] * $y->[1] + $x->[2] * $y->[2]; $s > 0 ? $s : 0; }

my $big = [ 120, 120, 0, 120 ]; my $sm = [ 27, 67, -90, 80 ]; my $light = normalize([ 120, -130, -150 ]); sub draw { my ($k, $amb) = @_; binmode STDOUT, ":raw"; print "P5\n", $big->[0] * 2 + 3, " ", $big->[1] * 2 + 3, "\n255\n"; for my $y (($big->[1] - $big->[3] - 1) .. ($big->[1] + $big->[3] + 1)) { my @row = (); for my $x (($big->[0] - $big->[3] - 1) .. ($big->[0] + $big->[3] + 1)) { my ($hit, @hs) = 0; my @h = hit($big, $x, $y);

if (!@h) { $hit = 0 } elsif (!(@hs = hit($sm, $x, $y))) { $hit = 1 } elsif ($hs[0] > $h[0]) { $hit = 1 } elsif ($hs[1] > $h[0]) { $hit = $hs[1] > $h[1] ? 0 : 2 } else { $hit = 1 }

my ($val, $v); if ($hit == 0) { $val = 0 } elsif ($hit == 1) { $v = [ $x - $big->[0], $y - $big->[1], $h[0] - $big->[2] ]; } else { $v = [ $sm->[0] - $x, $sm->[1] - $y, $sm->[2] - $hs[1] ]; } if ($v) { normalize($v); $val = int((dot($v, $light) ** $k + $amb) * 255); $val = ($val > 255) ? 255 : ($val < 0) ? 0 : $val; } push @row, $val; } print pack("C*", @row); } }

draw(4, 0.2);</lang>

Perl 6

Translation of C. Reimplemented to output a .pgm image.

<lang perl6>class sphere {

  has $.cx; # center x coordinate
  has $.cy; # center y coordinate
  has $.cz; # center z coordinate
  has $.r;  # radius

}

my $depth = 255; # image color depth

my $x = my $y = 255; # dimensions of generated .pgm; must be odd

my $s = ($x - 1)/2; # scaled dimension to build geometry

my @light = normalize([ -2, 4, -5 ]);

  1. large sphere at origin

my $lg = sphere.new(

   cx => 0,
   cy => 0,
   cz => 0,
   r  => $s.Int

);

  1. small sphere offset to upper left

my $sm = sphere.new(

   cx => (-$s*.65).Int,
   cy => (-$s*.65).Int,
   cz => (-$s*.5).Int,
   r  => ($s*.5).Int

);

sub MAIN ($outfile = 'deathstar-perl6.pgm') {

   my $out = open( $outfile, :w, :bin ) or die "$!\n";
   $out.say("P5\n$x $y\n$depth"); # .pgm header
   say 'Calculating row:';
   $out.print( draw_ds(3, .15)».chrs );
   $out.close;

}

sub draw_ds ( $k, $ambient ) {

   my @pixels;
   my $bs = 8.chr x 8;
   for ($lg.cy - $lg.r) .. ($lg.cy + $lg.r) -> $y {
       print $bs, $y, ' '; # monitor progress
       for ($lg.cx - $lg.r) .. ($lg.cx + $lg.r) -> $x {

# black if we don't hit large sphere, ignore small sphere

           if not hit($lg, $x, $y, my $lgz) {
               @pixels.push(0);

next; }

           my @vec;

# is front of large sphere inside small sphere?

           if hit($sm, $x, $y, my $smz) and $smz.min < $lgz.min < $smz.max {

# make black if whole large sphere eaten here

               if $smz.min < $lgz.max < $smz.max { @pixels.push(0); next; }
               # render inside of small sphere
               @vec = normalize([$sm.cx - $x, $sm.cy - $y, -$smz.max - $sm.cz]);
           }

else { # render outside of large sphere @vec = normalize([$x - $lg.cx, $y - $lg.cy, $lgz.max - $lg.cz]); }

           my $intensity = dot(@light, @vec) ** $k + $ambient;
           @pixels.push( ($intensity * $depth).Int min $depth );
       }
   }
   say $bs, 'Writing file.';
   return @pixels;

}

  1. normalize a vector

sub normalize (@vec) { return @vec »/» ([+] @vec Z* @vec).sqrt }

  1. dot product of two vectors

sub dot (@x, @y) { return -([+] @x Z* @y) max 0 }

  1. are the coordinates within the radius of the sphere?

sub hit ($sphere, $x is copy, $y is copy, $z is rw) {

   $x -= $sphere.cx;
   $y -= $sphere.cy;
   my $z2 = $sphere.r * $sphere.r - $x * $x - $y * $y;
   return 0 if $z2 < 0;
   $z2 = $z2.sqrt;
   $z = $sphere.cz - $z2 .. $sphere.cz + $z2;
   return 1;

}</lang>

Openscad

<lang openscad>// We are performing geometric subtraction

difference() {

 // Create the primary sphere of radius 60 centred at the origin
 translate(v = [0,0,0]) {
   sphere(60);
 }
 /*Subtract an overlapping sphere with a radius of 40
    The resultant hole will be smaller than this, because we only
    only catch the edge
 */
 translate(v = [0,90,0]) {
   sphere(40);
 }

}</lang>

POV-Ray

<lang POV-Ray>camera { perspective location <0.0 , .8 ,-3.0> look_at 0

        aperture .1 blur_samples 20 variance 1/100000 focal_point 0}
                           

light_source{< 3,3,-3> color rgb 1}

sky_sphere { pigment{ color rgb <0,.2,.5>}}

plane {y,-5 pigment {color rgb .54} normal {hexagon} }

difference {

sphere { 0,1 }
sphere { <-1,1,-1>,1 }
 texture { 
   pigment{ granite } 
   finish { phong 1 reflection {0.10 metallic 0.5} }
 } 

} </lang>