【发布时间】:2017-03-15 19:27:04
【问题描述】:
我编写了一个小的 javascript 程序来查看 julia set images [link]。我一直在实现多个迭代算法(不仅仅是z^2 + c),最近还实现了exp(z)。但是,我的输出与维基百科页面图像不同。这是c值-0.65的比较:
显然,他们的输出更加丰富多彩,并且显示了更多细节!我在想这可能是我的实现中的错误,或者可能是我的着色算法?或者维基百科的结果是由一些不同的方式产生的?下面是一个只有e^z + c 算法和相关函数的 MVE。如果您想随意玩整个程序,它的托管地址为on my site。
//globals
var MAXITERATION = 2500;
var BOUNDARY = 4;
var CANVASID = "juliaDraw";
var CANVAS = document.getElementById("juliaDraw");
var CONTEXT = document.getElementById("juliaDraw").getContext('2d');
var HEIGHT = 750;
var WIDTH = 750;
var CONVERGENCEITERCOUNT = 2500;
/** Complex number functions **/
function complexNum(real, imaginary) {
this.real = real;
this.imaginary = imaginary;
return this;
}
// This is the bit that might be a problem but it seems to return correct results?
function raiseNumberToComplexPower(x, c) {
var s = Math.pow(x, c.real);
var pow = c.imaginary * Math.log(x);
var num = new complexNum(Math.cos(pow), Math.sin(pow));
return scalarComplex(s, num);
}
function addComplex(c1, c2) {
var real = c1.real + c2.real;
var imaginary = c1.imaginary + c2.imaginary;
return new complexNum(real, imaginary);
}
function multComplex(c1, c2) {
var real = (c1.real * c2.real) - (c1.imaginary * c2.imaginary);
var imaginary = (c1.real * c2.imaginary) + (c2.real * c1.imaginary);
return new complexNum(real, imaginary);
}
function scalarComplex(s, c) {
return new complexNum(c.real * s, c.imaginary * s);
}
function getComplexModulus(c) {
return Math.sqrt((c.real * c.real) + (c.imaginary * c.imaginary));
}
/** Drawing and manipulation **/
function createArray(length) {
var arr = new Array(length || 0),
i = length;
if (arguments.length > 1) {
var args = Array.prototype.slice.call(arguments, 1);
while (i--) arr[length - 1 - i] = createArray.apply(this, args);
}
return arr;
}
function drawJulia() {
CONTEXT.clearRect(0, 0, WIDTH, HEIGHT);
var start = new complexNum(-2, 2);
var c = new complexNum(readInput('realValue') * 1, readInput('imagValue') * 1);
STARTPOS = {
real: -2,
imaginary: 2
}
RANGE = 4;
plotJuliaSet(CANVASID, c);
}
function plotJuliaSet(canvasID, c) {
var complexNumberArray = createArray(WIDTH + 1, HEIGHT + 1);
var doesPointEscapeArray = createArray(WIDTH + 1, HEIGHT + 1);
ITERALGO = exponential;
for (var x = 0; x <= WIDTH; x++) {
for (var y = 0; y <= HEIGHT; y++) {
complexNumberArray[x][y] = new coordsToComplex({
x: x,
y: y
});
complexNumberArray[x][y] = complexNumberArray[x][y];
doesPointEscapeArray[x][y] = doesPointEscape(c, complexNumberArray[x][y]);
if (doesPointEscapeArray[x][y] >= 0) {
drawPointOnCanvas(x, y, getColor(doesPointEscapeArray[x][y]));
} else {
drawPointOnCanvas(x, y, 'black');
}
}
}
console.log('done');
}
function doesPointEscape(c, complexNum) {
var iterations = 0;
var iterationsToEscape = -1;
var escaped = false;
while ((!escaped) && (iterations < MAXITERATION)) {
if (getComplexModulus(complexNum) > BOUNDARY) {
escaped = true;
iterationsToEscape = iterations;
}
complexNum = ITERALGO(complexNum, c);
iterations++;
}
return iterationsToEscape;
}
function exponential(complexNum, c) {
// e^z + c
return addComplex(raiseNumberToComplexPower(Math.E, complexNum), c);
}
function drawPointOnCanvas(x, y, color) {
CONTEXT.fillStyle = color;
CONTEXT.fillRect(x, y, 1, 1);
}
function getColor(iterations) {
//console.log("Iterations: "+getBaseLog(iterations+1,255));
var color = "rgb(" + Math.floor((8 * iterations) % 255) + "," + Math.floor(2 * iterations % 255) + "," + Math.floor(255 - ((8 * iterations) % 255)) + ")";
//console.log(color);
return color;
}
function coordsToComplex(coordinates) {
return {
real: ((coordinates.x / WIDTH) * RANGE + STARTPOS.real),
imaginary: ((coordinates.y / HEIGHT) * -RANGE + STARTPOS.imaginary)
};
}
function complexToCoords(c) {
return {
x: ((c.real - STARTPOS.real) / (RANGE)) * WIDTH,
y: ((c.imaginary - STARTPOS.imaginary) / -(RANGE)) * HEIGHT
};
}
function readInput(inputID) {
return document.getElementById(inputID).value;
}
.desc {
float: right;
width: 300px;
}
#juliaDraw {
border: 1px dotted;
float: left;
}
.canvasWrapper canvas {
position: absolute;
top: 0;
left: 0;
}
<div class="desc">
<h1>Julia Set Viewer</h1>
<form>
<label>Real:
<input type="text" id="realValue" value="-0.65">
</label>
<br>
<label>Imag:
<input type="text" id="imagValue" value="0">
</label>
<input type="button" onClick="drawJulia()" value="Draw">
</form>
</div>
<canvas id="juliaDraw" width=750 height=750 onClick="drawZoomJulia()"></canvas>
【问题讨论】:
-
getColor()函数背后的想法是什么? -
我需要将大量的迭代和整数压缩成一个 rgb 颜色 3 元组。由于 rgb 为 0-255,因此以 255 为模进行迭代是有意义的。不同的乘数会给出一系列颜色 - 使用相同的乘数会在一种颜色周围产生简单的渐变,这不是很有趣。我还将蓝色通道反转为从 255 开始,这样迭代将从蓝色开始,然后继续到红色,然后在它们之间循环。如果您访问我的网站,并使用右键单击缩放,您可以看到生成的颜色更好。
-
好吧,迭代次数永远不会超过 2500,对吧? RGB 颜色空间覆盖(名义上)1600 万个值。所以我看不出“浓缩”这个词与手头的问题有什么关系。
标签: javascript image canvas html5-canvas fractals