unit penroseunit; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Graphics, lowlevelunit, matheunit; type tTeil = record Drachen: boolean; Ursprung: tExtPoint; Richtung,Groesze: extended; id: int64; end; tPenrose = class private teile: array of tTeil; function teilEcken(i: longint): tExtPointArray; function id2color(id,mx: int64): tColor; public constructor create(level: longint); procedure Zeichnen(breite, hoehe: longint; Canvas: TCanvas); end; implementation uses math; constructor tPenrose.create(level: longint); var i,j: longint; begin setlength(teile,5); for j:=0 to 4 do begin teile[j].Drachen:=true; teile[j].Ursprung:=extPoint(0,0); teile[j].Richtung:=2*pi/5*j; teile[j].Groesze:=1; teile[j].id:=0; end; for i:=0 to level-1 do for j:=length(teile)-1 downto 0 do begin if teile[j].Richtung<0 then teile[j].Richtung:=teile[j].Richtung + 2*pi; if teile[j].Richtung>=2*pi then teile[j].Richtung:=teile[j].Richtung - 2*pi; if teile[j].Drachen then begin setlength(teile,length(teile)+2); teile[length(teile)-1].Drachen:=false; teile[length(teile)-1].Ursprung:=teile[j].Ursprung; teile[length(teile)-1].Richtung:=teile[j].Richtung + 36/180*pi; teile[length(teile)-1].Groesze:=teile[j].Groesze / 2 / cos(36/180*pi); teile[length(teile)-1].id:=2*teile[j].id; teile[length(teile)-2].Drachen:=true; teile[length(teile)-2].Ursprung:=teile[j].Ursprung + teile[j].Groesze * extPoint(cos(teile[j].Richtung + 36/180*pi),sin(teile[j].Richtung + 36/180*pi)); teile[length(teile)-2].Richtung:=teile[j].Richtung - 108/180*pi; teile[length(teile)-2].Groesze:=teile[j].Groesze / 2 / cos(36/180*pi); teile[length(teile)-2].id:=2*teile[j].id+1; teile[j].Ursprung:=teile[j].Ursprung + teile[j].Groesze * extPoint(cos(teile[j].Richtung - 36/180*pi),sin(teile[j].Richtung - 36/180*pi)); teile[j].Richtung:=teile[j].Richtung + 108/180*pi; teile[j].Groesze:=teile[j].Groesze / 2 / cos(36/180*pi); teile[j].id:=2*teile[j].id+1; end else begin setlength(teile,length(teile)+1); teile[length(teile)-1].Drachen:=true; teile[length(teile)-1].Ursprung:=teile[j].Ursprung; teile[length(teile)-1].Richtung:=teile[j].Richtung; teile[length(teile)-1].Groesze:=teile[j].Groesze / 2 / cos(36/180*pi); teile[length(teile)-1].id:=2*teile[j].id+1; teile[j].Ursprung:=teile[j].Ursprung + teile[j].Groesze * extPoint(cos(teile[j].Richtung - 36/180*pi),sin(teile[j].Richtung - 36/180*pi)); teile[j].Richtung:=teile[j].Richtung + 144/180*pi; teile[j].Groesze:=teile[j].Groesze / 2 / cos(36/180*pi); teile[j].id:=2*teile[j].id; end; end; end; function tPenrose.teilEcken(i: longint): tExtPointArray; var j: longint; begin setLength(result,4); result[0]:=extPoint(0,0); result[1]:=extPoint(cos(36/180*pi),sin(36/180*pi)); result[2]:=extPoint(cos(36/180*pi) + (2*byte(teile[i].Drachen)-1)*sin(36/180*pi)*tan(18/180*pi),0); result[3]:=extPoint(cos(-36/180*pi),sin(-36/180*pi)); for j:=0 to 3 do result[j]:=teile[i].Groesze * rotationsMatrix(teile[i].Richtung) * result[j] + teile[i].Ursprung; end; function tPenrose.id2color(id,mx: int64): tColor; const fLen = 6;//2; var farben: tExt3dPointArray; r: tExt3dPoint; i: integer; x: extended; begin setLength(farben,fLen+1); for i:=0 to 2 do farben[2*i]:=ext3dPoint($ff*byte(i=0),$ff*byte(i=1),$ff*byte(i=2)); for i:=0 to 2 do farben[7-2*i]:=ext3dPoint($ff,$ff,$ff)-farben[2*i]; (* for i:=0 to 1 do farben[i]:=i*ext3dPoint($ff,$ff,$ff); *) farben[fLen]:=farben[0]; x:=(id mod mx)/mx*fLen; i:=floor(x/fLen); r:=(x - i) * farben[i+1] + (1+i-x) * farben[i]; result:=round(r['x']) + (round(r['y']) shl 8) + (round(r['z']) shl 16) end; procedure tPenrose.Zeichnen(breite, hoehe: longint; Canvas: TCanvas); var lo,ru: tExtPoint; mx: int64; ecken: tExtPointArray; i,j: longint; c: char; Groesze: extended; Verschiebung: tExtPoint; points: array of tPoint; begin lo:=extPoint(0,0); ru:=lo; mx:=0; for i:=0 to length(teile)-1 do begin ecken:=teilEcken(i); for j:=0 to length(ecken)-1 do for c:='x' to 'y' do begin lo[c]:=min(lo[c],ecken[j][c]); ru[c]:=max(ru[c],ecken[j][c]); end; mx:=max(mx,teile[i].id); end; Groesze:=min( (breite-1) / (ru['x']-lo['x']), (hoehe-1) / (ru['y']-lo['y']) ); Verschiebung:=-Groesze * lo; mx:=max(1,mx div 4); canvas.brush.Color:=$ffffff; canvas.rectangle(-10,-10,breite+10,hoehe+10); for i:=0 to length(teile)-1 do begin ecken:=teilEcken(i); setLength(points,length(ecken)); for j:=0 to length(ecken)-1 do begin ecken[j]:=Verschiebung + Groesze * ecken[j]; points[j]:=point(round(ecken[j]['x']),round(ecken[j]['y'])); end; canvas.brush.Color:=id2color(teile[i].id,mx); canvas.pen.Color:=canvas.brush.Color; canvas.Polygon(points); end; end; end.