From c0d6d0ba900994527c2d37cc47c6beaa8e37c464 Mon Sep 17 00:00:00 2001 From: Alex Clink Date: Thu, 6 Jan 2022 20:25:39 -0500 Subject: [PATCH] Refactor drawing methods onto Sprite class --- assets/bricks.png | Bin 7691 -> 5219 bytes examples/3d.cr | 2 +- examples/affine.cr | 1 + examples/balls.cr | 27 ++-- examples/snow.cr | 2 - examples/snow_scroll.cr | 2 +- examples/sprite_example.cr | 2 +- examples/text.cr | 2 +- src/game.cr | 206 +++----------------------- src/pixel_text.cr | 6 +- src/sprite.cr | 35 ++++- src/sprite/draw_circle.cr | 35 +++++ src/sprite/draw_line.cr | 48 ++++++ src/sprite/draw_point.cr | 25 ++++ src/sprite/draw_rect.cr | 24 +++ src/sprite/draw_shape.cr | 10 ++ src/sprite/draw_triangle.cr | 10 ++ src/sprite/fill_rect.cr | 16 ++ src/{game => sprite}/fill_shape.cr | 20 +-- src/{game => sprite}/fill_triangle.cr | 10 +- 20 files changed, 258 insertions(+), 225 deletions(-) create mode 100644 src/sprite/draw_circle.cr create mode 100644 src/sprite/draw_line.cr create mode 100644 src/sprite/draw_point.cr create mode 100644 src/sprite/draw_rect.cr create mode 100644 src/sprite/draw_shape.cr create mode 100644 src/sprite/draw_triangle.cr create mode 100644 src/sprite/fill_rect.cr rename src/{game => sprite}/fill_shape.cr (81%) rename src/{game => sprite}/fill_triangle.cr (87%) diff --git a/assets/bricks.png b/assets/bricks.png index 71d18c92884f806b8a95885103cb7b37f350780f..97bb1ba370019806378170c69518b6d1c388fbcb 100644 GIT binary patch delta 5170 zcmV-26wT|4JmV;kBn<>-K}|sb0I`n?{9y%?kqRyl00v@9M??Vs0RI60puMM)ks%j< z6J<$6K~#9!y_;=}WLa5&pL6S0SAX|>?R3}7usfeSv%td6C=n4b7?p2=3O_LFk_hMz zqlwV~ffy4d#`p_=7y>_J6Zs=VgDkr1s;ny@sO$z=X1b@Rdv<2JyK4HotGc`DR^5AE zf1GpfJ@-~s&+I~cXF7F0-}`=@kN3TQ?^R#;+-K!mpZy#F)sYbnf-vZ9Q%V6KfQTSs z%DCSF@b*9dRIdK?$3Mf56O$^|)7k~iu z-QM=Q`-2H62cQKsaNI*%--DSkQ-0kaeJW-CUFRoq^Z)Ti^A$6Hd0O$}nlK!H!1<$! z#fD1zc3K*MTC2s+{N8U99>jP`^e$A#O}VxUM}~m_j*ddf2pW|=16M2x?)YdgZpVDR znP>RQwE3Nx$ojR0b z1VsScYMM1$Dj8f;BLPz*-Z~Z`H0!ok47EMQi6XJeSU`2${Q59GmT3oT`pz027==We z@@k)D<7>ZY2~O)u8EdOGngoFnh(aI$(XiNn(^C*AtT^DzghrsRma8LwBj`@o^rf4Z z7^$x8nzrR#!Tnp=6|@!BN>gxxn;EsHzDJ5fD`TR9(~i+zVhK5D?*LfXHkyqsKCHII z3Rc8eliP3ul?hu5ENMO2I z3$;D5n^4xLMX2rDKJ70Fx13DB!{PefpSDD)0JMgec7#yD{I(r~-J9(A8(Re&R(RWO z^S9NuOH>P@bxNz&g4UWun{uKxNgSiKW^Q4HP-7S?Y~*Nx6>qJs>_gR_mea%JtRU@E z@V&LnN?czNx590Im_mS}qs`i!_cS}dK0gIFc0jbS(;?l!rz)_~f~g_NtuC=Q zvI|pieMho(F`G!}G+HfO1!gRhwU)9(OHK$*5RSoASRS{v{k&N&x7wT>8BPV@cFi~j zt%dCl92z41^}!kN?3O{e?J+OFdduKjZ+XwOmU3tau5Q79RwM?mzz>^kru}131ZsA2 zbp%#g`8A(1VTDj{71v!RsGjwhT9t5Tzs1RsVOCmg(3)5hHd|&NNHlD;&Dcc4R>x^e zQh_+ex=B{|kMWgun`)(!yNj%~%>1r~IDyTM@a7M`9}u$y#54EmNOrvBSP)M*-qKiV zwbPr>so~0h00+SI&r~V^+}>{yD20fo9cy+F0wC5#TU#AiZRG{I)`ml6)3)7l$Dvue z8mL7Ph%jBLaJyx+GhL~q!dbO~U5#M81ap7?B#(XcH_TPonb(P7qxpUsTxv&#c&|Sj zD7LSi3Bp34l({OSu>T2zz_gdjl!Frc-Gm@W7%ydij5fRWrsrAD-Nb*SP>&*JDi!Kc zl+xPOnpo?!tWpax)*J@VjA6CY<#JF$J^iJ>lxy>K7Qg&ev*9SkO1qt7Kzk;p$UML| zKmH-U{M5e?1PZ0h-^|KgQwDB!ItXyOTuygxZBKx=KK>!*Ypa}}KF008dlD2RIx(k> z77`JEV!NBW=ecJ)PY>X3yUm?AMgb3%%W3=VPKO7}Wu}Km(zzOQ3e?+eDy0%{{LrtU zzVNxv_BGlI=T4>NB#r?ng<)>Y{K66kfsdm&c5Z$#EnmEFmJ8=jrN^F9iuuLm1Mv-n zA~%I2=np>ervUucM?Y-3KY8U!j{s_|7B7x}4s-PA5kBzC?_=@vPu%;#F9V#X2k`ve ztbBL`w!9^F_G6Ey&vtYC@kLzy`UgGRjSKq`Qym$hD)7w4W^S_YvlpF~ABQk*Hcb6y zBQFSljL$%k`Fr*IuC?n6ij)0GzbGo#S8q075#vP{cM$ya$%!8eo}$nW!VyYtgEI_& zUpWrnTlFq0g~`4UifuQo6LMxUd&vC?t=?=}d}jRI{37K(4Ah&=9E0^;6K}X5{6z>6 z>-=CYF)1Q@7r1cl6btU6)M%tko|!ZPt2Z*Fi_JXq?(})7ac|<>oO-j_vjqAv;G46f z*+rjn!Wao} z-PkB#-JA2kn29v4w=&W~Ez&a?V5uo+G57WF-qpO}0UOrRM*E4j0ZH$A+Az0eCM%{q zH4HoL0Yuv6kXWJ2@4&eUu>uu;Hrt@J38=+dxVx8kdhP}5eJrEyDQjsTPK`pSwdT$~ zys8kcQa~amT9Jt6`VO2QhclxvXV-Y56^YL7*|AnkRSZ*OW!Ts^e)>+7JvghIC&ius zlO(Ih8g!B{Jt8bOH1~oNW3bZ0^dNyLVr@LoFitepN}dofPER=iNfHu&t%(!3wv*q- zX00GZpkZCtjaHuNjlyoe(aMt*>t1{S-2@&z`w(}&`&}l;6L+TtuPVeZZZ{6@FSf-R zZtg<3u%~%&xK~DI6A|VXYD|Qh)qY-J0S;GSE6PIOhbt~{F?fz!*T<{_x#n4mPBc-R za5w6;fQ7xDuq6);!|ir|CO{u%hRmEIgvNdJTI?p&O@tWu{1ac|=f$i;2g^}L<%vob zx+x0D)x!W1QP!U~%Vj;vkFD(+A*7b56rvN_u_jIm+~9@E+{Q_s!G6p>G?Hzyw`{z@ z2q4KL2BDiB2z<4b@CJdpW!(ATGnEP*ZDKF=sKZPu48!E%GHk?u0TK(K*^6!X!Xw(( zpo>MUb*Z0lu%{J*;R=;`alAIn@ zlmc_8FYeiZf(Re@oe%TR^LKda>1ROTp>{;gy2g)8i4BP$d7{d zzTx?)!#w)vYg45i6we6z`OHPQmpA9)2yV*bhri({p?{rQ!wzn>@C zLx;stt*!6_jMerFsQ5gGMI^7p-}jLhh2S&qn7NlAT=~k0%gm!Yl}>$ZQrfFfg~C{~JW#pa4n8%Y>ay zR-P)HI)h?;4lW)$823Ti9n|M=I_BrrO`E^u4hoG0MWzNZyVDswuAg-KEd^gg{WNya z@;fMr!pF1M>D^C&G;L*Oe1ck|VP338Lb%h(H0jEJMaF*=__M$GE3Vk`#bbG553&e1 zt~h7VT=9Qh{K9+f=LU#q+bv9%?GkWII_8T)7!-3GBpfHuXOzIkCS#*2Wy&YK$XL;w z{c`TC7GZYHn|IXw&ThbHXsp16yv!@}?CQMIXo3hkT_dNk)--WIrBuq0`*n=ZjOVkx z?iGN4bn}lMJ;I}pz83!LKMvHUV`6q4E*w6+mJfyPrA+lGtz_ zup7g8Xr30tc2JyXx=DhmCtR3F(zZ^V(1|rstZ64EL>6n!Qp4ulI2>BD)G&W(TQ?@w znw@qwCf1sVr|k2*w#3pACz>SA>cfG5ZMS2FOQt+hGS5l8zC>$cEiCP~5S{SePkaQO zXuiE>`WhYcYx0GJuw#P_F1Vm<<%M-qZpT?1pgL^QTcSAA)OZNDcAQj=gd+{xo5E5% z;%8?b;JOVsxOoD6vz5=~DO$icYngRydhv>tc0|?YA9R&@*I(Q;Ywd_I2$-FJU*`EQ zJi(ieKu5!-cWQO*5(=X@v3UWeA#mbQhN*fKrSIh1ah!WlpcIiuTssznM_|PUOuG9m zZtd+OK-Ff5XoQI}G`szoS2>|bA%>{Ay|>TIaD|QDKoRwJ#7ZaT*M9Yb>Glgt{m0-srRF=8UqpxOv*p>ml^PT@rH^yoh<~GWHaQ5v#N2lH9P49ju&;G-c z+}_Uz_FdwG?sM$<@V0lqGc8}Rzu)-!KQUeDjQ}l2UHjt3guX7l_Z?jQ+EZu|R-*`w z$sBRkp#JS^e`7CC8nMe^ZlOkceG*7I9b%h<6NI7pE0K2Op%7r&WbSVAWW5+{aEFGMzb^A5lCuD9^DXTHPx z-t|@xGhRdpN+q$iCL{7ICuwmv8zQnQIh)sc~bp49VqP0-_uzx(NQ z?Efcp-}aWY{Oq&O@!I`=_RFRr?B}!?7|i%V;QQY9g8Lmew+VY)^RmUkp0|G-#C#8a ztM2dJ70w;*;|@DN;L3F77x*y9E%hzseg${G<1QY_3j>TWwr$%lh=M_u>Rr52e#E@} zMucE$$TYe?pJ|`@)IYe-h|%0;KD;)!d0*3d#uw*ZJdziP3yh|J%%^VexN&(q*%f~> z^zxRqxlZRILj63!gWADtIL`EnK@cabxy?M$uax;BTs(h<>o@LrA8chw)+>!B+mX5N z8{c2E6$x9B@dLIZBaDF&A>TzF)PDA3kM||MpUJ))3%GbB!_za*n)Gf**!}zyoc+kJ zdp^H+trlT2G_UG^T;0vzikz$5Cno5-!u8#ZfPUI9hMS!k`b0Znyc`Sgg(tx5reR=q zBh&EZ(%ABu=suLJUI>bw14Zf=`v0khW^DBA(E z8~HJQz1VjUT-$(4hclmWcGI+pz}0mzePUB-yRp67r>bXvvSdE&a+-Iq&8wp(4Rkj*{T-fBRmT|33K2FsW zM6693t?|TvdKusK&T5x0upPl09)N3Gd6sv(FcupBV=V0d(SsYE#5Os5zs>0=qMIai z?VbGODBNmhlW*^uIk$E*;eLNRqbrQHX1DA4)dle1OdlW4>ep>D!^5YlJh#8kg^AuI zgIF66vFZ6xBEq-pc`FbR#zWYQ%=m5s&90>0rJI+3;*H@yH_e^C+iug*njw3WeyM5f zq#46RpDn)F9&X#@1E;~)Ou-MmOpQBsPdH&yXy$hE2$OD?*d8~ve=$)qJiUGrzHhTfnpSz+ zo@IuAORW~wN`-A}qTlk8^TtDyNCf7S6M##TFzcnGBvFT0YwomK=?4-07<5pJB4&n$ za$j^5{dwo9jhBRxz|{Q_tM>bVLqB<$q|;@k-RAhvQ2Lz!KoUSBhG+Y1zN?+=NK@}Q zeOeAmL0ax+;yPK*)`^Jlwhz8PU7yc>#e8pnFkMJe@R_{Z>2Tck`wWQ)tDO$DIA+GC zvG?SE zT$xe{RVtY!8OKPH@P7!8{OZI}E>{2m03~!qSaf7zbY(hYa%Ew3WdJfTGBPbNF)c7M zR53O>G&DLiGAl4JIxsNhgZRk+001R)MObuXVRU6WZEs|0W_bWIFfuVMF)}SOHB>M( gIx#UiG%_nNFgh?WD1Qr600000NkvWtM6N<$g7JD1)c^nh delta 7661 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 0 && x < @width && y >= 0 && y < @height - pixel_pointer(x, y, surface).value = pixel.format(surface.format) - end - end - - # ditto - def draw_point(point : Vector(Int, 2), pixel : Pixel = Pixel.new, surface = @screen) - draw_point(point.x, point.y, pixel, surface) - end - - # ditto - def draw_point(point : Vector(Float, 2), pixel : Pixel = Pixel.new, surface = @screen) - draw_point(point.to_i32, pixel, surface) - end - - # ================= - - # Draw a line using Bresenham’s Algorithm - def draw_line(x1 : Int, y1 : Int, x2 : Int, y2 : Int, pixel : Pixel = Pixel.new, surface = @screen) - # The slope for each axis - slope = Vector[(x2 - x1).abs, -(y2 - y1).abs] - - # The step direction in both axis - step = Vector[x1 < x2 ? 1 : -1, y1 < y2 ? 1 : -1] - - # The final decision accumulation - # Initialized to the height of x and y - decision = slope.x + slope.y - - point = Vector[x1, y1] - + def run! loop do - draw_point(point.x, point.y, pixel, surface) - # Break if we've reached the ending point - break if point.x == x2 && point.y == y2 - - # Square the decision to avoid floating point calculations - decision_squared = decision + decision - - # if decision_squared is greater than - if decision_squared >= slope.y - decision += slope.y - point.x += step.x + case event = SDL::Event.poll + when SDL::Event::Quit + break end - if decision_squared <= slope.x - decision += slope.x - point.y += step.y - end + engine_update(event) + engine_draw + + break unless @running end + ensure + SDL.quit end - # ditto - def draw_line(p1 : Vector(Int, 2), p2 : Vector(Int, 2), pixel : Pixel = Pixel.new, surface = @screen) - draw_line(p1.x, p1.y, p2.x, p2.y, pixel, surface) - end - - # ditto - def draw_line(p1 : Vector(Float, 2), p2 : Vector(Float, 2), pixel : Pixel = Pixel.new, surface = @screen) - draw_line(p1.to_i32, p2.to_i32, pixel, surface) - end - - # ================= - - # Draw the outline of a square rect - def draw_rect(x1 : Int, y1 : Int, x2 : Int, y2 : Int, pixel : Pixel = Pixel.new, surface = @screen) - # draw from top left to bottom right - y1, y2 = y2, y1 if y1 > y2 - x1, x2 = x2, x1 if x1 > x2 - - x1.upto(x2) do |x| - draw_point(x, y1, pixel, surface) - draw_point(x, y2, pixel, surface) - end - - y1.upto(y2) do |y| - draw_point(x1, y, pixel, surface) - draw_point(x2, y, pixel, surface) - end - end - - def draw_rect(p1 : PF::Vector(Int, 2), p2 : PF::Vector(Int, 2), pixel : Pixel = Pixel.new, surface = @screen) - draw_rect(p1.x, p1.y, p2.x, p2.y, pixel, surface) - end - - # ================= - - # Draw lines enclosing a shape - def draw_shape(frame : Enumerable(Point), pixel : Pixel = Pixel.new, surface = @screen) - 0.upto(frame.size - 1) do |n| - draw_line(frame[n], frame[(n + 1) % frame.size], pixel, surface) - end - end - - # ================= - - # Draw a circle using Bresenham’s Algorithm - def draw_circle(cx : Int, cy : Int, r : Int, pixel : Pixel = Pixel.new, surface = @screen) - x, y = 0, r - d = 3 - 2 * r - - loop do - draw_point(cx + x, cy + y, pixel) - draw_point(cx - x, cy + y, pixel) - draw_point(cx + x, cy - y, pixel) - draw_point(cx - x, cy - y, pixel) - draw_point(cx + y, cy + x, pixel) - draw_point(cx - y, cy + x, pixel) - draw_point(cx + y, cy - x, pixel) - draw_point(cx - y, cy - x, pixel) - - break if x > y - - x += 1 - - if d > 0 - y -= 1 - d = d + 4 * (x - y) + 10 - else - d = d + 4 * x + 6 - end - end - end - - def draw_circle(c : Vector(Int, 2), r : Int, pixel : Pixel = Pixel.new, surface = @screen) - draw_circle(c.x, c.y, r, pixel, surface) - end - - # ================= - - def draw_triangle(p1 : Vector, p2 : Vector, p3 : Vector, pixel : Pixel = Pixel.new, surface = @screen) - draw_line(p1, p2, pixel, surface) - draw_line(p2, p3, pixel, surface) - draw_line(p3, p1, pixel, surface) - end - - # ================= - - # Fill a rect - def fill_rect(x1 : Int, y1 : Int, x2 : Int, y2 : Int, pixel : Pixel = Pixel.new, surface = @screen) - # draw from top left to bottom right - y1, y2 = y2, y1 if y1 > y2 - x1, x2 = x2, x1 if x1 > x2 - - y1.upto(y2) do |y| - x1.upto(x2) do |x| - draw_point(x, y, pixel, surface) - end - end - end - - # ===================== - # END drawing functions - # ===================== - private def engine_update(event) et = elapsed_time calculate_fps(et) @@ -255,24 +105,8 @@ module PF draw end - @renderer.copy(@screen) + @renderer.copy(@screen.surface) @renderer.present end - - def run! - loop do - case event = SDL::Event.poll - when SDL::Event::Quit - break - end - - engine_update(event) - engine_draw - - break unless @running - end - ensure - SDL.quit - end end end diff --git a/src/pixel_text.cr b/src/pixel_text.cr index 1b97397..9b3d65c 100644 --- a/src/pixel_text.cr +++ b/src/pixel_text.cr @@ -21,7 +21,7 @@ module PF end end - def draw(surface : SDL::Surface, text : String, x : Int32 = 0, y : Int32 = 0) + def draw_to(surface : SDL::Surface, text : String, x : Int32 = 0, y : Int32 = 0) ix = 0 iy = 0 text.each_char do |char| @@ -47,5 +47,9 @@ module PF ix += 1 end end + + def draw_to(sprite : Sprite, text : String, x : Int32 = 0, y : Int32 = 0) + draw_to(sprite.surface, text, x, y) + end end end diff --git a/src/sprite.cr b/src/sprite.cr index a213c1e..8d4a5fc 100644 --- a/src/sprite.cr +++ b/src/sprite.cr @@ -1,11 +1,12 @@ require "sdl/image" require "./vector" +require "./sprite/*" module PF class Sprite property surface : SDL::Surface - delegate :convert, :format, to: @surface + delegate :fill, :lock, :format, to: @surface def initialize(@surface) end @@ -26,10 +27,22 @@ module PF Vector[width, height] end - def draw(surface : SDL::Surface, x : Int32, y : Int32) + def convert(other : SDL::Surface) + @surface = @surface.convert(other) + end + + def convert(other : Sprite) + @surface = @surface.convert(other.surface) + end + + def draw_to(surface : SDL::Surface, x : Int32, y : Int32) @surface.blit(surface, nil, SDL::Rect.new(x, y, width, height)) end + def draw_to(sprite : Sprite, x : Int32, y : Int32) + draw_to(sprite.surface, x, y) + end + # Raw access to the pixels as a Slice def pixels Slice.new(@surface.pixels.as(Pointer(UInt32)), width * height) @@ -42,10 +55,9 @@ module PF r = uninitialized UInt8 g = uninitialized UInt8 b = uninitialized UInt8 - a = uninitialized UInt8 - LibSDL.get_rgba(raw_pixel, format, pointerof(r), pointerof(g), pointerof(b), pointerof(a)) - Pixel.new(r, g, b, a) + LibSDL.get_rgb(raw_pixel, format, pointerof(r), pointerof(g), pointerof(b)) + Pixel.new(r, g, b) end # ditto @@ -53,6 +65,19 @@ module PF sample(point.x, point.y) end + # Sample a color with alhpa + def sample(x : Int, y : Int, alpha = true) + raw_pixel = pixel_pointer(x, y).value + + r = uninitialized UInt8 + g = uninitialized UInt8 + b = uninitialized UInt8 + a = uninitialized UInt8 + + LibSDL.get_rgba(raw_pixel, format, pointerof(r), pointerof(g), pointerof(b), pointerof(a)) + Pixel.new(r, g, b, a) + end + # Get the pointer to a pixel private def pixel_pointer(x : Int32, y : Int32) target = @surface.pixels + (y * @surface.pitch) + (x * 4) diff --git a/src/sprite/draw_circle.cr b/src/sprite/draw_circle.cr new file mode 100644 index 0000000..a0d8997 --- /dev/null +++ b/src/sprite/draw_circle.cr @@ -0,0 +1,35 @@ +module PF + class Sprite + # Draw a circle using Bresenham’s Algorithm + def draw_circle(cx : Int, cy : Int, r : Int, pixel : Pixel = Pixel.new) + x, y = 0, r + d = 3 - 2 * r + + loop do + draw_point(cx + x, cy + y, pixel) + draw_point(cx - x, cy + y, pixel) + draw_point(cx + x, cy - y, pixel) + draw_point(cx - x, cy - y, pixel) + draw_point(cx + y, cy + x, pixel) + draw_point(cx - y, cy + x, pixel) + draw_point(cx + y, cy - x, pixel) + draw_point(cx - y, cy - x, pixel) + + break if x > y + + x += 1 + + if d > 0 + y -= 1 + d = d + 4 * (x - y) + 10 + else + d = d + 4 * x + 6 + end + end + end + + def draw_circle(c : Vector(Int, 2), r : Int, pixel : Pixel = Pixel.new) + draw_circle(c.x, c.y, r, pixel) + end + end +end diff --git a/src/sprite/draw_line.cr b/src/sprite/draw_line.cr new file mode 100644 index 0000000..52447bc --- /dev/null +++ b/src/sprite/draw_line.cr @@ -0,0 +1,48 @@ +module PF + class Sprite + # Draw a line using Bresenham’s Algorithm + def draw_line(x1 : Int, y1 : Int, x2 : Int, y2 : Int, pixel : Pixel = Pixel.new) + # The slope for each axis + slope = Vector[(x2 - x1).abs, -(y2 - y1).abs] + + # The step direction in both axis + step = Vector[x1 < x2 ? 1 : -1, y1 < y2 ? 1 : -1] + + # The final decision accumulation + # Initialized to the height of x and y + decision = slope.x + slope.y + + point = Vector[x1, y1] + + loop do + draw_point(point.x, point.y, pixel) + # Break if we've reached the ending point + break if point.x == x2 && point.y == y2 + + # Square the decision to avoid floating point calculations + decision_squared = decision + decision + + # if decision_squared is greater than + if decision_squared >= slope.y + decision += slope.y + point.x += step.x + end + + if decision_squared <= slope.x + decision += slope.x + point.y += step.y + end + end + end + + # ditto + def draw_line(p1 : Vector(Int, 2), p2 : Vector(Int, 2), pixel : Pixel = Pixel.new) + draw_line(p1.x, p1.y, p2.x, p2.y, pixel) + end + + # ditto + def draw_line(p1 : Vector(Float, 2), p2 : Vector(Float, 2), pixel : Pixel = Pixel.new) + draw_line(p1.to_i32, p2.to_i32, pixel) + end + end +end diff --git a/src/sprite/draw_point.cr b/src/sprite/draw_point.cr new file mode 100644 index 0000000..a716760 --- /dev/null +++ b/src/sprite/draw_point.cr @@ -0,0 +1,25 @@ +module PF + class Sprite + # Draw a single point + def draw_point(x : Int32, y : Int32, color : UInt32) + if x >= 0 && x < width && y >= 0 && y < height + pixel_pointer(x, y).value = color + end + end + + # ditto + def draw_point(x : Int32, y : Int32, pixel : Pixel = Pixel.new) + draw_point(x, y, pixel.format(format)) + end + + # ditto + def draw_point(point : Vector(Int, 2), pixel : Pixel = Pixel.new) + draw_point(point.x, point.y, pixel) + end + + # ditto + def draw_point(point : Vector(Float, 2), pixel : Pixel = Pixel.new) + draw_point(point.to_i32, pixel) + end + end +end diff --git a/src/sprite/draw_rect.cr b/src/sprite/draw_rect.cr new file mode 100644 index 0000000..725170b --- /dev/null +++ b/src/sprite/draw_rect.cr @@ -0,0 +1,24 @@ +module PF + class Sprite + # Draw the outline of a square rect + def draw_rect(x1 : Int, y1 : Int, x2 : Int, y2 : Int, pixel : Pixel = Pixel.new) + # draw from top left to bottom right + y1, y2 = y2, y1 if y1 > y2 + x1, x2 = x2, x1 if x1 > x2 + + x1.upto(x2) do |x| + draw_point(x, y1, pixel) + draw_point(x, y2, pixel) + end + + y1.upto(y2) do |y| + draw_point(x1, y, pixel) + draw_point(x2, y, pixel) + end + end + + def draw_rect(p1 : PF::Vector(Int, 2), p2 : PF::Vector(Int, 2), pixel : Pixel = Pixel.new) + draw_rect(p1.x, p1.y, p2.x, p2.y, pixel) + end + end +end diff --git a/src/sprite/draw_shape.cr b/src/sprite/draw_shape.cr new file mode 100644 index 0000000..1cef655 --- /dev/null +++ b/src/sprite/draw_shape.cr @@ -0,0 +1,10 @@ +module PF + class Sprite + # Draw lines enclosing a shape + def draw_shape(frame : Enumerable(Point), pixel : Pixel = Pixel.new) + 0.upto(frame.size - 1) do |n| + draw_line(frame[n], frame[(n + 1) % frame.size], pixel) + end + end + end +end diff --git a/src/sprite/draw_triangle.cr b/src/sprite/draw_triangle.cr new file mode 100644 index 0000000..a47574c --- /dev/null +++ b/src/sprite/draw_triangle.cr @@ -0,0 +1,10 @@ +module PF + class Sprite + # Draws 3 lines + def draw_triangle(p1 : Vector, p2 : Vector, p3 : Vector, pixel : Pixel = Pixel.new) + draw_line(p1, p2, pixel) + draw_line(p2, p3, pixel) + draw_line(p3, p1, pixel) + end + end +end diff --git a/src/sprite/fill_rect.cr b/src/sprite/fill_rect.cr new file mode 100644 index 0000000..1d28eaf --- /dev/null +++ b/src/sprite/fill_rect.cr @@ -0,0 +1,16 @@ +module PF + class Sprite + # Fill a rect + def fill_rect(x1 : Int, y1 : Int, x2 : Int, y2 : Int, pixel : Pixel = Pixel.new) + # draw from top left to bottom right + y1, y2 = y2, y1 if y1 > y2 + x1, x2 = x2, x1 if x1 > x2 + + y1.upto(y2) do |y| + x1.upto(x2) do |x| + draw_point(x, y, pixel) + end + end + end + end +end diff --git a/src/game/fill_shape.cr b/src/sprite/fill_shape.cr similarity index 81% rename from src/game/fill_shape.cr rename to src/sprite/fill_shape.cr index 7642be9..6585c7a 100644 --- a/src/game/fill_shape.cr +++ b/src/sprite/fill_shape.cr @@ -1,11 +1,11 @@ module PF - abstract class Game + class Sprite # Fill an abitrary polygon. Expects a clockwise winding of points - def fill_shape(points : Enumerable(Vector), color : Pixel = Pixel.new, surface = @screen) + def fill_shape(points : Enumerable(Vector), color : Pixel = Pixel.new) return if points.empty? - return draw_point(points[0], color, surface) if points.size == 1 - return draw_line(points[0], points[1], color, surface) if points.size == 2 - return draw_triangle(points[0], points[1], points[2], color, surface) if points.size == 3 + return draw_point(points[0], color) if points.size == 1 + return draw_line(points[0], points[1], color) if points.size == 2 + return draw_triangle(points[0], points[1], points[2], color) if points.size == 3 # set initial bounding box top = points[0].y @@ -50,7 +50,7 @@ module PF # Only draw points within x values of an ascending slope, # descending slope indicates that the point is outside of the shape if intercepts[n][1] || x == intercepts[n][0] # Always draw the border itself - draw_point(x, y, color, surface) + draw_point(x, y, color) end # # While condition for overlapping points @@ -61,13 +61,13 @@ module PF end end - def fill_shape(*points : Vector, color : Pixel = Pixel.new, surface = @screen) - fill_shape(points, color, surface) + def fill_shape(*points : Vector, color : Pixel = Pixel.new) + fill_shape(points, color) end - def draw_shape(*points : Vector, color : Pixel = Pixel.new, surface = @screen) + def draw_shape(*points : Vector, color : Pixel = Pixel.new) 0.upto(points.size - 1) do |n| - draw_line(points[n], points[(n + 1) % points.size], color, surface) + draw_line(points[n], points[(n + 1) % points.size], color) end end end diff --git a/src/game/fill_triangle.cr b/src/sprite/fill_triangle.cr similarity index 87% rename from src/game/fill_triangle.cr rename to src/sprite/fill_triangle.cr index 3815d6b..20d3d5c 100644 --- a/src/game/fill_triangle.cr +++ b/src/sprite/fill_triangle.cr @@ -1,8 +1,8 @@ require "../line" module PF - abstract class Game - def fill_triangle(p1 : Vector, p2 : Vector, p3 : Vector, pixel : Pixel = Pixel.new, surface = @screen) + class Sprite + def fill_triangle(p1 : Vector, p2 : Vector, p3 : Vector, pixel : Pixel = Pixel.new) # Sort points from top to bottom p1, p2 = p2, p1 if p2.y < p1.y p1, p3 = p3, p1 if p3.y < p1.y @@ -39,7 +39,7 @@ module PF end x_left.upto(x_right) do |x| - draw_point(x, y + c, pixel, surface) + draw_point(x, y + c, pixel) end if y == mid @@ -54,8 +54,8 @@ module PF end end - def fill_triangle(points : Enumerable(Vector), pixel : Pixel = Pixel.new, surface = @screen) - fill_triangle(points[0], points[1], points[2], pixel, surface) + def fill_triangle(points : Enumerable(Vector), pixel : Pixel = Pixel.new) + fill_triangle(points[0], points[1], points[2], pixel) end end end