정보
요청한 주제가 아래에 표시됩니다. 그러나 이 주제는 이 라이브러리에 포함되지 않습니다.

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

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

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

Mandelbrot 2


<!DOCTYPE html>
<html>

<head>
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
  <title>Mandelbrot 2</title>
  <style>
    html, body {
      margin: 0;
      padding: 0;
      text-align: center;
    }
    
    canvas {
      border: 1px black solid;
    }
  </style>
</head>

<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)을 생성합니다.

다음으로 z0(항상 0이어야 함)를 생성하여 zn+1 = zn + 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 점화식을 확장해 봅니다.

반복 1에 대한 Mandelbrot 점화식 기호 확장

zn+1 = An + Bni이므로 다음과 같이 실수 AnBn 값을 사용하여 zn+2를 계산할 수 있습니다.

반복 2에 대한 Mandelbrot 점화식 기호 확장(1부)

또한 다음과 같이 AnBn 값을 사용하여 An+1Bn+1을 계산할 수 있습니다.

반복 2에 대한 Mandelbrot 점화식 기호 확장(2부)

An+1Bn+1을 사용하여 위와 같이 zn+3이 계산됩니다.

반복 3에 대한 Mandelbrot 점화식 기호 확장

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


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

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

B sub n + 1 방정식

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

A sub n + 1 방정식

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

마지막으로 zn의 절대값이 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)의 픽셀이 흰색으로 유지됩니다.

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

관련 항목

확대/축소 상자 구현

 

 

표시:
© 2014 Microsoft