Death Star: Difference between revisions
Alextretyak (talk | contribs) m (→{{header|11l}}: named tuples) |
|||
(45 intermediate revisions by 21 users not shown) | |||
Line 17: | Line 17: | ||
;Related |
;Related tasks: |
||
* |
* [[Draw_a_sphere|draw a sphere]] |
||
* [[Draw_a_cuboid|draw a cuboid]] |
|||
* [[Draw_a_rotating_cube|draw a rotating cube]] |
|||
* [[Write_language_name_in_3D_ASCII|write language name in 3D ASCII]] |
|||
<br><br> |
<br><br> |
||
=={{header|11l}}== |
|||
{{trans|Python}} |
|||
<syntaxhighlight lang="11l">T Sphere = (Float cx, Float cy, Float cz, Float r) |
|||
F dotp(v1, v2) |
|||
V d = dot(v1, v2) |
|||
R I d < 0 {-d} E 0.0 |
|||
F hit_sphere(sph, x0, y0) |
|||
V x = x0 - sph.cx |
|||
V y = y0 - sph.cy |
|||
V zsq = sph.r ^ 2 - (x ^ 2 + y ^ 2) |
|||
I zsq < 0 |
|||
R (0B, 0.0, 0.0) |
|||
V szsq = sqrt(zsq) |
|||
R (1B, sph.cz - szsq, sph.cz + szsq) |
|||
F draw_sphere(k, ambient, light) |
|||
V shades = ‘.:!*oe&#%@’ |
|||
V pos = Sphere(20.0, 20.0, 0.0, 20.0) |
|||
V neg = Sphere(1.0, 1.0, -6.0, 20.0) |
|||
L(i) Int(floor(pos.cy - pos.r)) .< Int(ceil(pos.cy + pos.r) + 1) |
|||
V y = i + 0.5 |
|||
L(j) Int(floor(pos.cx - 2 * pos.r)) .< Int(ceil(pos.cx + 2 * pos.r) + 1) |
|||
V x = (j - pos.cx) / 2.0 + 0.5 + pos.cx |
|||
V (h, zb1, zb2) = hit_sphere(pos, x, y) |
|||
Int hit_result |
|||
Float zs2 |
|||
I !h |
|||
hit_result = 0 |
|||
E |
|||
(h, V zs1, zs2) = hit_sphere(neg, x, y) |
|||
I !h |
|||
hit_result = 1 |
|||
E I zs1 > zb1 |
|||
hit_result = 1 |
|||
E I zs2 > zb2 |
|||
hit_result = 0 |
|||
E I zs2 > zb1 |
|||
hit_result = 2 |
|||
E |
|||
hit_result = 1 |
|||
V vec = (0.0, 0.0, 0.0) |
|||
I hit_result == 0 |
|||
print(‘ ’, end' ‘’) |
|||
L.continue |
|||
E I hit_result == 1 |
|||
vec = (x - pos.cx, y - pos.cy, zb1 - pos.cz) |
|||
E I hit_result == 2 |
|||
vec = (neg.cx - x, neg.cy - y, neg.cz - zs2) |
|||
vec = normalize(vec) |
|||
V b = dotp(light, vec) ^ k + ambient |
|||
V intensity = Int((1 - b) * shades.len) |
|||
intensity = min(shades.len, max(0, intensity)) |
|||
print(shades[intensity], end' ‘’) |
|||
print() |
|||
V light = normalize((-50.0, 30.0, 50.0)) |
|||
draw_sphere(2, 0.5, light)</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
eeeee::::::: |
|||
eeeeeeeee.............. |
|||
ooeeeeeeeeee.................. |
|||
ooooeeeeeeeee...................... |
|||
oooooooeeeeeeee.......................... |
|||
ooooooooooeeeee.............................. |
|||
**ooooooooooeeee................................. |
|||
****ooooooooooee..................................... |
|||
!*****ooooooooooe....................................... |
|||
!!!*****ooooooooo:.......................................... |
|||
:!!!!*****ooooooo:::........................................... |
|||
:::!!!!*****ooooo!:::::........................................... |
|||
::::!!!!!*****ooo!!!!::::............................................ |
|||
.::::!!!!*****oo*!!!!!::::............................................ |
|||
...::::!!!!*********!!!!:::::............................................ |
|||
...::::!!!!****o*****!!!!!::::............................................ |
|||
....::::!!!!***ooo******!!!!!::::............................................ |
|||
....::::!!!!*ooooooo*****!!!!!:::::........................................... |
|||
...::::!!!!!oooooooooo*****!!!!!:::::.......................................... |
|||
:::::!!!!eeooooooooooo******!!!!!:::::......................................... |
|||
!!!!!eeeeeeeooooooooooo******!!!!!:::::........................................ |
|||
eeeeeeeeeeeeoooooooooooo******!!!!!:::::....................................... |
|||
eeeeeeeeeeeeeoooooooooooo******!!!!!!:::::..................................... |
|||
eeeeeeeeeeeeeeoooooooooooo******!!!!!!:::::.................................... |
|||
eeeeeeeeeeeeeeoooooooooooo*******!!!!!!:::::................................. |
|||
eeeeeeeeeeeeeeeooooooooooooo******!!!!!!::::::..............................: |
|||
eeeeeeeeeeeeeeeooooooooooooo*******!!!!!!:::::::..........................: |
|||
eeeeeeeeeeeeeeeeoooooooooooooo*******!!!!!!!:::::::.....................::! |
|||
eeeeeeeeeeeeeeeeeooooooooooooo********!!!!!!!:::::::::..............::::! |
|||
eeeeeeeeeeeeeeeeeoooooooooooooo********!!!!!!!!::::::::::::::::::::::!* |
|||
eeeeeeeeeeeeeeeeeeooooooooooooooo********!!!!!!!!!!:::::::::::::!!!!* |
|||
eeeeeeeeeeeeeeeeeoooooooooooooooo**********!!!!!!!!!!!!!!!!!!!!!* |
|||
eeeeeeeeeeeeeeeeeeooooooooooooooooo************!!!!!!!!!!!!**** |
|||
eeeeeeeeeeeeeeeeeeoooooooooooooooooo**********************o |
|||
eeeeeeeeeeeeeeeeeeeooooooooooooooooooooo************ooo |
|||
eeeeeeeeeeeeeeeeeeeeooooooooooooooooooooooooooooooo |
|||
eeeeeeeeeeeeeeeeeeeeooooooooooooooooooooooooo |
|||
eeeeeeeeeeeeeeeeeeeeeoooooooooooooooooo |
|||
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeee |
|||
eeeeeeeeeeeeeeeee |
|||
</pre> |
|||
=={{header|Ada}}== |
|||
{{libheader|SDLAda}}{{trans|Go}} |
|||
<syntaxhighlight lang="ada">with Ada.Numerics.Elementary_Functions; |
|||
with Ada.Numerics.Generic_Real_Arrays; |
|||
with SDL.Video.Windows.Makers; |
|||
with SDL.Video.Renderers.Makers; |
|||
with SDL.Video.Palettes; |
|||
with SDL.Events.Events; |
|||
procedure Death_Star is |
|||
Width : constant := 400; |
|||
Height : constant := 400; |
|||
package Float_Arrays is |
|||
new Ada.Numerics.Generic_Real_Arrays (Float); |
|||
use Ada.Numerics.Elementary_Functions; |
|||
use Float_Arrays; |
|||
Window : SDL.Video.Windows.Window; |
|||
Renderer : SDL.Video.Renderers.Renderer; |
|||
subtype Vector_3 is Real_Vector (1 .. 3); |
|||
type Sphere_Type is record |
|||
Cx, Cy, Cz : Integer; |
|||
R : Integer; |
|||
end record; |
|||
function Normalize (V : Vector_3) return Vector_3 is |
|||
(V / Sqrt (V * V)); |
|||
procedure Hit (S : Sphere_Type; |
|||
X, Y : Integer; |
|||
Z1, Z2 : out Float; |
|||
Is_Hit : out Boolean) |
|||
is |
|||
NX : constant Integer := X - S.Cx; |
|||
NY : constant Integer := Y - S.Cy; |
|||
Zsq : constant Integer := S.R * S.R - (NX * NX + NY * NY); |
|||
Zsqrt : Float; |
|||
begin |
|||
if Zsq >= 0 then |
|||
Zsqrt := Sqrt (Float (Zsq)); |
|||
Z1 := Float (S.Cz) - Zsqrt; |
|||
Z2 := Float (S.Cz) + Zsqrt; |
|||
Is_Hit := True; |
|||
return; |
|||
end if; |
|||
Z1 := 0.0; |
|||
Z2 := 0.0; |
|||
Is_Hit := False; |
|||
end Hit; |
|||
procedure Draw_Death_Star (Pos, Neg : Sphere_Type; |
|||
K, Amb : Float; |
|||
Dir : Vector_3) |
|||
is |
|||
Vec : Vector_3; |
|||
ZB1, ZB2 : Float; |
|||
ZS1, ZS2 : Float; |
|||
Is_Hit : Boolean; |
|||
S : Float; |
|||
Lum : Integer; |
|||
begin |
|||
for Y in Pos.Cy - Pos.R .. Pos.Cy + Pos.R loop |
|||
for X in Pos.Cx - Pos.R .. Pos.Cx + Pos.R loop |
|||
Hit (Pos, X, Y, ZB1, ZB2, Is_Hit); |
|||
if not Is_Hit then |
|||
goto Continue; |
|||
end if; |
|||
Hit (Neg, X, Y, ZS1, ZS2, Is_Hit); |
|||
if Is_Hit then |
|||
if ZS1 > ZB1 then |
|||
Is_Hit := False; |
|||
elsif ZS2 > ZB2 then |
|||
goto Continue; |
|||
end if; |
|||
end if; |
|||
if Is_Hit then |
|||
Vec := (Float (Neg.Cx - X), |
|||
Float (Neg.Cy - Y), |
|||
Float (Neg.Cz) - ZS2); |
|||
else |
|||
Vec := (Float (X - Pos.Cx), |
|||
Float (Y - Pos.Cy), |
|||
ZB1 - Float (Pos.Cz)); |
|||
end if; |
|||
S := Float'Max (0.0, Dir * Normalize (Vec)); |
|||
Lum := Integer (255.0 * (S ** K + Amb) / (1.0 + Amb)); |
|||
Lum := Integer'Max (0, Lum); |
|||
Lum := Integer'Min (Lum, 255); |
|||
Renderer.Set_Draw_Colour ((SDL.Video.Palettes.Colour_Component (Lum), |
|||
SDL.Video.Palettes.Colour_Component (Lum), |
|||
SDL.Video.Palettes.Colour_Component (Lum), |
|||
255)); |
|||
Renderer.Draw (Point => (SDL.C.int (X + Width / 2), |
|||
SDL.C.int (Y + Height / 2))); |
|||
<<Continue>> |
|||
end loop; |
|||
end loop; |
|||
end Draw_Death_Star; |
|||
procedure Wait is |
|||
use type SDL.Events.Event_Types; |
|||
Event : SDL.Events.Events.Events; |
|||
begin |
|||
loop |
|||
while SDL.Events.Events.Poll (Event) loop |
|||
if Event.Common.Event_Type = SDL.Events.Quit then |
|||
return; |
|||
end if; |
|||
end loop; |
|||
delay 0.100; |
|||
end loop; |
|||
end Wait; |
|||
Direction : constant Vector_3 := Normalize ((20.0, -40.0, -10.0)); |
|||
Positive : constant Sphere_Type := (0, 0, 0, 120); |
|||
Negative : constant Sphere_Type := (-90, -90, -30, 100); |
|||
begin |
|||
if not SDL.Initialise (Flags => SDL.Enable_Screen) then |
|||
return; |
|||
end if; |
|||
SDL.Video.Windows.Makers.Create (Win => Window, |
|||
Title => "Death star", |
|||
Position => SDL.Natural_Coordinates'(X => 10, Y => 10), |
|||
Size => SDL.Positive_Sizes'(Width, Height), |
|||
Flags => 0); |
|||
SDL.Video.Renderers.Makers.Create (Renderer, Window.Get_Surface); |
|||
Renderer.Set_Draw_Colour ((0, 0, 0, 255)); |
|||
Renderer.Fill (Rectangle => (0, 0, Width, Height)); |
|||
Draw_Death_Star (Positive, Negative, 1.5, 0.2, Direction); |
|||
Window.Update_Surface; |
|||
Wait; |
|||
Window.Finalize; |
|||
SDL.Finalise; |
|||
end Death_Star;</syntaxhighlight> |
|||
=={{header|ALGOL 68}}== |
|||
{{Trans|C}}{{Trans|11l}} |
|||
<syntaxhighlight lang="algol68"> |
|||
BEGIN # draw a "Death Star" - translated from the C and 11l samples # |
|||
STRING shades = ".:!*oe&#%@"; |
|||
PROC normalize = ( []REAL v )[]REAL: |
|||
BEGIN |
|||
REAL len = sqrt( v[ 1 ] * v[ 1 ] + v[ 2 ] * v[ 2 ] + v[ 3 ] * v[ 3 ] ); |
|||
( v[ 1 ] / len, v[ 2 ] / len, v[ 3 ] / len ) |
|||
END # normalize # ; |
|||
PROC dot = ( []REAL x, y )REAL: |
|||
BEGIN |
|||
REAL d = x[ 1 ] * y[ 1 ] + x[ 2 ] * y[ 2 ] + x[ 3 ] * y[ 3 ]; |
|||
IF d < 0 THEN - d ELSE 0 FI |
|||
END # dot # ; |
|||
MODE SPHERE = STRUCT( REAL cx, cy, cz, r ); |
|||
# positive shpere and negative sphere # |
|||
SPHERE pos = ( 20, 20, 0, 20 ), neg = ( 1, 1, -6, 20 ); |
|||
# 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 # |
|||
PROC hit_sphere = ( SPHERE sph, REAL x in, y in, REF REAL z1, z2 )BOOL: |
|||
IF REAL x = x in - cx OF sph; |
|||
REAL y = y in - cy OF sph; |
|||
REAL zsq := r OF sph * r OF sph - ( x * x + y * y ); |
|||
zsq < 0 |
|||
THEN FALSE |
|||
ELSE zsq := sqrt( zsq ); |
|||
z1 := cz OF sph - zsq; |
|||
z2 := cz OF sph + zsq; |
|||
TRUE |
|||
FI # hit_sphere # ; |
|||
PROC draw_sphere = ( REAL k, ambient, []REAL light )VOID: |
|||
FOR i FROM ENTIER ( cy OF pos - r OF pos ) TO ENTIER ( cy OF pos + r OF pos ) + 1 DO |
|||
REAL y := i + 0.5; |
|||
FOR j FROM ENTIER ( cx OF pos - 2 * r OF pos ) TO ENTIER (cx OF pos + 2 * r OF pos ) + 1 DO |
|||
REAL x := ( j - cx OF pos ) / 2.0 + 0.5 + cx OF pos; |
|||
REAL zb1 := 0, zb2 := 0, zs1 := 0, zs2 := 0; |
|||
INT hit_result |
|||
= IF NOT hit_sphere( pos, x, y, zb1, zb2 ) THEN |
|||
0 # ray lands in blank space, draw bg # |
|||
ELIF NOT hit_sphere( neg, x, y, zs1, zs2 ) THEN |
|||
1 # ray hits pos sphere but not neg, draw pos sphere surface # |
|||
ELIF zs1 > zb1 THEN |
|||
1 # ray hits both, but pos front surface is closer # |
|||
ELIF zs2 > zb2 THEN |
|||
0 # pos sphere surface is inside neg sphere, show bg # |
|||
ELIF zs2 > zb1 THEN |
|||
2 # back surface on neg sphere is inside pos sphere, # |
|||
# the only place where neg sphere surface will be shown # |
|||
ELSE |
|||
1 # show the pos sphere # |
|||
FI; |
|||
IF hit_result = 0 THEN |
|||
print( ( " " ) ) |
|||
ELSE |
|||
[]REAL vec = |
|||
normalize( IF hit_result = 1 |
|||
THEN []REAL( x - cx OF pos |
|||
, y - cy OF pos |
|||
, zb1 - cz OF pos |
|||
) |
|||
ELSE []REAL( cx OF neg - x |
|||
, cy OF neg - y |
|||
, cz OF neg - zs2 |
|||
) |
|||
FI |
|||
); |
|||
REAL b = ( dot( light, vec ) ^ k ) + ambient; |
|||
INT intensity := ENTIER ( ( 1 - b ) * ( ( UPB shades - LWB shades ) + 1 ) ) + 1; |
|||
IF intensity < LWB shades THEN intensity := LWB shades |
|||
ELIF intensity > UPB shades THEN intensity := UPB shades |
|||
FI; |
|||
print( ( shades[ intensity ] ) ) |
|||
FI |
|||
OD; |
|||
print( ( newline ) ) |
|||
OD # draw_sphere # ; |
|||
BEGIN |
|||
[]REAL light = ( -50, 30, 50 ); |
|||
draw_sphere( 2, 0.5, normalize( light ) ) |
|||
END |
|||
END |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
Same as 11l |
|||
=={{header|AutoHotkey}}== |
=={{header|AutoHotkey}}== |
||
{{libheader|GDIP}} |
{{libheader|GDIP}} |
||
< |
<syntaxhighlight lang="ahk">#NoEnv |
||
SetBatchLines, -1 |
SetBatchLines, -1 |
||
#SingleInstance, Force |
#SingleInstance, Force |
||
Line 102: | Line 454: | ||
; gdi+ may now be shutdown on exiting the program |
; gdi+ may now be shutdown on exiting the program |
||
Gdip_Shutdown(pToken) |
Gdip_Shutdown(pToken) |
||
ExitApp</ |
ExitApp</syntaxhighlight> |
||
=={{header|Brlcad}}== |
=={{header|Brlcad}}== |
||
< |
<syntaxhighlight lang="brlcad"># We need a database to hold the objects |
||
opendb deathstar.g y |
opendb deathstar.g y |
||
Line 130: | Line 482: | ||
# We now trigger the raytracer to see our finished product |
# We now trigger the raytracer to see our finished product |
||
rt</ |
rt</syntaxhighlight> |
||
=={{header|C}}== |
=={{header|C}}== |
||
Primitive ray tracing. |
Primitive ray tracing. |
||
< |
<syntaxhighlight lang="c">#include <stdio.h> |
||
#include <math.h> |
#include <math.h> |
||
#include <unistd.h> |
#include <unistd.h> |
||
Line 244: | Line 597: | ||
} |
} |
||
return 0; |
return 0; |
||
}</ |
}</syntaxhighlight> |
||
=={{header|D}}== |
=={{header|D}}== |
||
{{trans|C}} |
{{trans|C}} |
||
< |
<syntaxhighlight lang="d">import std.stdio, std.math, std.numeric, std.algorithm; |
||
struct V3 { |
struct V3 { |
||
Line 356: | Line 709: | ||
immutable light = [-50, 30, 50].V3.normalize; |
immutable light = [-50, 30, 50].V3.normalize; |
||
drawSphere(2, 0.5, light); |
drawSphere(2, 0.5, light); |
||
}</ |
}</syntaxhighlight> |
||
The output is the same of the C version. |
The output is the same of the C version. |
||
=={{header|Delphi}}== |
|||
{{libheader| Winapi.Windows}} |
|||
{{libheader| System.SysUtils}} |
|||
{{libheader| system.Math}} |
|||
{{libheader| Vcl.Graphics}} |
|||
{{libheader| Vcl.Imaging.pngimage}} |
|||
{{Trans|C}} |
|||
Translate of [[#C]] and [[#Go]], with copy of some parts of [[#DWScript]]. |
|||
<syntaxhighlight lang="delphi"> |
|||
program Death_Star; |
|||
{$APPTYPE CONSOLE} |
|||
uses |
|||
Winapi.Windows, |
|||
System.SysUtils, |
|||
system.Math, |
|||
Vcl.Graphics, |
|||
Vcl.Imaging.pngimage; |
|||
type |
|||
TVector = array of double; |
|||
var |
|||
light: TVector = [20, -40, -10]; |
|||
function ClampInt(value, amin, amax: Integer): Integer; |
|||
begin |
|||
Result := Max(amin, Min(amax, value)) |
|||
end; |
|||
procedure Normalize(var v: TVector); |
|||
begin |
|||
var len := Sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); |
|||
v[0] := v[0] / len; |
|||
v[1] := v[1] / len; |
|||
v[2] := v[2] / len; |
|||
end; |
|||
function Dot(x, y: TVector): Double; |
|||
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: Double; |
|||
end; |
|||
const |
|||
pos: TSphere = ( |
|||
cx: 0; |
|||
cy: 0; |
|||
cz: 0; |
|||
r: 120 |
|||
); |
|||
const |
|||
neg: TSphere = ( |
|||
cx: -90; |
|||
cy: -90; |
|||
cz: -30; |
|||
r: 80 |
|||
); |
|||
function HitSphere(sph: TSphere; x, y: double; var z1, z2: Double): Boolean; |
|||
begin |
|||
x := x - sph.cx; |
|||
y := 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; |
|||
function DeathStar(pos, neg: TSphere; k, amb: Double; light: TVector): TBitmap; |
|||
var |
|||
w, h, yMax, xMax, s: double; |
|||
zp1, zp2, zn1, zn2, b: Double; |
|||
x, y: Integer; |
|||
hit: Boolean; |
|||
vec: TVector; |
|||
intensity: Byte; |
|||
ox, oy: Integer; |
|||
begin |
|||
w := pos.r * 4; |
|||
h := pos.r * 3; |
|||
ox := -trunc(pos.cx - w / 2); |
|||
oy := -trunc(pos.cy - h / 2); |
|||
vec := [0, 0, 0]; |
|||
Result := TBitmap.Create; |
|||
Result.SetSize(trunc(w), trunc(h)); |
|||
yMax := pos.cy + pos.r; |
|||
for y := Trunc(pos.cy - pos.r) to Trunc(yMax) do |
|||
begin |
|||
xMax := pos.cx + pos.r; |
|||
for x := trunc(pos.cy - pos.r) to trunc(xMax) do |
|||
begin |
|||
hit := HitSphere(pos, x, y, zp1, zp2); |
|||
if not hit then |
|||
continue; |
|||
hit := HitSphere(neg, x, y, zn1, zn2); |
|||
if hit then |
|||
begin |
|||
if zn1 > zp1 then |
|||
hit := false |
|||
else if zn2 > zp2 then |
|||
continue; |
|||
end; |
|||
if hit then |
|||
begin |
|||
vec[0] := neg.cx - x; |
|||
vec[1] := neg.cy - y; |
|||
vec[2] := neg.cz - zn2; |
|||
end |
|||
else |
|||
begin |
|||
vec[0] := x - pos.cx; |
|||
vec[1] := y - pos.cy; |
|||
vec[2] := zp1 - pos.cz; |
|||
end; |
|||
Normalize(vec); |
|||
s := max(0, dot(light, vec)); |
|||
b := Power(s, k) + amb; |
|||
intensity := ClampInt(round(255 * b / (1 + amb)), 0, 254); |
|||
Result.Canvas.Pixels[x + ox, y + oy] := rgb(intensity, intensity, intensity); |
|||
end; |
|||
end; |
|||
end; |
|||
var |
|||
bmp: TBitmap; |
|||
begin |
|||
Normalize(light); |
|||
bmp := DeathStar(pos, neg, 1.2, 0.3, light); |
|||
with TPngImage.Create do |
|||
begin |
|||
Assign(bmp); |
|||
TransparentColor := clwhite; |
|||
SaveToFile('out.png'); |
|||
bmp.Free; |
|||
Free; |
|||
end; |
|||
end.</syntaxhighlight> |
|||
=={{header|DWScript}}== |
=={{header|DWScript}}== |
||
{{trans|C}} |
{{trans|C}} |
||
< |
<syntaxhighlight lang="delphi">const cShades = '.:!*oe&#%@'; |
||
type TVector = array [0..2] of Float; |
type TVector = array [0..2] of Float; |
||
Line 455: | Line 970: | ||
Normalize(light); |
Normalize(light); |
||
DrawSphere(2, 0.3);</ |
DrawSphere(2, 0.3);</syntaxhighlight> |
||
=={{header|Frink}}== |
|||
This program not only draws a Death Star and renders it onscreen projected on the x,y, and z axes but also outputs a .stl file for 3-D printing. Frink has [https://frinklang.org/3d/frink/graphics/package-summary.html built-in routines for 3-D modeling]. |
|||
<syntaxhighlight lang="frink">res = 254 / in |
|||
v = callJava["frink.graphics.VoxelArray", "makeSphere", [1/2 inch res]] |
|||
dish = callJava["frink.graphics.VoxelArray", "makeSphere", [1/2 inch res]] |
|||
dish.translate[round[.45 inch res], round[.45 inch res], round[.45 inch res]] |
|||
v.remove[dish] |
|||
v.projectX[undef].show["X"] |
|||
v.projectY[undef].show["Y"] |
|||
v.projectZ[undef].show["Z"] |
|||
filename = "DeathStar.stl" |
|||
print["Writing $filename..."] |
|||
w = new Writer[filename] |
|||
w.println[v.toSTLFormat["DeathStar", 1/(res mm)]] |
|||
w.close[] |
|||
println["done."] |
|||
</syntaxhighlight> |
|||
=={{header|Go}}== |
=={{header|Go}}== |
||
[[file:GoDstar.png|right|thumb|Output png]] |
[[file:GoDstar.png|right|thumb|Output png]] |
||
{{trans|C}} |
{{trans|C}} |
||
< |
<syntaxhighlight lang="go">package main |
||
import ( |
import ( |
||
Line 561: | Line 1,098: | ||
fmt.Println(err) |
fmt.Println(err) |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
=={{header|Haskell}}== |
|||
=== ASCII art === |
|||
<syntaxhighlight lang="haskell">import Data.List (genericLength) |
|||
shades = ".:!*oe%#&@" |
|||
n = genericLength shades |
|||
dot a b = sum $ zipWith (*) a b |
|||
normalize x = (/ sqrt (x `dot` x)) <$> x |
|||
deathStar r k amb = unlines $ |
|||
[ [ if x*x + y*y <= r*r |
|||
then let vec = normalize $ normal x y |
|||
b = (light `dot` vec) ** k + amb |
|||
intensity = (1 - b)*(n - 1) |
|||
in shades !! round ((0 `max` intensity) `min` n) |
|||
else ' ' |
|||
| y <- map (/2.12) [- 2*r - 0.5 .. 2*r + 0.5] ] |
|||
| x <- [ - r - 0.5 .. r + 0.5] ] |
|||
where |
|||
light = normalize [-30,-30,-50] |
|||
normal x y |
|||
| (x+r)**2 + (y+r)**2 <= r**2 = [x+r, y+r, sph2 x y] |
|||
| otherwise = [x, y, sph1 x y] |
|||
sph1 x y = sqrt (r*r - x*x - y*y) |
|||
sph2 x y = r - sqrt (r*r - (x+r)**2 - (y+r)**2)</syntaxhighlight> |
|||
<pre>λ> putStrLn $ deathStar 10 4 0.1 |
|||
eeeeoo*&&&&&&& |
|||
eeeeeoooo**!&&&&&&&&&&&& |
|||
eeooooooo***!!&&&&&&&&&&&&&&&& |
|||
eooooo*****!!!::&&&&&&&&&&&&&&&&&& |
|||
eooo****!!!!:::.&&&&&&&&&&&&&&&&&&&& |
|||
eeeoo***!!!::::.&&&&&&##############&&&& |
|||
eeeoo***!!:::...&&&########%%%%%%%%#####&& |
|||
eeoo**!!!::...&&######%%%%%%%eeeeee%%%%### |
|||
eooo**!!::..&&&#####%%%%%eeeeeooooooeeee%%#& |
|||
oo**!!:&&&&&&&####%%%%eeeoooo********oooee%# |
|||
&&&&&&&&&&&&#####%%%eeeoo****!!!!!!!!***oe%# |
|||
&&&&&&&&&&&####%%%eeeoo***!!!::::::::!!**oe# |
|||
&&&&&&&&&&###%%%eeooo**!!:::.......::!!*oe |
|||
&&&&&&&&&####%%eeeoo**!!::..........::!*o% |
|||
&&&&&&&&####%%eeoo**!!::...........:!*o% |
|||
&&&&&&####%%eeoo**!!::..........:!*o |
|||
&&&&&&###%%%eeoo**!!::......::!*oe |
|||
&&&&&###%%%eeoo**!!!!:::!!!*o% |
|||
&&&&###%%%eeoooo****ooe% |
|||
&&####%%%%%%%# |
|||
</pre> |
|||
=={{header|J}}== |
=={{header|J}}== |
||
{{Trans|Python}} |
{{Trans|Python}} |
||
<syntaxhighlight lang="j"> |
|||
<lang J> |
|||
load'graphics/viewmat' |
load'graphics/viewmat' |
||
mag =: +/&.:*:"1 |
mag =: +/&.:*:"1 |
||
Line 606: | Line 1,197: | ||
env=.(2; 0.5; (norm _50 30 50)) |
env=.(2; 0.5; (norm _50 30 50)) |
||
sph=. 20 20 0; 20; 1 1 _6; 20 |
sph=. 20 20 0; 20; 1 1 _6; 20 |
||
'rgb' viewmat togray env draw_sphere sph</ |
'rgb' viewmat togray env draw_sphere sph</syntaxhighlight> |
||
=={{header|Java}}== |
=={{header|Java}}== |
||
{{libheader|JavaFX}} |
|||
<lang Java> |
|||
<syntaxhighlight lang="java">import javafx.application.Application; |
|||
import javafx.application.Application; |
|||
import javafx.event.EventHandler; |
import javafx.event.EventHandler; |
||
import javafx.geometry.Point3D; |
import javafx.geometry.Point3D; |
||
Line 826: | Line 1,415: | ||
} |
} |
||
</syntaxhighlight> |
|||
</lang> |
|||
===Using Java 11=== |
|||
Alternatively, without using JavaFX, which has been removed from the JavaJDK since version 11. |
|||
<syntaxhighlight lang="java"> |
|||
import java.awt.Color; |
|||
import java.awt.Graphics; |
|||
import java.awt.image.BufferedImage; |
|||
import java.io.File; |
|||
import java.io.IOException; |
|||
import java.util.List; |
|||
import javax.imageio.ImageIO; |
|||
public final class DeathStar { |
|||
public static void main(String[] aArgs) throws IOException { |
|||
Vector direction = new Vector(20.0, -40.0, -10.0); |
|||
direction.normalise(); |
|||
Sphere positive = new Sphere(0, 0, 0, 120); |
|||
Sphere negative = new Sphere(-90, -90, -30, 100); |
|||
BufferedImage image = deathStar(positive, negative, direction, 1.5, 0.5); |
|||
ImageIO.write(image, "png", new File("DeathStarJava.png")); |
|||
} |
|||
private static BufferedImage deathStar( |
|||
Sphere aPositive, Sphere aNegative, Vector aDirection, double aShadow, double aBrightness) { |
|||
final int width = aPositive.radius * 4; |
|||
final int height = aPositive.radius * 3; |
|||
BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); |
|||
Graphics graphics = result.getGraphics(); |
|||
graphics.setColor(Color.CYAN); |
|||
graphics.fillRect(0, 0, width, height); |
|||
Vector ray = new Vector(0.0, 0.0, 0.0); |
|||
final int deltaX = aPositive.x - width / 2; |
|||
final int deltaY = aPositive.y - height / 2; |
|||
double xMax = aPositive.x + aPositive.radius; |
|||
double yMax = aPositive.y + aPositive.radius; |
|||
for ( int y = aPositive.y - aPositive.radius; y < yMax; y++ ) { |
|||
for ( int x = aPositive.x - aPositive.radius; x < xMax; x++ ) { |
|||
List<Object> contacts = aPositive.contact(x, y); |
|||
final double zb1 = (double) contacts.get(0); |
|||
final int zb2 = (int) contacts.get(1); |
|||
final boolean positiveHit = (boolean) contacts.get(2); |
|||
if ( ! positiveHit ) { |
|||
continue; |
|||
} |
|||
contacts = aNegative.contact(x, y); |
|||
final double zs1 = (double) contacts.get(0); |
|||
final int zs2 = (int) contacts.get(1); |
|||
boolean negativeHit = (boolean) contacts.get(2); |
|||
if ( negativeHit ) { |
|||
if ( zs1 > zb1 ) { |
|||
negativeHit = false; |
|||
} else if ( zs2 > zb2 ) { |
|||
continue; |
|||
} |
|||
} |
|||
if ( negativeHit ) { |
|||
ray.x = aNegative.x - x; |
|||
ray.y = aNegative.y - y; |
|||
ray.z = aNegative.z - zs2; |
|||
} else { |
|||
ray.x = x - aPositive.x; |
|||
ray.y = y - aPositive.y; |
|||
ray.z = zb1 - aPositive.z; |
|||
} |
|||
ray.normalise(); |
|||
double rayComponent = ray.scalarProduct(aDirection); |
|||
if ( rayComponent < 0 ) { |
|||
rayComponent = 0; |
|||
} |
|||
int color = (int) ( 255 * ( Math.pow(rayComponent, aShadow) + aBrightness) / ( 1 + aBrightness ) ); |
|||
if ( color < 0 ) { |
|||
color = 0; |
|||
} else if ( color > 255 ) { |
|||
color = 255; |
|||
} |
|||
result.setRGB(x - deltaX, y - deltaY, color); |
|||
} |
|||
} |
|||
return result; |
|||
} |
|||
private static class Vector { |
|||
public Vector(double aX, double aY, double aZ) { |
|||
x = aX; y = aY; z = aZ; |
|||
} |
|||
public double scalarProduct(Vector aOther) { |
|||
return x * aOther.x + y * aOther.y + z * aOther.z; |
|||
} |
|||
public Vector normalise() { |
|||
final double magnitude = Math.sqrt(this.scalarProduct(this)); |
|||
return new Vector(x /= magnitude, y /= magnitude, z /= magnitude); |
|||
} |
|||
private double x, y, z; |
|||
} |
|||
private static class Sphere { |
|||
public Sphere(int aX, int aY, int aZ, int aRadius) { |
|||
x = aX; y = aY; z = aZ; radius = aRadius; |
|||
} |
|||
public List<Object> contact(int aX, int aY) { |
|||
final int xx = aX - x; |
|||
final int yy = aY - y; |
|||
final int zSquared = radius * radius - ( xx * xx + yy * yy ); |
|||
if ( zSquared >= 0 ) { |
|||
final double zz = Math.sqrt(zSquared); |
|||
return List.of(z - zz, z, true); |
|||
} |
|||
return List.of( 0.0, 0, false ); |
|||
} |
|||
private int x, y, z, radius; |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
{{ out }} |
|||
[[Media:DeathStarJava.png]] |
|||
=={{header|JavaScript}}== |
=={{header|JavaScript}}== |
||
Layer circles and gradients to achieve result similar to that of the Wikipedia page for the [http://en.wikipedia.org/wiki/Death_Star Death Star]. |
Layer circles and gradients to achieve result similar to that of the Wikipedia page for the [http://en.wikipedia.org/wiki/Death_Star Death Star]. |
||
<syntaxhighlight lang="javascript"> |
|||
<lang JavaScript> |
|||
<!DOCTYPE html> |
<!DOCTYPE html> |
||
<html> |
<html> |
||
Line 873: | Line 1,594: | ||
</html> |
</html> |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Julia}}== |
=={{header|Julia}}== |
||
< |
<syntaxhighlight lang="julia"># run in REPL |
||
using GLMakie |
|||
# run from the Julia REPL command line |
|||
n = 20 |
|||
θ = [0;(0.5:n-0.5)/n;1] |
|||
φ = [(0:2n-2)*2/(2n-1);2] |
|||
x = [(x1 = cospi(φ)*sinpi(θ)) > 0.85 ? 0.85 : x1 for θ in θ, φ in φ] |
|||
y = [sinpi(φ)*sinpi(θ) for θ in θ, φ in φ] |
|||
z = [cospi(θ) for θ in θ, φ in φ] |
|||
function deathstar() |
|||
plt = surface(x, y, z, color = rand(RGBAf0, 124, 124)) |
|||
n = 60 |
|||
axis = plt[Axis] |
|||
θ = [0; (0.5: n - 0.5) / n; 1] |
|||
axis[:showgrid] = (false,false,false) |
|||
φ = [(0: 2n - 2) * 2 / (2n - 1); 2] |
|||
axis[:showaxis] = (false,false,false) |
|||
# if x is +0.9 radius units, replace it with the coordinates of sphere surface |
|||
</lang> {{output}}[[File:deathstar-julia.jpg|300px|Death Star]] |
|||
# at (1.2,0,0) center, radius 0.5 units |
|||
x = [(x1 = cospi(φ)*sinpi(θ)) > 0.9 ? 1.2 - x1 * 0.5 : x1 for θ in θ, φ in φ] |
|||
y = [sinpi(φ)*sinpi(θ) for θ in θ, φ in φ] |
|||
z = [cospi(θ) for θ in θ, φ in φ] |
|||
scene = Scene(backgroundcolor=:black) |
|||
surface!(scene, x, y, z, color = rand(RGBAf0, 124, 124), show_axis=false) |
|||
return scene |
|||
end |
|||
scene = deathstar() |
|||
</syntaxhighlight> |
|||
=={{header|LSL}}== |
=={{header|LSL}}== |
||
Rez a box on the ground, raise it up a few meters, add the following as a New Script. |
Rez a box on the ground, raise it up a few meters, add the following as a New Script. |
||
< |
<syntaxhighlight lang="lsl">default { |
||
state_entry() { |
state_entry() { |
||
llSetPrimitiveParams([PRIM_NAME, "RosettaCode DeathStar"]); |
llSetPrimitiveParams([PRIM_NAME, "RosettaCode DeathStar"]); |
||
Line 907: | Line 1,632: | ||
llSetPrimitiveParams([PRIM_OMEGA, <0.0, 0.0, 1.0>, 1.0, 1.0]); |
llSetPrimitiveParams([PRIM_OMEGA, <0.0, 0.0, 1.0>, 1.0, 1.0]); |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
Output: |
Output: |
||
[[File:Death_Star_LSL.jpg|200px|Death Star]] |
[[File:Death_Star_LSL.jpg|200px|Death Star]] |
||
=={{header|Lua}}== |
|||
{{trans|C}} |
|||
<syntaxhighlight lang="lua">function V3(x,y,z) return {x=x,y=y,z=z} end |
|||
function dot(v,w) return v.x*w.x + v.y*w.y + v.z*w.z end |
|||
function norm(v) local m=math.sqrt(dot(v,v)) return V3(v.x/m, v.y/m, v.z/m) end |
|||
function clamp(n,lo,hi) return math.floor(math.min(math.max(lo,n),hi)) end |
|||
function hittest(s, x, y) |
|||
local z = s.r^2 - (x-s.x)^2 - (y-s.y)^2 |
|||
if z >= 0 then |
|||
z = math.sqrt(z) |
|||
return true, s.z-z, s.z+z |
|||
end |
|||
return false, 0, 0 |
|||
end |
|||
function deathstar(pos, neg, sun, k, amb) |
|||
shades = {[0]=" ",".",":","!","*","o","e","&","#","%","@"} |
|||
for y = pos.x-pos.r-0.5, pos.x+pos.r+0.5 do |
|||
for x = pos.x-pos.r-0.5, pos.x+pos.r+0.5, 0.5 do |
|||
local hitpos, pz1, pz2 = hittest(pos, x, y) |
|||
local result, hitneg, nz1, nz2 = 0 |
|||
if hitpos then |
|||
hitneg, nz1, nz2 = hittest(neg, x, y) |
|||
if not hitneg or nz1 > pz1 then result = 1 |
|||
elseif nz2 > pz2 then result = 0 |
|||
elseif nz2 > pz1 then result = 2 |
|||
else result = 1 |
|||
end |
|||
end |
|||
local shade = 0 |
|||
if result > 0 then |
|||
if result == 1 then |
|||
shade = clamp((1-dot(sun, norm(V3(x-pos.x, y-pos.y, pz1-pos.z)))^k+amb) * #shades, 1, #shades) |
|||
else |
|||
shade = clamp((1-dot(sun, norm(V3(neg.x-x, neg.y-y, neg.z-nz2)))^k+amb) * #shades, 1, #shades) |
|||
end |
|||
end |
|||
io.write(shades[shade]) |
|||
end |
|||
io.write("\n") |
|||
end |
|||
end |
|||
deathstar({x=20, y=20, z=0, r=20}, {x=10, y=10, z=-15, r=10}, norm(V3(-2,1,3)), 2, 0.1)</syntaxhighlight> |
|||
{{out}} |
|||
<pre style="font-size:50%"> @@@%%%%%%%%%#########% |
|||
@@@@@%%%%%%#######&&&&&&&&&&&&&&&&&&## |
|||
@@@@@@%%%%%%######&&&&&&&eeeeeeeeeeeeeeeeeeeee&& |
|||
@@@@@@@@@@@@@@@@@@@&&&&&&eeeeeeeoooooooooooooooooooooeee& |
|||
@@@@@&####%%%%@@@@@@@@@@@@%eeeeoooooooo*******************oooee |
|||
@@@@eeee&&&&####%%%@@@@@@@@@@@@%oooooo********!!!!!!!!!!!!!!*****oooe |
|||
@@@**ooooeeee&&&####%%%@@@@@@@@@@@%oo*******!!!!!!!!!!!!!!!!!!!!!!!****oo& |
|||
@@@!!!****ooooeee&&&###%%%%@@@@@@@@@@%*****!!!!!!!!:::::::::::::::::!!!!!**ooe |
|||
@@@:::!!!!!****oooeee&&###%%%%@@@@@@@@@%***!!!!!!!::::::::::::::::::::::::!!!***oe |
|||
@@@@::::::::!!!***oooeee&&&##%%%%@@@@@@@@@**!!!!!!!:::::::::............::::::!!!**oo |
|||
@@@.......::::!!!!***ooeee&&###%%%@@@@@@@@@*!!!!!!::::::::..................:::::!!!**oe |
|||
%@@@@.........::::!!!**oooee&&&##%%%@@@@@@@@*!!!!!!::::::::......................::::!!!*oe |
|||
%@@@@...........:::!!!***ooeee&&###%%%@@@@@@**!!!!!!:::::::........................::::!!!*oo |
|||
%@@@@@...........:::!!!***ooeee&&###%%@@@@@@***!!!!!!:::::::.........................::::!!!*oe |
|||
@@@@@@..........::::!!!**oooee&&&##%%%@@@@@****!!!!!!:::::::..........................::::!!**oe |
|||
%@@@@@@::::...:::::!!!!***ooeee&&###%%@@@@o*****!!!!!!:::::::..........................::::!!!**o |
|||
@@@@@@@@!!!:::::!!!!!***oooee&&&##%%%@@oooo******!!!!!!:::::::.........................:::::!!**oe |
|||
%@@@@@@@@@o****!******ooooeee&&###%%%eeeooooo*****!!!!!!!:::::::........................::::!!!**oe |
|||
%@@@@@@@@@@@eeoooooooeeeee&&&##%%%&eeeeeeooooo*****!!!!!!!::::::::.....................:::::!!!**oo& |
|||
%@@@@@@@@@@@@@@@@##&&&#####%###&&&&&eeeeeoooooo******!!!!!!:::::::::..................:::::!!!!**oe& |
|||
%@@@@@@@@@@@@@@@@@@@%%%%%%######&&&&&eeeeeoooooo******!!!!!!!:::::::::::...........:::::::!!!!***oe& |
|||
%%@@@@@@@@@@@@@@@@@@@%%%%%%######&&&&&eeeeeeooooo*******!!!!!!!::::::::::::::::::::::::::!!!!***ooe& |
|||
#%@@@@@@@@@@@@@@@@@@@%%%%%%%######&&&&&eeeeeeoooooo*******!!!!!!!!::::::::::::::::::::!!!!!!***ooee |
|||
%%@@@@@@@@@@@@@@@@@@@%%%%%%%######&&&&&&eeeeeeoooooo*******!!!!!!!!!!!!::::::::::!!!!!!!!****ooee& |
|||
#%@@@@@@@@@@@@@@@@@@@@%%%%%%%######&&&&&&eeeeeeooooooo*********!!!!!!!!!!!!!!!!!!!!!!!!****oooee& |
|||
%%@@@@@@@@@@@@@@@@@@@@@%%%%%%%######&&&&&&eeeeeeeooooooo**********!!!!!!!!!!!!!!!*******ooooee&# |
|||
&%%@@@@@@@@@@@@@@@@@@@@@%%%%%%%#######&&&&&&eeeeeeeooooooooo*************************oooooeee&# |
|||
#%%@@@@@@@@@@@@@@@@@@@@@@%%%%%%%#######&&&&&&&eeeeeeeooooooooooo****************ooooooeeee&&# |
|||
&%%@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%#######&&&&&&&&eeeeeeeeeoooooooooooooooooooooooooeeeee&&## |
|||
#%%@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%########&&&&&&&&eeeeeeeeeeeeoooooooooooooeeeeeeee&&&##% |
|||
#%%@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########&&&&&&&&&&eeeeeeeeeeeeeeeeeeeeeeee&&&&&##% |
|||
#%%@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%#########&&&&&&&&&&&&&&&&eeeee&&&&&&&&&&####%% |
|||
#%%@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%%###########&&&&&&&&&&&&&&&&&&&&######%%% |
|||
#%%@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%%%#############################%%%%@ |
|||
#%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%%%%%%%###############%%%%%%%@ |
|||
#%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%%%%%%%%%%%%%%%%%%%@@@ |
|||
#%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%@@@@@@@@@ |
|||
%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
|||
%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
|||
@@@@@@@@@@@@@@@@@@@@@@</pre> |
|||
=={{header|Maple}}== |
|||
<syntaxhighlight lang="maple">with(plots): |
|||
with(plottools): |
|||
plots:-display( |
|||
implicitplot3d(x^2 + y^2 + z^2 = 1, x = -1..0.85, y = -1..1, z = -1..1, style = surface, grid = [50,50,50]), |
|||
translate(rotate(implicitplot3d(x^2 + y^2 + z^2 = 1, x = 0.85..1, y = -1..1, z = -1..1, style = surface, grid = [50,50,50]), 0, Pi, 0), 1.70, 0, 0), |
|||
axes = none, scaling = constrained, color = gray)</syntaxhighlight> |
|||
=={{header|Mathematica}} / {{header|Wolfram Language}}== |
=={{header|Mathematica}} / {{header|Wolfram Language}}== |
||
< |
<syntaxhighlight lang="mathematica">RegionPlot3D[x^2 + y^2 + z^2 < 1 && (x + 1.7)^2 + y^2 + z^2 > 1, |
||
{x, -1, 1}, {y, -1, 1}, {z, -1, 1}, |
{x, -1, 1}, {y, -1, 1}, {z, -1, 1}, |
||
Boxed -> False, Mesh -> False, Axes -> False, Background -> Black, PlotPoints -> 100]</ |
Boxed -> False, Mesh -> False, Axes -> False, Background -> Black, PlotPoints -> 100]</syntaxhighlight> |
||
=={{header|Nim}}== |
|||
{{trans|Go}} |
|||
{{libheader|nimPNG}} |
|||
The result is written in a PNG file. For this purpose, we used the modules “bitmap” and “grayscale_image” created for the tasks “Bitmap” and “Grayscale image”. To write the PNG file, we use the third party library “nimPNG”. |
|||
<syntaxhighlight lang="nim">import math |
|||
import bitmap, grayscale_image, nimPNG |
|||
type |
|||
Vector = array[3, float] |
|||
Sphere = object |
|||
cx, cy, cz: int |
|||
r: int |
|||
#--------------------------------------------------------------------------------------------------- |
|||
func dot(x, y: Vector): float {.inline.} = |
|||
x[0] * y[0] + x[1] * y[1] + x[2] * y[2] |
|||
#--------------------------------------------------------------------------------------------------- |
|||
func normalize(v: var Vector) = |
|||
let invLen = 1 / sqrt(dot(v, v)) |
|||
v[0] *= invLen |
|||
v[1] *= invLen |
|||
v[2] *= invLen |
|||
#--------------------------------------------------------------------------------------------------- |
|||
func hit(s: Sphere; x, y: int): tuple[z1, z2: float; hit: bool] = |
|||
let x = x - s.cx |
|||
let y = y - s.cy |
|||
let zsq = s.r * s.r - (x * x + y * y) |
|||
if zsq >= 0: |
|||
let zsqrt = sqrt(zsq.toFloat) |
|||
result = (s.cz.toFloat - zsqrt, s.cz.toFloat, true) |
|||
else: |
|||
result = (0.0, 0.0, false) |
|||
#--------------------------------------------------------------------------------------------------- |
|||
func deathStar(pos, neg: Sphere; k, amb: float; dir: Vector): GrayImage = |
|||
let w = pos.r * 4 |
|||
let h = pos.r * 3 |
|||
result = newGrayImage(w, h) |
|||
var vect: Vector |
|||
let deltaX = pos.cx - w div 2 |
|||
let deltaY = pos.cy - h div 2 |
|||
let xMax = pos.cx + pos.r |
|||
let yMax = pos.cy + pos.r |
|||
for y in (pos.cy - pos.r)..yMax: |
|||
for x in (pos.cx - pos.r)..xMax: |
|||
let (zb1, zb2, posHit) = pos.hit(x, y) |
|||
if not posHit: continue |
|||
var (zs1, zs2, negHit) = neg.hit(x, y) |
|||
if negHit: |
|||
if zs1 > zb1: negHit = false |
|||
elif zs2 > zb2: continue |
|||
if negHit: |
|||
vect[0] = (neg.cx - x).toFloat |
|||
vect[1] = (neg.cy - y).toFloat |
|||
vect[2] = neg.cz.toFloat - zs2 |
|||
else: |
|||
vect[0] = (x - pos.cx).toFloat |
|||
vect[1] = (y - pos.cy).toFloat |
|||
vect[2] = zb1 - pos.cz.toFloat |
|||
vect.normalize() |
|||
var s = dot(dir, vect) |
|||
if s < 0: s = 0 |
|||
var lum = (255 * (s.pow(k) + amb) / (1 + amb)).toInt |
|||
if lum < 0: lum = 0 |
|||
elif lum > 255: lum = 255 |
|||
result[x - deltaX, y - deltaY] = Luminance(lum) |
|||
#——————————————————————————————————————————————————————————————————————————————————————————————————— |
|||
var dir: Vector = [float 20, -40, -10] |
|||
dir.normalize() |
|||
let pos = Sphere(cx: 0, cy: 0, cz: 0, r: 120) |
|||
let neg = Sphere(cx: -90, cy: -90, cz: -30, r: 100) |
|||
let grayImage = deathStar(pos, neg, 1.5, 0.2, dir) |
|||
# Save to PNG. We convert to an RGB image then transform the pixels |
|||
# in a sequence of bytes (actually a copy) in order to call "savePNG24". |
|||
let rgbImage = grayImage.toImage |
|||
var data = newSeqOfCap[byte](rgbImage.pixels.len * 3) |
|||
for color in rgbImage.pixels: |
|||
data.add([color.r, color.g, color.b]) |
|||
echo savePNG24("death_star.png", data, rgbImage.w, rgbImage.h)</syntaxhighlight> |
|||
=={{header|Openscad}}== |
=={{header|Openscad}}== |
||
< |
<syntaxhighlight lang="openscad">// We are performing geometric subtraction |
||
difference() { |
difference() { |
||
Line 935: | Line 1,851: | ||
sphere(40); |
sphere(40); |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
=={{header|Perl}}== |
=={{header|Perl}}== |
||
[[file:death-star-perl.png|thumb]] |
[[file:death-star-perl.png|thumb]] |
||
Writes a PGM to stdout. |
Writes a PGM to stdout. |
||
< |
<syntaxhighlight lang="perl">use strict; |
||
sub sq { |
sub sq { |
||
Line 1,014: | Line 1,930: | ||
} |
} |
||
draw(2, 0.2);</ |
draw(2, 0.2);</syntaxhighlight> |
||
=={{header|Perl 6}}== |
|||
{{trans|C}}Reimplemented to output a .pgm image. |
|||
{{works with|Rakudo|2018.10}} |
|||
[[File:Deathstar-perl6.png|thumb]] |
|||
<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 $width = my $height = 255; # dimensions of generated .pgm; must be odd |
|||
my $s = ($width - 1)/2; # scaled dimension to build geometry |
|||
my @light = normalize([ 4, -1, -3 ]); |
|||
# positive sphere at origin |
|||
my $pos = sphere.new( |
|||
cx => 0, |
|||
cy => 0, |
|||
cz => 0, |
|||
r => $s.Int |
|||
); |
|||
# negative sphere offset to upper left |
|||
my $neg = sphere.new( |
|||
cx => (-$s*.90).Int, |
|||
cy => (-$s*.90).Int, |
|||
cz => (-$s*.3).Int, |
|||
r => ($s*.7).Int |
|||
); |
|||
sub MAIN ($outfile = 'deathstar-perl6.pgm') { |
|||
spurt $outfile, ("P5\n$width $height\n$depth\n"); # .pgm header |
|||
my $out = open( $outfile, :a, :bin ) orelse .die; |
|||
say 'Working...'; |
|||
$out.write( Blob.new( |draw_ds(3, .15) ) ); |
|||
say 'File written.'; |
|||
$out.close; |
|||
} |
|||
sub draw_ds ( $k, $ambient ) { |
|||
my @pixels[$height]; |
|||
(($pos.cy - $pos.r) .. ($pos.cy + $pos.r)).race.map: -> $y { |
|||
my @row[$width]; |
|||
(($pos.cx - $pos.r) .. ($pos.cx + $pos.r)).map: -> $x { |
|||
# black if we don't hit positive sphere, ignore negative sphere |
|||
if not hit($pos, $x, $y, my $posz) { |
|||
@row[$x + $s] = 0; |
|||
next; |
|||
} |
|||
my @vec; |
|||
# is front of positive sphere inside negative sphere? |
|||
if hit($neg, $x, $y, my $negz) and $negz.min < $posz.min < $negz.max { |
|||
# make black if whole positive sphere eaten here |
|||
if $negz.min < $posz.max < $negz.max { @row[$x + $s] = 0; next; } |
|||
# render inside of negative sphere |
|||
@vec = normalize([$neg.cx - $x, $neg.cy - $y, -$negz.max - $neg.cz]); |
|||
} |
|||
else { |
|||
# render outside of positive sphere |
|||
@vec = normalize([$x - $pos.cx, $y - $pos.cy, $posz.max - $pos.cz]); |
|||
} |
|||
my $intensity = dot(@light, @vec) ** $k + $ambient; |
|||
@row[$x + $s] = ($intensity * $depth).Int min $depth; |
|||
} |
|||
@pixels[$y + $s] = @row; |
|||
} |
|||
flat |@pixels.map: *.list; |
|||
} |
|||
# normalize a vector |
|||
sub normalize (@vec) { @vec »/» ([+] @vec »*« @vec).sqrt } |
|||
# dot product of two vectors |
|||
sub dot (@x, @y) { -([+] @x »*« @y) max 0 } |
|||
# 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; |
|||
1; |
|||
}</lang> |
|||
=={{header|Phix}}== |
=={{header|Phix}}== |
||
{{trans|Go}} |
{{trans|Go}} |
||
{{libheader|pGUI}} |
{{libheader|Phix/pGUI}} |
||
{{libheader|Phix/online}} |
|||
<lang Phix>-- |
|||
You can run this online [http://phix.x10.mx/p2js/deathstar.htm here]. Note it is rather slow to redraw fullscreen. |
|||
-- demo\rosetta\DeathStar.exw |
|||
<!--<syntaxhighlight lang="phix">(phixonline)--> |
|||
-- |
|||
<span style="color: #000080;font-style:italic;">-- |
|||
include pGUI.e |
|||
-- demo\rosetta\DeathStar.exw |
|||
-- ========================== |
|||
Ihandle dlg, canvas |
|||
-- |
|||
cdCanvas cddbuffer, cdcanvas |
|||
-- Translated from Go. |
|||
--</span> |
|||
function dot(sequence x, sequence y) |
|||
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span> |
|||
return sum(sq_mul(x,y)) |
|||
<span style="color: #008080;">include</span> <span style="color: #000000;">pGUI</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span> |
|||
end function |
|||
function normalize(sequence v) |
|||
atom len = sqrt(dot(v, v)) |
|||
if len=0 then return {0,0,0} end if |
|||
return sq_mul(v,1/len) |
|||
end function |
|||
enum X,Y,Z |
|||
function hit(sequence s, atom x, y, atom r) |
|||
x -= s[X] |
|||
y -= s[Y] |
|||
atom zsq := r*r - (x*x + y*y) |
|||
if zsq >= 0 then |
|||
atom zsqrt := sqrt(zsq) |
|||
return {s[Z] - zsqrt, s[Z] + zsqrt, true} |
|||
end if |
|||
return {0, 0, false} |
|||
end function |
|||
<span style="color: #008080;">constant</span> <span style="color: #000000;">title</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"Death Star"</span> |
|||
procedure deathStar(integer width, height, atom k, atom amb, sequence direction) |
|||
<span style="color: #004080;">Ihandle</span> <span style="color: #000000;">dlg</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">canvas</span> |
|||
integer lum |
|||
<span style="color: #004080;">cdCanvas</span> <span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">cdcanvas</span> |
|||
sequence vec |
|||
integer r = floor((min(width,height)-40)/2) |
|||
integer cx = floor(width/2) |
|||
integer cy = floor(height/2) |
|||
sequence pos = {0,0,0}, |
|||
neg = {r*-3/4,r*-3/4,r*-1/4} |
|||
for y = pos[Y]-r to pos[Y]+r do |
|||
for x = pos[X]-r to pos[X]+r do |
|||
atom {zb1, zb2, hit1} := hit(pos, x, y, r) |
|||
if hit1 then |
|||
atom {zs1, zs2, hit2} := hit(neg, x, y, r/2) |
|||
if not hit2 or zs2<=zb2 then |
|||
if hit2 and zs1<=zb1 then |
|||
vec = {neg[X] - x, neg[Y] - y, neg[Z] - zs2} |
|||
else |
|||
vec = {x - pos[X], y - pos[Y], zb1 - pos[Z]} |
|||
-- vec = {x, y, zb1} |
|||
end if |
|||
atom s = dot(direction, normalize(vec)) |
|||
lum = and_bits(#FF,255*(iff(s<0?0:power(s,k))+amb)/(1+amb)) |
|||
lum += lum*#100+lum*#10000 |
|||
cdCanvasPixel(cddbuffer, cx+x, cy-y, lum) |
|||
end if |
|||
end if |
|||
end for |
|||
end for |
|||
end procedure |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">dot</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">x</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">sequence</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">)</span> |
|||
function redraw_cb(Ihandle /*ih*/, integer /*posx*/, integer /*posy*/) |
|||
<span style="color: #008080;">return</span> <span style="color: #7060A8;">sum</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">sq_mul</span><span style="color: #0000FF;">(</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">))</span> |
|||
integer {width, height} = IupGetIntInt(canvas, "DRAWSIZE") |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
cdCanvasActivate(cddbuffer) |
|||
cdCanvasClear(cddbuffer) |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">normalize</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">v</span><span style="color: #0000FF;">)</span> |
|||
deathStar(width, height, 1.5, 0.2, normalize({20, -40, -10})) |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">len</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sqrt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dot</span><span style="color: #0000FF;">(</span><span style="color: #000000;">v</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">v</span><span style="color: #0000FF;">))</span> |
|||
cdCanvasFlush(cddbuffer) |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">len</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">}</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
return IUP_DEFAULT |
|||
<span style="color: #008080;">return</span> <span style="color: #7060A8;">sq_mul</span><span style="color: #0000FF;">(</span><span style="color: #000000;">v</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">/</span><span style="color: #000000;">len</span><span style="color: #0000FF;">)</span> |
|||
end function |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
function map_cb(Ihandle ih) |
|||
<span style="color: #008080;">enum</span> <span style="color: #000000;">X</span><span style="color: #0000FF;">,</span><span style="color: #000000;">Y</span><span style="color: #0000FF;">,</span><span style="color: #000000;">Z</span> |
|||
cdcanvas = cdCreateCanvas(CD_IUP, ih) |
|||
cddbuffer = cdCreateCanvas(CD_DBUFFER, cdcanvas) |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">hit</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">atom</span> <span style="color: #000000;">x</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">r</span><span style="color: #0000FF;">)</span> |
|||
cdCanvasSetBackground(cddbuffer, CD_BLACK) |
|||
<span style="color: #000000;">x</span> <span style="color: #0000FF;">-=</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">X</span><span style="color: #0000FF;">]</span> |
|||
return IUP_DEFAULT |
|||
<span style="color: #000000;">y</span> <span style="color: #0000FF;">-=</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">Y</span><span style="color: #0000FF;">]</span> |
|||
end function |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">zsq</span> <span style="color: #0000FF;">:=</span> <span style="color: #000000;">r</span><span style="color: #0000FF;">*</span><span style="color: #000000;">r</span> <span style="color: #0000FF;">-</span> <span style="color: #0000FF;">(</span><span style="color: #000000;">x</span><span style="color: #0000FF;">*</span><span style="color: #000000;">x</span> <span style="color: #0000FF;">+</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">*</span><span style="color: #000000;">y</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">zsq</span> <span style="color: #0000FF;">>=</span> <span style="color: #000000;">0</span> <span style="color: #008080;">then</span> |
|||
function esc_close(Ihandle /*ih*/, atom c) |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">zsqrt</span> <span style="color: #0000FF;">:=</span> <span style="color: #7060A8;">sqrt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">zsq</span><span style="color: #0000FF;">)</span> |
|||
if c=K_ESC then return IUP_CLOSE end if |
|||
<span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">Z</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">-</span> <span style="color: #000000;">zsqrt</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">Z</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">+</span> <span style="color: #000000;">zsqrt</span><span style="color: #0000FF;">,</span> <span style="color: #004600;">true</span><span style="color: #0000FF;">}</span> |
|||
return IUP_CONTINUE |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
end function |
|||
<span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">0</span><span style="color: #0000FF;">,</span> <span style="color: #004600;">false</span><span style="color: #0000FF;">}</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
procedure main() |
|||
IupOpen() |
|||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">deathStar</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">width</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">height</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">atom</span> <span style="color: #000000;">k</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">atom</span> <span style="color: #000000;">amb</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">sequence</span> <span style="color: #000000;">direction</span><span style="color: #0000FF;">)</span> |
|||
canvas = IupCanvas(NULL) |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">t0</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">time</span><span style="color: #0000FF;">()+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">t1</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">t0</span><span style="color: #0000FF;">,</span> |
|||
IupSetAttribute(canvas, "RASTERSIZE", "340x340") -- initial size |
|||
<span style="color: #000000;">lmul</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">255</span><span style="color: #0000FF;">/(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">+</span><span style="color: #000000;">amb</span><span style="color: #0000FF;">)</span> |
|||
IupSetCallback(canvas, "MAP_CB", Icallback("map_cb")) |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">r</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">floor</span><span style="color: #0000FF;">((</span><span style="color: #7060A8;">min</span><span style="color: #0000FF;">(</span><span style="color: #000000;">width</span><span style="color: #0000FF;">,</span><span style="color: #000000;">height</span><span style="color: #0000FF;">)-</span><span style="color: #000000;">40</span><span style="color: #0000FF;">)/</span><span style="color: #000000;">2</span><span style="color: #0000FF;">),</span> |
|||
<span style="color: #000000;">cx</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">floor</span><span style="color: #0000FF;">(</span><span style="color: #000000;">width</span><span style="color: #0000FF;">/</span><span style="color: #000000;">2</span><span style="color: #0000FF;">),</span> |
|||
dlg = IupDialog(canvas) |
|||
<span style="color: #000000;">cy</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">floor</span><span style="color: #0000FF;">(</span><span style="color: #000000;">height</span><span style="color: #0000FF;">/</span><span style="color: #000000;">2</span><span style="color: #0000FF;">)</span> |
|||
IupSetAttribute(dlg, "TITLE", "Draw a sphere") |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">pos</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">},</span> |
|||
IupSetCallback(dlg, "K_ANY", Icallback("esc_close")) |
|||
<span style="color: #000000;">neg</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">r</span><span style="color: #0000FF;">*-</span><span style="color: #000000;">3</span><span style="color: #0000FF;">/</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">r</span><span style="color: #0000FF;">*-</span><span style="color: #000000;">3</span><span style="color: #0000FF;">/</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">r</span><span style="color: #0000FF;">*-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">/</span><span style="color: #000000;">4</span><span style="color: #0000FF;">}</span> |
|||
IupSetCallback(canvas, "ACTION", Icallback("redraw_cb")) |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">y</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">r</span> <span style="color: #008080;">to</span> <span style="color: #0000FF;">+</span><span style="color: #000000;">r</span> <span style="color: #008080;">do</span> |
|||
IupMap(dlg) |
|||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">time</span><span style="color: #0000FF;">()></span><span style="color: #000000;">t1</span> <span style="color: #008080;">then</span> |
|||
IupSetAttribute(canvas, "RASTERSIZE", NULL) -- release the minimum limitation |
|||
<span style="color: #000080;font-style:italic;">-- Let the user know we aren't completely dead just yet</span> |
|||
IupShowXY(dlg,IUP_CENTER,IUP_CENTER) |
|||
<span style="color: #7060A8;">IupSetStrAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dlg</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"TITLE"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%s - drawing (%d%%)"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">title</span><span style="color: #0000FF;">,</span><span style="color: #000000;">100</span><span style="color: #0000FF;">*(</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">r</span><span style="color: #0000FF;">)/(</span><span style="color: #000000;">2</span><span style="color: #0000FF;">*</span><span style="color: #000000;">r</span><span style="color: #0000FF;">)})</span> |
|||
IupMainLoop() |
|||
<span style="color: #000000;">t1</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">time</span><span style="color: #0000FF;">()+</span><span style="color: #000000;">1</span> |
|||
IupClose() |
|||
<span style="color: #000080;font-style:italic;">-- |
|||
end procedure |
|||
-- Hmm, not entirely sure why this is needed, but without it |
|||
-- after ~7 seconds the window gets a "(Not Responding)" and |
|||
main()</lang> |
|||
-- then something decides to force a full repaint, which at |
|||
-- fullscreen will never finish in < 7s on this ancient box. |
|||
-- I suppose this is the corrollary to the above, this time |
|||
-- letting Windows 10 know the process is not quite dead... |
|||
-- Currently and possibly forever neither of these routines |
|||
-- exist in pGUI.js, the browser is more forgiving anyway. |
|||
--</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">platform</span><span style="color: #0000FF;">()!=</span><span style="color: #004600;">JS</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">IupLoopStep</span><span style="color: #0000FF;">()=</span><span style="color: #004600;">IUP_CLOSE</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #7060A8;">IupExitLoop</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #008080;">exit</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">x</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">r</span> <span style="color: #008080;">to</span> <span style="color: #0000FF;">+</span><span style="color: #000000;">r</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">atom</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">zb1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">zb2</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">hit1</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">:=</span> <span style="color: #000000;">hit</span><span style="color: #0000FF;">(</span><span style="color: #000000;">pos</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">x</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">r</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">hit1</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #004080;">atom</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">zs1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">zs2</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">hit2</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">:=</span> <span style="color: #000000;">hit</span><span style="color: #0000FF;">(</span><span style="color: #000000;">neg</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">x</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">r</span><span style="color: #0000FF;">/</span><span style="color: #000000;">2</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #008080;">not</span> <span style="color: #000000;">hit2</span> <span style="color: #008080;">or</span> <span style="color: #000000;">zs2</span><span style="color: #0000FF;"><=</span><span style="color: #000000;">zb2</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #004080;">bool</span> <span style="color: #000000;">dish</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">hit2</span> <span style="color: #008080;">and</span> <span style="color: #000000;">zs1</span><span style="color: #0000FF;"><=</span><span style="color: #000000;">zb1</span> |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">vec</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dish</span><span style="color: #0000FF;">?</span><span style="color: #7060A8;">sq_sub</span><span style="color: #0000FF;">(</span><span style="color: #000000;">neg</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">,</span><span style="color: #000000;">zs2</span><span style="color: #0000FF;">}):{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">,</span><span style="color: #000000;">zb1</span><span style="color: #0000FF;">})</span> |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">s</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">dot</span><span style="color: #0000FF;">(</span><span style="color: #000000;">direction</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">normalize</span><span style="color: #0000FF;">(</span><span style="color: #000000;">vec</span><span style="color: #0000FF;">)),</span> |
|||
<span style="color: #000000;">l</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;"><=</span><span style="color: #000000;">0</span><span style="color: #0000FF;">?</span><span style="color: #000000;">0</span><span style="color: #0000FF;">:</span><span style="color: #7060A8;">power</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">,</span><span style="color: #000000;">k</span><span style="color: #0000FF;">))</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">lum</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">and_bits</span><span style="color: #0000FF;">(</span><span style="color: #000000;">#FF</span><span style="color: #0000FF;">,</span><span style="color: #000000;">lmul</span><span style="color: #0000FF;">*(</span><span style="color: #000000;">l</span><span style="color: #0000FF;">+</span><span style="color: #000000;">amb</span><span style="color: #0000FF;">))</span> |
|||
<span style="color: #7060A8;">cdCanvasPixel</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">cx</span><span style="color: #0000FF;">+</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">cy</span><span style="color: #0000FF;">-</span><span style="color: #000000;">y</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">lum</span><span style="color: #0000FF;">*</span><span style="color: #000000;">#10101</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">t1</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">t0</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #7060A8;">IupSetStrAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dlg</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"TITLE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">title</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">redraw_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000080;font-style:italic;">/*ih*/</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">integer</span> <span style="color: #000080;font-style:italic;">/*posx*/</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">/*posy*/</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">width</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">height</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupGetIntInt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"DRAWSIZE"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">cdCanvasActivate</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">cdCanvasClear</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">deathStar</span><span style="color: #0000FF;">(</span><span style="color: #000000;">width</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">height</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">1.5</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">0.2</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">normalize</span><span style="color: #0000FF;">({</span><span style="color: #000000;">20</span><span style="color: #0000FF;">,</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">40</span><span style="color: #0000FF;">,</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">10</span><span style="color: #0000FF;">}))</span> |
|||
<span style="color: #7060A8;">cdCanvasFlush</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">map_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000000;">ih</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">cdcanvas</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">cdCreateCanvas</span><span style="color: #0000FF;">(</span><span style="color: #004600;">CD_IUP</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">ih</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">cddbuffer</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">cdCreateCanvas</span><span style="color: #0000FF;">(</span><span style="color: #004600;">CD_DBUFFER</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">cdcanvas</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">cdCanvasSetBackground</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cddbuffer</span><span style="color: #0000FF;">,</span> <span style="color: #004600;">CD_BLACK</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">main</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #7060A8;">IupOpen</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #000000;">canvas</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupCanvas</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"RASTERSIZE=340x340"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">IupSetCallbacks</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">,</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"MAP_CB"</span><span style="color: #0000FF;">,</span> <span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"map_cb"</span><span style="color: #0000FF;">),</span> |
|||
<span style="color: #008000;">"ACTION"</span><span style="color: #0000FF;">,</span> <span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"redraw_cb"</span><span style="color: #0000FF;">)})</span> |
|||
<span style="color: #000000;">dlg</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupDialog</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">,</span><span style="color: #008000;">`TITLE="%s"`</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">title</span><span style="color: #0000FF;">})</span> |
|||
<span style="color: #7060A8;">IupMap</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dlg</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">IupSetAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">canvas</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"RASTERSIZE"</span><span style="color: #0000FF;">,</span> <span style="color: #004600;">NULL</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- release the minimum limitation</span> |
|||
<span style="color: #7060A8;">IupShow</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dlg</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">platform</span><span style="color: #0000FF;">()!=</span><span style="color: #004600;">JS</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #7060A8;">IupMainLoop</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #7060A8;">IupClose</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span> |
|||
<span style="color: #000000;">main</span><span style="color: #0000FF;">()</span> |
|||
<!--</syntaxhighlight>--> |
|||
=={{header|POV-Ray}}== |
=={{header|POV-Ray}}== |
||
< |
<syntaxhighlight lang="pov">camera { perspective location <0.0 , .8 ,-3.0> look_at 0 |
||
aperture .1 blur_samples 20 variance 1/100000 focal_point 0} |
aperture .1 blur_samples 20 variance 1/100000 focal_point 0} |
||
Line 1,232: | Line 2,078: | ||
finish { phong 1 reflection {0.10 metallic 0.5} } |
finish { phong 1 reflection {0.10 metallic 0.5} } |
||
} |
} |
||
} </ |
} </syntaxhighlight> |
||
[[image:PovRay-deathstar.jpg]] |
[[image:PovRay-deathstar.jpg]] |
||
=={{header|Python}}== |
=={{header|Python}}== |
||
{{trans|C}} |
{{trans|C}} |
||
< |
<syntaxhighlight lang="python">import sys, math, collections |
||
Sphere = collections.namedtuple("Sphere", "cx cy cz r") |
Sphere = collections.namedtuple("Sphere", "cx cy cz r") |
||
Line 1,303: | Line 2,149: | ||
light = normalize(V3(-50, 30, 50)) |
light = normalize(V3(-50, 30, 50)) |
||
draw_sphere(2, 0.5, light)</ |
draw_sphere(2, 0.5, light)</syntaxhighlight> |
||
=={{header|Q}}== |
=={{header|Q}}== |
||
write an image in BMP format: |
write an image in BMP format: |
||
<syntaxhighlight lang="q"> |
|||
<lang Q> |
|||
/ https://en.wikipedia.org/wiki/BMP_file_format |
/ https://en.wikipedia.org/wiki/BMP_file_format |
||
/ BITMAPINFOHEADER / RGB24 |
/ BITMAPINFOHEADER / RGB24 |
||
Line 1,352: | Line 2,196: | ||
/ fn:`:demo.bmp; |
/ fn:`:demo.bmp; |
||
/ writebmp[w;h;fcn;fn]; |
/ writebmp[w;h;fcn;fn]; |
||
</syntaxhighlight> |
|||
</lang> |
|||
Create the death star image: |
Create the death star image: |
||
<syntaxhighlight lang="q"> |
|||
<lang Q> |
|||
w:400; h:300; r:150; l:-0.5 0.7 0.5 |
w:400; h:300; r:150; l:-0.5 0.7 0.5 |
||
sqrt0:{$[x>0;sqrt x;0]}; |
sqrt0:{$[x>0;sqrt x;0]}; |
||
Line 1,396: | Line 2,240: | ||
writebmp[w;h;fcn;fn]; |
writebmp[w;h;fcn;fn]; |
||
</ |
</syntaxhighlight>(converted to JPG ...) |
||
[[image:qdstar.jpg]] |
[[image:qdstar.jpg]] |
||
=={{header|Racket}}== |
=={{header|Racket}}== |
||
< |
<syntaxhighlight lang="racket"> |
||
#lang racket |
#lang racket |
||
(require plot) |
(require plot) |
||
Line 1,411: | Line 2,255: | ||
#:y-min -1/2 #:y-max 1/2 |
#:y-min -1/2 #:y-max 1/2 |
||
#:z-min 0 #:z-max 1) |
#:z-min 0 #:z-max 1) |
||
</syntaxhighlight> |
|||
</lang> |
|||
[[File:death-star.png]] |
[[File:death-star.png]] |
||
=={{header|Raku}}== |
|||
(formerly Perl 6) |
|||
{{trans|C}}Reimplemented to output a .pgm image. |
|||
{{works with|Rakudo|2018.10}} |
|||
[[File:Deathstar-perl6.png|thumb]] |
|||
<syntaxhighlight lang="raku" line>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 $width = my $height = 255; # dimensions of generated .pgm; must be odd |
|||
my $s = ($width - 1)/2; # scaled dimension to build geometry |
|||
my @light = normalize([ 4, -1, -3 ]); |
|||
# positive sphere at origin |
|||
my $pos = sphere.new( |
|||
cx => 0, |
|||
cy => 0, |
|||
cz => 0, |
|||
r => $s.Int |
|||
); |
|||
# negative sphere offset to upper left |
|||
my $neg = sphere.new( |
|||
cx => (-$s*.90).Int, |
|||
cy => (-$s*.90).Int, |
|||
cz => (-$s*.3).Int, |
|||
r => ($s*.7).Int |
|||
); |
|||
sub MAIN ($outfile = 'deathstar-perl6.pgm') { |
|||
spurt $outfile, ("P5\n$width $height\n$depth\n"); # .pgm header |
|||
my $out = open( $outfile, :a, :bin ) orelse .die; |
|||
say 'Working...'; |
|||
$out.write( Blob.new( |draw_ds(3, .15) ) ); |
|||
say 'File written.'; |
|||
$out.close; |
|||
} |
|||
sub draw_ds ( $k, $ambient ) { |
|||
my @pixels[$height]; |
|||
(($pos.cy - $pos.r) .. ($pos.cy + $pos.r)).race.map: -> $y { |
|||
my @row[$width]; |
|||
(($pos.cx - $pos.r) .. ($pos.cx + $pos.r)).map: -> $x { |
|||
# black if we don't hit positive sphere, ignore negative sphere |
|||
if not hit($pos, $x, $y, my $posz) { |
|||
@row[$x + $s] = 0; |
|||
next; |
|||
} |
|||
my @vec; |
|||
# is front of positive sphere inside negative sphere? |
|||
if hit($neg, $x, $y, my $negz) and $negz.min < $posz.min < $negz.max { |
|||
# make black if whole positive sphere eaten here |
|||
if $negz.min < $posz.max < $negz.max { @row[$x + $s] = 0; next; } |
|||
# render inside of negative sphere |
|||
@vec = normalize([$neg.cx - $x, $neg.cy - $y, -$negz.max - $neg.cz]); |
|||
} |
|||
else { |
|||
# render outside of positive sphere |
|||
@vec = normalize([$x - $pos.cx, $y - $pos.cy, $posz.max - $pos.cz]); |
|||
} |
|||
my $intensity = dot(@light, @vec) ** $k + $ambient; |
|||
@row[$x + $s] = ($intensity * $depth).Int min $depth; |
|||
} |
|||
@pixels[$y + $s] = @row; |
|||
} |
|||
flat |@pixels.map: *.list; |
|||
} |
|||
# normalize a vector |
|||
sub normalize (@vec) { @vec »/» ([+] @vec »*« @vec).sqrt } |
|||
# dot product of two vectors |
|||
sub dot (@x, @y) { -([+] @x »*« @y) max 0 } |
|||
# 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 False if $z2 < 0; |
|||
$z2 = $z2.sqrt; |
|||
$z = $sphere.cz - $z2 .. $sphere.cz + $z2; |
|||
True; |
|||
}</syntaxhighlight> |
|||
=={{header|REXX}}== |
=={{header|REXX}}== |
||
{{trans|D}} |
{{trans|D}} |
||
(Apologies for the comments making the lines so wide, but it was easier to read and compare to the original '''D''' source.) |
(Apologies for the comments making the lines so wide, but it was easier to read and compare to the original '''D''' source.) |
||
< |
<syntaxhighlight lang="rexx">/*REXX program displays a sphere with another sphere subtracted where it's superimposed.*/ |
||
call deathStar 2, .5, v3('-50 30 50') |
call deathStar 2, .5, v3('-50 30 50') |
||
exit /*stick a fork in it, we're all done. */ |
exit /*stick a fork in it, we're all done. */ |
||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
||
Line 1,427: | Line 2,365: | ||
v3: procedure; parse arg a b c; #=sqrt(a**2 + b**2 + c**2); return a/# b/# c/# |
v3: procedure; parse arg a b c; #=sqrt(a**2 + b**2 + c**2); return a/# b/# c/# |
||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
||
sqrt: procedure; parse arg x; |
sqrt: procedure; parse arg x; if x=0 then return 0; d=digits(); h= d+6; numeric digits |
||
numeric form; |
m.=9; numeric form; parse value format(x,2,1,,0) 'E0' with g 'E' _ .; g=g*.5'e'_%2 |
||
do j=0 while h>9; m.j= h; h= h % 2 + 1; end /*j*/ |
|||
do k=j+5 to 0 by -1; numeric digits m.k; g= (g +x/g)* .5; end /*k*/; return g |
|||
numeric digits d; return g/1 |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
||
hitSphere: procedure expose !.; |
hitSphere: procedure expose !.; parse arg xx yy zz r,x0,y0; z= r*r -(x0-xx)**2-(y0-yy)**2 |
||
if z<0 then return 0; |
if z<0 then return 0; _= sqrt(z); !.z1= zz - _; !.z2= zz + _; return 1 |
||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
||
deathStar: procedure; parse arg k,ambient,sun /* [↓] display the deathstar to screen*/ |
deathStar: procedure; parse arg k,ambient,sun /* [↓] display the deathstar to screen*/ |
||
parse var sun s1 s2 s3 /*identify the light source coördinates*/ |
parse var sun s1 s2 s3 /*identify the light source coördinates*/ |
||
if |
if 5=="f5"x then shading= '.:!*oe&#%@' /*dithering chars for an EBCDIC machine*/ |
||
else shading= '·:!ºoe@░▒▓' /* " |
else shading= '·:!ºoe@░▒▓' /* " " " " ASCII " */ |
||
shadingL=length(shading) |
shadingL= length(shading) /*the number of dithering characters. */ |
||
shades.=' '; do i=1 for shadingL; shades.i=substr(shading, i, 1) |
shades.= ' '; do i=1 for shadingL; shades.i= substr(shading, i, 1) |
||
end /*i*/ |
|||
ship= 20 20 0 20 ; parse var ship shipX shipY shipZ shipR |
|||
hole= ' 1 1 -6 20' ; parse var hole holeX holeY holeZ . |
|||
do i=floor(shipY-shipR ) to ceil(shipY+shipR )+1; y= i +.5; @= /*@ is a single line of the deathstar to be displayed.*/ |
|||
ship= 20 20 0 20 ; parse var ship shipX shipY shipZ shipR |
|||
do j=floor(shipX-shipR*2) to ceil(shipX+shipR*2)+1; !.= 0 |
|||
hole= ' 1 1 -6 20' ; parse var hole holeX holeY holeZ . |
|||
x=.5 * (j-shipX+1) + shipX; $bg= 0; $pos= 0; $neg= 0 /*$BG, $POS, and $NEG are boolean values. */ |
|||
?= hitSphere(ship, x, y); b1= !.z1; b2= !.z2 /*? is boolean, "true" indicates ray hits the sphere.*/ |
|||
/*$BG: if 1, its background; if zero, it's foreground.*/ |
|||
do j=floor(shipX-shipR*2) to ceil(shipX+shipR*2)+1; !.=0 |
|||
if \? then $bg= 1 /*ray lands in blank space, so draw the background. */ |
|||
?=hitSphere( |
else do; ?= hitSphere(hole, x, y); s1= !.z1; s2= !.z2 |
||
if \? then $pos= 1 /*ray hits ship but not the hole, so draw ship surface. */ |
|||
if \? then $bg=1 /*ray lands in blank space, so draw the background. */ |
|||
else do; ?=hitSphere(hole, x, y); s1=!.z1; s2=!.z2 |
|||
if \? then $pos=1 /*ray hits ship but not the hole, so draw ship surface. */ |
|||
else if s1>b1 then $pos=1 /*ray hits both, but ship front surface is closer. */ |
else if s1>b1 then $pos=1 /*ray hits both, but ship front surface is closer. */ |
||
else if s2>b2 then $bg= |
else if s2>b2 then $bg= 1 /*ship surface is inside hole, so show the background. */ |
||
else if s2>b1 then $neg=1 /*hole back surface is inside ship; the only place |
else if s2>b1 then $neg=1 /*hole back surface is inside ship; the only place ··· */ |
||
else $pos=1 /* |
else $pos=1 /*························ a hole surface will be shown.*/ |
||
end |
end |
||
select |
select |
||
when $bg then do; |
when $bg then do; @= @' '; iterate j; end /*append a blank character to the line to be displayed. */ |
||
when $pos then vec_= v3(x-shipX y-shipY b1-shipZ) |
when $pos then vec_= v3(x-shipX y-shipY b1-shipZ) |
||
when $neg then vec_= v3(holeX-x holeY-y holeZ-s2) |
when $neg then vec_= v3(holeX-x holeY-y holeZ-s2) |
||
end /*select*/ |
end /*select*/ |
||
b=1 +min(shadingL, max(0, trunc((1 - (dot.(sun, v3(vec_))**k + ambient)) * shadingL))) |
b=1 +min(shadingL, max(0, trunc((1 - (dot.(sun, v3(vec_))**k + ambient)) * shadingL))) |
||
@=@ || shades.b /*B |
@=@ || shades.b /*B: the ray's intensity│brightness*/ |
||
end /*j*/ /* [↑] build a line for the sphere.*/ |
end /*j*/ /* [↑] build a line for the sphere.*/ |
||
if @\='' then say strip(@, 'T') /*strip trailing blanks from line. */ |
if @\='' then say strip(@, 'T') /*strip trailing blanks from line. */ |
||
end /*i*/ /* [↑] show all lines for sphere. */ |
end /*i*/ /* [↑] show all lines for sphere. */ |
||
return</ |
return</syntaxhighlight> |
||
{{out|output|text= when using the internal default input:}} |
|||
'''output''' |
|||
(Shown at <big>'''<sup>1</sup>/<sub>2</sub>'''</big> size.) |
|||
<pre> |
|||
<pre style="font-size:50%"> |
|||
eeeee::::::: |
eeeee::::::: |
||
eeeeeeeee·············· |
eeeeeeeee·············· |
||
Line 1,517: | Line 2,455: | ||
=={{header|Set lang}}== |
=={{header|Set lang}}== |
||
< |
<syntaxhighlight lang="set_lang">set ! 32 |
||
set ! 32 |
set ! 32 |
||
set ! 46 |
set ! 46 |
||
Line 1,576: | Line 2,514: | ||
set ! 46 |
set ! 46 |
||
set ! 45 |
set ! 45 |
||
set ! 126</ |
set ! 126</syntaxhighlight> |
||
Outputs: |
Outputs: |
||
<pre> .-~""~-. |
<pre> .-~""~-. |
||
Line 1,588: | Line 2,526: | ||
{{trans|Perl}} |
{{trans|Perl}} |
||
Writes a PGM to stdout. |
Writes a PGM to stdout. |
||
< |
<syntaxhighlight lang="ruby">func hitf(sph, x, y) { |
||
x -= sph[0] |
|||
y -= sph[1] |
|||
} |
|||
var z = (sph[3]**2 - (x**2 + y**2)) |
|||
x -= sph[0]; |
|||
y -= sph[1]; |
|||
z < 0 && return nil |
|||
z < 0 && return nil; |
|||
z.sqrt! |
z.sqrt! |
||
[sph[2] - z, sph[2] + z] |
[sph[2] - z, sph[2] + z] |
||
} |
} |
||
func normalize(v) { |
func normalize(v) { |
||
v / v.abs |
|||
v »/» n; |
|||
} |
} |
||
func dot(x, y) { |
func dot(x, y) { |
||
max(0, x*y) |
|||
s > 0 ? s : 0; |
|||
} |
} |
||
var pos = [120, 120, 0, 120] |
var pos = [120, 120, 0, 120] |
||
var neg = [-77, -33, -100, 190] |
var neg = [-77, -33, -100, 190] |
||
var light = normalize( |
var light = normalize(Vector(-12, 13, -10)) |
||
func draw(k, amb) { |
func draw(k, amb) { |
||
STDOUT.binmode(':raw') |
STDOUT.binmode(':raw') |
||
print ("P5\n", pos[0]*2 + 3, " ", pos[1]*2 + 3, "\n255\n") |
print ("P5\n", pos[0]*2 + 3, " ", pos[1]*2 + 3, "\n255\n") |
||
for y in ((pos[1] - pos[3] - 1) .. (pos[1] + pos[3] + 1)) { |
for y in ((pos[1] - pos[3] - 1) .. (pos[1] + pos[3] + 1)) { |
||
var row = [] |
var row = [] |
||
for x in ((pos[0] - pos[3] - 1) .. (pos[0] + pos[3] + 1)) { |
for x in ((pos[0] - pos[3] - 1) .. (pos[0] + pos[3] + 1)) { |
||
var hit = 0; |
|||
var |
var hit = 0 |
||
var |
var hs = [] |
||
var h = hitf(pos, x, y) |
|||
if (!h) { hit = 0; h = [0, 0] } |
if (!h) { hit = 0; h = [0, 0] } |
||
Line 1,632: | Line 2,566: | ||
elsif (hs[0] > h[0]) { hit = 1 } |
elsif (hs[0] > h[0]) { hit = 1 } |
||
elsif (hs[1] > h[0]) { hit = (hs[1] > h[1] ? 0 : 2) } |
elsif (hs[1] > h[0]) { hit = (hs[1] > h[1] ? 0 : 2) } |
||
else { hit = 1 } |
else { hit = 1 } |
||
var (val, v) |
|||
var (val, v); |
|||
given(hit) { |
given(hit) { |
||
when (0) { val = 0} |
when (0) { val = 0} |
||
when (1) { v = |
when (1) { v = Vector(x-pos[0], y-pos[1], h[0]-pos[2]) } |
||
default { v = |
default { v = Vector(neg[0]-x, neg[1]-y, neg[2]-hs[1]) } |
||
} |
} |
||
if (v) { |
if (defined(v)) { |
||
v = normalize(v) |
v = normalize(v) |
||
val = int((dot(v, light)**k + amb) * 255) |
val = int((dot(v, light)**k + amb) * 255) |
||
val = (val > 255 ? 255 : (val < 0 ? 0 : val)) |
val = (val > 255 ? 255 : (val < 0 ? 0 : val)) |
||
} |
} |
||
row.append(val) |
row.append(val) |
||
} |
} |
||
print 'C*'.pack(row...) |
print 'C*'.pack(row...) |
||
} |
} |
||
} |
} |
||
draw(2, 0.2) |
draw(2, 0.2)</syntaxhighlight> |
||
Output image: [https://github.com/trizen/rc/blob/master/img/death_star_sidef.png here]. |
|||
=={{header|Tcl}}== |
=={{header|Tcl}}== |
||
{{trans|C}} |
{{trans|C}} |
||
Note that this code has a significant amount of refactoring relative to the C version, including the addition of specular reflections and the separation of the scene code from the raytracing from the rendering. |
Note that this code has a significant amount of refactoring relative to the C version, including the addition of specular reflections and the separation of the scene code from the raytracing from the rendering. |
||
< |
<syntaxhighlight lang="tcl">package require Tcl 8.5 |
||
proc normalize vec { |
proc normalize vec { |
||
Line 1,771: | Line 2,707: | ||
} |
} |
||
} |
} |
||
textDeathStar 3 10 0.7 0.3</ |
textDeathStar 3 10 0.7 0.3</syntaxhighlight> |
||
Output: |
Output: |
||
<pre> |
<pre> |
||
Line 1,818: | Line 2,754: | ||
{{libheader|Tk}} |
{{libheader|Tk}} |
||
[[File:Deathstar-tcl.gif|200px|thumb|Rendering of the Death Star by the Tcl solution.]] |
[[File:Deathstar-tcl.gif|200px|thumb|Rendering of the Death Star by the Tcl solution.]] |
||
< |
<syntaxhighlight lang="tcl"># Render as a picture (with many hard-coded settings) |
||
package require Tk |
package require Tk |
||
proc guiDeathStar {photo diff spec lightBrightness ambient} { |
proc guiDeathStar {photo diff spec lightBrightness ambient} { |
||
Line 1,835: | Line 2,771: | ||
} |
} |
||
pack [label .l -image [image create photo ds]] |
pack [label .l -image [image create photo ds]] |
||
guiDeathStar ds 3 10 0.7 0.3</ |
guiDeathStar ds 3 10 0.7 0.3</syntaxhighlight> |
||
=={{header|VBScript}}== |
|||
ASCII graphics. Should be invoked with cscript. Modified from LUA |
|||
<syntaxhighlight lang="vb"> |
|||
'deathstar ascii graphics |
|||
option explicit |
|||
const x_=0 |
|||
const y_=1 |
|||
const z_=2 |
|||
const r_=3 |
|||
function clamp(x,b,t) |
|||
if x<b then |
|||
clamp=b |
|||
elseif x>t then |
|||
clamp =t |
|||
else |
|||
clamp=x |
|||
end if |
|||
end function |
|||
function dot(v,w) dot=v(x_)*w(x_)+v(y_)*w(y_)+v(z_)*w(z_): end function |
|||
function normal (byval v) |
|||
dim ilen:ilen=1/sqr(dot(v,v)): |
|||
v(x_)=v(x_)*ilen: v(y_)=v(y_)*ilen: v(z_)=v(z_)*ilen: |
|||
normal=v: |
|||
end function |
|||
function hittest(s,x,y) |
|||
dim z |
|||
z = s(r_)^2 - (x-s(x_))^2 - (y-s(y_))^2 |
|||
if z>=0 then |
|||
z=sqr(z) |
|||
hittest=array(s(z_)-z,s(z_)+z) |
|||
else |
|||
hittest=0 |
|||
end if |
|||
end function |
|||
sub deathstar(pos, neg, sun, k, amb) |
|||
dim x,y,shades,result,shade,hp,hn,xx,b |
|||
shades=array(" ",".",":","!","*","o","e","&","#","%","@") |
|||
for y = pos(y_)-pos(r_)-0.5 to pos(y_)+pos(r_)+0.5 |
|||
for x = pos(x_)-pos(r_)-0.5 to pos(x_)+pos(r_)+.5 |
|||
hp=hittest (pos, x, y) |
|||
hn=hittest(neg,x,y) |
|||
if not isarray(hp) then |
|||
result=0 |
|||
elseif not isarray(hn) then |
|||
result=1 |
|||
elseif hn(0)>hp(0) then |
|||
result=1 |
|||
elseif hn(1)>hp(1) then |
|||
result=0 |
|||
elseif hn(1)>hp(0) then |
|||
result=2 |
|||
else |
|||
result=1 |
|||
end if |
|||
shade=-1 |
|||
select case result |
|||
case 0 |
|||
shade=0 |
|||
case 1 |
|||
xx=normal(array(x-pos(x_),y-pos(y_),hp(0)-pos(z_))) |
|||
'shade=clamp(1-dot(sun,xx)^k+amb,1,ubound(shades)) |
|||
case 2 |
|||
xx=normal(array(neg(x_)-x,neg(y_)-y,neg(z_)-hn(1))) |
|||
'shade=clamp(1-dot(sun,xx)^k+amb,1,ubound(shades)) |
|||
end select |
|||
if shade <>0 then |
|||
b=dot(sun,xx)^k+amb |
|||
shade=clamp((1-b) *ubound(shades),1,ubound(shades)) |
|||
end if |
|||
wscript.stdout.write string(2,shades(shade)) |
|||
next |
|||
wscript.stdout.write vbcrlf |
|||
next |
|||
end sub |
|||
deathstar array(20, 20, 0, 20),array(10,10,-15,10), normal(array(-2,1,3)), 2, 0.1 |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
####&&&&&&&&&&&& |
|||
%%######&&&&eeeeeeeeooooooooee&& |
|||
%%%%####&&&&eeeeeeoooooo************ooee |
|||
%%%%####%%%%%%eeoooooo******!!!!!!!!!!!!**oo |
|||
%%%%ee&&&&####%%%%%%##oo****!!!!!!!!::::::::!!!!**oo |
|||
%%%%ooooeeee&&####%%%%%%##**!!!!!!::::::::::::::::!!!!oo |
|||
%%%%!!****ooee&&&&##%%%%%%##!!!!::::::::............::::!!** |
|||
%%%%::::!!****ooee&&####%%%%%%&&!!::::....................::!!oo |
|||
%%....::::!!**ooooee&&##%%%%%%##::::........................::!! |
|||
%%........::::!!**ooee&&####%%%%##::::..........................::!! |
|||
##%%..........::!!**ooee&&&&##%%%%##::..............................::oo |
|||
%%%%..........::!!!!**ooee&&##%%%%::::..............................::!! |
|||
##%%..............::!!**ooee&&##%%%%::::................................::oo |
|||
##%%..............::!!**ooee&&##%%%%::::................................::** |
|||
%%%%............::::!!**oo&&&&##%%!!::::................................::!! |
|||
%%%%............::!!**ooee&&##%%!!!!::::..................................!! |
|||
##%%%%%%........::::!!**ooee&&##**!!!!::::..................................::** |
|||
##%%%%%%!!::::::!!!!**ooee&&##****!!!!::::..................................::** |
|||
##%%%%%%%%**********ooee&&##oo****!!!!!!::::................................::** |
|||
##%%%%%%%%%%##eeeeee&&eeeeeeoooo****!!!!::::..............................::!!** |
|||
##%%%%%%%%%%######&&&&&&eeeeoooo****!!!!::::::............................::!!** |
|||
##%%%%%%%%%%%%######&&&&eeeeoooo******!!!!::::::..........................::!!oo |
|||
&&##%%%%%%%%%%######&&&&eeeeeeoooo****!!!!!!::::::......................::::!!oo |
|||
&&##%%%%%%%%%%######&&&&&&eeeeoooooo****!!!!!!::::::..................::::!!**ee |
|||
##%%%%%%%%%%%%######&&&&eeeeeeoooo******!!!!!!::::::::..........::::::!!!!** |
|||
##%%%%%%%%%%%%######&&&&&&eeeeeeoooo******!!!!!!::::::::::::::::::::!!!!**oo |
|||
&&##%%%%%%%%%%%%######&&&&&&eeeeoooooo******!!!!!!!!::::::::::::!!!!!!**ooee |
|||
ee##%%%%%%%%%%%%########&&&&eeeeeeoooooo********!!!!!!!!!!!!!!!!!!!!****oo&& |
|||
&&##%%%%%%%%%%%%######&&&&&&eeeeeeoooooo**********!!!!!!!!!!!!******ooee |
|||
ee##%%%%%%%%%%%%%%######&&&&&&eeeeeeoooooooo********************ooooee&& |
|||
&&##%%%%%%%%%%%%########&&&&&&&&eeeeeeoooooooooo********ooooooooee&& |
|||
####%%%%%%%%%%%%########&&&&&&&&eeeeeeeeooooooooooooooooooeeee&& |
|||
ee##%%%%%%%%%%%%%%##########&&&&&&&&eeeeeeeeeeeeeeeeeeeeeeee&&## |
|||
&&##%%%%%%%%%%%%%%%%########&&&&&&&&&&eeeeeeeeeeeeee&&&&&&## |
|||
&&##%%%%%%%%%%%%%%%%##########&&&&&&&&&&&&&&&&&&&&&&#### |
|||
&&##%%%%%%%%%%%%%%%%%%##############&&&&&&&&######%% |
|||
####%%%%%%%%%%%%%%%%%%###################### |
|||
&&##%%%%%%%%%%%%%%%%%%%%%%%%######%%%%%% |
|||
&&##%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
|||
##%%%%%%%%%%%%%% |
|||
</pre> |
|||
=={{header|Wren}}== |
|||
{{trans|Go}} |
|||
{{libheader|DOME}} |
|||
<syntaxhighlight lang="wren">import "dome" for Window |
|||
import "graphics" for Canvas, Color, ImageData |
|||
import "math" for Vector |
|||
var Normalize = Fn.new{ |vec| |
|||
var invLen = 1 / vec.dot(vec).sqrt |
|||
vec.x = vec.x * invLen |
|||
vec.y = vec.y * invLen |
|||
vec.z = vec.z * invLen |
|||
} |
|||
class Sphere { |
|||
construct new(cx, cy, cz, r) { |
|||
_cx = cx |
|||
_cy = cy |
|||
_cz = cz |
|||
_r = r |
|||
} |
|||
cx { _cx } |
|||
cy { _cy } |
|||
cz { _cz } |
|||
r { _r } |
|||
hit(x, y) { |
|||
x = x - _cx |
|||
y = y - _cy |
|||
var zsq = _r*_r - x*x - y*y |
|||
if (zsq >= 0) { |
|||
var zsqrt = zsq.sqrt |
|||
return [_cz - zsqrt, _cz + zsqrt, true] |
|||
} |
|||
return [0, 0, false] |
|||
} |
|||
} |
|||
class DeathStar { |
|||
construct new(width, height) { |
|||
Window.title = "Death star" |
|||
Window.resize(width, height) |
|||
Canvas.resize(width, height) |
|||
} |
|||
init() { |
|||
Canvas.cls(Color.white) |
|||
var dir = Vector.new(20, -40, 10) |
|||
Normalize.call(dir) |
|||
var pos = Sphere.new(220, 190, 220, 120) |
|||
var neg = Sphere.new(130, 100, 190, 100) |
|||
deathStar(pos, neg, 1.5, 0.2, dir) |
|||
} |
|||
deathStar(pos, neg, k, amb, dir) { |
|||
var w = pos.r * 4 |
|||
var h = pos.r * 3 |
|||
var img = ImageData.create("deathStar", w, h) |
|||
var vec = Vector.new(0, 0, 0) |
|||
for (y in pos.cy - pos.r..pos.cy + pos.r) { |
|||
for (x in pos.cx - pos.r..pos.cx + pos.r) { |
|||
var res = pos.hit(x, y) |
|||
var zb1 = res[0] |
|||
var zb2 = res[1] |
|||
var hit = res[2] |
|||
if (!hit) continue |
|||
res = neg.hit(x, y) |
|||
var zs1 = res[0] |
|||
var zs2 = res[1] |
|||
hit = res[2] |
|||
if (hit) { |
|||
if (zs1 > zb1) { |
|||
hit = false |
|||
} else if (zs2 > zb2) { |
|||
continue |
|||
} |
|||
} |
|||
if (hit) { |
|||
vec.x = neg.cx - x |
|||
vec.y = neg.cy - y |
|||
vec.z = neg.cz - zs2 |
|||
} else { |
|||
vec.x = x - pos.cx |
|||
vec.y = y - pos.cy |
|||
vec.z = zb1 - pos.cz |
|||
} |
|||
Normalize.call(vec) |
|||
var s = dir.dot(vec) |
|||
if (s < 0) s = 0 |
|||
var lum = 255 * (s.pow(k) + amb) / (1 + amb) |
|||
lum = lum.clamp(0, 255) |
|||
img.pset(x, y, Color.rgb(lum, lum, lum)) |
|||
} |
|||
} |
|||
img.draw(pos.cx - w/2, pos.cy - h/2) |
|||
img.saveToFile("deathStar.png") |
|||
} |
|||
update() { |
|||
} |
|||
draw(alpha) { |
|||
} |
|||
} |
|||
var Game = DeathStar.new(400, 400)</syntaxhighlight> |
|||
=={{header|Yabasic}}== |
=={{header|Yabasic}}== |
||
< |
<syntaxhighlight lang="yabasic">open window 100,100 |
||
window origin "cc" |
window origin "cc" |
||
backcolor 0,0,0 |
backcolor 0,0,0 |
||
Line 1,993: | Line 3,166: | ||
draw_sphere(2, .3) |
draw_sphere(2, .3) |
||
wend |
wend |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Zig}}== |
|||
{{trans|C}} |
|||
Primitive ray tracing. Writes a PGM to stdout. |
|||
<syntaxhighlight lang="zig"> |
|||
const std = @import("std"); |
|||
const Allocator = std.mem.Allocator; |
|||
</syntaxhighlight> |
|||
<syntaxhighlight lang="zig">pub fn main() !void { |
|||
// buffer stdout -------------------------------------- |
|||
const stdout_file = std.io.getStdOut().writer(); |
|||
var bw = std.io.bufferedWriter(stdout_file); |
|||
const stdout = bw.writer(); |
|||
// allocator ------------------------------------------ |
|||
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; |
|||
defer { |
|||
const ok = gpa.deinit(); |
|||
std.debug.assert(ok == .ok); |
|||
} |
|||
const allocator = gpa.allocator(); |
|||
// deathstar ------------------------------------------ |
|||
var dstar = try DeathStar(f32).init(allocator); |
|||
defer dstar.deinit(); |
|||
// print deathstar PGM to stdout ---------------------- |
|||
const comments = [_][]const u8{ |
|||
"Rosetta Code", |
|||
"DeathStar", |
|||
"https://rosettacode.org/wiki/Death_Star", |
|||
}; |
|||
try dstar.print(stdout, comments[0..]); |
|||
// ---------------------------------------------------- |
|||
try bw.flush(); |
|||
}</syntaxhighlight> |
|||
<syntaxhighlight lang="zig">fn Vector(comptime T: type) type { |
|||
return struct { |
|||
const Self = @This(); |
|||
x: T, |
|||
y: T, |
|||
z: T, |
|||
pub fn init(x: T, y: T, z: T) Self { |
|||
return Self{ .x = x, .y = y, .z = z }; |
|||
} |
|||
pub fn zero() Self { |
|||
return Self{ .x = 0.0, .y = 0.0, .z = 0.0 }; |
|||
} |
|||
fn dot(a: *const Self, b: *const Self) T { |
|||
return a.x * b.x + a.y * b.y + a.z * b.z; |
|||
} |
|||
fn length(self: *const Self) T { |
|||
return std.math.sqrt(self.dot(self)); |
|||
} |
|||
pub fn normalize(self: *Self) void { |
|||
const inv_length = 1 / self.length(); |
|||
self.*.x *= inv_length; |
|||
self.*.y *= inv_length; |
|||
self.*.z *= inv_length; |
|||
} |
|||
}; |
|||
}</syntaxhighlight> |
|||
<syntaxhighlight lang="zig"> |
|||
fn SphereHit(comptime T: type) type { |
|||
return struct { z1: T, z2: T }; |
|||
}</syntaxhighlight> |
|||
<syntaxhighlight lang="zig"> |
|||
fn Sphere(comptime T: type) type { |
|||
return struct { |
|||
const Self = @This(); |
|||
cx: T, |
|||
cy: T, |
|||
cz: T, |
|||
r: T, |
|||
pub fn init(cx: T, cy: T, cz: T, r: T) Self { |
|||
return Self{ .cx = cx, .cy = cy, .cz = cz, .r = r }; |
|||
} |
|||
/// 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. |
|||
pub fn hit(self: *const Self, xx: T, yy: T) ?SphereHit(T) { |
|||
const x = xx - self.cx; |
|||
const y = yy - self.cy; |
|||
const zsq = self.r * self.r - x * x - y * y; |
|||
if (zsq >= 0) { |
|||
const zsqrt = std.math.sqrt(zsq); |
|||
return .{ .z1 = self.cz - zsqrt, .z2 = self.cz + zsqrt }; |
|||
} |
|||
return null; |
|||
} |
|||
}; |
|||
}</syntaxhighlight> |
|||
<syntaxhighlight lang="zig"> |
|||
fn DeathStar(comptime T: type) type { |
|||
return struct { |
|||
const Self = @This(); |
|||
allocator: Allocator, |
|||
w: usize, |
|||
h: usize, |
|||
img: ImageData(), |
|||
const Hit = enum { background, neg, pos }; |
|||
pub fn init(allocator: Allocator) !Self { |
|||
var dir = Vector(T).init(20, -40, 10); |
|||
dir.normalize(); |
|||
// positive sphere and negative sphere |
|||
const pos = Sphere(T).init(180, 240, 220, 120); |
|||
const neg = Sphere(T).init(60, 150, 100, 100); |
|||
const k: T = 1.5; |
|||
const amb: T = 0.2; |
|||
const w: usize = @intFromFloat(pos.r * 4); |
|||
const h: usize = @intFromFloat(pos.r * 3); |
|||
var img = try ImageData().init(allocator, "deathStar", w, h); |
|||
var vec = Vector(T).zero(); |
|||
const start_y: usize = @intFromFloat(pos.cy - pos.r); |
|||
const end_y: usize = @intFromFloat(pos.cy + pos.r); |
|||
const start_x: usize = @intFromFloat(pos.cx - pos.r); |
|||
const end_x: usize = @intFromFloat(pos.cx + pos.r); |
|||
for (start_y..end_y + 1) |j| { |
|||
for (start_x..end_x + 1) |i| { |
|||
const x: T = @floatFromInt(i); |
|||
const y: T = @floatFromInt(j); |
|||
const result_pos = pos.hit(x, y); |
|||
// ray lands in blank space, show bg |
|||
if (result_pos == null) |
|||
continue; |
|||
const zb1 = result_pos.?.z1; |
|||
const zb2 = result_pos.?.z2; |
|||
const result_neg = neg.hit(x, y); |
|||
switch (calcHit(result_neg, zb1, zb2)) { |
|||
.background => continue, |
|||
.neg => { |
|||
vec.x = neg.cx - x; |
|||
vec.y = neg.cy - y; |
|||
vec.z = neg.cz - result_neg.?.z2; // zs2 |
|||
}, |
|||
.pos => { |
|||
vec.x = x - pos.cx; |
|||
vec.y = y - pos.cy; |
|||
vec.z = zb1 - pos.cz; |
|||
}, |
|||
} |
|||
vec.normalize(); |
|||
var s = dir.dot(&vec); |
|||
if (s < 0) s = 0; |
|||
const lum = 255 * (std.math.pow(T, s, k) + amb) / (1 + amb); |
|||
const lumi: u8 = @intFromFloat(std.math.clamp(lum, 0, 255)); |
|||
img.pset(i, j, Gray{ .w = lumi }); |
|||
} |
|||
} |
|||
return Self{ .allocator = allocator, .w = w, .h = h, .img = img }; |
|||
} |
|||
pub fn deinit(self: *Self) void { |
|||
self.img.deinit(); |
|||
} |
|||
pub fn print(self: *Self, writer: anytype, optional_comments: ?[]const []const u8) !void { |
|||
try self.img.print(writer, optional_comments); |
|||
} |
|||
/// Ray has hit the positive sphere. |
|||
/// How does it intersect the negative sphere ? |
|||
fn calcHit(neg_hit: ?SphereHit(T), zb1: T, zb2: T) Hit { |
|||
if (neg_hit) |result| { |
|||
const zs1 = result.z1; |
|||
const zs2 = result.z2; |
|||
if (zs1 > zb1) { |
|||
// ray hits both, but pos front surface is closer |
|||
return Hit.pos; |
|||
} else if (zs2 > zb2) { |
|||
// pos sphere surface is inside neg sphere, show bg |
|||
return Hit.background; |
|||
} else if (zs2 > zb1) { |
|||
// back surface on neg sphere is inside pos sphere, |
|||
// the only place where neg sphere surface will be shown |
|||
return Hit.neg; |
|||
} else { |
|||
return Hit.pos; |
|||
} |
|||
} else { |
|||
// ray hits pos sphere but not neg, draw pos sphere surface |
|||
return Hit.pos; |
|||
} |
|||
} |
|||
}; |
|||
}</syntaxhighlight> |
|||
<syntaxhighlight lang="zig"> |
|||
const Gray = struct { |
|||
w: u8, |
|||
const black = Gray{ .w = 0 }; |
|||
};</syntaxhighlight> |
|||
<syntaxhighlight lang="zig"> |
|||
fn ImageData() type { |
|||
return struct { |
|||
const Self = @This(); |
|||
allocator: Allocator, |
|||
name: []const u8, |
|||
w: usize, |
|||
h: usize, |
|||
image: []Gray, |
|||
pub fn init(allocator: Allocator, name: []const u8, w: usize, h: usize) !Self { |
|||
const image = try allocator.alloc(Gray, h * w); |
|||
// black background fill |
|||
for (image) |*pixel| pixel.* = Gray.black; |
|||
return Self{ .allocator = allocator, .image = image, .name = name, .w = w, .h = h }; |
|||
} |
|||
pub fn deinit(self: *Self) void { |
|||
self.allocator.free(self.image); |
|||
} |
|||
pub fn pset(self: *Self, x: usize, y: usize, gray: Gray) void { |
|||
self.image[x * self.w + y] = gray; |
|||
} |
|||
/// Write PGM P2 ASCII to 'writer' |
|||
pub fn print(self: *const Self, writer: anytype, optional_comments: ?[]const []const u8) !void { |
|||
try writer.print("P2\n", .{}); |
|||
if (optional_comments) |lines| { |
|||
for (lines) |line| |
|||
try writer.print("# {s}\n", .{line}); |
|||
} |
|||
try writer.print("{d} {d}\n{d}\n", .{ self.w, self.h, 255 }); |
|||
for (self.image, 0..) |pixel, i| { |
|||
const sep = if (i % self.w == self.w - 1) "\n" else " "; |
|||
try writer.print("{d}{s}", .{ pixel.w, sep }); |
|||
} |
|||
} |
|||
}; |
|||
}</syntaxhighlight> |
Latest revision as of 11:34, 18 May 2024
You are encouraged to solve this task according to the task description, using any language you may know.
- Task
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".)
- Related tasks
11l
T Sphere = (Float cx, Float cy, Float cz, Float r)
F dotp(v1, v2)
V d = dot(v1, v2)
R I d < 0 {-d} E 0.0
F hit_sphere(sph, x0, y0)
V x = x0 - sph.cx
V y = y0 - sph.cy
V zsq = sph.r ^ 2 - (x ^ 2 + y ^ 2)
I zsq < 0
R (0B, 0.0, 0.0)
V szsq = sqrt(zsq)
R (1B, sph.cz - szsq, sph.cz + szsq)
F draw_sphere(k, ambient, light)
V shades = ‘.:!*oe&#%@’
V pos = Sphere(20.0, 20.0, 0.0, 20.0)
V neg = Sphere(1.0, 1.0, -6.0, 20.0)
L(i) Int(floor(pos.cy - pos.r)) .< Int(ceil(pos.cy + pos.r) + 1)
V y = i + 0.5
L(j) Int(floor(pos.cx - 2 * pos.r)) .< Int(ceil(pos.cx + 2 * pos.r) + 1)
V x = (j - pos.cx) / 2.0 + 0.5 + pos.cx
V (h, zb1, zb2) = hit_sphere(pos, x, y)
Int hit_result
Float zs2
I !h
hit_result = 0
E
(h, V zs1, zs2) = hit_sphere(neg, x, y)
I !h
hit_result = 1
E I zs1 > zb1
hit_result = 1
E I zs2 > zb2
hit_result = 0
E I zs2 > zb1
hit_result = 2
E
hit_result = 1
V vec = (0.0, 0.0, 0.0)
I hit_result == 0
print(‘ ’, end' ‘’)
L.continue
E I hit_result == 1
vec = (x - pos.cx, y - pos.cy, zb1 - pos.cz)
E I hit_result == 2
vec = (neg.cx - x, neg.cy - y, neg.cz - zs2)
vec = normalize(vec)
V b = dotp(light, vec) ^ k + ambient
V intensity = Int((1 - b) * shades.len)
intensity = min(shades.len, max(0, intensity))
print(shades[intensity], end' ‘’)
print()
V light = normalize((-50.0, 30.0, 50.0))
draw_sphere(2, 0.5, light)
- Output:
eeeee::::::: eeeeeeeee.............. ooeeeeeeeeee.................. ooooeeeeeeeee...................... oooooooeeeeeeee.......................... ooooooooooeeeee.............................. **ooooooooooeeee................................. ****ooooooooooee..................................... !*****ooooooooooe....................................... !!!*****ooooooooo:.......................................... :!!!!*****ooooooo:::........................................... :::!!!!*****ooooo!:::::........................................... ::::!!!!!*****ooo!!!!::::............................................ .::::!!!!*****oo*!!!!!::::............................................ ...::::!!!!*********!!!!:::::............................................ ...::::!!!!****o*****!!!!!::::............................................ ....::::!!!!***ooo******!!!!!::::............................................ ....::::!!!!*ooooooo*****!!!!!:::::........................................... ...::::!!!!!oooooooooo*****!!!!!:::::.......................................... :::::!!!!eeooooooooooo******!!!!!:::::......................................... !!!!!eeeeeeeooooooooooo******!!!!!:::::........................................ eeeeeeeeeeeeoooooooooooo******!!!!!:::::....................................... eeeeeeeeeeeeeoooooooooooo******!!!!!!:::::..................................... eeeeeeeeeeeeeeoooooooooooo******!!!!!!:::::.................................... eeeeeeeeeeeeeeoooooooooooo*******!!!!!!:::::................................. eeeeeeeeeeeeeeeooooooooooooo******!!!!!!::::::..............................: eeeeeeeeeeeeeeeooooooooooooo*******!!!!!!:::::::..........................: eeeeeeeeeeeeeeeeoooooooooooooo*******!!!!!!!:::::::.....................::! eeeeeeeeeeeeeeeeeooooooooooooo********!!!!!!!:::::::::..............::::! eeeeeeeeeeeeeeeeeoooooooooooooo********!!!!!!!!::::::::::::::::::::::!* eeeeeeeeeeeeeeeeeeooooooooooooooo********!!!!!!!!!!:::::::::::::!!!!* eeeeeeeeeeeeeeeeeoooooooooooooooo**********!!!!!!!!!!!!!!!!!!!!!* eeeeeeeeeeeeeeeeeeooooooooooooooooo************!!!!!!!!!!!!**** eeeeeeeeeeeeeeeeeeoooooooooooooooooo**********************o eeeeeeeeeeeeeeeeeeeooooooooooooooooooooo************ooo eeeeeeeeeeeeeeeeeeeeooooooooooooooooooooooooooooooo eeeeeeeeeeeeeeeeeeeeooooooooooooooooooooooooo eeeeeeeeeeeeeeeeeeeeeoooooooooooooooooo eeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeee
Ada
with Ada.Numerics.Elementary_Functions;
with Ada.Numerics.Generic_Real_Arrays;
with SDL.Video.Windows.Makers;
with SDL.Video.Renderers.Makers;
with SDL.Video.Palettes;
with SDL.Events.Events;
procedure Death_Star is
Width : constant := 400;
Height : constant := 400;
package Float_Arrays is
new Ada.Numerics.Generic_Real_Arrays (Float);
use Ada.Numerics.Elementary_Functions;
use Float_Arrays;
Window : SDL.Video.Windows.Window;
Renderer : SDL.Video.Renderers.Renderer;
subtype Vector_3 is Real_Vector (1 .. 3);
type Sphere_Type is record
Cx, Cy, Cz : Integer;
R : Integer;
end record;
function Normalize (V : Vector_3) return Vector_3 is
(V / Sqrt (V * V));
procedure Hit (S : Sphere_Type;
X, Y : Integer;
Z1, Z2 : out Float;
Is_Hit : out Boolean)
is
NX : constant Integer := X - S.Cx;
NY : constant Integer := Y - S.Cy;
Zsq : constant Integer := S.R * S.R - (NX * NX + NY * NY);
Zsqrt : Float;
begin
if Zsq >= 0 then
Zsqrt := Sqrt (Float (Zsq));
Z1 := Float (S.Cz) - Zsqrt;
Z2 := Float (S.Cz) + Zsqrt;
Is_Hit := True;
return;
end if;
Z1 := 0.0;
Z2 := 0.0;
Is_Hit := False;
end Hit;
procedure Draw_Death_Star (Pos, Neg : Sphere_Type;
K, Amb : Float;
Dir : Vector_3)
is
Vec : Vector_3;
ZB1, ZB2 : Float;
ZS1, ZS2 : Float;
Is_Hit : Boolean;
S : Float;
Lum : Integer;
begin
for Y in Pos.Cy - Pos.R .. Pos.Cy + Pos.R loop
for X in Pos.Cx - Pos.R .. Pos.Cx + Pos.R loop
Hit (Pos, X, Y, ZB1, ZB2, Is_Hit);
if not Is_Hit then
goto Continue;
end if;
Hit (Neg, X, Y, ZS1, ZS2, Is_Hit);
if Is_Hit then
if ZS1 > ZB1 then
Is_Hit := False;
elsif ZS2 > ZB2 then
goto Continue;
end if;
end if;
if Is_Hit then
Vec := (Float (Neg.Cx - X),
Float (Neg.Cy - Y),
Float (Neg.Cz) - ZS2);
else
Vec := (Float (X - Pos.Cx),
Float (Y - Pos.Cy),
ZB1 - Float (Pos.Cz));
end if;
S := Float'Max (0.0, Dir * Normalize (Vec));
Lum := Integer (255.0 * (S ** K + Amb) / (1.0 + Amb));
Lum := Integer'Max (0, Lum);
Lum := Integer'Min (Lum, 255);
Renderer.Set_Draw_Colour ((SDL.Video.Palettes.Colour_Component (Lum),
SDL.Video.Palettes.Colour_Component (Lum),
SDL.Video.Palettes.Colour_Component (Lum),
255));
Renderer.Draw (Point => (SDL.C.int (X + Width / 2),
SDL.C.int (Y + Height / 2)));
<<Continue>>
end loop;
end loop;
end Draw_Death_Star;
procedure Wait is
use type SDL.Events.Event_Types;
Event : SDL.Events.Events.Events;
begin
loop
while SDL.Events.Events.Poll (Event) loop
if Event.Common.Event_Type = SDL.Events.Quit then
return;
end if;
end loop;
delay 0.100;
end loop;
end Wait;
Direction : constant Vector_3 := Normalize ((20.0, -40.0, -10.0));
Positive : constant Sphere_Type := (0, 0, 0, 120);
Negative : constant Sphere_Type := (-90, -90, -30, 100);
begin
if not SDL.Initialise (Flags => SDL.Enable_Screen) then
return;
end if;
SDL.Video.Windows.Makers.Create (Win => Window,
Title => "Death star",
Position => SDL.Natural_Coordinates'(X => 10, Y => 10),
Size => SDL.Positive_Sizes'(Width, Height),
Flags => 0);
SDL.Video.Renderers.Makers.Create (Renderer, Window.Get_Surface);
Renderer.Set_Draw_Colour ((0, 0, 0, 255));
Renderer.Fill (Rectangle => (0, 0, Width, Height));
Draw_Death_Star (Positive, Negative, 1.5, 0.2, Direction);
Window.Update_Surface;
Wait;
Window.Finalize;
SDL.Finalise;
end Death_Star;
ALGOL 68
BEGIN # draw a "Death Star" - translated from the C and 11l samples #
STRING shades = ".:!*oe&#%@";
PROC normalize = ( []REAL v )[]REAL:
BEGIN
REAL len = sqrt( v[ 1 ] * v[ 1 ] + v[ 2 ] * v[ 2 ] + v[ 3 ] * v[ 3 ] );
( v[ 1 ] / len, v[ 2 ] / len, v[ 3 ] / len )
END # normalize # ;
PROC dot = ( []REAL x, y )REAL:
BEGIN
REAL d = x[ 1 ] * y[ 1 ] + x[ 2 ] * y[ 2 ] + x[ 3 ] * y[ 3 ];
IF d < 0 THEN - d ELSE 0 FI
END # dot # ;
MODE SPHERE = STRUCT( REAL cx, cy, cz, r );
# positive shpere and negative sphere #
SPHERE pos = ( 20, 20, 0, 20 ), neg = ( 1, 1, -6, 20 );
# 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 #
PROC hit_sphere = ( SPHERE sph, REAL x in, y in, REF REAL z1, z2 )BOOL:
IF REAL x = x in - cx OF sph;
REAL y = y in - cy OF sph;
REAL zsq := r OF sph * r OF sph - ( x * x + y * y );
zsq < 0
THEN FALSE
ELSE zsq := sqrt( zsq );
z1 := cz OF sph - zsq;
z2 := cz OF sph + zsq;
TRUE
FI # hit_sphere # ;
PROC draw_sphere = ( REAL k, ambient, []REAL light )VOID:
FOR i FROM ENTIER ( cy OF pos - r OF pos ) TO ENTIER ( cy OF pos + r OF pos ) + 1 DO
REAL y := i + 0.5;
FOR j FROM ENTIER ( cx OF pos - 2 * r OF pos ) TO ENTIER (cx OF pos + 2 * r OF pos ) + 1 DO
REAL x := ( j - cx OF pos ) / 2.0 + 0.5 + cx OF pos;
REAL zb1 := 0, zb2 := 0, zs1 := 0, zs2 := 0;
INT hit_result
= IF NOT hit_sphere( pos, x, y, zb1, zb2 ) THEN
0 # ray lands in blank space, draw bg #
ELIF NOT hit_sphere( neg, x, y, zs1, zs2 ) THEN
1 # ray hits pos sphere but not neg, draw pos sphere surface #
ELIF zs1 > zb1 THEN
1 # ray hits both, but pos front surface is closer #
ELIF zs2 > zb2 THEN
0 # pos sphere surface is inside neg sphere, show bg #
ELIF zs2 > zb1 THEN
2 # back surface on neg sphere is inside pos sphere, #
# the only place where neg sphere surface will be shown #
ELSE
1 # show the pos sphere #
FI;
IF hit_result = 0 THEN
print( ( " " ) )
ELSE
[]REAL vec =
normalize( IF hit_result = 1
THEN []REAL( x - cx OF pos
, y - cy OF pos
, zb1 - cz OF pos
)
ELSE []REAL( cx OF neg - x
, cy OF neg - y
, cz OF neg - zs2
)
FI
);
REAL b = ( dot( light, vec ) ^ k ) + ambient;
INT intensity := ENTIER ( ( 1 - b ) * ( ( UPB shades - LWB shades ) + 1 ) ) + 1;
IF intensity < LWB shades THEN intensity := LWB shades
ELIF intensity > UPB shades THEN intensity := UPB shades
FI;
print( ( shades[ intensity ] ) )
FI
OD;
print( ( newline ) )
OD # draw_sphere # ;
BEGIN
[]REAL light = ( -50, 30, 50 );
draw_sphere( 2, 0.5, normalize( light ) )
END
END
- Output:
Same as 11l
AutoHotkey
#NoEnv
SetBatchLines, -1
#SingleInstance, Force
; Uncomment if Gdip.ahk is not in your standard library
#Include, Gdip.ahk
; Settings
X := 200, Y := 200, Width := 200, Height := 200 ; Location and size of sphere
rotation := 60 ; degrees
ARGB := 0xFFFF0000 ; Color=Solid Red
If !pToken := Gdip_Startup() ; Start gdi+
{
MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
ExitApp
}
OnExit, Exit
Gui, -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs ; Create GUI
Gui, Show, NA ; Show GUI
hwnd1 := WinExist() ; Get a handle to this window we have created in order to update it later
hbm := CreateDIBSection(A_ScreenWidth, A_ScreenHeight) ; Create a gdi bitmap drawing area
hdc := CreateCompatibleDC() ; Get a device context compatible with the screen
obm := SelectObject(hdc, hbm) ; Select the bitmap into the device context
pGraphics := Gdip_GraphicsFromHDC(hdc) ; Get a pointer to the graphics of the bitmap, for use with drawing functions
Gdip_SetSmoothingMode(pGraphics, 4) ; Set the smoothing mode to antialias = 4 to make shapes appear smother
Gdip_TranslateWorldTransform(pGraphics, X, Y)
Gdip_RotateWorldTransform(pGraphics, rotation)
; Base ellipse
pBrush := Gdip_CreateLineBrushFromRect(0, 0, Width, Height, ARGB, 0xFF000000)
Gdip_FillEllipse(pGraphics, pBrush, 0, 0, Width, Height)
; First highlight ellipse
pBrush := Gdip_CreateLineBrushFromRect(Width*0.1, Height*0.01, Width*0.8, Height*0.6, 0x33FFFFFF, 0x00FFFFFF)
Gdip_FillEllipse(pGraphics, pBrush, Width*0.1, Height*0.01, Width*0.8, Height*0.6)
; Second highlight ellipse
pBrush := Gdip_CreateLineBrushFromRect(Width*0.3, Height*0.02, Width*0.3, Height*0.2, 0xBBFFFFFF, 0x00FFFFFF)
Gdip_FillEllipse(pGraphics, pBrush, Width*0.3, Height*0.02, Width*0.3, Height*0.2)
; Reset variables for smaller subtracted sphere
X-=150
Y-=10
Width*=0.5
Height*=0.4
rotation-=180
Gdip_TranslateWorldTransform(pGraphics, X, Y)
Gdip_RotateWorldTransform(pGraphics, rotation)
; Base ellipse
pBrush := Gdip_CreateLineBrushFromRect(0, 0, Width, Height, ARGB, 0xFF000000)
Gdip_FillEllipse(pGraphics, pBrush, 0, 0, Width, Height)
; First highlight ellipse
pBrush := Gdip_CreateLineBrushFromRect(Width*0.1, Height*0.01, Width*0.8, Height*0.6, 0x33FFFFFF, 0x00FFFFFF)
Gdip_FillEllipse(pGraphics, pBrush, Width*0.1, Height*0.01, Width*0.8, Height*0.6)
; Second highlight ellipse
pBrush := Gdip_CreateLineBrushFromRect(Width*0.3, Height*0.02, Width*0.3, Height*0.2, 0xBBFFFFFF, 0x00FFFFFF)
Gdip_FillEllipse(pGraphics, pBrush, Width*0.3, Height*0.02, Width*0.3, Height*0.2)
UpdateLayeredWindow(hwnd1, hdc, 0, 0, A_ScreenWidth, A_ScreenHeight)
SelectObject(hdc, obm) ; Select the object back into the hdc
Gdip_DeletePath(Path)
Gdip_DeleteBrush(pBrush)
DeleteObject(hbm) ; Now the bitmap may be deleted
DeleteDC(hdc) ; Also the device context related to the bitmap may be deleted
Gdip_DeleteGraphics(G) ; The graphics may now be deleted
Return
Exit:
; gdi+ may now be shutdown on exiting the program
Gdip_Shutdown(pToken)
ExitApp
Brlcad
# We need a database to hold the objects
opendb deathstar.g y
# We will be measuring in kilometers
units km
# Create a sphere of radius 60km centred at the origin
in sph1.s sph 0 0 0 60
# We will be subtracting an overlapping sphere with a radius of 40km
# The resultant hole will be smaller than this, because we only
# only catch the edge
in sph2.s sph 0 90 0 40
# Create a region named deathstar.r which consists of big minus small sphere
r deathstar.r u sph1.s - sph2.s
# We will use a plastic material texture with rgb colour 224,224,224
# with specular lighting value of 0.1 and no inheritance
mater deathstar.r "plastic sp=0.1" 224 224 224 0
# Clear the wireframe display and draw the deathstar
B deathstar.r
# We now trigger the raytracer to see our finished product
rt
C
Primitive ray tracing.
#include <stdio.h>
#include <math.h>
#include <unistd.h>
const char *shades = ".:!*oe&#%@";
double light[3] = { -50, 0, 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;
/* positive shpere and negative sphere */
sphere_t pos = { 20, 20, 0, 20 }, neg = { 1, 1, -6, 20 };
/* 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(pos.cy - pos.r); i <= ceil(pos.cy + pos.r); i++) {
y = i + .5;
for (j = floor(pos.cx - 2 * pos.r); j <= ceil(pos.cx + 2 * pos.r); j++) {
x = (j - pos.cx) / 2. + .5 + pos.cx;
/* ray lands in blank space, draw bg */
if (!hit_sphere(&pos, x, y, &zb1, &zb2))
hit_result = 0;
/* ray hits pos sphere but not neg, draw pos sphere surface */
else if (!hit_sphere(&neg, x, y, &zs1, &zs2))
hit_result = 1;
/* ray hits both, but pos front surface is closer */
else if (zs1 > zb1) hit_result = 1;
/* pos sphere surface is inside neg sphere, show bg */
else if (zs2 > zb2) hit_result = 0;
/* back surface on neg sphere is inside pos sphere,
the only place where neg 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 - pos.cx;
vec[1] = y - pos.cy;
vec[2] = zb1 - pos.cz;
break;
default:
vec[0] = neg.cx - x;
vec[1] = neg.cy - y;
vec[2] = neg.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()
{
double ang = 0;
while (1) {
printf("\033[H");
light[1] = cos(ang * 2);
light[2] = cos(ang);
light[0] = sin(ang);
normalize(light);
ang += .05;
draw_sphere(2, .3);
usleep(100000);
}
return 0;
}
D
import std.stdio, std.math, std.numeric, std.algorithm;
struct V3 {
double[3] v;
@property V3 normalize() pure nothrow const @nogc {
immutable double len = dotProduct(v, v).sqrt;
return [v[0] / len, v[1] / len, v[2] / len].V3;
}
double dot(in ref V3 y) pure nothrow const @nogc {
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(in ref Sphere sph,
in double x0, in double y0,
out double z1,
out double z2) pure nothrow @nogc {
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 = zsq.sqrt;
z1 = sph.cz - szsq;
z2 = sph.cz + szsq;
return true;
}
immutable shades = ".:!*oe&#%@";
// Positive and negative spheres.
immutable pos = Sphere(20, 20, 0, 20);
immutable neg = Sphere(1, 1, -6, 20);
foreach (immutable int i; cast(int)floor(pos.cy - pos.r) ..
cast(int)ceil(pos.cy + pos.r) + 1) {
immutable double y = i + 0.5;
JLOOP:
foreach (int j; cast(int)floor(pos.cx - 2 * pos.r) ..
cast(int)ceil(pos.cx + 2 * pos.r) + 1) {
immutable double x = (j - pos.cx) / 2.0 + 0.5 + pos.cx;
enum Hit { background, posSphere, negSphere }
double zb1, zs2;
immutable Hit hitResult = {
double zb2, zs1;
if (!hitSphere(pos, x, y, zb1, zb2)) {
// Ray lands in blank space, draw bg.
return Hit.background;
} else if (!hitSphere(neg, x, y, zs1, zs2)) {
// Ray hits pos sphere but not neg one,
// draw pos sphere surface.
return Hit.posSphere;
} else if (zs1 > zb1) {
// ray hits both, but pos front surface is closer.
return Hit.posSphere;
} else if (zs2 > zb2) {
// pos sphere surface is inside neg sphere,
// show bg.
return Hit.background;
} else if (zs2 > zb1) {
// Back surface on neg sphere is inside pos
// sphere, the only place where neg sphere
// surface will be shown.
return Hit.negSphere;
} else {
return Hit.posSphere;
}
}();
V3 vec_;
final switch (hitResult) {
case Hit.background:
' '.putchar;
continue JLOOP;
case Hit.posSphere:
vec_ = [x - pos.cx, y - pos.cy, zb1 - pos.cz].V3;
break;
case Hit.negSphere:
vec_ = [neg.cx - x, neg.cy - y, neg.cz - zs2].V3;
break;
}
immutable nvec = vec_.normalize;
immutable double b = light.dot(nvec) ^^ k + ambient;
immutable intensity = cast(int)((1 - b) * shades.length);
immutable normInt = min(shades.length, max(0, intensity));
shades[normInt].putchar;
}
'\n'.putchar;
}
}
void main() {
immutable light = [-50, 30, 50].V3.normalize;
drawSphere(2, 0.5, light);
}
The output is the same of the C version.
Delphi
Translate of #C and #Go, with copy of some parts of #DWScript.
program Death_Star;
{$APPTYPE CONSOLE}
uses
Winapi.Windows,
System.SysUtils,
system.Math,
Vcl.Graphics,
Vcl.Imaging.pngimage;
type
TVector = array of double;
var
light: TVector = [20, -40, -10];
function ClampInt(value, amin, amax: Integer): Integer;
begin
Result := Max(amin, Min(amax, value))
end;
procedure Normalize(var v: TVector);
begin
var len := Sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
v[0] := v[0] / len;
v[1] := v[1] / len;
v[2] := v[2] / len;
end;
function Dot(x, y: TVector): Double;
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: Double;
end;
const
pos: TSphere = (
cx: 0;
cy: 0;
cz: 0;
r: 120
);
const
neg: TSphere = (
cx: -90;
cy: -90;
cz: -30;
r: 80
);
function HitSphere(sph: TSphere; x, y: double; var z1, z2: Double): Boolean;
begin
x := x - sph.cx;
y := 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;
function DeathStar(pos, neg: TSphere; k, amb: Double; light: TVector): TBitmap;
var
w, h, yMax, xMax, s: double;
zp1, zp2, zn1, zn2, b: Double;
x, y: Integer;
hit: Boolean;
vec: TVector;
intensity: Byte;
ox, oy: Integer;
begin
w := pos.r * 4;
h := pos.r * 3;
ox := -trunc(pos.cx - w / 2);
oy := -trunc(pos.cy - h / 2);
vec := [0, 0, 0];
Result := TBitmap.Create;
Result.SetSize(trunc(w), trunc(h));
yMax := pos.cy + pos.r;
for y := Trunc(pos.cy - pos.r) to Trunc(yMax) do
begin
xMax := pos.cx + pos.r;
for x := trunc(pos.cy - pos.r) to trunc(xMax) do
begin
hit := HitSphere(pos, x, y, zp1, zp2);
if not hit then
continue;
hit := HitSphere(neg, x, y, zn1, zn2);
if hit then
begin
if zn1 > zp1 then
hit := false
else if zn2 > zp2 then
continue;
end;
if hit then
begin
vec[0] := neg.cx - x;
vec[1] := neg.cy - y;
vec[2] := neg.cz - zn2;
end
else
begin
vec[0] := x - pos.cx;
vec[1] := y - pos.cy;
vec[2] := zp1 - pos.cz;
end;
Normalize(vec);
s := max(0, dot(light, vec));
b := Power(s, k) + amb;
intensity := ClampInt(round(255 * b / (1 + amb)), 0, 254);
Result.Canvas.Pixels[x + ox, y + oy] := rgb(intensity, intensity, intensity);
end;
end;
end;
var
bmp: TBitmap;
begin
Normalize(light);
bmp := DeathStar(pos, neg, 1.2, 0.3, light);
with TPngImage.Create do
begin
Assign(bmp);
TransparentColor := clwhite;
SaveToFile('out.png');
bmp.Free;
Free;
end;
end.
DWScript
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);
Frink
This program not only draws a Death Star and renders it onscreen projected on the x,y, and z axes but also outputs a .stl file for 3-D printing. Frink has built-in routines for 3-D modeling.
res = 254 / in
v = callJava["frink.graphics.VoxelArray", "makeSphere", [1/2 inch res]]
dish = callJava["frink.graphics.VoxelArray", "makeSphere", [1/2 inch res]]
dish.translate[round[.45 inch res], round[.45 inch res], round[.45 inch res]]
v.remove[dish]
v.projectX[undef].show["X"]
v.projectY[undef].show["Y"]
v.projectZ[undef].show["Z"]
filename = "DeathStar.stl"
print["Writing $filename..."]
w = new Writer[filename]
w.println[v.toSTLFormat["DeathStar", 1/(res mm)]]
w.close[]
println["done."]
Go
package main
import (
"fmt"
"image"
"image/color"
"image/png"
"math"
"os"
)
type vector [3]float64
func (v *vector) normalize() {
invLen := 1 / math.Sqrt(dot(v, v))
v[0] *= invLen
v[1] *= invLen
v[2] *= invLen
}
func dot(x, y *vector) float64 {
return x[0]*y[0] + x[1]*y[1] + x[2]*y[2]
}
type sphere struct {
cx, cy, cz int
r int
}
func (s *sphere) hit(x, y int) (z1, z2 float64, hit bool) {
x -= s.cx
y -= s.cy
if zsq := s.r*s.r - (x*x + y*y); zsq >= 0 {
zsqrt := math.Sqrt(float64(zsq))
return float64(s.cz) - zsqrt, float64(s.cz) + zsqrt, true
}
return 0, 0, false
}
func deathStar(pos, neg *sphere, k, amb float64, dir *vector) *image.Gray {
w, h := pos.r*4, pos.r*3
bounds := image.Rect(pos.cx-w/2, pos.cy-h/2, pos.cx+w/2, pos.cy+h/2)
img := image.NewGray(bounds)
vec := new(vector)
for y, yMax := pos.cy-pos.r, pos.cy+pos.r; y <= yMax; y++ {
for x, xMax := pos.cx-pos.r, pos.cx+pos.r; x <= xMax; x++ {
zb1, zb2, hit := pos.hit(x, y)
if !hit {
continue
}
zs1, zs2, hit := neg.hit(x, y)
if hit {
if zs1 > zb1 {
hit = false
} else if zs2 > zb2 {
continue
}
}
if hit {
vec[0] = float64(neg.cx - x)
vec[1] = float64(neg.cy - y)
vec[2] = float64(neg.cz) - zs2
} else {
vec[0] = float64(x - pos.cx)
vec[1] = float64(y - pos.cy)
vec[2] = zb1 - float64(pos.cz)
}
vec.normalize()
s := dot(dir, vec)
if s < 0 {
s = 0
}
lum := 255 * (math.Pow(s, k) + amb) / (1 + amb)
if lum < 0 {
lum = 0
} else if lum > 255 {
lum = 255
}
img.SetGray(x, y, color.Gray{uint8(lum)})
}
}
return img
}
func main() {
dir := &vector{20, -40, -10}
dir.normalize()
pos := &sphere{0, 0, 0, 120}
neg := &sphere{-90, -90, -30, 100}
img := deathStar(pos, neg, 1.5, .2, dir)
f, err := os.Create("dstar.png")
if err != nil {
fmt.Println(err)
return
}
if err = png.Encode(f, img); err != nil {
fmt.Println(err)
}
if err = f.Close(); err != nil {
fmt.Println(err)
}
}
Haskell
ASCII art
import Data.List (genericLength)
shades = ".:!*oe%#&@"
n = genericLength shades
dot a b = sum $ zipWith (*) a b
normalize x = (/ sqrt (x `dot` x)) <$> x
deathStar r k amb = unlines $
[ [ if x*x + y*y <= r*r
then let vec = normalize $ normal x y
b = (light `dot` vec) ** k + amb
intensity = (1 - b)*(n - 1)
in shades !! round ((0 `max` intensity) `min` n)
else ' '
| y <- map (/2.12) [- 2*r - 0.5 .. 2*r + 0.5] ]
| x <- [ - r - 0.5 .. r + 0.5] ]
where
light = normalize [-30,-30,-50]
normal x y
| (x+r)**2 + (y+r)**2 <= r**2 = [x+r, y+r, sph2 x y]
| otherwise = [x, y, sph1 x y]
sph1 x y = sqrt (r*r - x*x - y*y)
sph2 x y = r - sqrt (r*r - (x+r)**2 - (y+r)**2)
λ> putStrLn $ deathStar 10 4 0.1 eeeeoo*&&&&&&& eeeeeoooo**!&&&&&&&&&&&& eeooooooo***!!&&&&&&&&&&&&&&&& eooooo*****!!!::&&&&&&&&&&&&&&&&&& eooo****!!!!:::.&&&&&&&&&&&&&&&&&&&& eeeoo***!!!::::.&&&&&&##############&&&& eeeoo***!!:::...&&&########%%%%%%%%#####&& eeoo**!!!::...&&######%%%%%%%eeeeee%%%%### eooo**!!::..&&&#####%%%%%eeeeeooooooeeee%%#& oo**!!:&&&&&&&####%%%%eeeoooo********oooee%# &&&&&&&&&&&&#####%%%eeeoo****!!!!!!!!***oe%# &&&&&&&&&&&####%%%eeeoo***!!!::::::::!!**oe# &&&&&&&&&&###%%%eeooo**!!:::.......::!!*oe &&&&&&&&&####%%eeeoo**!!::..........::!*o% &&&&&&&&####%%eeoo**!!::...........:!*o% &&&&&&####%%eeoo**!!::..........:!*o &&&&&&###%%%eeoo**!!::......::!*oe &&&&&###%%%eeoo**!!!!:::!!!*o% &&&&###%%%eeoooo****ooe% &&####%%%%%%%#
J
load'graphics/viewmat'
mag =: +/&.:*:"1
norm=: %"1 0 mag
dot =: +/@:*"1
NB. (pos;posr;neg;negr) getvec (x,y)
getvec =: 4 :0 "1
pt =. y
'pos posr neg negr' =. x
if. (dot~ pt-}:pos) > *:posr do.
0 0 0
else.
zb =. ({:pos) (-,+) posr -&.:*: pt mag@:- }:pos
if. (dot~ pt-}:neg) > *:negr do.
(pt,{:zb) - pos
else.
zs =. ({:neg) (-,+) negr -&.:*: pt mag@:- }:neg
if. zs >&{. zb do. (pt,{:zb) - pos
elseif. zs >&{: zb do. 0 0 0
elseif. ({.zs) < ({:zb) do. neg - (pt,{.zs)
elseif. do. (pt,{.zb) - pos end.
end.
end.
)
NB. (k;ambient;light) draw_sphere (pos;posr;neg;negr)
draw_sphere =: 4 :0
'pos posr neg negr' =. y
'k ambient light' =. x
vec=. norm y getvec ,"0// (2{.pos) +/ i: 200 j.~ 0.5+posr
b=. (mag vec) * ambient + k * 0>. light dot vec
)
togray =: 256#. 255 255 255 <.@*"1 0 (%>./@,)
env=.(2; 0.5; (norm _50 30 50))
sph=. 20 20 0; 20; 1 1 _6; 20
'rgb' viewmat togray env draw_sphere sph
Java
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Point3D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
public class DeathStar extends Application {
private static final int DIVISION = 200;// the bigger the higher resolution
float radius = 300;// radius of the sphere
@Override
public void start(Stage primaryStage) throws Exception {
Point3D otherSphere = new Point3D(-radius, 0, -radius * 1.5);
final TriangleMesh triangleMesh = createMesh(DIVISION, radius, otherSphere);
MeshView a = new MeshView(triangleMesh);
a.setTranslateY(radius);
a.setTranslateX(radius);
a.setRotationAxis(Rotate.Y_AXIS);
Scene scene = new Scene(new Group(a));
// uncomment if you want to move the other sphere
// scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
// Point3D sphere = otherSphere;
//
// @Override
// public void handle(KeyEvent e) {
// KeyCode code = e.getCode();
// switch (code) {
// case UP:
// sphere = sphere.add(0, -10, 0);
// break;
// case DOWN:
// sphere = sphere.add(0, 10, 0);
// break;
// case LEFT:
// sphere = sphere.add(-10, 0, 0);
// break;
// case RIGHT:
// sphere = sphere.add(10, 0, 0);
// break;
// case W:
// sphere = sphere.add(0, 0, 10);
// break;
// case S:
// sphere = sphere.add(0, 0, -10);
// break;
// default:
// return;
// }
// a.setMesh(createMesh(DIVISION, radius, sphere));
//
// }
// });
primaryStage.setScene(scene);
primaryStage.show();
}
static TriangleMesh createMesh(final int division, final float radius, final Point3D centerOtherSphere) {
Rotate rotate = new Rotate(180, centerOtherSphere);
final int div2 = division / 2;
final int nPoints = division * (div2 - 1) + 2;
final int nTPoints = (division + 1) * (div2 - 1) + division * 2;
final int nFaces = division * (div2 - 2) * 2 + division * 2;
final float rDiv = 1.f / division;
float points[] = new float[nPoints * 3];
float tPoints[] = new float[nTPoints * 2];
int faces[] = new int[nFaces * 6];
int pPos = 0, tPos = 0;
for (int y = 0; y < div2 - 1; ++y) {
float va = rDiv * (y + 1 - div2 / 2) * 2 * (float) Math.PI;
float sin_va = (float) Math.sin(va);
float cos_va = (float) Math.cos(va);
float ty = 0.5f + sin_va * 0.5f;
for (int i = 0; i < division; ++i) {
double a = rDiv * i * 2 * (float) Math.PI;
float hSin = (float) Math.sin(a);
float hCos = (float) Math.cos(a);
points[pPos + 0] = hSin * cos_va * radius;
points[pPos + 2] = hCos * cos_va * radius;
points[pPos + 1] = sin_va * radius;
final Point3D point3D = new Point3D(points[pPos + 0], points[pPos + 1], points[pPos + 2]);
double distance = centerOtherSphere.distance(point3D);
if (distance <= radius) {
Point3D subtract = centerOtherSphere.subtract(point3D);
Point3D transform = rotate.transform(subtract);
points[pPos + 0] = (float) transform.getX();
points[pPos + 1] = (float) transform.getY();
points[pPos + 2] = (float) transform.getZ();
}
tPoints[tPos + 0] = 1 - rDiv * i;
tPoints[tPos + 1] = ty;
pPos += 3;
tPos += 2;
}
tPoints[tPos + 0] = 0;
tPoints[tPos + 1] = ty;
tPos += 2;
}
points[pPos + 0] = 0;
points[pPos + 1] = -radius;
points[pPos + 2] = 0;
points[pPos + 3] = 0;
points[pPos + 4] = radius;
points[pPos + 5] = 0;
pPos += 6;
int pS = (div2 - 1) * division;
float textureDelta = 1.f / 256;
for (int i = 0; i < division; ++i) {
tPoints[tPos + 0] = rDiv * (0.5f + i);
tPoints[tPos + 1] = textureDelta;
tPos += 2;
}
for (int i = 0; i < division; ++i) {
tPoints[tPos + 0] = rDiv * (0.5f + i);
tPoints[tPos + 1] = 1 - textureDelta;
tPos += 2;
}
int fIndex = 0;
for (int y = 0; y < div2 - 2; ++y) {
for (int x = 0; x < division; ++x) {
int p0 = y * division + x;
int p1 = p0 + 1;
int p2 = p0 + division;
int p3 = p1 + division;
int t0 = p0 + y;
int t1 = t0 + 1;
int t2 = t0 + division + 1;
int t3 = t1 + division + 1;
// add p0, p1, p2
faces[fIndex + 0] = p0;
faces[fIndex + 1] = t0;
faces[fIndex + 2] = p1 % division == 0 ? p1 - division : p1;
faces[fIndex + 3] = t1;
faces[fIndex + 4] = p2;
faces[fIndex + 5] = t2;
fIndex += 6;
// add p3, p2, p1
faces[fIndex + 0] = p3 % division == 0 ? p3 - division : p3;
faces[fIndex + 1] = t3;
faces[fIndex + 2] = p2;
faces[fIndex + 3] = t2;
faces[fIndex + 4] = p1 % division == 0 ? p1 - division : p1;
faces[fIndex + 5] = t1;
fIndex += 6;
}
}
int p0 = pS;
int tB = (div2 - 1) * (division + 1);
for (int x = 0; x < division; ++x) {
int p2 = x, p1 = x + 1, t0 = tB + x;
faces[fIndex + 0] = p0;
faces[fIndex + 1] = t0;
faces[fIndex + 2] = p1 == division ? 0 : p1;
faces[fIndex + 3] = p1;
faces[fIndex + 4] = p2;
faces[fIndex + 5] = p2;
fIndex += 6;
}
p0 = p0 + 1;
tB = tB + division;
int pB = (div2 - 2) * division;
for (int x = 0; x < division; ++x) {
int p1 = pB + x, p2 = pB + x + 1, t0 = tB + x;
int t1 = (div2 - 2) * (division + 1) + x, t2 = t1 + 1;
faces[fIndex + 0] = p0;
faces[fIndex + 1] = t0;
faces[fIndex + 2] = p1;
faces[fIndex + 3] = t1;
faces[fIndex + 4] = p2 % division == 0 ? p2 - division : p2;
faces[fIndex + 5] = t2;
fIndex += 6;
}
TriangleMesh m = new TriangleMesh();
m.getPoints().setAll(points);
m.getTexCoords().setAll(tPoints);
m.getFaces().setAll(faces);
return m;
}
public static void main(String[] args) {
launch(args);
}
}
Using Java 11
Alternatively, without using JavaFX, which has been removed from the JavaJDK since version 11.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.imageio.ImageIO;
public final class DeathStar {
public static void main(String[] aArgs) throws IOException {
Vector direction = new Vector(20.0, -40.0, -10.0);
direction.normalise();
Sphere positive = new Sphere(0, 0, 0, 120);
Sphere negative = new Sphere(-90, -90, -30, 100);
BufferedImage image = deathStar(positive, negative, direction, 1.5, 0.5);
ImageIO.write(image, "png", new File("DeathStarJava.png"));
}
private static BufferedImage deathStar(
Sphere aPositive, Sphere aNegative, Vector aDirection, double aShadow, double aBrightness) {
final int width = aPositive.radius * 4;
final int height = aPositive.radius * 3;
BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics graphics = result.getGraphics();
graphics.setColor(Color.CYAN);
graphics.fillRect(0, 0, width, height);
Vector ray = new Vector(0.0, 0.0, 0.0);
final int deltaX = aPositive.x - width / 2;
final int deltaY = aPositive.y - height / 2;
double xMax = aPositive.x + aPositive.radius;
double yMax = aPositive.y + aPositive.radius;
for ( int y = aPositive.y - aPositive.radius; y < yMax; y++ ) {
for ( int x = aPositive.x - aPositive.radius; x < xMax; x++ ) {
List<Object> contacts = aPositive.contact(x, y);
final double zb1 = (double) contacts.get(0);
final int zb2 = (int) contacts.get(1);
final boolean positiveHit = (boolean) contacts.get(2);
if ( ! positiveHit ) {
continue;
}
contacts = aNegative.contact(x, y);
final double zs1 = (double) contacts.get(0);
final int zs2 = (int) contacts.get(1);
boolean negativeHit = (boolean) contacts.get(2);
if ( negativeHit ) {
if ( zs1 > zb1 ) {
negativeHit = false;
} else if ( zs2 > zb2 ) {
continue;
}
}
if ( negativeHit ) {
ray.x = aNegative.x - x;
ray.y = aNegative.y - y;
ray.z = aNegative.z - zs2;
} else {
ray.x = x - aPositive.x;
ray.y = y - aPositive.y;
ray.z = zb1 - aPositive.z;
}
ray.normalise();
double rayComponent = ray.scalarProduct(aDirection);
if ( rayComponent < 0 ) {
rayComponent = 0;
}
int color = (int) ( 255 * ( Math.pow(rayComponent, aShadow) + aBrightness) / ( 1 + aBrightness ) );
if ( color < 0 ) {
color = 0;
} else if ( color > 255 ) {
color = 255;
}
result.setRGB(x - deltaX, y - deltaY, color);
}
}
return result;
}
private static class Vector {
public Vector(double aX, double aY, double aZ) {
x = aX; y = aY; z = aZ;
}
public double scalarProduct(Vector aOther) {
return x * aOther.x + y * aOther.y + z * aOther.z;
}
public Vector normalise() {
final double magnitude = Math.sqrt(this.scalarProduct(this));
return new Vector(x /= magnitude, y /= magnitude, z /= magnitude);
}
private double x, y, z;
}
private static class Sphere {
public Sphere(int aX, int aY, int aZ, int aRadius) {
x = aX; y = aY; z = aZ; radius = aRadius;
}
public List<Object> contact(int aX, int aY) {
final int xx = aX - x;
final int yy = aY - y;
final int zSquared = radius * radius - ( xx * xx + yy * yy );
if ( zSquared >= 0 ) {
final double zz = Math.sqrt(zSquared);
return List.of(z - zz, z, true);
}
return List.of( 0.0, 0, false );
}
private int x, y, z, radius;
}
}
- Output:
JavaScript
Layer circles and gradients to achieve result similar to that of the Wikipedia page for the Death Star.
<!DOCTYPE html>
<html>
<body style="margin:0">
<canvas id="myCanvas" width="250" height="250" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.
</canvas>
<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
//Fill the canvas with a dark gray background
ctx.fillStyle = "#222222";
ctx.fillRect(0,0,250,250);
// Create radial gradient for large base circle
var grd = ctx.createRadialGradient(225,175,190,225,150,130);
grd.addColorStop(0,"#EEEEEE");
grd.addColorStop(1,"black");
//Apply gradient and fill circle
ctx.fillStyle = grd;
ctx.beginPath();
ctx.arc(125,125,105,0,2*Math.PI);
ctx.fill();
// Create linear gradient for small inner circle
var grd = ctx.createLinearGradient(75,90,102,90);
grd.addColorStop(0,"black");
grd.addColorStop(1,"gray");
//Apply gradient and fill circle
ctx.fillStyle = grd;
ctx.beginPath();
ctx.arc(90,90,30,0,2*Math.PI);
ctx.fill();
//Add another small circle on top of the previous one to enhance the "shadow"
ctx.fillStyle = "black";
ctx.beginPath();
ctx.arc(80,90,17,0,2*Math.PI);
ctx.fill();
</script>
</body>
</html>
Julia
# run in REPL
using GLMakie
function deathstar()
n = 60
θ = [0; (0.5: n - 0.5) / n; 1]
φ = [(0: 2n - 2) * 2 / (2n - 1); 2]
# if x is +0.9 radius units, replace it with the coordinates of sphere surface
# at (1.2,0,0) center, radius 0.5 units
x = [(x1 = cospi(φ)*sinpi(θ)) > 0.9 ? 1.2 - x1 * 0.5 : x1 for θ in θ, φ in φ]
y = [sinpi(φ)*sinpi(θ) for θ in θ, φ in φ]
z = [cospi(θ) for θ in θ, φ in φ]
scene = Scene(backgroundcolor=:black)
surface!(scene, x, y, z, color = rand(RGBAf0, 124, 124), show_axis=false)
return scene
end
scene = deathstar()
LSL
Rez a box on the ground, raise it up a few meters, add the following as a New Script.
default {
state_entry() {
llSetPrimitiveParams([PRIM_NAME, "RosettaCode DeathStar"]);
llSetPrimitiveParams([PRIM_DESC, llGetObjectName()]);
llSetPrimitiveParams([PRIM_TYPE, PRIM_TYPE_SPHERE, PRIM_HOLE_CIRCLE, <0.0, 1.0, 0.0>, 0.0, <0.0, 0.0, 0.0>, <0.12, 1.0, 0.0>]);
llSetPrimitiveParams([PRIM_ROTATION, <-0.586217, 0.395411, -0.586217, 0.395411>]);
llSetPrimitiveParams([PRIM_TEXTURE, ALL_SIDES, TEXTURE_BLANK, ZERO_VECTOR, ZERO_VECTOR, 0.0]);
llSetPrimitiveParams([PRIM_TEXT, llGetObjectName(), <1.0, 1.0, 1.0>, 1.0]);
llSetPrimitiveParams([PRIM_COLOR, ALL_SIDES, <0.5, 0.5, 0.5>, 1.0]);
llSetPrimitiveParams([PRIM_BUMP_SHINY, ALL_SIDES, PRIM_SHINY_HIGH, PRIM_BUMP_NONE]);
llSetPrimitiveParams([PRIM_SIZE, <10.0, 10.0, 10.0>]);
llSetPrimitiveParams([PRIM_OMEGA, <0.0, 0.0, 1.0>, 1.0, 1.0]);
}
}
Lua
function V3(x,y,z) return {x=x,y=y,z=z} end
function dot(v,w) return v.x*w.x + v.y*w.y + v.z*w.z end
function norm(v) local m=math.sqrt(dot(v,v)) return V3(v.x/m, v.y/m, v.z/m) end
function clamp(n,lo,hi) return math.floor(math.min(math.max(lo,n),hi)) end
function hittest(s, x, y)
local z = s.r^2 - (x-s.x)^2 - (y-s.y)^2
if z >= 0 then
z = math.sqrt(z)
return true, s.z-z, s.z+z
end
return false, 0, 0
end
function deathstar(pos, neg, sun, k, amb)
shades = {[0]=" ",".",":","!","*","o","e","&","#","%","@"}
for y = pos.x-pos.r-0.5, pos.x+pos.r+0.5 do
for x = pos.x-pos.r-0.5, pos.x+pos.r+0.5, 0.5 do
local hitpos, pz1, pz2 = hittest(pos, x, y)
local result, hitneg, nz1, nz2 = 0
if hitpos then
hitneg, nz1, nz2 = hittest(neg, x, y)
if not hitneg or nz1 > pz1 then result = 1
elseif nz2 > pz2 then result = 0
elseif nz2 > pz1 then result = 2
else result = 1
end
end
local shade = 0
if result > 0 then
if result == 1 then
shade = clamp((1-dot(sun, norm(V3(x-pos.x, y-pos.y, pz1-pos.z)))^k+amb) * #shades, 1, #shades)
else
shade = clamp((1-dot(sun, norm(V3(neg.x-x, neg.y-y, neg.z-nz2)))^k+amb) * #shades, 1, #shades)
end
end
io.write(shades[shade])
end
io.write("\n")
end
end
deathstar({x=20, y=20, z=0, r=20}, {x=10, y=10, z=-15, r=10}, norm(V3(-2,1,3)), 2, 0.1)
- Output:
@@@%%%%%%%%%#########% @@@@@%%%%%%#######&&&&&&&&&&&&&&&&&&## @@@@@@%%%%%%######&&&&&&&eeeeeeeeeeeeeeeeeeeee&& @@@@@@@@@@@@@@@@@@@&&&&&&eeeeeeeoooooooooooooooooooooeee& @@@@@&####%%%%@@@@@@@@@@@@%eeeeoooooooo*******************oooee @@@@eeee&&&&####%%%@@@@@@@@@@@@%oooooo********!!!!!!!!!!!!!!*****oooe @@@**ooooeeee&&&####%%%@@@@@@@@@@@%oo*******!!!!!!!!!!!!!!!!!!!!!!!****oo& @@@!!!****ooooeee&&&###%%%%@@@@@@@@@@%*****!!!!!!!!:::::::::::::::::!!!!!**ooe @@@:::!!!!!****oooeee&&###%%%%@@@@@@@@@%***!!!!!!!::::::::::::::::::::::::!!!***oe @@@@::::::::!!!***oooeee&&&##%%%%@@@@@@@@@**!!!!!!!:::::::::............::::::!!!**oo @@@.......::::!!!!***ooeee&&###%%%@@@@@@@@@*!!!!!!::::::::..................:::::!!!**oe %@@@@.........::::!!!**oooee&&&##%%%@@@@@@@@*!!!!!!::::::::......................::::!!!*oe %@@@@...........:::!!!***ooeee&&###%%%@@@@@@**!!!!!!:::::::........................::::!!!*oo %@@@@@...........:::!!!***ooeee&&###%%@@@@@@***!!!!!!:::::::.........................::::!!!*oe @@@@@@..........::::!!!**oooee&&&##%%%@@@@@****!!!!!!:::::::..........................::::!!**oe %@@@@@@::::...:::::!!!!***ooeee&&###%%@@@@o*****!!!!!!:::::::..........................::::!!!**o @@@@@@@@!!!:::::!!!!!***oooee&&&##%%%@@oooo******!!!!!!:::::::.........................:::::!!**oe %@@@@@@@@@o****!******ooooeee&&###%%%eeeooooo*****!!!!!!!:::::::........................::::!!!**oe %@@@@@@@@@@@eeoooooooeeeee&&&##%%%&eeeeeeooooo*****!!!!!!!::::::::.....................:::::!!!**oo& %@@@@@@@@@@@@@@@@##&&&#####%###&&&&&eeeeeoooooo******!!!!!!:::::::::..................:::::!!!!**oe& %@@@@@@@@@@@@@@@@@@@%%%%%%######&&&&&eeeeeoooooo******!!!!!!!:::::::::::...........:::::::!!!!***oe& %%@@@@@@@@@@@@@@@@@@@%%%%%%######&&&&&eeeeeeooooo*******!!!!!!!::::::::::::::::::::::::::!!!!***ooe& #%@@@@@@@@@@@@@@@@@@@%%%%%%%######&&&&&eeeeeeoooooo*******!!!!!!!!::::::::::::::::::::!!!!!!***ooee %%@@@@@@@@@@@@@@@@@@@%%%%%%%######&&&&&&eeeeeeoooooo*******!!!!!!!!!!!!::::::::::!!!!!!!!****ooee& #%@@@@@@@@@@@@@@@@@@@@%%%%%%%######&&&&&&eeeeeeooooooo*********!!!!!!!!!!!!!!!!!!!!!!!!****oooee& %%@@@@@@@@@@@@@@@@@@@@@%%%%%%%######&&&&&&eeeeeeeooooooo**********!!!!!!!!!!!!!!!*******ooooee&# &%%@@@@@@@@@@@@@@@@@@@@@%%%%%%%#######&&&&&&eeeeeeeooooooooo*************************oooooeee&# #%%@@@@@@@@@@@@@@@@@@@@@@%%%%%%%#######&&&&&&&eeeeeeeooooooooooo****************ooooooeeee&&# &%%@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%#######&&&&&&&&eeeeeeeeeoooooooooooooooooooooooooeeeee&&## #%%@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%########&&&&&&&&eeeeeeeeeeeeoooooooooooooeeeeeeee&&&##% #%%@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%########&&&&&&&&&&eeeeeeeeeeeeeeeeeeeeeeee&&&&&##% #%%@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%#########&&&&&&&&&&&&&&&&eeeee&&&&&&&&&&####%% #%%@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%%###########&&&&&&&&&&&&&&&&&&&&######%%% #%%@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%%%#############################%%%%@ #%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%%%%%%%###############%%%%%%%@ #%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%%%%%%%%%%%%%%%%%%%@@@ #%%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%@@@@@@@@@ %%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ %@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@
Maple
with(plots):
with(plottools):
plots:-display(
implicitplot3d(x^2 + y^2 + z^2 = 1, x = -1..0.85, y = -1..1, z = -1..1, style = surface, grid = [50,50,50]),
translate(rotate(implicitplot3d(x^2 + y^2 + z^2 = 1, x = 0.85..1, y = -1..1, z = -1..1, style = surface, grid = [50,50,50]), 0, Pi, 0), 1.70, 0, 0),
axes = none, scaling = constrained, color = gray)
Mathematica / Wolfram Language
RegionPlot3D[x^2 + y^2 + z^2 < 1 && (x + 1.7)^2 + y^2 + z^2 > 1,
{x, -1, 1}, {y, -1, 1}, {z, -1, 1},
Boxed -> False, Mesh -> False, Axes -> False, Background -> Black, PlotPoints -> 100]
Nim
The result is written in a PNG file. For this purpose, we used the modules “bitmap” and “grayscale_image” created for the tasks “Bitmap” and “Grayscale image”. To write the PNG file, we use the third party library “nimPNG”.
import math
import bitmap, grayscale_image, nimPNG
type
Vector = array[3, float]
Sphere = object
cx, cy, cz: int
r: int
#---------------------------------------------------------------------------------------------------
func dot(x, y: Vector): float {.inline.} =
x[0] * y[0] + x[1] * y[1] + x[2] * y[2]
#---------------------------------------------------------------------------------------------------
func normalize(v: var Vector) =
let invLen = 1 / sqrt(dot(v, v))
v[0] *= invLen
v[1] *= invLen
v[2] *= invLen
#---------------------------------------------------------------------------------------------------
func hit(s: Sphere; x, y: int): tuple[z1, z2: float; hit: bool] =
let x = x - s.cx
let y = y - s.cy
let zsq = s.r * s.r - (x * x + y * y)
if zsq >= 0:
let zsqrt = sqrt(zsq.toFloat)
result = (s.cz.toFloat - zsqrt, s.cz.toFloat, true)
else:
result = (0.0, 0.0, false)
#---------------------------------------------------------------------------------------------------
func deathStar(pos, neg: Sphere; k, amb: float; dir: Vector): GrayImage =
let w = pos.r * 4
let h = pos.r * 3
result = newGrayImage(w, h)
var vect: Vector
let deltaX = pos.cx - w div 2
let deltaY = pos.cy - h div 2
let xMax = pos.cx + pos.r
let yMax = pos.cy + pos.r
for y in (pos.cy - pos.r)..yMax:
for x in (pos.cx - pos.r)..xMax:
let (zb1, zb2, posHit) = pos.hit(x, y)
if not posHit: continue
var (zs1, zs2, negHit) = neg.hit(x, y)
if negHit:
if zs1 > zb1: negHit = false
elif zs2 > zb2: continue
if negHit:
vect[0] = (neg.cx - x).toFloat
vect[1] = (neg.cy - y).toFloat
vect[2] = neg.cz.toFloat - zs2
else:
vect[0] = (x - pos.cx).toFloat
vect[1] = (y - pos.cy).toFloat
vect[2] = zb1 - pos.cz.toFloat
vect.normalize()
var s = dot(dir, vect)
if s < 0: s = 0
var lum = (255 * (s.pow(k) + amb) / (1 + amb)).toInt
if lum < 0: lum = 0
elif lum > 255: lum = 255
result[x - deltaX, y - deltaY] = Luminance(lum)
#———————————————————————————————————————————————————————————————————————————————————————————————————
var dir: Vector = [float 20, -40, -10]
dir.normalize()
let pos = Sphere(cx: 0, cy: 0, cz: 0, r: 120)
let neg = Sphere(cx: -90, cy: -90, cz: -30, r: 100)
let grayImage = deathStar(pos, neg, 1.5, 0.2, dir)
# Save to PNG. We convert to an RGB image then transform the pixels
# in a sequence of bytes (actually a copy) in order to call "savePNG24".
let rgbImage = grayImage.toImage
var data = newSeqOfCap[byte](rgbImage.pixels.len * 3)
for color in rgbImage.pixels:
data.add([color.r, color.g, color.b])
echo savePNG24("death_star.png", data, rgbImage.w, rgbImage.h)
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);
}
}
Perl
Writes a PGM to stdout.
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 $pos = [ 120, 120, 0, 120 ];
my $neg = [ -77, -33, -100, 190 ];
my $light = normalize([ -12, 13, -10 ]);
sub draw {
my ($k, $amb) = @_;
binmode STDOUT, ":raw";
print "P5\n", $pos->[0] * 2 + 3, " ", $pos->[1] * 2 + 3, "\n255\n";
for my $y (($pos->[1] - $pos->[3] - 1) .. ($pos->[1] + $pos->[3] + 1)) {
my @row = ();
for my $x (($pos->[0] - $pos->[3] - 1) .. ($pos->[0] + $pos->[3] + 1)) {
my ($hit, @hs) = 0;
my @h = hit($pos, $x, $y);
if (!@h) { $hit = 0 }
elsif (!(@hs = hit($neg, $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 - $pos->[0],
$y - $pos->[1],
$h[0] - $pos->[2] ];
} else {
$v = [ $neg->[0] - $x,
$neg->[1] - $y,
$neg->[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(2, 0.2);
Phix
You can run this online here. Note it is rather slow to redraw fullscreen.
-- -- demo\rosetta\DeathStar.exw -- ========================== -- -- Translated from Go. -- with javascript_semantics include pGUI.e constant title = "Death Star" Ihandle dlg, canvas cdCanvas cddbuffer, cdcanvas function dot(sequence x, sequence y) return sum(sq_mul(x,y)) end function function normalize(sequence v) atom len = sqrt(dot(v, v)) if len=0 then return {0,0,0} end if return sq_mul(v,1/len) end function enum X,Y,Z function hit(sequence s, atom x, y, r) x -= s[X] y -= s[Y] atom zsq := r*r - (x*x + y*y) if zsq >= 0 then atom zsqrt := sqrt(zsq) return {s[Z] - zsqrt, s[Z] + zsqrt, true} end if return {0, 0, false} end function procedure deathStar(integer width, height, atom k, atom amb, sequence direction) atom t0 = time()+1, t1 = t0, lmul = 255/(1+amb) integer r = floor((min(width,height)-40)/2), cx = floor(width/2), cy = floor(height/2) sequence pos = {0,0,0}, neg = {r*-3/4,r*-3/4,r*-1/4} for y = -r to +r do if time()>t1 then -- Let the user know we aren't completely dead just yet IupSetStrAttribute(dlg,"TITLE","%s - drawing (%d%%)",{title,100*(y+r)/(2*r)}) t1 = time()+1 -- -- Hmm, not entirely sure why this is needed, but without it -- after ~7 seconds the window gets a "(Not Responding)" and -- then something decides to force a full repaint, which at -- fullscreen will never finish in < 7s on this ancient box. -- I suppose this is the corrollary to the above, this time -- letting Windows 10 know the process is not quite dead... -- Currently and possibly forever neither of these routines -- exist in pGUI.js, the browser is more forgiving anyway. -- if platform()!=JS then if IupLoopStep()=IUP_CLOSE then IupExitLoop() exit end if end if end if for x = -r to +r do atom {zb1, zb2, hit1} := hit(pos, x, y, r) if hit1 then atom {zs1, zs2, hit2} := hit(neg, x, y, r/2) if not hit2 or zs2<=zb2 then bool dish = hit2 and zs1<=zb1 sequence vec = iff(dish?sq_sub(neg,{x,y,zs2}):{x,y,zb1}) atom s = dot(direction, normalize(vec)), l = iff(s<=0?0:power(s,k)) integer lum = and_bits(#FF,lmul*(l+amb)) cdCanvasPixel(cddbuffer, cx+x, cy-y, lum*#10101) end if end if end for end for if t1!=t0 then IupSetStrAttribute(dlg,"TITLE",title) end if end procedure function redraw_cb(Ihandle /*ih*/, integer /*posx*/, /*posy*/) integer {width, height} = IupGetIntInt(canvas, "DRAWSIZE") cdCanvasActivate(cddbuffer) cdCanvasClear(cddbuffer) deathStar(width, height, 1.5, 0.2, normalize({20, -40, -10})) cdCanvasFlush(cddbuffer) return IUP_DEFAULT end function function map_cb(Ihandle ih) cdcanvas = cdCreateCanvas(CD_IUP, ih) cddbuffer = cdCreateCanvas(CD_DBUFFER, cdcanvas) cdCanvasSetBackground(cddbuffer, CD_BLACK) return IUP_DEFAULT end function procedure main() IupOpen() canvas = IupCanvas("RASTERSIZE=340x340") IupSetCallbacks(canvas, {"MAP_CB", Icallback("map_cb"), "ACTION", Icallback("redraw_cb")}) dlg = IupDialog(canvas,`TITLE="%s"`,{title}) IupMap(dlg) IupSetAttribute(canvas, "RASTERSIZE", NULL) -- release the minimum limitation IupShow(dlg) if platform()!=JS then IupMainLoop() IupClose() end if end procedure main()
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} }
}
}
Python
import sys, math, collections
Sphere = collections.namedtuple("Sphere", "cx cy cz r")
V3 = collections.namedtuple("V3", "x y z")
def normalize((x, y, z)):
len = math.sqrt(x**2 + y**2 + z**2)
return V3(x / len, y / len, z / len)
def dot(v1, v2):
d = v1.x*v2.x + v1.y*v2.y + v1.z*v2.z
return -d if d < 0 else 0.0
def hit_sphere(sph, x0, y0):
x = x0 - sph.cx
y = y0 - sph.cy
zsq = sph.r ** 2 - (x ** 2 + y ** 2)
if zsq < 0:
return (False, 0, 0)
szsq = math.sqrt(zsq)
return (True, sph.cz - szsq, sph.cz + szsq)
def draw_sphere(k, ambient, light):
shades = ".:!*oe&#%@"
pos = Sphere(20.0, 20.0, 0.0, 20.0)
neg = Sphere(1.0, 1.0, -6.0, 20.0)
for i in xrange(int(math.floor(pos.cy - pos.r)),
int(math.ceil(pos.cy + pos.r) + 1)):
y = i + 0.5
for j in xrange(int(math.floor(pos.cx - 2 * pos.r)),
int(math.ceil(pos.cx + 2 * pos.r) + 1)):
x = (j - pos.cx) / 2.0 + 0.5 + pos.cx
(h, zb1, zb2) = hit_sphere(pos, x, y)
if not h:
hit_result = 0
else:
(h, zs1, zs2) = hit_sphere(neg, x, y)
if not h:
hit_result = 1
elif zs1 > zb1:
hit_result = 1
elif zs2 > zb2:
hit_result = 0
elif zs2 > zb1:
hit_result = 2
else:
hit_result = 1
if hit_result == 0:
sys.stdout.write(' ')
continue
elif hit_result == 1:
vec = V3(x - pos.cx, y - pos.cy, zb1 - pos.cz)
elif hit_result == 2:
vec = V3(neg.cx-x, neg.cy-y, neg.cz-zs2)
vec = normalize(vec)
b = dot(light, vec) ** k + ambient
intensity = int((1 - b) * len(shades))
intensity = min(len(shades), max(0, intensity))
sys.stdout.write(shades[intensity])
print
light = normalize(V3(-50, 30, 50))
draw_sphere(2, 0.5, light)
Q
write an image in BMP format:
/ https://en.wikipedia.org/wiki/BMP_file_format
/ BITMAPINFOHEADER / RGB24
/ generate a header
genheader:{[w;h]
0x424d, "x"$(f2i4[54+4*h*w],0,0,0,0,54,0,0,0,40,0,0,0,
f2i4[h],f2i4[w],1,0,24,0,0,0,0,0,
f2i4[h*((w*3)+((w*3)mod 4))],
19,11,0,0,19,11,0,0,0,0,0,0,0,0,0,0)};
/ generate a raster line at a vertical position
genrow:{[w;y;fcn]
row:enlist 0i;xx:0i;do[w;row,:fcn[xx;y];xx+:1i];row,:((w mod 4)#0i);1_row};
/ generate a bitmap
genbitmap:{[w;h;fcn]
ary:enlist 0i;yy:0i;do[h;ary,:genrow[w;yy;fcn];yy+:1i];"x"$1_ary};
/ deal with endianness
/ might need to reverse last line if host computer is not a PC
f2i4:{[x] r:x;
s0:r mod 256;r-:s0; r%:256;
s1:r mod 256;r-:s1; r%:256;
s2:r mod 256;r-:s2; r%:256;
s3:r mod 256;
"h"$(s0,s1,s2,s3)}
/ compose and write a file
writebmp:{[w;h;fcn;fn]
fn 1: (genheader[h;w],genbitmap[w;h;fcn])};
/ / usage example:
/ w:400;
/ h:300;
/ fcn:{x0:x-w%2;y0:y-h%2;r:175;$[(r*r)>((x0*x0)+(y0*y0));(0;0;255);(0;255;0)]};
/ fn:`:demo.bmp;
/ writebmp[w;h;fcn;fn];
Create the death star image:
w:400; h:300; r:150; l:-0.5 0.7 0.5
sqrt0:{$[x>0;sqrt x;0]};
/ get x,y,z position of point on sphere given x,y,r
z:{[x;y;r]sqrt0((r*r)-((x*x)+(y*y)))};
/ get diffused light at point on sphere
is:{[x;y;r]
z0:z[x;y;r];
s:(x;y;z0)%r;
$[z0>0;i:0.5*1+(+/)(s*l);i:0];
i};
/ get pixel value at given image position
fcn:{[xpx;ypx]
x:xpx-w%2;
y:ypx-h%2;
z1:z[x;y;r];
x2:x+190;
z2:170-z[x2;y;r];
$[(r*r)<((x*x)+(y*y));
$[y>-50;
i:3#0;
i:200 100 50];
$[z2>z1;
i:3#is[x;y;r]*140;
i:3#is[(-1*x2);(-1*y);r]*120]
];
"i"$i};
/ do it ...
\l bmp.q
fn:`:demo.bmp;
writebmp[w;h;fcn;fn];
(converted to JPG ...)
Racket
#lang racket
(require plot)
(plot3d (polar3d (λ (φ θ) (real-part (- (sin θ) (sqrt (- (sqr 1/3) (sqr (cos θ)))))))
#:samples 100 #:line-style 'transparent #:color 9)
#:altitude 60 #:angle 80
#:height 500 #:width 400
#:x-min -1/2 #:x-max 1/2
#:y-min -1/2 #:y-max 1/2
#:z-min 0 #:z-max 1)
Raku
(formerly Perl 6)
Reimplemented to output a .pgm image.
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 $width = my $height = 255; # dimensions of generated .pgm; must be odd
my $s = ($width - 1)/2; # scaled dimension to build geometry
my @light = normalize([ 4, -1, -3 ]);
# positive sphere at origin
my $pos = sphere.new(
cx => 0,
cy => 0,
cz => 0,
r => $s.Int
);
# negative sphere offset to upper left
my $neg = sphere.new(
cx => (-$s*.90).Int,
cy => (-$s*.90).Int,
cz => (-$s*.3).Int,
r => ($s*.7).Int
);
sub MAIN ($outfile = 'deathstar-perl6.pgm') {
spurt $outfile, ("P5\n$width $height\n$depth\n"); # .pgm header
my $out = open( $outfile, :a, :bin ) orelse .die;
say 'Working...';
$out.write( Blob.new( |draw_ds(3, .15) ) );
say 'File written.';
$out.close;
}
sub draw_ds ( $k, $ambient ) {
my @pixels[$height];
(($pos.cy - $pos.r) .. ($pos.cy + $pos.r)).race.map: -> $y {
my @row[$width];
(($pos.cx - $pos.r) .. ($pos.cx + $pos.r)).map: -> $x {
# black if we don't hit positive sphere, ignore negative sphere
if not hit($pos, $x, $y, my $posz) {
@row[$x + $s] = 0;
next;
}
my @vec;
# is front of positive sphere inside negative sphere?
if hit($neg, $x, $y, my $negz) and $negz.min < $posz.min < $negz.max {
# make black if whole positive sphere eaten here
if $negz.min < $posz.max < $negz.max { @row[$x + $s] = 0; next; }
# render inside of negative sphere
@vec = normalize([$neg.cx - $x, $neg.cy - $y, -$negz.max - $neg.cz]);
}
else {
# render outside of positive sphere
@vec = normalize([$x - $pos.cx, $y - $pos.cy, $posz.max - $pos.cz]);
}
my $intensity = dot(@light, @vec) ** $k + $ambient;
@row[$x + $s] = ($intensity * $depth).Int min $depth;
}
@pixels[$y + $s] = @row;
}
flat |@pixels.map: *.list;
}
# normalize a vector
sub normalize (@vec) { @vec »/» ([+] @vec »*« @vec).sqrt }
# dot product of two vectors
sub dot (@x, @y) { -([+] @x »*« @y) max 0 }
# 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 False if $z2 < 0;
$z2 = $z2.sqrt;
$z = $sphere.cz - $z2 .. $sphere.cz + $z2;
True;
}
REXX
(Apologies for the comments making the lines so wide, but it was easier to read and compare to the original D source.)
/*REXX program displays a sphere with another sphere subtracted where it's superimposed.*/
call deathStar 2, .5, v3('-50 30 50')
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
dot: #=0; do j=1 for words(x); #=# + word(x,j)*word(y,j); end; return #
dot.: procedure; parse arg x,y; d=dot(x,y); if d<0 then return -d; return 0
ceil: procedure; parse arg x; _=trunc(x); return _+(x>0)*(x\=_)
floor: procedure; parse arg x; _=trunc(x); return _-(x<0)*(x\=_)
v3: procedure; parse arg a b c; #=sqrt(a**2 + b**2 + c**2); return a/# b/# c/#
/*──────────────────────────────────────────────────────────────────────────────────────*/
sqrt: procedure; parse arg x; if x=0 then return 0; d=digits(); h= d+6; numeric digits
m.=9; numeric form; parse value format(x,2,1,,0) 'E0' with g 'E' _ .; g=g*.5'e'_%2
do j=0 while h>9; m.j= h; h= h % 2 + 1; end /*j*/
do k=j+5 to 0 by -1; numeric digits m.k; g= (g +x/g)* .5; end /*k*/; return g
/*──────────────────────────────────────────────────────────────────────────────────────*/
hitSphere: procedure expose !.; parse arg xx yy zz r,x0,y0; z= r*r -(x0-xx)**2-(y0-yy)**2
if z<0 then return 0; _= sqrt(z); !.z1= zz - _; !.z2= zz + _; return 1
/*──────────────────────────────────────────────────────────────────────────────────────*/
deathStar: procedure; parse arg k,ambient,sun /* [↓] display the deathstar to screen*/
parse var sun s1 s2 s3 /*identify the light source coördinates*/
if 5=="f5"x then shading= '.:!*oe&#%@' /*dithering chars for an EBCDIC machine*/
else shading= '·:!ºoe@░▒▓' /* " " " " ASCII " */
shadingL= length(shading) /*the number of dithering characters. */
shades.= ' '; do i=1 for shadingL; shades.i= substr(shading, i, 1)
end /*i*/
ship= 20 20 0 20 ; parse var ship shipX shipY shipZ shipR
hole= ' 1 1 -6 20' ; parse var hole holeX holeY holeZ .
do i=floor(shipY-shipR ) to ceil(shipY+shipR )+1; y= i +.5; @= /*@ is a single line of the deathstar to be displayed.*/
do j=floor(shipX-shipR*2) to ceil(shipX+shipR*2)+1; !.= 0
x=.5 * (j-shipX+1) + shipX; $bg= 0; $pos= 0; $neg= 0 /*$BG, $POS, and $NEG are boolean values. */
?= hitSphere(ship, x, y); b1= !.z1; b2= !.z2 /*? is boolean, "true" indicates ray hits the sphere.*/
/*$BG: if 1, its background; if zero, it's foreground.*/
if \? then $bg= 1 /*ray lands in blank space, so draw the background. */
else do; ?= hitSphere(hole, x, y); s1= !.z1; s2= !.z2
if \? then $pos= 1 /*ray hits ship but not the hole, so draw ship surface. */
else if s1>b1 then $pos=1 /*ray hits both, but ship front surface is closer. */
else if s2>b2 then $bg= 1 /*ship surface is inside hole, so show the background. */
else if s2>b1 then $neg=1 /*hole back surface is inside ship; the only place ··· */
else $pos=1 /*························ a hole surface will be shown.*/
end
select
when $bg then do; @= @' '; iterate j; end /*append a blank character to the line to be displayed. */
when $pos then vec_= v3(x-shipX y-shipY b1-shipZ)
when $neg then vec_= v3(holeX-x holeY-y holeZ-s2)
end /*select*/
b=1 +min(shadingL, max(0, trunc((1 - (dot.(sun, v3(vec_))**k + ambient)) * shadingL)))
@=@ || shades.b /*B: the ray's intensity│brightness*/
end /*j*/ /* [↑] build a line for the sphere.*/
if @\='' then say strip(@, 'T') /*strip trailing blanks from line. */
end /*i*/ /* [↑] show all lines for sphere. */
return
- output when using the internal default input:
(Shown at 1/2 size.)
eeeee::::::: eeeeeeeee·············· ooeeeeeeeeee·················· ooooeeeeeeeee······················ oooooooeeeeeeee·························· ooooooooooeeeee······························ ººooooooooooeeee································· ººººooooooooooee····································· !ºººººooooooooooe······································· !!!ºººººooooooooo:·········································· :!!!!ºººººooooooo:::··········································· :::!!!!ºººººooooo!:::::··········································· ::::!!!!!ºººººooo!!!!::::············································ ·::::!!!!ºººººooº!!!!!::::············································ ···::::!!!!ººººººººº!!!!:::::············································ ···::::!!!!ººººoººººº!!!!!::::············································ ····::::!!!!ºººoooºººººº!!!!!::::············································ ····::::!!!!ºoooooooººººº!!!!!:::::··········································· ···::::!!!!!ooooooooooººººº!!!!!:::::·········································· :::::!!!!eeoooooooooooºººººº!!!!!:::::········································· !!!!!eeeeeeeoooooooooooºººººº!!!!!:::::········································ eeeeeeeeeeeeooooooooooooºººººº!!!!!:::::······································· eeeeeeeeeeeeeooooooooooooºººººº!!!!!!:::::····································· eeeeeeeeeeeeeeooooooooooooºººººº!!!!!!:::::···································· eeeeeeeeeeeeeeooooooooooooººººººº!!!!!!:::::································· eeeeeeeeeeeeeeeoooooooooooooºººººº!!!!!!::::::······························: eeeeeeeeeeeeeeeoooooooooooooººººººº!!!!!!:::::::··························: eeeeeeeeeeeeeeeeooooooooooooooººººººº!!!!!!!:::::::·····················::! eeeeeeeeeeeeeeeeeoooooooooooooºººººººº!!!!!!!:::::::::··············::::! eeeeeeeeeeeeeeeeeooooooooooooooºººººººº!!!!!!!!::::::::::::::::::::::!º eeeeeeeeeeeeeeeeeeoooooooooooooooºººººººº!!!!!!!!!!:::::::::::::!!!!º eeeeeeeeeeeeeeeeeooooooooooooooooºººººººººº!!!!!!!!!!!!!!!!!!!!!º eeeeeeeeeeeeeeeeeeoooooooooooooooooºººººººººººº!!!!!!!!!!!!ºººº eeeeeeeeeeeeeeeeeeooooooooooooooooooººººººººººººººººººººººo eeeeeeeeeeeeeeeeeeeoooooooooooooooooooooººººººººººººooo eeeeeeeeeeeeeeeeeeeeooooooooooooooooooooooooooooooo eeeeeeeeeeeeeeeeeeeeooooooooooooooooooooooooo eeeeeeeeeeeeeeeeeeeeeoooooooooooooooooo eeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeee
Set lang
set ! 32
set ! 32
set ! 46
set ! 45
set ! 126
set ! 34
set ! 34
set ! 126
set ! 45
set ! 46
set ! 10
set ! 46
set ! 39
set ! 40
set ! 95
set ! 41
set ! 32
set ! 32
set ! 32
set ! 32
set ! 32
set ! 39
set ! 46
set ! 10
set ! 124
set ! 61
set ! 61
set ! 61
set ! 61
set ! 61
set ! 61
set ! 61
set ! 61
set ! 61
set ! 61
set ! 124
set ! 10
set ! 39
set ! 46
set ! 32
set ! 32
set ! 32
set ! 32
set ! 32
set ! 32
set ! 32
set ! 32
set ! 46
set ! 39
set ! 10
set ! 32
set ! 32
set ! 126
set ! 45
set ! 46
set ! 95
set ! 95
set ! 46
set ! 45
set ! 126
Outputs:
.-~""~-. .'(_) '. |==========| '. .' ~-.__.-~
(it's the best I could do!)
Sidef
Writes a PGM to stdout.
func hitf(sph, x, y) {
x -= sph[0]
y -= sph[1]
var z = (sph[3]**2 - (x**2 + y**2))
z < 0 && return nil
z.sqrt!
[sph[2] - z, sph[2] + z]
}
func normalize(v) {
v / v.abs
}
func dot(x, y) {
max(0, x*y)
}
var pos = [120, 120, 0, 120]
var neg = [-77, -33, -100, 190]
var light = normalize(Vector(-12, 13, -10))
func draw(k, amb) {
STDOUT.binmode(':raw')
print ("P5\n", pos[0]*2 + 3, " ", pos[1]*2 + 3, "\n255\n")
for y in ((pos[1] - pos[3] - 1) .. (pos[1] + pos[3] + 1)) {
var row = []
for x in ((pos[0] - pos[3] - 1) .. (pos[0] + pos[3] + 1)) {
var hit = 0
var hs = []
var h = hitf(pos, x, y)
if (!h) { hit = 0; h = [0, 0] }
elsif (!(hs = hitf(neg, x, y))) { hit = 1; hs = [0, 0] }
elsif (hs[0] > h[0]) { hit = 1 }
elsif (hs[1] > h[0]) { hit = (hs[1] > h[1] ? 0 : 2) }
else { hit = 1 }
var (val, v)
given(hit) {
when (0) { val = 0}
when (1) { v = Vector(x-pos[0], y-pos[1], h[0]-pos[2]) }
default { v = Vector(neg[0]-x, neg[1]-y, neg[2]-hs[1]) }
}
if (defined(v)) {
v = normalize(v)
val = int((dot(v, light)**k + amb) * 255)
val = (val > 255 ? 255 : (val < 0 ? 0 : val))
}
row.append(val)
}
print 'C*'.pack(row...)
}
}
draw(2, 0.2)
Output image: here.
Tcl
Note that this code has a significant amount of refactoring relative to the C version, including the addition of specular reflections and the separation of the scene code from the raytracing from the rendering.
package require Tcl 8.5
proc normalize vec {
upvar 1 $vec v
lassign $v x y z
set len [expr {sqrt($x**2 + $y**2 + $z**2)}]
set v [list [expr {$x/$len}] [expr {$y/$len}] [expr {$z/$len}]]
return
}
proc dot {a b} {
lassign $a ax ay az
lassign $b bx by bz
return [expr {-($ax*$bx + $ay*$by + $az*$bz)}]
}
# Intersection code; assumes that the vector is parallel to the Z-axis
proc hitSphere {sphere x y z1 z2} {
dict with sphere {
set x [expr {$x - $cx}]
set y [expr {$y - $cy}]
set zsq [expr {$r**2 - $x**2 - $y**2}]
if {$zsq < 0} {return 0}
upvar 1 $z1 _1 $z2 _2
set zsq [expr {sqrt($zsq)}]
set _1 [expr {$cz - $zsq}]
set _2 [expr {$cz + $zsq}]
return 1
}
}
# How to do the intersection with our scene
proc intersectDeathStar {x y vecName} {
global big small
if {![hitSphere $big $x $y zb1 zb2]} {
# ray lands in blank space
return 0
}
upvar 1 $vecName vec
# ray hits big sphere; check if it hit the small one first
set vec [if {
![hitSphere $small $x $y zs1 zs2] || $zs1 > $zb1 || $zs2 <= $zb1
} then {
dict with big {
list [expr {$x - $cx}] [expr {$y - $cy}] [expr {$zb1 - $cz}]
}
} else {
dict with small {
list [expr {$cx - $x}] [expr {$cy - $y}] [expr {$cz - $zs2}]
}
}]
normalize vec
return 1
}
# Intensity calculators for different lighting components
proc diffuse {k intensity L N} {
expr {[dot $L $N] ** $k * $intensity}
}
proc specular {k intensity L N S} {
# Calculate reflection vector
set r [expr {2 * [dot $L $N]}]
foreach l $L n $N {lappend R [expr {$l-$r*$n}]}
normalize R
# Calculate the specular reflection term
return [expr {[dot $R $S] ** $k * $intensity}]
}
# Simple raytracing engine that uses parallel rays
proc raytraceEngine {diffparms specparms ambient intersector shades renderer fx tx sx fy ty sy} {
global light
for {set y $fy} {$y <= $ty} {set y [expr {$y + $sy}]} {
set line {}
for {set x $fx} {$x <= $tx} {set x [expr {$x + $sx}]} {
if {![$intersector $x $y vec]} {
# ray lands in blank space
set intensity end
} else {
# ray hits something; we've got the normalized vector
set b [expr {
[diffuse {*}$diffparms $light $vec]
+ [specular {*}$specparms $light $vec {0 0 -1}]
+ $ambient
}]
set intensity [expr {int((1-$b) * ([llength $shades]-1))}]
if {$intensity < 0} {
set intensity 0
} elseif {$intensity >= [llength $shades]-1} {
set intensity end-1
}
}
lappend line [lindex $shades $intensity]
}
{*}$renderer $line
}
}
# The general scene settings
set light {-50 30 50}
set big {cx 20 cy 20 cz 0 r 20}
set small {cx 7 cy 7 cz -10 r 15}
normalize light
# Render as text
proc textDeathStar {diff spec lightBrightness ambient} {
global big
dict with big {
raytraceEngine [list $diff $lightBrightness] \
[list $spec $lightBrightness] $ambient intersectDeathStar \
[split ".:!*oe&#%@ " {}] {apply {l {puts [join $l ""]}}} \
[expr {$cx+floor(-$r)}] [expr {$cx+ceil($r)+0.5}] 0.5 \
[expr {$cy+floor(-$r)+0.5}] [expr {$cy+ceil($r)+0.5}] 1
}
}
textDeathStar 3 10 0.7 0.3
Output:
#######&eeeeeeeee ee&&&&&&########%eeoooooooooooe **oooee&&&&&&########%ooooo**********oo !!!***oooee&&&&&&########%********!!!!!!!!*** !!!!!!!****ooee&&&&&&#######%*****!!!!!!!!!!!!!!!** ::::!!!!!!***oooee&&&&&&######***!!!!!!!::::::::::::!!* :::::::!!!!!!***ooeee&&&&&&#####**!!!!!!:::::::::::::::::!* ::::::::::!!!!!***oooee&&&&&&####*!!!!!!::::::::.........::::!* ::::::::::!!!!!!***oooeee&&&&&&###!!!!!!:::::::..............:::! ..:::::::::!!!!!!****oooeee&&&&&&##!!!!!!::::::..................::!* ...::::::::!!!!!!****ooooeee&&&&&&!!!!!!:::::::....................::!* ....::::::!!!!!!*****ooooeeee&&&&&!!!!!!:::::::......................::!* ....::::::!!!!!*****oooooeeeee&&&&!!!!!!::::::::.......................::!* ...::::::!!!!!*****oooooeeeee&&&!!!!!!:::::::::.........................::! ...:::::!!!!!*****oooooeeeeee&&!!!!!!!:::::::::..........................::!* ..:::::!!!!!****oooooeeeeee&&&!!!!!!!::::::::::..........................::!! .::::::!!!!*****ooooeeeeee&&*!!!!!!!::::::::::::.........................:::!!* :::::!!!!!****oooooeeeee&&**!!!!!!!::::::::::::::.......................::::!!* !!!!!!!!****oooooeeeee&****!!!!!!!::::::::::::::::::..................::::::!!* #!!!******oooooeeeeeoo*****!!!!!!!:::::::::::::::::::::::::::::::::::::::::!!!* ##oooooooooooeeeeeeoooo****!!!!!!!:::::::::::::::::::::::::::::::::::::::!!!!** %#####eeee&&&&&&&eeeooo****!!!!!!!!:::::::::::::::::::::::::::::::::::!!!!!!**o %#########&&&&&&&&eeeooo****!!!!!!!!!::::::::::::::::::!!!!!!!!!!!!!!!!!!!****o %##########&&&&&&&&eeeooo****!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!****ooe %##########&&&&&&&&eeeooo*****!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!**********ooo %%##########&&&&&&&&eeeoooo*****!!!!!!!!!!!!!!!!!!!*********************ooooe %%##########&&&&&&&&eeeoooo***************************************oooooooee @%###########&&&&&&&&&eeeooooo*************************ooooooooooooooooeee& @%###########&&&&&&&&&eeeeoooooo*************ooooooooooooooooooooooeeeee& @%%##########&&&&&&&&&&eeeeoooooooooooooooooooooooooooooooeeeeeeeeeee&& @%%###########&&&&&&&&&&eeeeeoooooooooooooooooooeeeeeeeeeeeeeeeeee&&& %%############&&&&&&&&&&eeeeeeeeeeooeeeeeeeeeeeeeeeeeeeeeeee&&&&& @%%###########&&&&&&&&&&&&eeeeeeeeeeeeeeeeeeeeeeeeee&&&&&&&&&&& %%############&&&&&&&&&&&&&&eeeeeeeeeeeeeee&&&&&&&&&&&&&&&& %%############&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& %%#############&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& %%#############&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& %##############&&&&&&&&&&&&&&&&&&&&&&&& %##############&&&&&&&&&&&&&&&& #################
To render it as an image, we just supply different code to map the intensities to displayable values:
# Render as a picture (with many hard-coded settings)
package require Tk
proc guiDeathStar {photo diff spec lightBrightness ambient} {
set row 0
for {set i 255} {$i>=0} {incr i -1} {
lappend shades [format "#%02x%02x%02x" $i $i $i]
}
raytraceEngine [list $diff $lightBrightness] \
[list $spec $lightBrightness] $ambient intersectDeathStar \
$shades {apply {l {
upvar 2 photo photo row row
$photo put [list $l] -to 0 $row
incr row
update
}}} 0 40 0.0625 0 40 0.0625
}
pack [label .l -image [image create photo ds]]
guiDeathStar ds 3 10 0.7 0.3
VBScript
ASCII graphics. Should be invoked with cscript. Modified from LUA
'deathstar ascii graphics
option explicit
const x_=0
const y_=1
const z_=2
const r_=3
function clamp(x,b,t)
if x<b then
clamp=b
elseif x>t then
clamp =t
else
clamp=x
end if
end function
function dot(v,w) dot=v(x_)*w(x_)+v(y_)*w(y_)+v(z_)*w(z_): end function
function normal (byval v)
dim ilen:ilen=1/sqr(dot(v,v)):
v(x_)=v(x_)*ilen: v(y_)=v(y_)*ilen: v(z_)=v(z_)*ilen:
normal=v:
end function
function hittest(s,x,y)
dim z
z = s(r_)^2 - (x-s(x_))^2 - (y-s(y_))^2
if z>=0 then
z=sqr(z)
hittest=array(s(z_)-z,s(z_)+z)
else
hittest=0
end if
end function
sub deathstar(pos, neg, sun, k, amb)
dim x,y,shades,result,shade,hp,hn,xx,b
shades=array(" ",".",":","!","*","o","e","&","#","%","@")
for y = pos(y_)-pos(r_)-0.5 to pos(y_)+pos(r_)+0.5
for x = pos(x_)-pos(r_)-0.5 to pos(x_)+pos(r_)+.5
hp=hittest (pos, x, y)
hn=hittest(neg,x,y)
if not isarray(hp) then
result=0
elseif not isarray(hn) then
result=1
elseif hn(0)>hp(0) then
result=1
elseif hn(1)>hp(1) then
result=0
elseif hn(1)>hp(0) then
result=2
else
result=1
end if
shade=-1
select case result
case 0
shade=0
case 1
xx=normal(array(x-pos(x_),y-pos(y_),hp(0)-pos(z_)))
'shade=clamp(1-dot(sun,xx)^k+amb,1,ubound(shades))
case 2
xx=normal(array(neg(x_)-x,neg(y_)-y,neg(z_)-hn(1)))
'shade=clamp(1-dot(sun,xx)^k+amb,1,ubound(shades))
end select
if shade <>0 then
b=dot(sun,xx)^k+amb
shade=clamp((1-b) *ubound(shades),1,ubound(shades))
end if
wscript.stdout.write string(2,shades(shade))
next
wscript.stdout.write vbcrlf
next
end sub
deathstar array(20, 20, 0, 20),array(10,10,-15,10), normal(array(-2,1,3)), 2, 0.1
- Output:
####&&&&&&&&&&&& %%######&&&&eeeeeeeeooooooooee&& %%%%####&&&&eeeeeeoooooo************ooee %%%%####%%%%%%eeoooooo******!!!!!!!!!!!!**oo %%%%ee&&&&####%%%%%%##oo****!!!!!!!!::::::::!!!!**oo %%%%ooooeeee&&####%%%%%%##**!!!!!!::::::::::::::::!!!!oo %%%%!!****ooee&&&&##%%%%%%##!!!!::::::::............::::!!** %%%%::::!!****ooee&&####%%%%%%&&!!::::....................::!!oo %%....::::!!**ooooee&&##%%%%%%##::::........................::!! %%........::::!!**ooee&&####%%%%##::::..........................::!! ##%%..........::!!**ooee&&&&##%%%%##::..............................::oo %%%%..........::!!!!**ooee&&##%%%%::::..............................::!! ##%%..............::!!**ooee&&##%%%%::::................................::oo ##%%..............::!!**ooee&&##%%%%::::................................::** %%%%............::::!!**oo&&&&##%%!!::::................................::!! %%%%............::!!**ooee&&##%%!!!!::::..................................!! ##%%%%%%........::::!!**ooee&&##**!!!!::::..................................::** ##%%%%%%!!::::::!!!!**ooee&&##****!!!!::::..................................::** ##%%%%%%%%**********ooee&&##oo****!!!!!!::::................................::** ##%%%%%%%%%%##eeeeee&&eeeeeeoooo****!!!!::::..............................::!!** ##%%%%%%%%%%######&&&&&&eeeeoooo****!!!!::::::............................::!!** ##%%%%%%%%%%%%######&&&&eeeeoooo******!!!!::::::..........................::!!oo &&##%%%%%%%%%%######&&&&eeeeeeoooo****!!!!!!::::::......................::::!!oo &&##%%%%%%%%%%######&&&&&&eeeeoooooo****!!!!!!::::::..................::::!!**ee ##%%%%%%%%%%%%######&&&&eeeeeeoooo******!!!!!!::::::::..........::::::!!!!** ##%%%%%%%%%%%%######&&&&&&eeeeeeoooo******!!!!!!::::::::::::::::::::!!!!**oo &&##%%%%%%%%%%%%######&&&&&&eeeeoooooo******!!!!!!!!::::::::::::!!!!!!**ooee ee##%%%%%%%%%%%%########&&&&eeeeeeoooooo********!!!!!!!!!!!!!!!!!!!!****oo&& &&##%%%%%%%%%%%%######&&&&&&eeeeeeoooooo**********!!!!!!!!!!!!******ooee ee##%%%%%%%%%%%%%%######&&&&&&eeeeeeoooooooo********************ooooee&& &&##%%%%%%%%%%%%########&&&&&&&&eeeeeeoooooooooo********ooooooooee&& ####%%%%%%%%%%%%########&&&&&&&&eeeeeeeeooooooooooooooooooeeee&& ee##%%%%%%%%%%%%%%##########&&&&&&&&eeeeeeeeeeeeeeeeeeeeeeee&&## &&##%%%%%%%%%%%%%%%%########&&&&&&&&&&eeeeeeeeeeeeee&&&&&&## &&##%%%%%%%%%%%%%%%%##########&&&&&&&&&&&&&&&&&&&&&&#### &&##%%%%%%%%%%%%%%%%%%##############&&&&&&&&######%% ####%%%%%%%%%%%%%%%%%%###################### &&##%%%%%%%%%%%%%%%%%%%%%%%%######%%%%%% &&##%%%%%%%%%%%%%%%%%%%%%%%%%%%% ##%%%%%%%%%%%%%%
Wren
import "dome" for Window
import "graphics" for Canvas, Color, ImageData
import "math" for Vector
var Normalize = Fn.new{ |vec|
var invLen = 1 / vec.dot(vec).sqrt
vec.x = vec.x * invLen
vec.y = vec.y * invLen
vec.z = vec.z * invLen
}
class Sphere {
construct new(cx, cy, cz, r) {
_cx = cx
_cy = cy
_cz = cz
_r = r
}
cx { _cx }
cy { _cy }
cz { _cz }
r { _r }
hit(x, y) {
x = x - _cx
y = y - _cy
var zsq = _r*_r - x*x - y*y
if (zsq >= 0) {
var zsqrt = zsq.sqrt
return [_cz - zsqrt, _cz + zsqrt, true]
}
return [0, 0, false]
}
}
class DeathStar {
construct new(width, height) {
Window.title = "Death star"
Window.resize(width, height)
Canvas.resize(width, height)
}
init() {
Canvas.cls(Color.white)
var dir = Vector.new(20, -40, 10)
Normalize.call(dir)
var pos = Sphere.new(220, 190, 220, 120)
var neg = Sphere.new(130, 100, 190, 100)
deathStar(pos, neg, 1.5, 0.2, dir)
}
deathStar(pos, neg, k, amb, dir) {
var w = pos.r * 4
var h = pos.r * 3
var img = ImageData.create("deathStar", w, h)
var vec = Vector.new(0, 0, 0)
for (y in pos.cy - pos.r..pos.cy + pos.r) {
for (x in pos.cx - pos.r..pos.cx + pos.r) {
var res = pos.hit(x, y)
var zb1 = res[0]
var zb2 = res[1]
var hit = res[2]
if (!hit) continue
res = neg.hit(x, y)
var zs1 = res[0]
var zs2 = res[1]
hit = res[2]
if (hit) {
if (zs1 > zb1) {
hit = false
} else if (zs2 > zb2) {
continue
}
}
if (hit) {
vec.x = neg.cx - x
vec.y = neg.cy - y
vec.z = neg.cz - zs2
} else {
vec.x = x - pos.cx
vec.y = y - pos.cy
vec.z = zb1 - pos.cz
}
Normalize.call(vec)
var s = dir.dot(vec)
if (s < 0) s = 0
var lum = 255 * (s.pow(k) + amb) / (1 + amb)
lum = lum.clamp(0, 255)
img.pset(x, y, Color.rgb(lum, lum, lum))
}
}
img.draw(pos.cx - w/2, pos.cy - h/2)
img.saveToFile("deathStar.png")
}
update() {
}
draw(alpha) {
}
}
var Game = DeathStar.new(400, 400)
Yabasic
open window 100,100
window origin "cc"
backcolor 0,0,0
clear window
tonos = 100
interv = int(255 / tonos)
dim shades(tonos)
shades(1) = 255
for i = 2 to tonos
shades(i) = shades(i-1) - interv
next i
dim light(3)
light(0) = 30
light(1) = 30
light(2) = -50
sub normalize(v())
local long
long = sqrt(v(0)*v(0) + v(1)*v(1) + v(2)*v(2))
v(0) = v(0) / long
v(1) = v(1) / long
v(2) = v(2) / long
end sub
sub punto(x(), y())
local d
d = x(0)*y(0) + x(1)*y(1) + x(2)*y(2)
if d < 0 then
return -d
else
return 0
end if
end sub
//* positive shpere and negative sphere */
dim pos(3)
dim neg(3)
// x, y, z, r
pos(0) = 10
pos(1) = 10
pos(2) = 0
pos(3) = 20
neg(0) = 0
neg(1) = 0
neg(2) = -5
neg(3) = 15
sub hit_sphere(sph(), x, y)
local zsq
x = x - sph(0)
y = y - sph(1)
zsq = sph(3) * sph(3) - (x * x + y * y)
if (zsq < 0) then
return 0
else
return sqrt(zsq)
end if
end sub
sub draw_sphere(k, ambient)
local i, j, intensity, hit_result, result, b, vec(3), x, y, zb1, zb2, zs1, zs2, ini1, fin1, ini2, fin2
ini1 = int(pos(1) - pos(3))
fin1 = int(pos(1) + pos(3) + .5)
for i = ini1 to fin1
y = i + .5
ini2 = int(pos(0) - 2 * pos(3))
fin2 = int(pos(0) + 2 * pos(3) + .5)
for j = ini2 to fin2
x = (j - pos(0)) / 2 + .5 + pos(0)
// ray lands in blank space, draw bg
result = hit_sphere(pos(), x, y)
if not result then
hit_result = 0
//* ray hits pos sphere but not neg, draw pos sphere surface */
else
zb1 = pos(2) - result
zb2 = pos(2) + result
result = hit_sphere(neg(), x, y)
if not result then
hit_result = 1
else
zs1 = neg(2) - result
zs2 = neg(2) + result
if (zs1 > zb1) then
hit_result = 1
elseif (zs2 > zb2) then
hit_result = 0
elseif (zs2 > zb1) then
hit_result = 2
else
hit_result = 1
end if
end if
end if
if not hit_result then
color 0,0,0
dot x, y
else
switch(hit_result)
case 1:
vec(0) = x - pos(0)
vec(1) = y - pos(1)
vec(2) = zb1 - pos(2)
break
default:
vec(0) = neg(0) - x
vec(1) = neg(1) - y
vec(2) = neg(2) - zs2
end switch
normalize(vec())
b = (punto(light(), vec())^k) + ambient
intensity = (1 - b) * tonos
if (intensity < 1) intensity = 1
if (intensity > tonos) intensity = tonos
color shades(intensity),shades(intensity),shades(intensity)
dot x,y
end if
next j
next i
end sub
ang = 0
while(true)
//clear window
light(1) = cos(ang * 2)
light(2) = cos(ang)
light(0) = sin(ang)
normalize(light())
ang = ang + .05
draw_sphere(2, .3)
wend
Zig
Primitive ray tracing. Writes a PGM to stdout.
const std = @import("std");
const Allocator = std.mem.Allocator;
pub fn main() !void {
// buffer stdout --------------------------------------
const stdout_file = std.io.getStdOut().writer();
var bw = std.io.bufferedWriter(stdout_file);
const stdout = bw.writer();
// allocator ------------------------------------------
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer {
const ok = gpa.deinit();
std.debug.assert(ok == .ok);
}
const allocator = gpa.allocator();
// deathstar ------------------------------------------
var dstar = try DeathStar(f32).init(allocator);
defer dstar.deinit();
// print deathstar PGM to stdout ----------------------
const comments = [_][]const u8{
"Rosetta Code",
"DeathStar",
"https://rosettacode.org/wiki/Death_Star",
};
try dstar.print(stdout, comments[0..]);
// ----------------------------------------------------
try bw.flush();
}
fn Vector(comptime T: type) type {
return struct {
const Self = @This();
x: T,
y: T,
z: T,
pub fn init(x: T, y: T, z: T) Self {
return Self{ .x = x, .y = y, .z = z };
}
pub fn zero() Self {
return Self{ .x = 0.0, .y = 0.0, .z = 0.0 };
}
fn dot(a: *const Self, b: *const Self) T {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
fn length(self: *const Self) T {
return std.math.sqrt(self.dot(self));
}
pub fn normalize(self: *Self) void {
const inv_length = 1 / self.length();
self.*.x *= inv_length;
self.*.y *= inv_length;
self.*.z *= inv_length;
}
};
}
fn SphereHit(comptime T: type) type {
return struct { z1: T, z2: T };
}
fn Sphere(comptime T: type) type {
return struct {
const Self = @This();
cx: T,
cy: T,
cz: T,
r: T,
pub fn init(cx: T, cy: T, cz: T, r: T) Self {
return Self{ .cx = cx, .cy = cy, .cz = cz, .r = r };
}
/// 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.
pub fn hit(self: *const Self, xx: T, yy: T) ?SphereHit(T) {
const x = xx - self.cx;
const y = yy - self.cy;
const zsq = self.r * self.r - x * x - y * y;
if (zsq >= 0) {
const zsqrt = std.math.sqrt(zsq);
return .{ .z1 = self.cz - zsqrt, .z2 = self.cz + zsqrt };
}
return null;
}
};
}
fn DeathStar(comptime T: type) type {
return struct {
const Self = @This();
allocator: Allocator,
w: usize,
h: usize,
img: ImageData(),
const Hit = enum { background, neg, pos };
pub fn init(allocator: Allocator) !Self {
var dir = Vector(T).init(20, -40, 10);
dir.normalize();
// positive sphere and negative sphere
const pos = Sphere(T).init(180, 240, 220, 120);
const neg = Sphere(T).init(60, 150, 100, 100);
const k: T = 1.5;
const amb: T = 0.2;
const w: usize = @intFromFloat(pos.r * 4);
const h: usize = @intFromFloat(pos.r * 3);
var img = try ImageData().init(allocator, "deathStar", w, h);
var vec = Vector(T).zero();
const start_y: usize = @intFromFloat(pos.cy - pos.r);
const end_y: usize = @intFromFloat(pos.cy + pos.r);
const start_x: usize = @intFromFloat(pos.cx - pos.r);
const end_x: usize = @intFromFloat(pos.cx + pos.r);
for (start_y..end_y + 1) |j| {
for (start_x..end_x + 1) |i| {
const x: T = @floatFromInt(i);
const y: T = @floatFromInt(j);
const result_pos = pos.hit(x, y);
// ray lands in blank space, show bg
if (result_pos == null)
continue;
const zb1 = result_pos.?.z1;
const zb2 = result_pos.?.z2;
const result_neg = neg.hit(x, y);
switch (calcHit(result_neg, zb1, zb2)) {
.background => continue,
.neg => {
vec.x = neg.cx - x;
vec.y = neg.cy - y;
vec.z = neg.cz - result_neg.?.z2; // zs2
},
.pos => {
vec.x = x - pos.cx;
vec.y = y - pos.cy;
vec.z = zb1 - pos.cz;
},
}
vec.normalize();
var s = dir.dot(&vec);
if (s < 0) s = 0;
const lum = 255 * (std.math.pow(T, s, k) + amb) / (1 + amb);
const lumi: u8 = @intFromFloat(std.math.clamp(lum, 0, 255));
img.pset(i, j, Gray{ .w = lumi });
}
}
return Self{ .allocator = allocator, .w = w, .h = h, .img = img };
}
pub fn deinit(self: *Self) void {
self.img.deinit();
}
pub fn print(self: *Self, writer: anytype, optional_comments: ?[]const []const u8) !void {
try self.img.print(writer, optional_comments);
}
/// Ray has hit the positive sphere.
/// How does it intersect the negative sphere ?
fn calcHit(neg_hit: ?SphereHit(T), zb1: T, zb2: T) Hit {
if (neg_hit) |result| {
const zs1 = result.z1;
const zs2 = result.z2;
if (zs1 > zb1) {
// ray hits both, but pos front surface is closer
return Hit.pos;
} else if (zs2 > zb2) {
// pos sphere surface is inside neg sphere, show bg
return Hit.background;
} else if (zs2 > zb1) {
// back surface on neg sphere is inside pos sphere,
// the only place where neg sphere surface will be shown
return Hit.neg;
} else {
return Hit.pos;
}
} else {
// ray hits pos sphere but not neg, draw pos sphere surface
return Hit.pos;
}
}
};
}
const Gray = struct {
w: u8,
const black = Gray{ .w = 0 };
};
fn ImageData() type {
return struct {
const Self = @This();
allocator: Allocator,
name: []const u8,
w: usize,
h: usize,
image: []Gray,
pub fn init(allocator: Allocator, name: []const u8, w: usize, h: usize) !Self {
const image = try allocator.alloc(Gray, h * w);
// black background fill
for (image) |*pixel| pixel.* = Gray.black;
return Self{ .allocator = allocator, .image = image, .name = name, .w = w, .h = h };
}
pub fn deinit(self: *Self) void {
self.allocator.free(self.image);
}
pub fn pset(self: *Self, x: usize, y: usize, gray: Gray) void {
self.image[x * self.w + y] = gray;
}
/// Write PGM P2 ASCII to 'writer'
pub fn print(self: *const Self, writer: anytype, optional_comments: ?[]const []const u8) !void {
try writer.print("P2\n", .{});
if (optional_comments) |lines| {
for (lines) |line|
try writer.print("# {s}\n", .{line});
}
try writer.print("{d} {d}\n{d}\n", .{ self.w, self.h, 255 });
for (self.image, 0..) |pixel, i| {
const sep = if (i % self.w == self.w - 1) "\n" else " ";
try writer.print("{d}{s}", .{ pixel.w, sep });
}
}
};
}
- Programming Tasks
- Constructive Solid Geometry
- Geometric Subtraction
- AWK/Omit
- Lotus 123 Macro Scripting/Omit
- ML/I/Omit
- Modula-2/Omit
- Retro/Omit
- SQL PL/Omit
- 11l
- Ada
- SDLAda
- ALGOL 68
- AutoHotkey
- GDIP
- Brlcad
- C
- D
- Delphi
- Winapi.Windows
- System.SysUtils
- System.Math
- Vcl.Graphics
- Vcl.Imaging.pngimage
- DWScript
- Frink
- Go
- Haskell
- J
- Java
- JavaFX
- JavaScript
- Julia
- LSL
- Lua
- Maple
- Mathematica
- Wolfram Language
- Nim
- NimPNG
- Openscad
- Perl
- Phix
- Phix/pGUI
- Phix/online
- POV-Ray
- Python
- Q
- Racket
- Raku
- REXX
- Set lang
- Sidef
- Tcl
- Tk
- VBScript
- Wren
- DOME
- Yabasic
- Zig