mirror of
https://github.com/bspeice/speice.io
synced 2025-04-27 00:01:45 -04:00
99 lines
74 KiB
HTML
99 lines
74 KiB
HTML
<!doctype html><html lang=en dir=ltr class="blog-wrapper blog-post-page plugin-blog plugin-id-default" data-has-hydrated=false><meta charset=UTF-8><meta name=generator content="Docusaurus v3.7.0"><title data-rh=true>Playing with fire: The camera | The Old Speice Guy</title><meta data-rh=true name=viewport content="width=device-width, initial-scale=1.0"><meta data-rh=true name=twitter:card content=summary_large_image><meta data-rh=true property=og:url content=https://speice.io/2025/03/playing-with-fire-camera/><meta data-rh=true property=og:locale content=en><meta data-rh=true name=docusaurus_locale content=en><meta data-rh=true name=docusaurus_tag content=default><meta data-rh=true name=docsearch:language content=en><meta data-rh=true name=docsearch:docusaurus_tag content=default><meta data-rh=true property=og:title content="Playing with fire: The camera | The Old Speice Guy"><meta data-rh=true name=description content="Something that bugged me while writing the first three articles on fractal flames were the constraints on"><meta data-rh=true property=og:description content="Something that bugged me while writing the first three articles on fractal flames were the constraints on"><meta data-rh=true property=og:type content=article><meta data-rh=true property=article:published_time content=2025-03-10T12:00:00.000Z><link data-rh=true rel=icon href=/img/favicon.ico><link data-rh=true rel=canonical href=https://speice.io/2025/03/playing-with-fire-camera/><link data-rh=true rel=alternate href=https://speice.io/2025/03/playing-with-fire-camera/ hreflang=en><link data-rh=true rel=alternate href=https://speice.io/2025/03/playing-with-fire-camera/ hreflang=x-default><script data-rh=true type=application/ld+json>{"@context":"https://schema.org","@id":"https://speice.io/2025/03/playing-with-fire-camera","@type":"BlogPosting","author":{"@type":"Person","name":"Bradlee Speice"},"dateModified":"2025-03-11T01:13:28.000Z","datePublished":"2025-03-10T12:00:00.000Z","description":"Something that bugged me while writing the first three articles on fractal flames were the constraints on","headline":"Playing with fire: The camera","isPartOf":{"@id":"https://speice.io/","@type":"Blog","name":"Blog"},"keywords":[],"mainEntityOfPage":"https://speice.io/2025/03/playing-with-fire-camera","name":"Playing with fire: The camera","url":"https://speice.io/2025/03/playing-with-fire-camera"}</script><link rel=alternate type=application/rss+xml href=/rss.xml title="The Old Speice Guy RSS Feed"><link rel=alternate type=application/atom+xml href=/atom.xml title="The Old Speice Guy Atom Feed"><link rel=stylesheet href=/katex/katex.min.css type=text/css><link rel=stylesheet href=/assets/css/styles.24ac2c37.css><script src=/assets/js/runtime~main.9167b4fd.js defer></script><script src=/assets/js/main.dff31118.js defer></script><body class=navigation-with-keyboard><script>!function(){var t,e=function(){try{return new URLSearchParams(window.location.search).get("docusaurus-theme")}catch(t){}}()||function(){try{return window.localStorage.getItem("theme")}catch(t){}}();t=null!==e?e:"light",document.documentElement.setAttribute("data-theme",t)}(),function(){try{for(var[t,e]of new URLSearchParams(window.location.search).entries())if(t.startsWith("docusaurus-data-")){var a=t.replace("docusaurus-data-","data-");document.documentElement.setAttribute(a,e)}}catch(t){}}()</script><div id=__docusaurus><div role=region aria-label="Skip to main content"><a class=skipToContent_fXgn href=#__docusaurus_skipToContent_fallback>Skip to main content</a></div><nav aria-label=Main class="navbar navbar--fixed-top"><div class=navbar__inner><div class=navbar__items><button aria-label="Toggle navigation bar" aria-expanded=false class="navbar__toggle clean-btn" type=button><svg width=30 height=30 viewBox="0 0 30 30" aria-hidden=true><path stroke=currentColor stroke-linecap=round stroke-miterlimit=10 stroke-width=2 d="M4 7h22M4 15h22M4 23h22"/></svg></button><a class=navbar__brand href=/><div class=navbar__logo><img src=/img/logo.svg alt="Sierpinski Gasket" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src=/img/logo-dark.svg alt="Sierpinski Gasket" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><b class="navbar__title text--truncate">The Old Speice Guy</b></a></div><div class="navbar__items navbar__items--right"><a href=https://github.com/bspeice target=_blank rel="noopener noreferrer" class="navbar__item navbar__link header-github-link"></a><div class="toggle_vylO colorModeToggle_DEke"><button class="clean-btn toggleButton_gllP toggleButtonDisabled_aARS" type=button disabled title="Switch between dark and light mode (currently light mode)" aria-label="Switch between dark and light mode (currently light mode)" aria-live=polite aria-pressed=false><svg viewBox="0 0 24 24" width=24 height=24 class=lightToggleIcon_pyhR><path fill=currentColor d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"/></svg><svg viewBox="0 0 24 24" width=24 height=24 class=darkToggleIcon_wfgR><path fill=currentColor d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/></svg></button></div><div class=navbarSearchContainer_Bca1><div class=navbar__search><span aria-label="expand searchbar" role=button class=search-icon tabindex=0></span><input id=search_input_react type=search placeholder=Loading... aria-label=Search class="navbar__search-input search-bar" disabled></div></div></div></div><div role=presentation class=navbar-sidebar__backdrop></div></nav><div id=__docusaurus_skipToContent_fallback class="main-wrapper mainWrapper_z2l0"><div class="container margin-vert--lg"><div class=row><aside class="col col--3"><nav class="sidebar_re4s thin-scrollbar" aria-label="Blog recent posts navigation"><div class="sidebarItemTitle_pO2u margin-bottom--md">All posts</div><div role=group><h3>2025</h3><div role=group><h4>Playing with fire</h4><div role=group style=padding-inline-start:1.5em><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a aria-current=page class="sidebarItemLink_mo7H sidebarItemLinkActive_I1ZP" href=/2025/03/playing-with-fire-camera/>The camera</a></ul></div></div></div><div role=group><h3>2024</h3><div role=group><h4>Playing with fire</h4><div role=group style=padding-inline-start:1.5em><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2024/11/playing-with-fire/>The fractal flame algorithm</a><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2024/11/playing-with-fire-transforms/>Transforms and variations</a><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2024/11/playing-with-fire-log-density/>Tone mapping and color</a></ul></div></div></div><div role=group><h3>2022</h3><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2011/11/webpack-industrial-complex/>The webpack industrial complex</a></ul></div><div role=group><h3>2019</h3><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2019/12/release-the-gil/>Release the GIL</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2019/09/binary-format-shootout/>Binary format shootout</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2019/06/high-performance-systems/>On building high performance systems</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2019/05/making-bread/>Making bread</a></ul><div role=group><h4>Allocations in Rust</h4><div role=group style=padding-inline-start:1.5em><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2019/02/understanding-allocations-in-rust/>Foreword</a><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2019/02/the-whole-world/>Global memory</a><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2019/02/stacking-up/>Fixed memory</a><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2019/02/a-heaping-helping/>Dynamic memory</a><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2019/02/08/compiler-optimizations/>Compiler optimizations</a><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2019/02/summary/>Summary</a></ul></div></div></div><div role=group><h3>2018</h3><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2018/12/allocation-safety/>QADAPT - debug_assert! for allocations</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2018/12/what-small-business-really-means/>More "what companies really mean"</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2018/10/case-study-optimization/>A case study in heaptrack</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2018/09/isomorphic-apps/>Isomorphic desktop apps with Rust</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2018/09/primitives-in-rust-are-weird/>Primitives in Rust are weird (and cool)</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2018/06/dateutil-parser-to-rust/>What I learned porting dateutil to Rust</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2018/05/hello/>Hello!</a></ul><div role=group><h4>Captain's Cookbook</h4><div role=group style=padding-inline-start:1.5em><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2018/01/captains-cookbook-part-1/>Project setup</a><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2018/01/captains-cookbook-part-2/>Practical usage</a></ul></div></div></div><div role=group><h3>2016</h3><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/11/pca-audio-compression/>PCA audio compression</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/10/rustic-repodcasting/>A Rustic re-podcasting server</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/06/event-studies-and-earnings-releases/>Event studies and earnings releases</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/05/the-unfair-casino/>The unfair casino</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/04/tick-tock/>Tick tock...</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/03/tweet-like-me/>Tweet like me</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/03/predicting-santander-customer-happiness/>Predicting Santander customer happiness</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/02/profitability-using-the-investment-formula/>Profitability using the investment formula</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/02/guaranteed-money-maker/>Guaranteed money maker</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/01/cloudy-in-seattle/>Cloudy in Seattle</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/01/complaining-about-the-weather/>Complaining about the weather</a></ul></div><div role=group><h3>2015</h3><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2015/12/testing-cramer/>Testing Cramer</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2015/11/autocallable/>Autocallable Bonds</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2015/11/welcome/>Welcome, and an algorithm</a></ul></div></nav></aside><main class="col col--7"><article class=""><header><h1 class=title_f1Hy>Playing with fire: The camera</h1><div class="container_mt6G margin-vert--md"><time datetime=2025-03-10T12:00:00.000Z>March 10, 2025</time> · <!-- -->6 min read</div><div class="margin-top--md margin-bottom--sm row"><div class="col col--12 authorCol_Hf19"><div class="avatar margin-bottom--sm"><div class="avatar__intro authorDetails_lV9A"><div class=avatar__name><span class=authorName_yefp>Bradlee Speice</span></div><div class=authorSocials_rSDt><a href=https://github.com/bspeice target=_blank rel="noopener noreferrer" class=authorSocialLink_owbf title=GitHub><svg xmlns=http://www.w3.org/2000/svg width=1em height=1em viewBox="0 0 256 250" preserveAspectRatio=xMidYMid style=--dark:#000;--light:#fff class="authorSocialLink_owbf githubSvg_Uu4N"><path d="M128.001 0C57.317 0 0 57.307 0 128.001c0 56.554 36.676 104.535 87.535 121.46 6.397 1.185 8.746-2.777 8.746-6.158 0-3.052-.12-13.135-.174-23.83-35.61 7.742-43.124-15.103-43.124-15.103-5.823-14.795-14.213-18.73-14.213-18.73-11.613-7.944.876-7.78.876-7.78 12.853.902 19.621 13.19 19.621 13.19 11.417 19.568 29.945 13.911 37.249 10.64 1.149-8.272 4.466-13.92 8.127-17.116-28.431-3.236-58.318-14.212-58.318-63.258 0-13.975 5-25.394 13.188-34.358-1.329-3.224-5.71-16.242 1.24-33.874 0 0 10.749-3.44 35.21 13.121 10.21-2.836 21.16-4.258 32.038-4.307 10.878.049 21.837 1.47 32.066 4.307 24.431-16.56 35.165-13.12 35.165-13.12 6.967 17.63 2.584 30.65 1.255 33.873 8.207 8.964 13.173 20.383 13.173 34.358 0 49.163-29.944 59.988-58.447 63.157 4.591 3.972 8.682 11.762 8.682 23.704 0 17.126-.148 30.91-.148 35.126 0 3.407 2.304 7.398 8.792 6.14C219.37 232.5 256 184.537 256 128.002 256 57.307 198.691 0 128.001 0Zm-80.06 182.34c-.282.636-1.283.827-2.194.39-.929-.417-1.45-1.284-1.15-1.922.276-.655 1.279-.838 2.205-.399.93.418 1.46 1.293 1.139 1.931Zm6.296 5.618c-.61.566-1.804.303-2.614-.591-.837-.892-.994-2.086-.375-2.66.63-.566 1.787-.301 2.626.591.838.903 1 2.088.363 2.66Zm4.32 7.188c-.785.545-2.067.034-2.86-1.104-.784-1.138-.784-2.503.017-3.05.795-.547 2.058-.055 2.861 1.075.782 1.157.782 2.522-.019 3.08Zm7.304 8.325c-.701.774-2.196.566-3.29-.49-1.119-1.032-1.43-2.496-.726-3.27.71-.776 2.213-.558 3.315.49 1.11 1.03 1.45 2.505.701 3.27Zm9.442 2.81c-.31 1.003-1.75 1.459-3.199 1.033-1.448-.439-2.395-1.613-2.103-2.626.301-1.01 1.747-1.484 3.207-1.028 1.446.436 2.396 1.602 2.095 2.622Zm10.744 1.193c.036 1.055-1.193 1.93-2.715 1.95-1.53.034-2.769-.82-2.786-1.86 0-1.065 1.202-1.932 2.733-1.958 1.522-.03 2.768.818 2.768 1.868Zm10.555-.405c.182 1.03-.875 2.088-2.387 2.37-1.485.271-2.861-.365-3.05-1.386-.184-1.056.893-2.114 2.376-2.387 1.514-.263 2.868.356 3.061 1.403Z"/></svg></a></div></div></div></div></div></header><div id=__blog-post-container class=markdown><p>Something that bugged me while writing the first three articles on fractal flames were the constraints on
|
||
output images. At the time, I had worked out how to render fractal flames by studying
|
||
<a href=https://sourceforge.net/projects/apophysis/ target=_blank rel="noopener noreferrer">Apophysis</a> and <a href=https://github.com/scottdraves/flam3 target=_blank rel="noopener noreferrer">flam3</a>; just enough to display images
|
||
in a browser.</p>
|
||
<p>Having spent more time with fractal flames and computer graphics, it's time to implement
|
||
some missing features.</p>
|
||
<h2 class="anchor anchorWithStickyNavbar_LWe7" id=restrictions>Restrictions<a href=#restrictions class=hash-link aria-label="Direct link to Restrictions" title="Direct link to Restrictions"></a></h2>
|
||
<p>To review, the restrictions we've had so far:</p>
|
||
<blockquote>
|
||
<p>...we need to convert from fractal flame coordinates to pixel coordinates.
|
||
To simplify things, we'll assume that we're plotting a square image with range <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mo stretchy=false>[</mo><mn>0</mn><mo separator=true>,</mo><mn>1</mn><mo stretchy=false>]</mo></mrow><annotation encoding=application/x-tex>[0,1]</annotation></semantics></math></span><span class=katex-html aria-hidden=true><span class=base><span class=strut style=height:1em;vertical-align:-0.25em></span><span class=mopen>[</span><span class=mord>0</span><span class=mpunct>,</span><span class=mspace style=margin-right:0.1667em></span><span class=mord>1</span><span class=mclose>]</span></span></span></span> for both x and y</p>
|
||
<p>-- <a href=/2024/11/playing-with-fire/>The fractal flame algorithm</a></p>
|
||
</blockquote>
|
||
<p>First, we've assumed that fractals get displayed in a square image. Ignoring aspect ratios simplifies
|
||
the render process, but we don't usually want square images. It's possible to render a large
|
||
square image and crop it to fit, but we'd rather render directly to the desired size.</p>
|
||
<p>Second, we've assumed that fractals have a pre-determined display range. For Sierpinski's Gasket,
|
||
that was <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mo stretchy=false>[</mo><mn>0</mn><mo separator=true>,</mo><mn>1</mn><mo stretchy=false>]</mo></mrow><annotation encoding=application/x-tex>[0, 1]</annotation></semantics></math></span><span class=katex-html aria-hidden=true><span class=base><span class=strut style=height:1em;vertical-align:-0.25em></span><span class=mopen>[</span><span class=mord>0</span><span class=mpunct>,</span><span class=mspace style=margin-right:0.1667em></span><span class=mord>1</span><span class=mclose>]</span></span></span></span> (the reference parameters used a range of <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mo stretchy=false>[</mo><mo>−</mo><mn>2</mn><mo separator=true>,</mo><mn>2</mn><mo stretchy=false>]</mo></mrow><annotation encoding=application/x-tex>[-2, 2]</annotation></semantics></math></span><span class=katex-html aria-hidden=true><span class=base><span class=strut style=height:1em;vertical-align:-0.25em></span><span class=mopen>[</span><span class=mord>−</span><span class=mord>2</span><span class=mpunct>,</span><span class=mspace style=margin-right:0.1667em></span><span class=mord>2</span><span class=mclose>]</span></span></span></span>). However, if we could
|
||
control the display range, it would let us zoom in and out of the image.</p>
|
||
<h2 class="anchor anchorWithStickyNavbar_LWe7" id=parameters>Parameters<a href=#parameters class=hash-link aria-label="Direct link to Parameters" title="Direct link to Parameters"></a></h2>
|
||
<p>For comparison, the camera controls available in Apophysis offer a lot of flexibility:</p>
|
||
<center><img decoding=async loading=lazy alt="Screenshot of Apophysis camera controls" src=/assets/images/camera-controls-9acf6770c681d143ce91123adec4924c.png width=390 height=293 class=img_ev3q></center>
|
||
<p>The remaining parameters to implement are: position (X and Y), rotation, zoom, and scale.</p>
|
||
<h3 class="anchor anchorWithStickyNavbar_LWe7" id=position>Position<a href=#position class=hash-link aria-label="Direct link to Position" title="Direct link to Position"></a></h3>
|
||
<p>Fractal flames normally use <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mo stretchy=false>(</mo><mn>0</mn><mo separator=true>,</mo><mn>0</mn><mo stretchy=false>)</mo></mrow><annotation encoding=application/x-tex>(0, 0)</annotation></semantics></math></span><span class=katex-html aria-hidden=true><span class=base><span class=strut style=height:1em;vertical-align:-0.25em></span><span class=mopen>(</span><span class=mord>0</span><span class=mpunct>,</span><span class=mspace style=margin-right:0.1667em></span><span class=mord>0</span><span class=mclose>)</span></span></span></span> as the image center. The position parameters (X and Y) move
|
||
the center point, which effectively pans the image. A positive X position shifts left, and a
|
||
negative X position shifts right. Similarly, a positive Y position shifts up, and a negative
|
||
Y position shifts the image down.</p>
|
||
<p>To apply the position parameters, simply subtract them from each point in the chaos game prior to plotting it:</p>
|
||
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain">x</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> y</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token plain"> </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> x </span><span class="token operator" style="color:hsl(221, 87%, 60%)">-</span><span class="token plain"> positionX</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> y </span><span class="token operator" style="color:hsl(221, 87%, 60%)">-</span><span class="token plain"> positionY</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24" class=copyButtonIcon_y97N><path fill=currentColor d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/></svg><svg viewBox="0 0 24 24" class=copyButtonSuccessIcon_LjdS><path fill=currentColor d=M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z /></svg></span></button></div></div></div>
|
||
<h3 class="anchor anchorWithStickyNavbar_LWe7" id=rotation>Rotation<a href=#rotation class=hash-link aria-label="Direct link to Rotation" title="Direct link to Rotation"></a></h3>
|
||
<p>After the position parameters, we can rotate the image around the (new) center point. To do so, we'll go back to the
|
||
<a href=https://en.wikipedia.org/wiki/Affine_transformation target=_blank rel="noopener noreferrer">affine transformations</a> we've been using so far.
|
||
Specifically, the rotation angle <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi>θ</mi></mrow><annotation encoding=application/x-tex>\theta</annotation></semantics></math></span><span class=katex-html aria-hidden=true><span class=base><span class=strut style=height:0.6944em></span><span class="mord mathnormal" style=margin-right:0.02778em>θ</span></span></span></span> gives us a transform matrix we can apply prior to plotting:</p>
|
||
<span class=katex-display><span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML display=block><semantics><mrow><mrow><mo fence=true>[</mo><mtable rowspacing=0.16em columnalign="center center" columnspacing=1em><mtr><mtd><mstyle scriptlevel=0 displaystyle=false><mrow><mtext>cos</mtext><mo stretchy=false>(</mo><mi>θ</mi><mo stretchy=false>)</mo></mrow></mstyle></mtd><mtd><mstyle scriptlevel=0 displaystyle=false><mrow><mo>−</mo><mtext>sin</mtext><mo stretchy=false>(</mo><mi>θ</mi><mo stretchy=false>)</mo></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel=0 displaystyle=false><mrow><mtext>sin</mtext><mo stretchy=false>(</mo><mi>θ</mi><mo stretchy=false>)</mo></mrow></mstyle></mtd><mtd><mstyle scriptlevel=0 displaystyle=false><mrow><mtext>cos</mtext><mo stretchy=false>(</mo><mi>θ</mi><mo stretchy=false>)</mo></mrow></mstyle></mtd></mtr></mtable><mo fence=true>]</mo></mrow><mrow><mo fence=true>[</mo><mtable rowspacing=0.16em columnalign=center columnspacing=1em><mtr><mtd><mstyle scriptlevel=0 displaystyle=false><mi>x</mi></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel=0 displaystyle=false><mi>y</mi></mstyle></mtd></mtr></mtable><mo fence=true>]</mo></mrow></mrow><annotation encoding=application/x-tex>\begin{bmatrix}
|
||
\text{cos}(\theta) & -\text{sin}(\theta) \\
|
||
\text{sin}(\theta) & \text{cos}(\theta)
|
||
\end{bmatrix}
|
||
|
||
\begin{bmatrix}
|
||
x \\
|
||
y
|
||
\end{bmatrix}</annotation></semantics></math></span><span class=katex-html aria-hidden=true><span class=base><span class=strut style=height:2.4em;vertical-align:-0.95em></span><span class=minner><span class="mopen delimcenter" style=top:0em><span class="delimsizing size3">[</span></span><span class=mord><span class=mtable><span class=col-align-c><span class="vlist-t vlist-t2"><span class=vlist-r><span class=vlist style=height:1.45em><span style=top:-3.61em><span class=pstrut style=height:3em></span><span class=mord><span class="mord text"><span class=mord>cos</span></span><span class=mopen>(</span><span class="mord mathnormal" style=margin-right:0.02778em>θ</span><span class=mclose>)</span></span></span><span style=top:-2.41em><span class=pstrut style=height:3em></span><span class=mord><span class="mord text"><span class=mord>sin</span></span><span class=mopen>(</span><span class="mord mathnormal" style=margin-right:0.02778em>θ</span><span class=mclose>)</span></span></span></span><span class=vlist-s></span></span><span class=vlist-r><span class=vlist style=height:0.95em><span></span></span></span></span></span><span class=arraycolsep style=width:0.5em></span><span class=arraycolsep style=width:0.5em></span><span class=col-align-c><span class="vlist-t vlist-t2"><span class=vlist-r><span class=vlist style=height:1.45em><span style=top:-3.61em><span class=pstrut style=height:3em></span><span class=mord><span class=mord>−</span><span class="mord text"><span class=mord>sin</span></span><span class=mopen>(</span><span class="mord mathnormal" style=margin-right:0.02778em>θ</span><span class=mclose>)</span></span></span><span style=top:-2.41em><span class=pstrut style=height:3em></span><span class=mord><span class="mord text"><span class=mord>cos</span></span><span class=mopen>(</span><span class="mord mathnormal" style=margin-right:0.02778em>θ</span><span class=mclose>)</span></span></span></span><span class=vlist-s></span></span><span class=vlist-r><span class=vlist style=height:0.95em><span></span></span></span></span></span></span></span><span class="mclose delimcenter" style=top:0em><span class="delimsizing size3">]</span></span></span><span class=mspace style=margin-right:0.1667em></span><span class=minner><span class="mopen delimcenter" style=top:0em><span class="delimsizing size3">[</span></span><span class=mord><span class=mtable><span class=col-align-c><span class="vlist-t vlist-t2"><span class=vlist-r><span class=vlist style=height:1.45em><span style=top:-3.61em><span class=pstrut style=height:3em></span><span class=mord><span class="mord mathnormal">x</span></span></span><span style=top:-2.41em><span class=pstrut style=height:3em></span><span class=mord><span class="mord mathnormal" style=margin-right:0.03588em>y</span></span></span></span><span class=vlist-s></span></span><span class=vlist-r><span class=vlist style=height:0.95em><span></span></span></span></span></span></span></span><span class="mclose delimcenter" style=top:0em><span class="delimsizing size3">]</span></span></span></span></span></span></span>
|
||
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain">x</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> y</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token plain"> </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> x </span><span class="token operator" style="color:hsl(221, 87%, 60%)">*</span><span class="token plain"> Math</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">cos</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token operator" style="color:hsl(221, 87%, 60%)">-</span><span class="token plain">rotate</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"> </span><span class="token operator" style="color:hsl(221, 87%, 60%)">-</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> y </span><span class="token operator" style="color:hsl(221, 87%, 60%)">*</span><span class="token plain"> Math</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">sin</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token operator" style="color:hsl(221, 87%, 60%)">-</span><span class="token plain">rotate</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> x </span><span class="token operator" style="color:hsl(221, 87%, 60%)">*</span><span class="token plain"> Math</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">sin</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token operator" style="color:hsl(221, 87%, 60%)">-</span><span class="token plain">rotate</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"> </span><span class="token operator" style="color:hsl(221, 87%, 60%)">+</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> y </span><span class="token operator" style="color:hsl(221, 87%, 60%)">*</span><span class="token plain"> Math</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">cos</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token operator" style="color:hsl(221, 87%, 60%)">-</span><span class="token plain">rotate</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24" class=copyButtonIcon_y97N><path fill=currentColor d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/></svg><svg viewBox="0 0 24 24" class=copyButtonSuccessIcon_LjdS><path fill=currentColor d=M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z /></svg></span></button></div></div></div>
|
||
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class=admonitionHeading_Gvgb><span class=admonitionIcon_Rf37><svg viewBox="0 0 14 16"><path fill-rule=evenodd d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"/></svg></span>note</div><div class=admonitionContent_BuS1><p>To match the behavior of Apophysis/<code>flam3</code>, we need to negate the rotation angle.</div></div>
|
||
<h3 class="anchor anchorWithStickyNavbar_LWe7" id=zoom>Zoom<a href=#zoom class=hash-link aria-label="Direct link to Zoom" title="Direct link to Zoom"></a></h3>
|
||
<p>This parameter does what the name implies; zoom in and out of the image. To do this, we multiply
|
||
the X and Y coordinates of each point by a zoom factor. For a zoom parameter <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mi>z</mi></mrow><annotation encoding=application/x-tex>z</annotation></semantics></math></span><span class=katex-html aria-hidden=true><span class=base><span class=strut style=height:0.4306em></span><span class="mord mathnormal" style=margin-right:0.04398em>z</span></span></span></span>, the zoom factor
|
||
will be <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mtext>pow</mtext><mo stretchy=false>(</mo><mn>2</mn><mo separator=true>,</mo><mi>z</mi><mo stretchy=false>)</mo></mrow><annotation encoding=application/x-tex>\text{pow}(2, z)</annotation></semantics></math></span><span class=katex-html aria-hidden=true><span class=base><span class=strut style=height:1em;vertical-align:-0.25em></span><span class="mord text"><span class=mord>pow</span></span><span class=mopen>(</span><span class=mord>2</span><span class=mpunct>,</span><span class=mspace style=margin-right:0.1667em></span><span class="mord mathnormal" style=margin-right:0.04398em>z</span><span class=mclose>)</span></span></span></span>.</p>
|
||
<p>For example, if the current point is <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mo stretchy=false>(</mo><mn>1</mn><mo separator=true>,</mo><mn>1</mn><mo stretchy=false>)</mo></mrow><annotation encoding=application/x-tex>(1, 1)</annotation></semantics></math></span><span class=katex-html aria-hidden=true><span class=base><span class=strut style=height:1em;vertical-align:-0.25em></span><span class=mopen>(</span><span class=mord>1</span><span class=mpunct>,</span><span class=mspace style=margin-right:0.1667em></span><span class=mord>1</span><span class=mclose>)</span></span></span></span>, a zoom parameter of 1 means we actually plot <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mo stretchy=false>(</mo><mn>1</mn><mo separator=true>,</mo><mn>1</mn><mo stretchy=false>)</mo><mo>⋅</mo><mtext>pow</mtext><mo stretchy=false>(</mo><mn>2</mn><mo separator=true>,</mo><mn>1</mn><mo stretchy=false>)</mo><mo>=</mo><mo stretchy=false>(</mo><mn>2</mn><mo separator=true>,</mo><mn>2</mn><mo stretchy=false>)</mo></mrow><annotation encoding=application/x-tex>(1, 1) \cdot \text{pow}(2, 1) = (2, 2)</annotation></semantics></math></span><span class=katex-html aria-hidden=true><span class=base><span class=strut style=height:1em;vertical-align:-0.25em></span><span class=mopen>(</span><span class=mord>1</span><span class=mpunct>,</span><span class=mspace style=margin-right:0.1667em></span><span class=mord>1</span><span class=mclose>)</span><span class=mspace style=margin-right:0.2222em></span><span class=mbin>⋅</span><span class=mspace style=margin-right:0.2222em></span></span><span class=base><span class=strut style=height:1em;vertical-align:-0.25em></span><span class="mord text"><span class=mord>pow</span></span><span class=mopen>(</span><span class=mord>2</span><span class=mpunct>,</span><span class=mspace style=margin-right:0.1667em></span><span class=mord>1</span><span class=mclose>)</span><span class=mspace style=margin-right:0.2778em></span><span class=mrel>=</span><span class=mspace style=margin-right:0.2778em></span></span><span class=base><span class=strut style=height:1em;vertical-align:-0.25em></span><span class=mopen>(</span><span class=mord>2</span><span class=mpunct>,</span><span class=mspace style=margin-right:0.1667em></span><span class=mord>2</span><span class=mclose>)</span></span></span></span>.</p>
|
||
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">[x, y] = [</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> x * Math.pow(2, zoom),</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> y * Math.pow(2, zoom)</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">];</span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24" class=copyButtonIcon_y97N><path fill=currentColor d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/></svg><svg viewBox="0 0 24 24" class=copyButtonSuccessIcon_LjdS><path fill=currentColor d=M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z /></svg></span></button></div></div></div>
|
||
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class=admonitionHeading_Gvgb><span class=admonitionIcon_Rf37><svg viewBox="0 0 14 16"><path fill-rule=evenodd d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"/></svg></span>info</div><div class=admonitionContent_BuS1><p>In addition to scaling the image, renderers also <a href=https://github.com/scottdraves/flam3/blob/f8b6c782012e4d922ef2cc2f0c2686b612c32504/rect.c#L796-L797 target=_blank rel="noopener noreferrer">scale the image quality</a>
|
||
to compensate for the reduced display range.</div></div>
|
||
<h3 class="anchor anchorWithStickyNavbar_LWe7" id=scale>Scale<a href=#scale class=hash-link aria-label="Direct link to Scale" title="Direct link to Scale"></a></h3>
|
||
<p>Finally, we need to convert from fractal flame coordinates to individual pixels. The scale parameter defines
|
||
how many pixels are in one unit of the fractal flame coordinate system, which gives us a mapping from one system
|
||
to the other.</p>
|
||
<p>If you open the <a href=/assets/files/params-79404da2c6b544cbaf223f19db41fabf.flame/ target=_blank>reference parameters</a> in a text editor, you'll see the following:</p>
|
||
<div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-xml codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token tag punctuation" style="color:hsl(119, 34%, 47%)"><</span><span class="token tag" style="color:hsl(5, 74%, 59%)">flame</span><span class="token tag" style="color:hsl(5, 74%, 59%)"> </span><span class="token tag attr-name" style="color:hsl(35, 99%, 36%)">name</span><span class="token tag attr-value punctuation attr-equals" style="color:hsl(119, 34%, 47%)">=</span><span class="token tag attr-value punctuation" style="color:hsl(119, 34%, 47%)">"</span><span class="token tag attr-value" style="color:hsl(119, 34%, 47%)">final xform</span><span class="token tag attr-value punctuation" style="color:hsl(119, 34%, 47%)">"</span><span class="token tag" style="color:hsl(5, 74%, 59%)"> </span><span class="token tag attr-name" style="color:hsl(35, 99%, 36%)">size</span><span class="token tag attr-value punctuation attr-equals" style="color:hsl(119, 34%, 47%)">=</span><span class="token tag attr-value punctuation" style="color:hsl(119, 34%, 47%)">"</span><span class="token tag attr-value" style="color:hsl(119, 34%, 47%)">600 600</span><span class="token tag attr-value punctuation" style="color:hsl(119, 34%, 47%)">"</span><span class="token tag" style="color:hsl(5, 74%, 59%)"> </span><span class="token tag attr-name" style="color:hsl(35, 99%, 36%)">center</span><span class="token tag attr-value punctuation attr-equals" style="color:hsl(119, 34%, 47%)">=</span><span class="token tag attr-value punctuation" style="color:hsl(119, 34%, 47%)">"</span><span class="token tag attr-value" style="color:hsl(119, 34%, 47%)">0 0</span><span class="token tag attr-value punctuation" style="color:hsl(119, 34%, 47%)">"</span><span class="token tag" style="color:hsl(5, 74%, 59%)"> </span><span class="token tag attr-name" style="color:hsl(35, 99%, 36%)">scale</span><span class="token tag attr-value punctuation attr-equals" style="color:hsl(119, 34%, 47%)">=</span><span class="token tag attr-value punctuation" style="color:hsl(119, 34%, 47%)">"</span><span class="token tag attr-value" style="color:hsl(119, 34%, 47%)">150</span><span class="token tag attr-value punctuation" style="color:hsl(119, 34%, 47%)">"</span><span class="token tag punctuation" style="color:hsl(119, 34%, 47%)">></span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24" class=copyButtonIcon_y97N><path fill=currentColor d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/></svg><svg viewBox="0 0 24 24" class=copyButtonSuccessIcon_LjdS><path fill=currentColor d=M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z /></svg></span></button></div></div></div>
|
||
<p>Here's what each element means:</p>
|
||
<ul>
|
||
<li><code>size="600 600"</code>: The image should be 600 pixels wide and 600 pixels tall</li>
|
||
<li><code>center="0 0"</code>: The image is centered at the point <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mo stretchy=false>(</mo><mn>0</mn><mo separator=true>,</mo><mn>0</mn><mo stretchy=false>)</mo></mrow><annotation encoding=application/x-tex>(0, 0)</annotation></semantics></math></span><span class=katex-html aria-hidden=true><span class=base><span class=strut style=height:1em;vertical-align:-0.25em></span><span class=mopen>(</span><span class=mord>0</span><span class=mpunct>,</span><span class=mspace style=margin-right:0.1667em></span><span class=mord>0</span><span class=mclose>)</span></span></span></span></li>
|
||
<li><code>scale="150"</code>: The image has 150 pixels per unit</li>
|
||
</ul>
|
||
<p>Let's break it down. Dividing the image width (600) by the image scale (150) gives us a value of 4.
|
||
This means the image should be 4 units wide (same for the height). Because the center is at <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mo stretchy=false>(</mo><mn>0</mn><mo separator=true>,</mo><mn>0</mn><mo stretchy=false>)</mo></mrow><annotation encoding=application/x-tex>(0, 0)</annotation></semantics></math></span><span class=katex-html aria-hidden=true><span class=base><span class=strut style=height:1em;vertical-align:-0.25em></span><span class=mopen>(</span><span class=mord>0</span><span class=mpunct>,</span><span class=mspace style=margin-right:0.1667em></span><span class=mord>0</span><span class=mclose>)</span></span></span></span>,
|
||
the final image is effectively using the range <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mo stretchy=false>[</mo><mo>−</mo><mn>2</mn><mo separator=true>,</mo><mn>2</mn><mo stretchy=false>]</mo></mrow><annotation encoding=application/x-tex>[-2, 2]</annotation></semantics></math></span><span class=katex-html aria-hidden=true><span class=base><span class=strut style=height:1em;vertical-align:-0.25em></span><span class=mopen>[</span><span class=mord>−</span><span class=mord>2</span><span class=mpunct>,</span><span class=mspace style=margin-right:0.1667em></span><span class=mord>2</span><span class=mclose>]</span></span></span></span> in fractal coordinates.</p>
|
||
<p>Now, to go from fractal coordinates to pixel coordinates we multiply by the scale,
|
||
then subtract half the image width and height:</p>
|
||
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain">pixelX</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> pixelY</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token plain"> </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> x </span><span class="token operator" style="color:hsl(221, 87%, 60%)">*</span><span class="token plain"> scale </span><span class="token operator" style="color:hsl(221, 87%, 60%)">-</span><span class="token plain"> imageWidth </span><span class="token operator" style="color:hsl(221, 87%, 60%)">/</span><span class="token plain"> </span><span class="token number" style="color:hsl(35, 99%, 36%)">2</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> y </span><span class="token operator" style="color:hsl(221, 87%, 60%)">*</span><span class="token plain"> scale </span><span class="token operator" style="color:hsl(221, 87%, 60%)">-</span><span class="token plain"> imageHeight </span><span class="token operator" style="color:hsl(221, 87%, 60%)">/</span><span class="token plain"> </span><span class="token number" style="color:hsl(35, 99%, 36%)">2</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24" class=copyButtonIcon_y97N><path fill=currentColor d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/></svg><svg viewBox="0 0 24 24" class=copyButtonSuccessIcon_LjdS><path fill=currentColor d=M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z /></svg></span></button></div></div></div>
|
||
<p>Scale and zoom have similar effects on images. If the reference parameters used <code>scale="300"</code>,
|
||
the same 600 pixels would instead be looking at the range <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mo stretchy=false>[</mo><mo>−</mo><mn>1</mn><mo separator=true>,</mo><mn>1</mn><mo stretchy=false>]</mo></mrow><annotation encoding=application/x-tex>[-1, 1]</annotation></semantics></math></span><span class=katex-html aria-hidden=true><span class=base><span class=strut style=height:1em;vertical-align:-0.25em></span><span class=mopen>[</span><span class=mord>−</span><span class=mord>1</span><span class=mpunct>,</span><span class=mspace style=margin-right:0.1667em></span><span class=mord>1</span><span class=mclose>]</span></span></span></span> in the fractal coordinate system.
|
||
Using <code>zoom="1"</code> would accomplish the same result.</p>
|
||
<p>However, this also demonstrates the biggest problem with using scale: it only controls the output image.
|
||
For example, if the output image changed to <code>size="1200 1200"</code> and we kept <code>scale="150"</code>, it would
|
||
have a display range of <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mo stretchy=false>[</mo><mo>−</mo><mn>4</mn><mo separator=true>,</mo><mn>4</mn><mo stretchy=false>]</mo></mrow><annotation encoding=application/x-tex>[-4, 4]</annotation></semantics></math></span><span class=katex-html aria-hidden=true><span class=base><span class=strut style=height:1em;vertical-align:-0.25em></span><span class=mopen>[</span><span class=mord>−</span><span class=mord>4</span><span class=mpunct>,</span><span class=mspace style=margin-right:0.1667em></span><span class=mord>4</span><span class=mclose>]</span></span></span></span>. There would be a lot of extra white space. Because the zoom parameter
|
||
has the same effect regardless of output image size, it is the preferred way to zoom in and out.</p>
|
||
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class=admonitionHeading_Gvgb><span class=admonitionIcon_Rf37><svg viewBox="0 0 14 16"><path fill-rule=evenodd d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"/></svg></span>info</div><div class=admonitionContent_BuS1><p>One final note about the camera controls: every step in this process (position, rotation, zoom, scale)
|
||
is an affine transformation. And because affine transformations can be chained together, it's possible to
|
||
express all the camera controls as a single transformation matrix. This is important for software optimization;
|
||
rather than applying parameters step-by-step, we can apply all of them at once.<p>They could also be implemented as part of the final transform, but in practice, it's helpful
|
||
to control them separately.</div></div>
|
||
<h2 class="anchor anchorWithStickyNavbar_LWe7" id=camera>Camera<a href=#camera class=hash-link aria-label="Direct link to Camera" title="Direct link to Camera"></a></h2>
|
||
<p>With the individual steps defined, we can put together a more robust "camera" for viewing the fractal flame.</p>
|
||
<!-- -->
|
||
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token keyword" style="color:hsl(301, 63%, 40%)">export</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">function</span><span class="token plain"> </span><span class="token function" style="color:hsl(221, 87%, 60%)">camera</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> x</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token builtin" style="color:hsl(119, 34%, 47%)">number</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> y</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token builtin" style="color:hsl(119, 34%, 47%)">number</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> width</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token builtin" style="color:hsl(119, 34%, 47%)">number</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> height</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token builtin" style="color:hsl(119, 34%, 47%)">number</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> positionX</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token builtin" style="color:hsl(119, 34%, 47%)">number</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> positionY</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token builtin" style="color:hsl(119, 34%, 47%)">number</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> rotate</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token builtin" style="color:hsl(119, 34%, 47%)">number</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> zoom</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token builtin" style="color:hsl(119, 34%, 47%)">number</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> scale</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token builtin" style="color:hsl(119, 34%, 47%)">number</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token operator" style="color:hsl(221, 87%, 60%)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token builtin" style="color:hsl(119, 34%, 47%)">number</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> </span><span class="token builtin" style="color:hsl(119, 34%, 47%)">number</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">{</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token comment" style="color:hsl(230, 4%, 64%)">// Position, rotation, and zoom are</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token comment" style="color:hsl(230, 4%, 64%)">// applied in IFS coordinates</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain">x</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> y</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token plain"> </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">x </span><span class="token operator" style="color:hsl(221, 87%, 60%)">-</span><span class="token plain"> positionX</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">y </span><span class="token operator" style="color:hsl(221, 87%, 60%)">-</span><span class="token plain"> positionY</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain">x</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> y</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token plain"> </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> x </span><span class="token operator" style="color:hsl(221, 87%, 60%)">*</span><span class="token plain"> Math</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">cos</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">rotate</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"> </span><span class="token operator" style="color:hsl(221, 87%, 60%)">-</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> y </span><span class="token operator" style="color:hsl(221, 87%, 60%)">*</span><span class="token plain"> Math</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">sin</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">rotate</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> x </span><span class="token operator" style="color:hsl(221, 87%, 60%)">*</span><span class="token plain"> Math</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">sin</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">rotate</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"> </span><span class="token operator" style="color:hsl(221, 87%, 60%)">+</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> y </span><span class="token operator" style="color:hsl(221, 87%, 60%)">*</span><span class="token plain"> Math</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">cos</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">rotate</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain">x</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> y</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token plain"> </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> x </span><span class="token operator" style="color:hsl(221, 87%, 60%)">*</span><span class="token plain"> Math</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">pow</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token number" style="color:hsl(35, 99%, 36%)">2</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> zoom</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> y </span><span class="token operator" style="color:hsl(221, 87%, 60%)">*</span><span class="token plain"> Math</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">pow</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token number" style="color:hsl(35, 99%, 36%)">2</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> zoom</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token comment" style="color:hsl(230, 4%, 64%)">// Scale transforms IFS coordinates</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token comment" style="color:hsl(230, 4%, 64%)">// to pixel coordinates. Shift by half</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token comment" style="color:hsl(230, 4%, 64%)">// the image width and height</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token comment" style="color:hsl(230, 4%, 64%)">// to compensate for IFS coordinates</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token comment" style="color:hsl(230, 4%, 64%)">// being symmetric around the origin</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">return</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> Math</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">floor</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">x </span><span class="token operator" style="color:hsl(221, 87%, 60%)">*</span><span class="token plain"> scale </span><span class="token operator" style="color:hsl(221, 87%, 60%)">+</span><span class="token plain"> width </span><span class="token operator" style="color:hsl(221, 87%, 60%)">/</span><span class="token plain"> </span><span class="token number" style="color:hsl(35, 99%, 36%)">2</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> Math</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token function" style="color:hsl(221, 87%, 60%)">floor</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">y </span><span class="token operator" style="color:hsl(221, 87%, 60%)">*</span><span class="token plain"> scale </span><span class="token operator" style="color:hsl(221, 87%, 60%)">+</span><span class="token plain"> height </span><span class="token operator" style="color:hsl(221, 87%, 60%)">/</span><span class="token plain"> </span><span class="token number" style="color:hsl(35, 99%, 36%)">2</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">}</span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24" class=copyButtonIcon_y97N><path fill=currentColor d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/></svg><svg viewBox="0 0 24 24" class=copyButtonSuccessIcon_LjdS><path fill=currentColor d=M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z /></svg></span></button></div></div></div>
|
||
<p>To demonstrate, this display has a 4:3 aspect ratio, removing the restriction of a square image.
|
||
In addition, the scale is automatically chosen so the image width covers the range <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mo stretchy=false>[</mo><mo>−</mo><mn>2</mn><mo separator=true>,</mo><mn>2</mn><mo stretchy=false>]</mo></mrow><annotation encoding=application/x-tex>[-2, 2]</annotation></semantics></math></span><span class=katex-html aria-hidden=true><span class=base><span class=strut style=height:1em;vertical-align:-0.25em></span><span class=mopen>[</span><span class=mord>−</span><span class=mord>2</span><span class=mpunct>,</span><span class=mspace style=margin-right:0.1667em></span><span class=mord>2</span><span class=mclose>]</span></span></span></span>.
|
||
Because of the 4:3 aspect ratio, the image height now covers the range <span class=katex><span class=katex-mathml><math xmlns=http://www.w3.org/1998/Math/MathML><semantics><mrow><mo stretchy=false>[</mo><mo>−</mo><mn>1.5</mn><mo separator=true>,</mo><mn>1.5</mn><mo stretchy=false>]</mo></mrow><annotation encoding=application/x-tex>[-1.5, 1.5]</annotation></semantics></math></span><span class=katex-html aria-hidden=true><span class=base><span class=strut style=height:1em;vertical-align:-0.25em></span><span class=mopen>[</span><span class=mord>−</span><span class=mord>1.5</span><span class=mpunct>,</span><span class=mspace style=margin-right:0.1667em></span><span class=mord>1.5</span><span class=mclose>]</span></span></span></span>.</p>
|
||
<!-- -->
|
||
<center><center><div style=width:95%;aspect-ratio:4/3></div></center></center>
|
||
<h2 class="anchor anchorWithStickyNavbar_LWe7" id=summary>Summary<a href=#summary class=hash-link aria-label="Direct link to Summary" title="Direct link to Summary"></a></h2>
|
||
<p>The previous fractal images relied on assumptions about the output format to make sure they looked correct.
|
||
Now, we have much more control. We can implement a 2D "camera" as a series of affine transformations, going from
|
||
fractal flame coordinates to pixel coordinates.</p>
|
||
<p>More recent fractal flame renderers like <a href=http://fractorium.com/ target=_blank rel="noopener noreferrer">Fractorium</a> can also operate in 3D,
|
||
and have to implement a more complex camera system to handle the extra dimension.</p>
|
||
<p>But for this blog series, it's nice to achieve feature parity with the reference implementation.</div></article><nav class="pagination-nav docusaurus-mt-lg" aria-label="Blog post page navigation"><a class="pagination-nav__link pagination-nav__link--prev" href=/2024/11/playing-with-fire-log-density/><div class=pagination-nav__sublabel>Older post</div><div class=pagination-nav__label>Playing with fire: Tone mapping and color</div></a></nav></main><div class="col col--2"><div class="tableOfContents_bqdL thin-scrollbar"><ul class="table-of-contents table-of-contents__left-border"><li><a href=#restrictions class="table-of-contents__link toc-highlight">Restrictions</a><li><a href=#parameters class="table-of-contents__link toc-highlight">Parameters</a><ul><li><a href=#position class="table-of-contents__link toc-highlight">Position</a><li><a href=#rotation class="table-of-contents__link toc-highlight">Rotation</a><li><a href=#zoom class="table-of-contents__link toc-highlight">Zoom</a><li><a href=#scale class="table-of-contents__link toc-highlight">Scale</a></ul><li><a href=#camera class="table-of-contents__link toc-highlight">Camera</a><li><a href=#summary class="table-of-contents__link toc-highlight">Summary</a></ul></div></div></div></div></div><footer class=footer><div class="container container-fluid"><div class="footer__bottom text--center"><div class=footer__copyright>Copyright © 2025 Bradlee Speice</div></div></div></footer></div> |