확대/축소 상자 구현 간소화

화면 좌표를 복소 평면에 매핑에서 제공한 정보를 기반으로 여기서는 확대/축소 상자 기능의 구현을 간소화하기 위해 Mandelbrot 1을 다시 작성하는 방법을 살펴보겠습니다.

성능상의 이유와 확대/축소 상자(왼쪽 위와 오른쪽 아래 모서리)의 좌표가 항상 캔버스 화면 좌표에 있다는 이유로 인해 다음과 같이 Mandelbrot 1을 다시 작성합니다.

Mandelbrot 2

```
<!DOCTYPE html>
<html>

<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<title>Mandelbrot 2</title>
<style>
html, body {
margin: 0;
text-align: center;
}

canvas {
border: 1px black solid;
}
</style>

<body>
<h1>Mandelbrot 2</h1>
<p>This example demonstrates an algorithm for drawing the Mandelbrot set using canvas screen coordinates.</p>
<canvas width="600" height="400">Canvas not supported - upgrade your browser</canvas>
<script>
var RE_MAX = 1.1; // This value will be adjusted as necessary to ensure that the rendered Mandelbrot set is never skewed (that is, true to it's actual shape).
var RE_MIN = -2.5;
var IM_MAX = 1.2;
var IM_MIN = -1.2;
var MAX_ITERATIONS = 300; // Increase to improve detection of complex c values that belong to the Mandelbrot set.

var globals = {}; // Store all would-be-global-variables in one handy global object.
globals.canvas = document.getElementsByTagName('canvas')[0];
globals.canvas.ctx = globals.canvas.getContext('2d');
globals.canvas.ctx.fillStyle = "black"

drawMandelbrotSet(RE_MAX, RE_MIN, IM_MAX, IM_MIN);

function drawMandelbrotSet(ReMax, ReMin, ImMax, ImMin) {
var canvasWidth = globals.canvas.width; // A small speed optimization.
var canvasHeight = globals.canvas.height; // A small speed optimization.

ReMax = canvasWidth * ( (ImMax - ImMin) / canvasHeight ) + ReMin; // Make the width and height of the complex plane proportional to the width and height of the canvas.

if (RE_MAX != ReMax) {
alert("RE_MAX has been adjusted to: " + ReMax); // The user should never see this if RE_MAX is set correctly above.
} // if

var ctx = globals.canvas.ctx;

var x_coefficient = (ReMax - ReMin) / canvasWidth; // Keep the Mandelbrot loop as computation-free as possible.
var y_coefficient = (ImMin - ImMax) / canvasHeight; // Keep the Mandelbrot loop as computation-free as possible.

for (var x = 0; x < canvasWidth; x++) {
var c_Re = (x * x_coefficient) + ReMin // Convert the canvas x-coordinate to a complex plane Re-coordinate. c_Re represents the real part of a c value.

for (var y = 0; y < canvasHeight; y++) {
var c_Im = (y * y_coefficient) + ImMax; // Recall that c = c_Re + c_Im*i

var z_Re = 0; // Recall that the first z value (Zo) must be 0.
var z_Im = 0; // Recall that the first z value (Zo) must be 0.

var c_belongsToMandelbrotSet = true;
for (var iterationCount = 1; iterationCount <= MAX_ITERATIONS; iterationCount++) {
var z_Re_squared = z_Re * z_Re; // A small speed optimization.
var z_Im_squared = z_Im * z_Im; // A small speed optimization.

// The next two lines perform Zn+1 = (Zn)^2 + c (note that (x + yi)^2 = x^2 - y^2 + 2xyi, thus the real part is x^2 - y^2 and the imaginary part is 2xyi).
z_Im = (2 * z_Re * z_Im) + c_Im; // We must calculate z_Im first because it's a function of z_Re.
z_Re = z_Re_squared - z_Im_squared + c_Re; // The is not a function of z_Re.

if ( z_Re_squared + z_Im_squared > 4 ) { // Checks if |z^2| is greater than 2.
c_belongsToMandelbrotSet = false; // This complex c value is not part of the Mandelbrot set.
break; // So we immediately check the next c value.
} // if
} // for

if (c_belongsToMandelbrotSet) {
ctx.fillRect(x, y, 1, 1);  // This c value is probably part of the Mandelbrot set, so set the color of the associated pixel to black. Increase MAX_ITERATIONS to increase the probability.
} // if
} // for
} // for
} // drawMandelbrotSet
</script>
</body>

</html>

```

먼저 복잡한 클래스가 성능이 보다 우수한 루프 내 계산으로 바뀌었습니다. 예를 들어 `z.modulus() > 2`와 관련된 비용이 많이 드는 제곱근 연산이 다음과 같이 `z_Re_squared + z_Im_squared > 4`로 바뀌었습니다.

이전 코드 예제의 주석에 표시된 대로 세 개(세 번 중첩)의 `for` 루프에서 최대한 많은 계산을 제거하기 위해 여러 가지 사소한 최적화 작업이 수행되었습니다.

다음으로 변환 방정식에서는 비례를 가정합니다(화면 좌표를 복소 평면에 매핑 참조). 다음 줄입니다.

