Mandelbrot 세트 시각화 도구 구축
라피🤖
@ 헤이즐라피
기술적으로 알고리즘은 없고 mandelbrot 집합만!!!시각적 효과가 있다🤩🤩🤩
2020년 10월 27일 오후 22:43
Raphi가 트위터에서 건의한 바와 같이 이 글에서 저는 Mandelbrot 집합이 무엇인지, 그리고 어떻게 JavaScript와 canvas로 Mandelbrot 집합의 시각화 도구를 구축하는지 대충 설명할 것입니다.
만델브로 뭐야?
Mandelbrot 컬렉션.1980년 Beno ît Mandelbrot에서 정의/발견.이것은 분형으로 대체적으로 그것은 유사한 무한한 복잡한 구조라는 것을 의미한다.시각화할 때 다음과 같이 보입니다.
(Prateek Rungta에서 작성, CC 2.0에 따라 Flickr 검색
Mandelbrot 세트는 어떻게 정의됩니까?
Mandelbrot 세트는 복수의 집합입니다.
ccc 회사
이 교체에 대해 의견 차이가 발생하지 않습니다.
z0=0zn+1=zn2+c
z_0=0\n 줄 바꿈 문자
z{n+1}=z ^{2}{n}+c
z0=0zn+1=zn2+c
미적분이나 복수에 익숙하지 않은 사람들에게'발산'과'복수'의 의미를 빠르게 돌릴 것이다.
수렴과 발산 함수
미적분은 변화다.우리가 함수 (또는 하나의 급수 또는 무한화) 가 어떤 값에 가깝고 거의 그것에 도달했는지, 그러나 완전히 도달하지 않았을 때, 우리는 수렴 함수에 대해 토론했다.
함수가 발산될 때, 그것은 무한대로 불든지, 음무한대로 불든지.그림의 두 그림은 수렴 함수와 발산 함수를 동시에 보여 준다.
(세 번째 함수는 교체 함수입니다. 이 함수들은 값 사이에서 진동하지만 거기에 머무르지 않습니다.)
그렇다면 이 Mandelbrot 집합에 대한 정의는 무엇을 의미합니까?이것은 의미한다
zn+1z{n+1}zn+1
무한대나 마이너스로 팽창하지 않는다.
복수
모든 숫자(0, 1, -13, Pi, e, 네가 생각할 수 있는 것)는 한 줄의 숫자에 배열할 수 있다.
어떤 숫자든 이 선 어딘가에 있다.디지털 선은 1차원이다.복수는 2차원을 도입했다.이 새로운 차원은 복수의'허부'라고 불리고, 통상적인 수선은 이 수의'실부'라고 불린다.따라서 복수는 다음과 같습니다.
a+bia+bia+bi
aaa급
진실한 부분이고,
비교하다
허부
셋,
. 복수의 예는 다음과 같다.
12+6i12+6i12+6i
또는
−삼.−87i-3-87i−삼.−87i
. 따라서 수선은 다음과 같은 숫자 평면으로 변한다.
2+1i2+1i2+1i
):
복수에는 특수한 계산 규칙이 있다.우리는 덧셈과 곱셈이 어떻게 작동하는지 알아야 한다.우리가 왜 그런지 깊이 있게 연구하기 전에, 우리는 규칙을 살펴보고 그것들을 따를 뿐이다.
곱하기: (a+bi)∗(c+di)=(ac)−bd)+(ad+bc)a 추가: (a+bi)+(c+di)= (a+c)+(b+d)i
곱하기: (a+bi)* (c+di) = (ac-bd) + (ad+bc) i\newline
덧셈: (a+bi) + (c+di) = (a+c) + (b+d)i
곱하기: (a+bi)∗(c+di)=(ac)−bd)+(ad+bc)a 추가: (a+bi)+(c+di)= (a+c)+(b+d)i
또 다른 방주: 기본적으로 모든 숫자는 복수입니다.만약 그것들이 숫자선에 있다면, 허부 0으로 표시한다.예:
555
사실은
5+0i5+0i5+0i
따라서 복수는 X/Y 평면에 표시됩니다.각 숫자에 대해
X+YiX+YiX+YiX+Yi
우리는 그것이 Mandelbrot 집합에 속하는지 여부를 말할 수 있다.
복수 평면에서 Mandelbrot 세트에 속하는 점을 다른 색상으로 지정하면 피쳐 패턴이 나타납니다.
이런 지식이 있으면 우리는 시작할 수 있다!
우리 이거 이루자.
우리는 복수의 표시부터 시작한다.
class Complex {
constructor(real, imaginary) {
this.real = real
this.imaginary = imaginary
}
plus(other) {
return new Complex(
this.real + other.real,
this.imaginary + other.imaginary
)
}
times(other) {
return new Complex(
(this.real * other.real - this.imaginary * other.imaginary),
(this.real * other.imaginary + other.real * this.imaginary)
)
}
}
곱셈과 덧셈의 규칙은 지금 이미 거기에 있다.이제 이러한 복수 객체를 사용할 수 있습니다.
const x = new Complex(1, 2) // (1 + 2i)
const y = new Complex(3, -3) // (3 - 3i)
console.log(x.plus(y), x.times(y))
경탄했어이제 주어진 복수와 주어진 반복이 수렴되는지 확인하는 함수를 실현합시다.
/**
* Calculates n+1
*/
const iterate = (n, c) => n.times(n).plus(c)
/**
* Checks if a complex number `c` diverges according to the Mandelbrot definition.
*/
const doesDiverge = (c, maxIter) => {
let n = new Complex(0, 0)
for (let i = 0; i < maxIter; i++) {
n = iterate(n, c)
}
// If the iteration diverges, these values will be `NaN` quite fast. Around 50 iterations is usually needed.
return isNaN(n.real) || isNaN(n.imaginary)
}
지금 우리는 이 함수로 하여금 복수를 우리에게 알려줄 수 있다
ccc 회사
Mandelbrot 컬렉션에서 다음을 수행합니다.
!doesDiverge(new Complex(1, 1), 100) // false
!doesDiverge(new Complex(0, 0), 100) // true
시각화 구축
지금까지는 좋았어, 우리는 곧 도착할 거야.이제 Mandelbrot 세트를 시각화할 수 있습니다.또한 클릭 크기 조정 옵션도 추가됩니다.이를 위해 우리는 캔버스와 기타 몇 가지 요소를 사용할 것이다.
<!-- Used to control the zoom level etc. -->
<div class="controls">
<div>
Zoom size:
<input type="range" min="2" max="50" value="10" id="zoomsize">
</div>
<input type="button" id="reset" value="Reset">
</div>
<!-- A little box that shows what part of the Mandelbrot set will be shown on click -->
<div class="selector"></div>
<!-- The canvas we'll render the Mandelbrot set on -->
<canvas class="canvas" />
스타일링:
html, body {
margin: 0;
padding: 0;
height: 100%;
}
.controls {
position: fixed;
background-color: #f0f0f0;
z-index: 1000;
}
.selector {
border: 2px solid #000;
opacity: .2;
position: fixed;
z-index: 999;
transform: translate(-50%, -50%);
pointer-events: none;
}
.canvas {
width: 100%;
height: 100vh;
}
지금까지 줄곧 괜찮았다.JS 섹션으로 넘어가겠습니다.독립적이기 때문에 선택기 상자부터 시작합니다.
// Size of the zoom compared to current screen size
// i.e. 1/10th of the screen's width and height.
let zoomsize = 10
/**
* Makes the selector follow the mouse
*/
document.addEventListener('mousemove', event => {
const selector = document.querySelector('.selector')
selector.style.top = `${event.clientY}px`
selector.style.left = `${event.clientX}px`
selector.style.width = `${window.innerWidth / zoomsize}px`
selector.style.height = `${window.innerHeight / zoomsize}px`
})
/**
* Zoom size adjustment.
*/
document.querySelector('#zoomsize').addEventListener(
'change',
event => {
zoomsize = parseInt(event.target.value)
}
)
이제 사용자는 클릭할 때 Mandelbrot 집합의 어떤 부분을 볼 수 있는지 명확하게 표시할 수 있습니다.
현재 계획은 다음과 같다. 우리는 복면의 어느 부분이 보이는지 정의하고 이를 실제 픽셀에 비추었다.이를 위해서는 초기 상태 및 재설정 버튼이 필요합니다.
// X coordinate
const realInitial = {
from: -2,
to: 2,
}
// Y coordinate, keep the aspect ratio
const imagInitial = {
from: realInitial.from / window.innerWidth * window.innerHeight,
to: realInitial.to / window.innerWidth * window.innerHeight,
}
// Ranging from negative to positive - which part of the plane is visible right now?
let real = realInitial
let imag = imagInitial
document.querySelector('#reset').addEventListener('click', () => {
real = realInitial
imag = imagInitial
// TODO: Trigger redraw.
})
아름답다이제 Mandelbrot 세트를 실제로 픽셀별로 렌더링하는 함수를 만듭니다.나는 좌표계의 변화를 상세하게 소개하지는 않겠지만, 주요 사상은 X와 Y 좌표의 숫자가 각 픽셀에 따라 얼마나 변화하는지 확인하는 것이다.예를 들어 50x100 픽셀의 격자가 5x10의 숫자 격자를 나타낼 때 각 픽셀은
0.10.10.1
.
/**
* Draws the Mandelbrot set.
*/
const drawMandelbrotSet = (realFrom, realTo, imagFrom, imagTo) => {
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
const winWidth = window.innerWidth
const winHeight = window.innerHeight
// Reset the canvas
canvas.width = winWidth
canvas.height = winHeight
ctx.clearRect(0, 0, winWidth, winHeight)
// Determine how big a change in number a single pixel is
const stepSizeReal = (realTo - realFrom) / winWidth
const stepSizeImaginary = (imagTo - imagFrom) / winHeight
// Loop through every pixel of the complex plane that is currently visible
for (let x = realFrom; x <= realTo; x += stepSizeReal) {
for (let y = imagFrom; y <= imagTo; y += stepSizeImaginary) {
// Determine if this coordinate is part of the Mandelbrot set.
const c = new Complex(x, y)
const isInMandelbrotSet = !doesDiverge(c, 50)
const r = isInMandelbrotSet ? 67 : 104
const g = isInMandelbrotSet ? 65 : 211
const b = isInMandelbrotSet ? 144 : 145
// Cast the coordinates on the complex plane back to actual pixel coordinates
const screenX = (x - realFrom) / (realTo - realFrom) * winWidth
const screenY = (y - imagFrom) / (imagTo - imagFrom) * winHeight
// Draw a single pixel
ctx.fillStyle = `rgb(${r}, ${g}, ${b})`
ctx.fillRect(screenX, screenY, 1, 1)
}
}
}
이제 우리가 알고 있는 Mandelbrot 세트가 렌더링되었을 것입니다.
drawMandelbrotSet(real.from, real.to, imag.from, imag.to)
마지막으로 가장 중요하지 않은 점은 캔버스를 클릭하면 선택한 섹션에 따라 real
및 imag
를 설정해야 한다는 것입니다.
/**
* Perform a zoom
*/
document.querySelector('canvas').addEventListener('click', event => {
const winWidth = window.innerWidth
const winHeight = window.innerHeight
const selectedWidth = winWidth / zoomsize
const selectedHeight = winHeight / zoomsize
const startX = (event.clientX - (selectedWidth / 2)) / winWidth
const endX = (event.clientX + (selectedWidth / 2)) / winWidth
const startY = (event.clientY - (selectedHeight / 2)) / winHeight
const endY = (event.clientY + (selectedHeight / 2)) / winHeight
real = {
from: ((real.to - real.from) * startX) + real.from,
to: ((real.to - real.from) * endX) + real.from,
}
imag = {
from: ((imag.to - imag.from) * startY) + imag.from,
to: ((imag.to - imag.from) * endY) + imag.from,
}
drawMandelbrotSet(real.from, real.to, imag.from, imag.to)
})
완료된 결과는 다음과 같습니다. (밝지 않거나 비어 있으면'재실행'을 누르십시오. - 이것은 iframes 때문이라고 생각합니다.)
이 무한히 복잡한 구조를 마음껏 탐색해라!
일부 화면 캡처
다음은 시각화된 화면 캡처입니다.
너는 마지막이 어디에 있는지 알아맞힐 수 있니?댓글에 너의 추측을 남겨라!
나는 여가 시간에 과학 기술 문장을 쓴다.만약 당신이 이 글을 읽는 것을 좋아한다면, 고려해 보세요buying me a coffee!
Reference
이 문제에 관하여(Mandelbrot 세트 시각화 도구 구축), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/thormeier/the-mandelbrot-set-demystified-building-a-visualizer-1nga
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Mandelbrot 세트는 복수의 집합입니다.
ccc 회사
이 교체에 대해 의견 차이가 발생하지 않습니다.
z0=0zn+1=zn2+c
z_0=0\n 줄 바꿈 문자
z{n+1}=z ^{2}{n}+c
z0=0zn+1=zn2+c
미적분이나 복수에 익숙하지 않은 사람들에게'발산'과'복수'의 의미를 빠르게 돌릴 것이다.
수렴과 발산 함수
미적분은 변화다.우리가 함수 (또는 하나의 급수 또는 무한화) 가 어떤 값에 가깝고 거의 그것에 도달했는지, 그러나 완전히 도달하지 않았을 때, 우리는 수렴 함수에 대해 토론했다.
함수가 발산될 때, 그것은 무한대로 불든지, 음무한대로 불든지.그림의 두 그림은 수렴 함수와 발산 함수를 동시에 보여 준다.
(세 번째 함수는 교체 함수입니다. 이 함수들은 값 사이에서 진동하지만 거기에 머무르지 않습니다.)
그렇다면 이 Mandelbrot 집합에 대한 정의는 무엇을 의미합니까?이것은 의미한다
zn+1z{n+1}zn+1
무한대나 마이너스로 팽창하지 않는다.
복수
모든 숫자(0, 1, -13, Pi, e, 네가 생각할 수 있는 것)는 한 줄의 숫자에 배열할 수 있다.
어떤 숫자든 이 선 어딘가에 있다.디지털 선은 1차원이다.복수는 2차원을 도입했다.이 새로운 차원은 복수의'허부'라고 불리고, 통상적인 수선은 이 수의'실부'라고 불린다.따라서 복수는 다음과 같습니다.
a+bia+bia+bi
aaa급
진실한 부분이고,
비교하다
허부
셋,
. 복수의 예는 다음과 같다.
12+6i12+6i12+6i
또는
−삼.−87i-3-87i−삼.−87i
. 따라서 수선은 다음과 같은 숫자 평면으로 변한다.
2+1i2+1i2+1i
):
복수에는 특수한 계산 규칙이 있다.우리는 덧셈과 곱셈이 어떻게 작동하는지 알아야 한다.우리가 왜 그런지 깊이 있게 연구하기 전에, 우리는 규칙을 살펴보고 그것들을 따를 뿐이다.
곱하기: (a+bi)∗(c+di)=(ac)−bd)+(ad+bc)a 추가: (a+bi)+(c+di)= (a+c)+(b+d)i
곱하기: (a+bi)* (c+di) = (ac-bd) + (ad+bc) i\newline
덧셈: (a+bi) + (c+di) = (a+c) + (b+d)i
곱하기: (a+bi)∗(c+di)=(ac)−bd)+(ad+bc)a 추가: (a+bi)+(c+di)= (a+c)+(b+d)i
또 다른 방주: 기본적으로 모든 숫자는 복수입니다.만약 그것들이 숫자선에 있다면, 허부 0으로 표시한다.예:
555
사실은
5+0i5+0i5+0i
따라서 복수는 X/Y 평면에 표시됩니다.각 숫자에 대해
X+YiX+YiX+YiX+Yi
우리는 그것이 Mandelbrot 집합에 속하는지 여부를 말할 수 있다.
복수 평면에서 Mandelbrot 세트에 속하는 점을 다른 색상으로 지정하면 피쳐 패턴이 나타납니다.
이런 지식이 있으면 우리는 시작할 수 있다!
우리 이거 이루자.
우리는 복수의 표시부터 시작한다.
class Complex {
constructor(real, imaginary) {
this.real = real
this.imaginary = imaginary
}
plus(other) {
return new Complex(
this.real + other.real,
this.imaginary + other.imaginary
)
}
times(other) {
return new Complex(
(this.real * other.real - this.imaginary * other.imaginary),
(this.real * other.imaginary + other.real * this.imaginary)
)
}
}
곱셈과 덧셈의 규칙은 지금 이미 거기에 있다.이제 이러한 복수 객체를 사용할 수 있습니다.
const x = new Complex(1, 2) // (1 + 2i)
const y = new Complex(3, -3) // (3 - 3i)
console.log(x.plus(y), x.times(y))
경탄했어이제 주어진 복수와 주어진 반복이 수렴되는지 확인하는 함수를 실현합시다.
/**
* Calculates n+1
*/
const iterate = (n, c) => n.times(n).plus(c)
/**
* Checks if a complex number `c` diverges according to the Mandelbrot definition.
*/
const doesDiverge = (c, maxIter) => {
let n = new Complex(0, 0)
for (let i = 0; i < maxIter; i++) {
n = iterate(n, c)
}
// If the iteration diverges, these values will be `NaN` quite fast. Around 50 iterations is usually needed.
return isNaN(n.real) || isNaN(n.imaginary)
}
지금 우리는 이 함수로 하여금 복수를 우리에게 알려줄 수 있다
ccc 회사
Mandelbrot 컬렉션에서 다음을 수행합니다.
!doesDiverge(new Complex(1, 1), 100) // false
!doesDiverge(new Complex(0, 0), 100) // true
시각화 구축
지금까지는 좋았어, 우리는 곧 도착할 거야.이제 Mandelbrot 세트를 시각화할 수 있습니다.또한 클릭 크기 조정 옵션도 추가됩니다.이를 위해 우리는 캔버스와 기타 몇 가지 요소를 사용할 것이다.
<!-- Used to control the zoom level etc. -->
<div class="controls">
<div>
Zoom size:
<input type="range" min="2" max="50" value="10" id="zoomsize">
</div>
<input type="button" id="reset" value="Reset">
</div>
<!-- A little box that shows what part of the Mandelbrot set will be shown on click -->
<div class="selector"></div>
<!-- The canvas we'll render the Mandelbrot set on -->
<canvas class="canvas" />
스타일링:
html, body {
margin: 0;
padding: 0;
height: 100%;
}
.controls {
position: fixed;
background-color: #f0f0f0;
z-index: 1000;
}
.selector {
border: 2px solid #000;
opacity: .2;
position: fixed;
z-index: 999;
transform: translate(-50%, -50%);
pointer-events: none;
}
.canvas {
width: 100%;
height: 100vh;
}
지금까지 줄곧 괜찮았다.JS 섹션으로 넘어가겠습니다.독립적이기 때문에 선택기 상자부터 시작합니다.
// Size of the zoom compared to current screen size
// i.e. 1/10th of the screen's width and height.
let zoomsize = 10
/**
* Makes the selector follow the mouse
*/
document.addEventListener('mousemove', event => {
const selector = document.querySelector('.selector')
selector.style.top = `${event.clientY}px`
selector.style.left = `${event.clientX}px`
selector.style.width = `${window.innerWidth / zoomsize}px`
selector.style.height = `${window.innerHeight / zoomsize}px`
})
/**
* Zoom size adjustment.
*/
document.querySelector('#zoomsize').addEventListener(
'change',
event => {
zoomsize = parseInt(event.target.value)
}
)
이제 사용자는 클릭할 때 Mandelbrot 집합의 어떤 부분을 볼 수 있는지 명확하게 표시할 수 있습니다.
현재 계획은 다음과 같다. 우리는 복면의 어느 부분이 보이는지 정의하고 이를 실제 픽셀에 비추었다.이를 위해서는 초기 상태 및 재설정 버튼이 필요합니다.
// X coordinate
const realInitial = {
from: -2,
to: 2,
}
// Y coordinate, keep the aspect ratio
const imagInitial = {
from: realInitial.from / window.innerWidth * window.innerHeight,
to: realInitial.to / window.innerWidth * window.innerHeight,
}
// Ranging from negative to positive - which part of the plane is visible right now?
let real = realInitial
let imag = imagInitial
document.querySelector('#reset').addEventListener('click', () => {
real = realInitial
imag = imagInitial
// TODO: Trigger redraw.
})
아름답다이제 Mandelbrot 세트를 실제로 픽셀별로 렌더링하는 함수를 만듭니다.나는 좌표계의 변화를 상세하게 소개하지는 않겠지만, 주요 사상은 X와 Y 좌표의 숫자가 각 픽셀에 따라 얼마나 변화하는지 확인하는 것이다.예를 들어 50x100 픽셀의 격자가 5x10의 숫자 격자를 나타낼 때 각 픽셀은
0.10.10.1
.
/**
* Draws the Mandelbrot set.
*/
const drawMandelbrotSet = (realFrom, realTo, imagFrom, imagTo) => {
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
const winWidth = window.innerWidth
const winHeight = window.innerHeight
// Reset the canvas
canvas.width = winWidth
canvas.height = winHeight
ctx.clearRect(0, 0, winWidth, winHeight)
// Determine how big a change in number a single pixel is
const stepSizeReal = (realTo - realFrom) / winWidth
const stepSizeImaginary = (imagTo - imagFrom) / winHeight
// Loop through every pixel of the complex plane that is currently visible
for (let x = realFrom; x <= realTo; x += stepSizeReal) {
for (let y = imagFrom; y <= imagTo; y += stepSizeImaginary) {
// Determine if this coordinate is part of the Mandelbrot set.
const c = new Complex(x, y)
const isInMandelbrotSet = !doesDiverge(c, 50)
const r = isInMandelbrotSet ? 67 : 104
const g = isInMandelbrotSet ? 65 : 211
const b = isInMandelbrotSet ? 144 : 145
// Cast the coordinates on the complex plane back to actual pixel coordinates
const screenX = (x - realFrom) / (realTo - realFrom) * winWidth
const screenY = (y - imagFrom) / (imagTo - imagFrom) * winHeight
// Draw a single pixel
ctx.fillStyle = `rgb(${r}, ${g}, ${b})`
ctx.fillRect(screenX, screenY, 1, 1)
}
}
}
이제 우리가 알고 있는 Mandelbrot 세트가 렌더링되었을 것입니다.
drawMandelbrotSet(real.from, real.to, imag.from, imag.to)
마지막으로 가장 중요하지 않은 점은 캔버스를 클릭하면 선택한 섹션에 따라 real
및 imag
를 설정해야 한다는 것입니다.
/**
* Perform a zoom
*/
document.querySelector('canvas').addEventListener('click', event => {
const winWidth = window.innerWidth
const winHeight = window.innerHeight
const selectedWidth = winWidth / zoomsize
const selectedHeight = winHeight / zoomsize
const startX = (event.clientX - (selectedWidth / 2)) / winWidth
const endX = (event.clientX + (selectedWidth / 2)) / winWidth
const startY = (event.clientY - (selectedHeight / 2)) / winHeight
const endY = (event.clientY + (selectedHeight / 2)) / winHeight
real = {
from: ((real.to - real.from) * startX) + real.from,
to: ((real.to - real.from) * endX) + real.from,
}
imag = {
from: ((imag.to - imag.from) * startY) + imag.from,
to: ((imag.to - imag.from) * endY) + imag.from,
}
drawMandelbrotSet(real.from, real.to, imag.from, imag.to)
})
완료된 결과는 다음과 같습니다. (밝지 않거나 비어 있으면'재실행'을 누르십시오. - 이것은 iframes 때문이라고 생각합니다.)
이 무한히 복잡한 구조를 마음껏 탐색해라!
일부 화면 캡처
다음은 시각화된 화면 캡처입니다.
너는 마지막이 어디에 있는지 알아맞힐 수 있니?댓글에 너의 추측을 남겨라!
나는 여가 시간에 과학 기술 문장을 쓴다.만약 당신이 이 글을 읽는 것을 좋아한다면, 고려해 보세요buying me a coffee!
Reference
이 문제에 관하여(Mandelbrot 세트 시각화 도구 구축), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/thormeier/the-mandelbrot-set-demystified-building-a-visualizer-1nga
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
class Complex {
constructor(real, imaginary) {
this.real = real
this.imaginary = imaginary
}
plus(other) {
return new Complex(
this.real + other.real,
this.imaginary + other.imaginary
)
}
times(other) {
return new Complex(
(this.real * other.real - this.imaginary * other.imaginary),
(this.real * other.imaginary + other.real * this.imaginary)
)
}
}
const x = new Complex(1, 2) // (1 + 2i)
const y = new Complex(3, -3) // (3 - 3i)
console.log(x.plus(y), x.times(y))
/**
* Calculates n+1
*/
const iterate = (n, c) => n.times(n).plus(c)
/**
* Checks if a complex number `c` diverges according to the Mandelbrot definition.
*/
const doesDiverge = (c, maxIter) => {
let n = new Complex(0, 0)
for (let i = 0; i < maxIter; i++) {
n = iterate(n, c)
}
// If the iteration diverges, these values will be `NaN` quite fast. Around 50 iterations is usually needed.
return isNaN(n.real) || isNaN(n.imaginary)
}
!doesDiverge(new Complex(1, 1), 100) // false
!doesDiverge(new Complex(0, 0), 100) // true
지금까지는 좋았어, 우리는 곧 도착할 거야.이제 Mandelbrot 세트를 시각화할 수 있습니다.또한 클릭 크기 조정 옵션도 추가됩니다.이를 위해 우리는 캔버스와 기타 몇 가지 요소를 사용할 것이다.
<!-- Used to control the zoom level etc. -->
<div class="controls">
<div>
Zoom size:
<input type="range" min="2" max="50" value="10" id="zoomsize">
</div>
<input type="button" id="reset" value="Reset">
</div>
<!-- A little box that shows what part of the Mandelbrot set will be shown on click -->
<div class="selector"></div>
<!-- The canvas we'll render the Mandelbrot set on -->
<canvas class="canvas" />
스타일링:html, body {
margin: 0;
padding: 0;
height: 100%;
}
.controls {
position: fixed;
background-color: #f0f0f0;
z-index: 1000;
}
.selector {
border: 2px solid #000;
opacity: .2;
position: fixed;
z-index: 999;
transform: translate(-50%, -50%);
pointer-events: none;
}
.canvas {
width: 100%;
height: 100vh;
}
지금까지 줄곧 괜찮았다.JS 섹션으로 넘어가겠습니다.독립적이기 때문에 선택기 상자부터 시작합니다.// Size of the zoom compared to current screen size
// i.e. 1/10th of the screen's width and height.
let zoomsize = 10
/**
* Makes the selector follow the mouse
*/
document.addEventListener('mousemove', event => {
const selector = document.querySelector('.selector')
selector.style.top = `${event.clientY}px`
selector.style.left = `${event.clientX}px`
selector.style.width = `${window.innerWidth / zoomsize}px`
selector.style.height = `${window.innerHeight / zoomsize}px`
})
/**
* Zoom size adjustment.
*/
document.querySelector('#zoomsize').addEventListener(
'change',
event => {
zoomsize = parseInt(event.target.value)
}
)
이제 사용자는 클릭할 때 Mandelbrot 집합의 어떤 부분을 볼 수 있는지 명확하게 표시할 수 있습니다.현재 계획은 다음과 같다. 우리는 복면의 어느 부분이 보이는지 정의하고 이를 실제 픽셀에 비추었다.이를 위해서는 초기 상태 및 재설정 버튼이 필요합니다.
// X coordinate
const realInitial = {
from: -2,
to: 2,
}
// Y coordinate, keep the aspect ratio
const imagInitial = {
from: realInitial.from / window.innerWidth * window.innerHeight,
to: realInitial.to / window.innerWidth * window.innerHeight,
}
// Ranging from negative to positive - which part of the plane is visible right now?
let real = realInitial
let imag = imagInitial
document.querySelector('#reset').addEventListener('click', () => {
real = realInitial
imag = imagInitial
// TODO: Trigger redraw.
})
아름답다이제 Mandelbrot 세트를 실제로 픽셀별로 렌더링하는 함수를 만듭니다.나는 좌표계의 변화를 상세하게 소개하지는 않겠지만, 주요 사상은 X와 Y 좌표의 숫자가 각 픽셀에 따라 얼마나 변화하는지 확인하는 것이다.예를 들어 50x100 픽셀의 격자가 5x10의 숫자 격자를 나타낼 때 각 픽셀은0.10.10.1
.
/**
* Draws the Mandelbrot set.
*/
const drawMandelbrotSet = (realFrom, realTo, imagFrom, imagTo) => {
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
const winWidth = window.innerWidth
const winHeight = window.innerHeight
// Reset the canvas
canvas.width = winWidth
canvas.height = winHeight
ctx.clearRect(0, 0, winWidth, winHeight)
// Determine how big a change in number a single pixel is
const stepSizeReal = (realTo - realFrom) / winWidth
const stepSizeImaginary = (imagTo - imagFrom) / winHeight
// Loop through every pixel of the complex plane that is currently visible
for (let x = realFrom; x <= realTo; x += stepSizeReal) {
for (let y = imagFrom; y <= imagTo; y += stepSizeImaginary) {
// Determine if this coordinate is part of the Mandelbrot set.
const c = new Complex(x, y)
const isInMandelbrotSet = !doesDiverge(c, 50)
const r = isInMandelbrotSet ? 67 : 104
const g = isInMandelbrotSet ? 65 : 211
const b = isInMandelbrotSet ? 144 : 145
// Cast the coordinates on the complex plane back to actual pixel coordinates
const screenX = (x - realFrom) / (realTo - realFrom) * winWidth
const screenY = (y - imagFrom) / (imagTo - imagFrom) * winHeight
// Draw a single pixel
ctx.fillStyle = `rgb(${r}, ${g}, ${b})`
ctx.fillRect(screenX, screenY, 1, 1)
}
}
}
이제 우리가 알고 있는 Mandelbrot 세트가 렌더링되었을 것입니다.drawMandelbrotSet(real.from, real.to, imag.from, imag.to)
마지막으로 가장 중요하지 않은 점은 캔버스를 클릭하면 선택한 섹션에 따라 real
및 imag
를 설정해야 한다는 것입니다./**
* Perform a zoom
*/
document.querySelector('canvas').addEventListener('click', event => {
const winWidth = window.innerWidth
const winHeight = window.innerHeight
const selectedWidth = winWidth / zoomsize
const selectedHeight = winHeight / zoomsize
const startX = (event.clientX - (selectedWidth / 2)) / winWidth
const endX = (event.clientX + (selectedWidth / 2)) / winWidth
const startY = (event.clientY - (selectedHeight / 2)) / winHeight
const endY = (event.clientY + (selectedHeight / 2)) / winHeight
real = {
from: ((real.to - real.from) * startX) + real.from,
to: ((real.to - real.from) * endX) + real.from,
}
imag = {
from: ((imag.to - imag.from) * startY) + imag.from,
to: ((imag.to - imag.from) * endY) + imag.from,
}
drawMandelbrotSet(real.from, real.to, imag.from, imag.to)
})
완료된 결과는 다음과 같습니다. (밝지 않거나 비어 있으면'재실행'을 누르십시오. - 이것은 iframes 때문이라고 생각합니다.)이 무한히 복잡한 구조를 마음껏 탐색해라!
일부 화면 캡처
다음은 시각화된 화면 캡처입니다.
너는 마지막이 어디에 있는지 알아맞힐 수 있니?댓글에 너의 추측을 남겨라!
나는 여가 시간에 과학 기술 문장을 쓴다.만약 당신이 이 글을 읽는 것을 좋아한다면, 고려해 보세요buying me a coffee!
Reference
이 문제에 관하여(Mandelbrot 세트 시각화 도구 구축), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/thormeier/the-mandelbrot-set-demystified-building-a-visualizer-1nga
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Reference
이 문제에 관하여(Mandelbrot 세트 시각화 도구 구축), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/thormeier/the-mandelbrot-set-demystified-building-a-visualizer-1nga텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)