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)
마지막으로 가장 중요하지 않은 점은 캔버스를 클릭하면 선택한 섹션에 따라 realimag 를 설정해야 한다는 것입니다.
/**
 * 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!

좋은 웹페이지 즐겨찾기