From 51db8085f8d2d31c3b2169d8338c25421aa1095a Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Mon, 17 May 2021 10:32:13 +0200 Subject: [PATCH] Demos: Add Mandelbrot demo This adds a very rudimentary Mandelbrot viewer. It supports zooming and pretty much nothing else. Not even color smoothing or super sampling. --- Base/res/apps/Mandelbrot.af | 4 + Base/res/icons/16x16/app-mandelbrot.png | Bin 0 -> 6610 bytes Base/res/icons/32x32/app-mandelbrot.png | Bin 0 -> 8853 bytes Userland/Demos/CMakeLists.txt | 1 + Userland/Demos/Mandelbrot/CMakeLists.txt | 6 + Userland/Demos/Mandelbrot/Mandelbrot.cpp | 219 +++++++++++++++++++++++ 6 files changed, 230 insertions(+) create mode 100644 Base/res/apps/Mandelbrot.af create mode 100644 Base/res/icons/16x16/app-mandelbrot.png create mode 100644 Base/res/icons/32x32/app-mandelbrot.png create mode 100644 Userland/Demos/Mandelbrot/CMakeLists.txt create mode 100644 Userland/Demos/Mandelbrot/Mandelbrot.cpp diff --git a/Base/res/apps/Mandelbrot.af b/Base/res/apps/Mandelbrot.af new file mode 100644 index 0000000000..8a1eea9242 --- /dev/null +++ b/Base/res/apps/Mandelbrot.af @@ -0,0 +1,4 @@ +[App] +Name=Mandelbrot +Executable=/bin/Mandelbrot +Category=Demos diff --git a/Base/res/icons/16x16/app-mandelbrot.png b/Base/res/icons/16x16/app-mandelbrot.png new file mode 100644 index 0000000000000000000000000000000000000000..c7a2e7676d5f37ab5863a9135c32169afce11195 GIT binary patch literal 6610 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 Vp9F7v#DPX4JYD@<);T3K0RV1yl&=5) literal 0 HcmV?d00001 diff --git a/Base/res/icons/32x32/app-mandelbrot.png b/Base/res/icons/32x32/app-mandelbrot.png new file mode 100644 index 0000000000000000000000000000000000000000..c06030d4b0c2633264d198bb22489352438fbe60 GIT binary patch literal 8853 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANO)2~{BxB|(Yh3I#>^X_+~x z3MG{VsS2qTnQ06R6}Q&Tj4V>N-sJLsV^^G065F)qi*MdO(B0p!JXyy}b!+VAyp1kF zmI{pp{QUe=BLDxdzx}6s{(~F?#b=YUyu|Gnw!M;yKUMmF%jdlH1^3SPSNnbZKK=2t z+CzmWKEHf#?_IU7UUvI?#&`Y4w*N2;kUZb{@2d;%`5#YjJDhLcleQ%CO!|UfJWuY{ z9#mZb--Upgm0<_70Z`|YZ6Gw+}H zUK2Mp+;BsA!?``ju2%9!uVtQ=+dOyM&D`rhWy%bWKA6TV^!yHkhOD+j$lVXq8J8}Y zepm1JtL6F$W)0Viuk3xEbiVj?<(&;9lrR0uxBV4=tuEYKv}bF0IWc6Rk9+v~et zygQsB=HPNkgW2~h?;4ey59;kq?NMJ0k4OZc*?2@M`jh6889}E!A6?&cUh(*xqI0U( z?{w)eJTl2k@931MsM4#-eH)j}%3is3+c9TpW%GA=F&CoB?|#3K>~H)1Qg%M`uO5R( z9?JbmZnC+%UaDIeUOuz=jMeQ;+*)flJo3_Bzv)$$^s3zbhx@GG?m4|~_ujzG&)UAO zzi#h-c3(g-yJqZ+`zs2w&#hNn^F)QqSfKb|Q-v6lz%9w#sj=o5@ zM&q>t%m+8D>At;h;&*&F{)^_lYn>xl@}K)j&w}5qJNFmFipu<>2|BX|u8x@csR8GU1MV&AEF^ zx0-}8?9)EBN}KC=(5&Jeaci5>Wjoc>WP?@TnDtp2x8IJtENpT} zaN7O4H!1G-_kQ1bMopz|%2Vmh?=nkYTiiKx>*jh_tFN96&1x4r7OXB_BR3=N$;mSo zZC2duXF68e8geEZ%}Tnsb=I1z0%CqzM%tEZAL;D&3g2?!_20~U>~_~=@0GKZ&)sNg z$#C$t)r|X+dw)lSR_$58V^Q9rNTHsW8g-|vqjamz7>4_8UcaIGuIWLu_&0ehN}Eb{ zu>2@|F1pYo(>kaxfqioP>4}Zfzs_yY4BYyA*L4YP;Wf9iXB+5z-QgbkWmax;^vsoc z_m9qs(EfNXJLUL7tA$ae&-D$s=d7+fySG?&c6j^t3%8!%;{Vd6xMO+XS()m%tK|>u zU-!wdPr33+J&M;<;q5i1S;c}kR4&(_gO!J}DK{ZF^9TT@zF7-93$|-jKVwn`(V6y0S>bBe}nL6=xeG_WEZO%_U<4`~G zdGCqzmfYA)Y^L8WE2BCD>r-aYy| zV;|YATlK0jIAQx%L53@alaB8_;(q_uTHm4z=a*O3R;oX$5c;_6zuta*ItR``15VwL|4^qT}G2bb~8JEVeaKtKj53>n|Xp|?P2|nCha%c@07P% zm$2Vj&?Mk>Zc}>YDv@YT)>@8hUA69492u=SeK=0W8-2FQy0)D2O5bn(ns=XHhM(7X zwN6aQdAIGgZwF2v{WW`o7-f{f}@HkEFWhYi~>BS7>gJHrqO7X{%gp z$I0DcM_Ih~sdztI+ri=CIdxNyB(w0v8SKw(+Tv&03vN?Bb#VUbXT_qXRcRI%i(7hj z#C!}XjNbXnX=-cv5{d4|3zwYSrk2^YMzC<*ye%Gw>a-tUI=Q1ZeU9rLjUPv*@0K@= zUh}hE>(f`^uW=zK`QE3yN!74)ME%OJIlk69bFYbbMnbrGgZcW_qbIumIhH)iaS@LQ zy%^e|W9(&lq;kr&xhxGHo7eoC!Q_;jqia94=&Hs&UB-QF&0AGY26qLX4Ak`JvC|Jp zbD3)B9J|P7r!LddP8C5te_4gvC5_n%CuXnL(At$+U73N9)tWq-W?D&<=RVyZcCvlg^4tdsPUDID4(Oy^eXlKX8*4e9%G$!>m zA9YF+Il1EYk}q*)&%WzrPMNlFQ<-}C?s-nfW=8l%?>n|Z_Lhut-6Vzkk^Qo+v5^`V z&qi&%J%g9Sg_9%NtN*UjlZxysGIFfTx@IeV(c3pi@nv7F>Q}>z-6gUcGvqo}B((a) z-df@j@+&)~BK1@ZYu2MR%O{_G@pR_IWno&P2D;pH**3m!ynXfAG*ubFGgej3?$4i| zv2A|$H{$-+xAt{OGc=p7@SYMEj#oQ<{6tljla_(UM(-0@Up`mZwFYY+c1G0SyRyxFZX3mUjhl_xP@jW#EHpU8$5}MBuH(y>qz;A>R_(6;=YC2reWkK5dB$QL={-hii=s2urnt*xq)qXc z*f>3Dp{4r8!e*cm64XIm=L?l<--`e4n zcx*lIk?;O8jNdlrOq_Zu=x|YoeuuB8-qw2a-CJ%gQ2G2iL`dc0>C~)WCb1kI&(khV z+w_9t;t`2$C!9Jtv!BmTZ2I}MZfDj!4qqAh)D^+*zox10n|qZdLgEQan%?B)rMK4D zt_$6sIYrL5H6XbrZ0kkCPSqH>kb&?_{}rOG-;~-PYN=&n}J+;Z(29T7GrX-J%<7lk)%P`nx`Vf1=7D z|G|OTmweOsqrJ;riv5+YWyc4{A6pQAK7`lH`0|1^qK}HdM)$mU+$U07tmNObO8#TN zah8zVsn1dR5;gjcPpxt;X|Lk6_%!Y936~|s-R@33G7_y45%zz?FX>4A53aoRH)z90 zw}PhK!OMTeUeOc{jJY=D%UbTvGsm5;?G~OU`+C*%x9gT)3H0f={5vfw<5zX`nptZv zXFr<6xoMlosny!0$Dc-7?FqW9T;Z~-WmAx8STz6QoFyu&pH2F^N`6yw)`a(6)B66r z*{#2Kg2F40t|bvKw(DnJJMlB(q{UoY(TZ0eROiVYTzsu;*{n$6-Zefe?uhwFL_Ej| zRG8CK#rjIBb)ol!4c~tRZ0ZoymuU#<3*s)$3|cYcg|z?j+`Y(`#J@+vr|0=9V}>U$m$zP**|{{i{b`nMu6Nltwt$^#3$l-@ z?z*kC{O1&fn@?7FmHKR#G5VXfKk7=7!SjHq+%qA4QlHuw4TZKG`jNM1uXy|;#Vd!_ zUge(coA1%ToJF8=`qI@hhs@JD7d<)HkiTHT>%iruH!bfaxvh)xSKWSrTYpI|kCmtX zZHc%r=0=|WJu~aolFOB!r3ebBL`hHjm6`kOV`JPmiRUfs_cyNU4Jtj#b81&#YvhvT z(DcWd6O>=BeYWOZ0DJCJL$5_T8UCLGZR6E)#WFsJ@3r1--^7+SRdqK{iT^f14(CIe z+3)++=Jat)^(yL6Sbx^A>QluW=C$Dqj`@_%oF>|`Mss4xY>Cu6ldL@6@&;%Iw;u_u z?W)lfIbKwvvTh>hCM%TwKHmAk=wnE<)Rs7Vllp-ZXI5UDeyH77${+@AaOH{|99lzkNEePJH#9tv9PwLXYm(zTs#6Gl{!p zO>}ja(OeemRXUSys6RPxra0@S@ukP*GlUo?J}GeDHqA8a^djc^U%L+fJ$-E3r>`qC zd@Ak+@2M|7+2Qj@sHu|0tXpI1HX+gETRTjw5IahDJ zuw&s{kLm5Uw}rp7-!jr%zC^j@=e=FAy-R9in7!PV&eq%UTGRZ}8jrd8FOKj0;xcdX z>M2%>PyGHS82rEVMp(tShV$>t)eh~gy7pMxJ$*3oBopa07^>$!)$*UD;ZF^FvI(z-vz5@XPImfFgZf>Ff;a zYcnuZ%&DDd>v7mY=4gCyw|t1pn*~af7FtCLh!$Sa5?Sc9LNuz>VCNU^Stcr)dVNjV z2M_iiUDe#Zc^%)nCWRl2kDfhS@`Cz7^@yr?Oe1hFO)xu`YcY}%Pm_Z#5S^UDv3-9bPzOC+VW~igvW)tEv@wz zzIs0iIuXjCe4NSag2L{)5B5LquAMq}&WA}&feIfUS;#9p{MHLtZE^1V#>ZFu82%d< zEH(eLRLkJAXVub$t^E!2avwa}wNSJAz*D{p|5HvZd*f9%%gKy;&-{z#OB?q^FUUUo zdhNdc%j^oCu}AY8nw=9&+;6_ty?6KGo!k4ZUF-b*w|MTBqnF4mMA2prf25aD!t#mUr8Y|#a1cY)Yrhbz&SM| z)1#^=HMq(zB)KX(*)m1R-j2(r!m1*-AUCxnQK2F?C$HG5!d3}vu2o*K6-ZcLNdc^+ zB->Ug!Z$#{Ilm}X!9>qQ&p_9;BD2g$$&O3GrYI%ND#*nRYD7^=nypesNlAf~zJ7Um zxn8-kUVc%!zM-Y1rM`iYzLAk`QA(O_ab;dfVufyAu`txD*r=poUlE7Wn$Yjn6BFhC*_Fu6{*gfxe-hfqrhTKC+JD64$a4{5pz5 zDhpEegHnt0ON)|IUCUDQN|cd}NJ)n4FDNa_0edGUSwA%=H8(Y{q*&ij&k)5*cTe8{ zxDHSdq-W+9fOS>m7NDps$xK587K(2`GGOl@JES7F0B$adYM9@_ios!I<&vLV3UZ#O zi>(sKeyf!H0k*^fLl2Rc-kr%=@}qI0&)^d((;RPZIyg7 z^GYia5+Rwnslg?QpwKilGcq(XF*Y+avam2QHa9^i3QH|2&dkpPnQ3UCX9SS}MT?bx zQD$mhNg^nn+A0}>byVaQSUDG^CYIzEh2-bw*eZdXq+q0HXaG*$3O0~P@yIML$uFw3 z1E*+kz6nk(gzz9)ASV+nrJw*#wpNMB5KD>^%TiOo7Ae4_k~0$X(o<7xm7oaMnMA{l$xlj%QI5*Y?ZVP46FQL70(Y)*J~21_t&LPhVH|r>ugUQYIz=t9~&sFzb7|IEF+V zUOU}BL%2}nxV`=rwvZFNT2q{iw(xPK6>fBv?Yh8MdSNMRY|NyETyGOvca>~l-5sJK z`zC;0{JYpKzEZ)gc0bXWCC*GrS5(=xZalTT^ZwKCr@wa3i2LkA$3^$T_#U!)NtQqnqkHdlB2&b_O|a&B7bR-F!UR6oW&Z_jwzjjqCs!C1$x7-kS#pvg_}MKf_6p?>1w2oTHe|hD?yivGeD24+Nr9K{ zS2D_K$JAz4Io}m;Jn(T>PSLYxHfk}PKT6CVGh9D%@uJfqg`=-GInOE5=1g6rA1pm_ z)k7ymHi3#muVdaH)0r>ZdVSj(w;vNZ5198ZV!u$lYMbrdw{epbtT`6kZ*=~lG4oAv zti$poh9wpGMf%CL6F!9ea;%>ixo*LQ2`ubFXOBLsxv@IsSx^~+{r8*~RT@(H*`2%O z*(;YTOp3ZbGxc)c^LaZaznrFe{avrc`@0bzyYnO&&boc!SehbW>ci4iV8s3N^LF{h z8QmT-g$we$7Cp<+R{WADy+zY#QLKW*_Ll7`JaZSPImk8{J-xc|YHO|JuE|Dy&d+nI zOruxdbG!6=r!>oN!HXwt45?gNON&9tPdt=H~&BHGvi

)%F;9c^jMC_NXgZ`tivU96% zq<64PxbW#wgrmJn_w!~KFE$Z9jSq2&!X+>J?{Z2jM7iw#r>9}n;AIf8_3FavWh<{n z@8^8{I;Puvu3MAK;i8jK&C0epc0~qHB>I&jzAs%MEwIq|48zpjx_$FrZR(S|*fhE6 s^DnD99`)o2_XA4eXH7o;z4ITx;oAT5^OCP$2Mwrry85}Sb4q9e03ROg761SM literal 0 HcmV?d00001 diff --git a/Userland/Demos/CMakeLists.txt b/Userland/Demos/CMakeLists.txt index 0d150f24bc..cf3be6ef53 100644 --- a/Userland/Demos/CMakeLists.txt +++ b/Userland/Demos/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(Fire) add_subdirectory(GLTeapot) add_subdirectory(LibGfxDemo) add_subdirectory(LibGfxScaleDemo) +add_subdirectory(Mandelbrot) add_subdirectory(Mouse) add_subdirectory(Screensaver) add_subdirectory(Starfield) diff --git a/Userland/Demos/Mandelbrot/CMakeLists.txt b/Userland/Demos/Mandelbrot/CMakeLists.txt new file mode 100644 index 0000000000..80a203d715 --- /dev/null +++ b/Userland/Demos/Mandelbrot/CMakeLists.txt @@ -0,0 +1,6 @@ +set(SOURCES + Mandelbrot.cpp +) + +serenity_app(Mandelbrot ICON app-mandelbrot) +target_link_libraries(Mandelbrot LibGUI LibCore LibGfx) diff --git a/Userland/Demos/Mandelbrot/Mandelbrot.cpp b/Userland/Demos/Mandelbrot/Mandelbrot.cpp new file mode 100644 index 0000000000..6c709987d5 --- /dev/null +++ b/Userland/Demos/Mandelbrot/Mandelbrot.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2021, Gunnar Beutner + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class MandelbrotSet { +public: + MandelbrotSet() + { + set_view(); + } + + void reset() + { + set_view(); + calculate(); + } + + void resize(Gfx::IntSize const& size) + { + m_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, size); + calculate(); + } + + void zoom(Gfx::IntRect const& rect) + { + set_view( + rect.left() * (m_x_end - m_x_start) / m_bitmap->width() + m_x_start, + rect.right() * (m_x_end - m_x_start) / m_bitmap->width() + m_x_start, + rect.top() * (m_y_end - m_y_start) / m_bitmap->height() + m_y_start, + rect.bottom() * (m_y_end - m_y_start) / m_bitmap->height() + m_y_start); + calculate(); + } + + i32 mandelbrot(double px, double py, i32 max_iterations) + { + // Based on https://en.wikipedia.org/wiki/Plotting_algorithms_for_the_Mandelbrot_set + const double x0 = px * (m_x_end - m_x_start) / m_bitmap->width() + m_x_start; + const double y0 = py * (m_y_end - m_y_start) / m_bitmap->height() + m_y_start; + double x = 0; + double y = 0; + i32 iteration = 0; + double x2 = 0; + double y2 = 0; + + while (x2 + y2 <= 4 && iteration < max_iterations) { + y = 2 * x * y + y0; + x = x2 - y2 + x0; + x2 = x * x; + y2 = y * y; + iteration++; + } + + return iteration; + } + + void calculate_pixel(int px, int py, int max_iterations) + { + auto iterations = mandelbrot(px, py, max_iterations); + double hue = (double)iterations * 360.0 / (double)max_iterations; + if (hue == 360.0) + hue = 0.0; + double saturation = 1.0; + double value = iterations < max_iterations ? 1.0 : 0; + m_bitmap->set_pixel(px, py, Color::from_hsv(hue, saturation, value)); + } + + void calculate(int max_iterations = 100) + { + if (!m_bitmap) + return; + + for (int py = 0; py < m_bitmap->height(); py++) + for (int px = 0; px < m_bitmap->width(); px++) + calculate_pixel(px, py, max_iterations); + } + + Gfx::Bitmap const& bitmap() const + { + return *m_bitmap; + } + +private: + double m_x_start { 0 }; + double m_x_end { 0 }; + double m_y_start { 0 }; + double m_y_end { 0 }; + RefPtr m_bitmap; + + void set_view(double x_start = -2.5, double x_end = 1.0, double y_start = -1.0, double y_end = 1.0) + { + m_x_start = x_start; + m_x_end = x_end; + m_y_start = y_start; + m_y_end = y_end; + } +}; + +class Mandelbrot : public GUI::Widget { + C_OBJECT(Mandelbrot) +private: + virtual void paint_event(GUI::PaintEvent&) override; + virtual void mousedown_event(GUI::MouseEvent& event) override; + virtual void mousemove_event(GUI::MouseEvent& event) override; + virtual void mouseup_event(GUI::MouseEvent& event) override; + virtual void resize_event(GUI::ResizeEvent& event) override; + + bool m_dragging { false }; + Gfx::IntPoint m_selection_start; + Gfx::IntPoint m_selection_end; + + MandelbrotSet m_set; +}; + +void Mandelbrot::paint_event(GUI::PaintEvent& event) +{ + GUI::Painter painter(*this); + painter.add_clip_rect(event.rect()); + painter.draw_scaled_bitmap(rect(), m_set.bitmap(), m_set.bitmap().rect()); + + if (m_dragging) + painter.draw_rect(Gfx::IntRect::from_two_points(m_selection_start, m_selection_end), Color::Blue); +} + +void Mandelbrot::mousedown_event(GUI::MouseEvent& event) +{ + if (event.button() == GUI::MouseButton::Left) { + if (!m_dragging) { + m_selection_start = event.position(); + m_selection_end = event.position(); + m_dragging = true; + update(); + } + } + + return GUI::Widget::mousedown_event(event); +} + +void Mandelbrot::mousemove_event(GUI::MouseEvent& event) +{ + if (m_dragging) { + m_selection_end = event.position(); + update(); + } + + return GUI::Widget::mousemove_event(event); +} + +void Mandelbrot::mouseup_event(GUI::MouseEvent& event) +{ + if (event.button() == GUI::MouseButton::Left) { + auto selection = Gfx::IntRect::from_two_points(m_selection_start, m_selection_end); + if (selection.width() > 0 && selection.height() > 0) + m_set.zoom(selection); + m_dragging = false; + update(); + } else if (event.button() == GUI::MouseButton::Right) { + m_set.reset(); + update(); + } + + return GUI::Widget::mouseup_event(event); +} + +void Mandelbrot::resize_event(GUI::ResizeEvent& event) +{ + m_set.resize(event.size()); +} + +int main(int argc, char** argv) +{ + auto app = GUI::Application::construct(argc, argv); + + if (pledge("stdio recvfd sendfd rpath", nullptr) < 0) { + perror("pledge"); + return 1; + } + + if (unveil("/res", "r") < 0) { + perror("unveil"); + return 1; + } + + if (unveil(nullptr, nullptr) < 0) { + perror("unveil"); + return 1; + } + + auto window = GUI::Window::construct(); + window->set_double_buffering_enabled(false); + window->set_title("Mandelbrot"); + window->set_minimum_size(320, 240); + window->resize(window->minimum_size() * 2); + + auto menubar = GUI::Menubar::construct(); + auto& file_menu = menubar->add_menu("&File"); + file_menu.add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); })); + window->set_menubar(move(menubar)); + window->set_main_widget(); + window->show(); + + auto app_icon = GUI::Icon::default_icon("app-mandelbrot"); + window->set_icon(app_icon.bitmap_for_size(16)); + + return app->exec(); +}