```ReMax = canvasWidth * ( (ImMax - ImMin) / canvasHeight ) + ReMin;
```

복소 평면의 너비와 높이가 캔버스의 너비와 높이에 비례하도록 합니다. 이 검사를 수행하지 않으면 `RE_MAX`에 대해 비례 가정을 위반하는 값을 선택하여 기울어진 Mandelbrot 집합 이미지가 생성될 수 있습니다.

그러나 Mandelbrot 1Mandelbrot 2의 주요 차이점은 각 캔버스 픽셀이 c 값으로 간주된다는 점입니다.

```for (var x = 0; x < canvasWidth; x++) {
var c_Re = (x * x_coefficient) + ReMin // Convert the canvas x-coordinate to a complex plane Re-coordinate. c_Re represents the real part of a c value.
for (var y = 0; y < canvasHeight; y++) {
var c_Im = (y * y_coefficient) + ImMax; // Recall that c = c_Re + c_Im*i
...
```

여기서는 각 캔버스 픽셀(`x`, `y`)을 반복하고 화면 좌표를 복소 평면에 매핑에 설명된 좌표 변환 방정식을 사용하여 복소 평면에 연관된(일치) c 점 (`c_Re`, `c_Im`)을 생성합니다.

다음으로 z₀(항상 0이어야 함)를 생성하여 zₙ₊₁ = zₙ + c의 반복으로 z의 동작을 관찰함으로써 c가 Mandelbrot 집합에 속하는지 여부를 확인합니다.

```
var z_Re = 0; // Recall that the first z value (Zo) must be 0.
var z_Im = 0; // Recall that the first z value (Zo) must be 0.

var c_belongsToMandelbrotSet = true;
for (var iterationCount = 1; iterationCount <= MAX_ITERATIONS; iterationCount++) {
var z_Re_squared = z_Re * z_Re; // A small speed optimization.
var z_Im_squared = z_Im * z_Im; // A small speed optimization.

// The next two lines perform Zn+1 = (Zn)^2 + c (note that (x + yi)^2 = x^2 - y^2 + 2xyi, thus the real part is x^2 - y^2 and the imaginary part is 2xyi).
z_Im = (2 * z_Re * z_Im) + c_Im; // We must calculate z_Im first because it's a function of z_Re.
z_Re = z_Re_squared - z_Im_squared + c_Re; // The is not a function of z_Re.

if ( z_Re_squared + z_Im_squared > 4 ) { // Checks if |z^2| is greater than 2.
c_belongsToMandelbrotSet = false; // This complex c value is not part of the Mandelbrot set.
break; // So we immediately check the next c value.
} // if
} // for

if (c_belongsToMandelbrotSet) {
ctx.fillRect(x, y, 1, 1);  // This c value is probably part of the Mandelbrot set, so set the color of the associated pixel to black. Increase MAX_ITERATIONS to increase the probability.
} // if

```

이 코드 조각을 설명하기 위해 다음과 같이 Mandelbrot 점화식을 확장해 봅니다.

zₙ₊₁ = Aₙ + Bi이므로 다음과 같이 실수 Aₙ와 Bₙ 값을 사용하여 zₙ₊₂를 계산할 수 있습니다.

또한 다음과 같이 Aₙ와 Bₙ 값을 사용하여 Aₙ₊₁과 Bₙ₊₁을 계산할 수 있습니다.

Aₙ₊₁과 Bₙ₊₁을 사용하여 위와 같이 zₙ₊₃이 계산됩니다.

이 귀납적 인수는 무제한으로 확장하여 필요한 만큼 많은 z 값을 계산할 수 있으며 특히 다음에서 반복되는 이전 두 줄을 설명합니다.

```
z_Im = (2 * z_Re * z_Im) + c_Im;
z_Re = z_Re_squared - z_Im_squared + c_Re;

```

즉, 첫 줄은 다음과 같습니다.

두 번째 줄은 다음과 같습니다.

`z_Re``z_Im`보다 먼저 계산된 경우(즉, 이전 두 줄이 바뀐 경우) `z_Im`에 대한 수식이 `z_Re`의 현재 값이 아니라 `z_Re`의 다음 값을 사용하여 잘못된 결과가 생성될 수 있습니다.

마지막으로 zₙ의 절대값이 `MAX_ITERATIONS` 내에서 무한(즉, `z_Re_squared + z_Im_squared <= 4`)으로 기울지 않는 경우 캔버스 좌표계에서 c를 나타내는 점 (`x`, `y`)가 연관된 c 값이 Mandelbrot 집합의 일부일 가능성이 높으므로 (`x`, `y`)의 픽셀을 검정색으로 칠합니다. 그렇지 않으면 (`z_Re_squared + z_Im_squared > 4`) 및 c가 Mandelbrot 집합의 일부가 아니며 (`x`, `y`)의 픽셀이 흰색으로 유지됩니다.

이제 캔버스 화면 좌표를 복소 평면으로 변환하는 방법을 확인했으므로 확대/축소 상자 구현에 설명된 대로 확대/축소 상자 기능을 구현할 수 있습니다.

확대/축소 상자 구현

표시: