Death Star

From Rosetta Code
Revision as of 02:07, 21 August 2011 by rosettacode>TimToady (→‎{{header|D}}: same typos and buglet copied from C)
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 is obscured by big */ else if (zs1 > zb1) hit_result = 1;

                       /* both large points entirely inside small sphere, show bg */
                       else if (zs2 > zb2)     hit_result = 0;

/* if deeper intersection on small sphere 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>

D

Translation of: C

<lang d>import std.stdio, std.math, std.numeric;

/*const*/ struct V3 {

   double[3] v;
   @property V3 normalize() pure nothrow const {
       //immutable double len = sqrt(dotProduct(v, v));
       immutable double len= sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
       // return V3(v[] / len);
       return V3([v[0] / len, v[1] / len, v[2] / len]);
   }
   double dot(const ref V3 y) pure nothrow const {
       immutable double d = dotProduct(v, y.v);
       return d < 0 ? -d : 0;
   }

}


const struct Sphere { double cx, cy, cz, r; }

void drawSphere(in double k, in double ambient, in V3 light)nothrow{

   /** 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 */
   static bool hitSphere(const ref Sphere sph,
                         in double x0, in double y0,
                         out double z1,
                         out double z2) pure nothrow {
       immutable double x = x0 - sph.cx;
       immutable double y = y0 - sph.cy;
       immutable double zsq = sph.r ^^ 2 - (x ^^ 2 + y ^^ 2);
       if (zsq < 0)
           return false;
       immutable double szsq = sqrt(zsq);
       z1 = sph.cz - szsq;
       z2 = sph.cz + szsq;
       return true;
   }
   enum string shades = ".:!*oe&#%@";
   enum big = Sphere(20, 20, 0, 20);
   enum small = Sphere(7, 7, -10, 15);
   foreach (int i; cast(int)floor(big.cy - big.r) ..
                   cast(int)ceil(big.cy + big.r) + 1) {
       immutable double y = i + 0.5;
       jloop:
       foreach (int j; cast(int)floor(big.cx - 2 * big.r) ..
                       cast(int)ceil(big.cx + 2 * big.r) + 1) {
           immutable double x = (j - big.cx) / 2.0 + 0.5 + big.cx;
           enum Hit { background, bigSphere, smallSphere }
           Hit hitResult;
           double zb1, zs2;
           {
               double zb2, zs1;
               if (!hitSphere(big, x, y, zb1, zb2))
                   // ray lands in blank space, draw bg
                   hitResult = Hit.background;
               else if (!hitSphere(small, x, y, zs1, zs2))
                   // ray hits big sphere but not small one,
                   // draw big sphere surface
                   hitResult = Hit.bigSphere;
               else if (zs1 > zb1)
                   // ray hits both, but small surface is
                   // obscured by big
                   hitResult = Hit.bigSphere;
               else if (zs2 > zb2) {
                   // both large points entirely inside
                   // small sphere, show bg
                   hitResult = Hit.background;
               }
               else if (zs2 > zb1)
                   // if deeper intersection on small sphere is
                   // inside big sphere, only place where small
                   // sphere surface will be shown
                   hitResult = Hit.smallSphere;
               else
                   hitResult = Hit.bigSphere;
           }
           V3 vec_;
           final switch (hitResult) {
               case Hit.background:
                   putchar(' ');
                   continue jloop;
               case Hit.bigSphere:
                   vec_ = V3([x - big.cx, y - big.cy, zb1 - big.cz]);
                   break;
               case Hit.smallSphere:
                   vec_ = V3([small.cx-x, small.cy-y, small.cz-zs2]);
                   break;
           }
           immutable V3 nvec = vec_.normalize;
           immutable double b = light.dot(nvec) ^^ k + ambient;
           immutable intensity = cast(int)((1 - b) * shades.length);
           if (intensity < 0)
               putchar(shades[0]);
           else if (intensity >= shades.length)
               putchar(shades[$ - 1]);
           else
               putchar(shades[intensity]);
       }
       putchar('\n');
   }

}

void main() {

   enum light = V3([-50, 30, 50]).normalize;
   drawSphere(2, 0.5, light);

}</lang> The output is the same of the C version.

DWScript

Translation of: C

<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>