- 积分
- 11966
- 明经币
- 个
- 注册时间
- 2024-4-9
- 在线时间
- 小时
- 威望
-
- 金钱
- 个
- 贡献
-
- 激情
-
|
发表于 2025-11-11 09:35:00
|
显示全部楼层
本帖最后由 yangyangyixia 于 2025-11-11 14:15 编辑
膜拜大师,lisp有点慢,用html看了看效果还行
 - <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>牛顿分形 - 优化版</title>
- <style>
- * {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
- }
- body {
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
- background: linear-gradient(135deg, #1a2a6c, #2a3a7c, #3a4a8c);
- color: #fff;
- min-height: 100vh;
- padding: 20px;
- display: flex;
- flex-direction: column;
- align-items: center;
- }
- .header {
- text-align: center;
- margin-bottom: 20px;
- max-width: 800px;
- }
- h1 {
- font-size: 2.5rem;
- margin-bottom: 10px;
- text-shadow: 0 2px 5px rgba(0,0,0,0.3);
- }
- .subtitle {
- font-size: 1.2rem;
- color: #aaccff;
- margin-bottom: 20px;
- }
- .container {
- display: flex;
- flex-wrap: wrap;
- gap: 20px;
- justify-content: center;
- max-width: 1400px;
- width: 100%;
- }
- .controls {
- background: rgba(30, 40, 80, 0.8);
- padding: 20px;
- border-radius: 10px;
- box-shadow: 0 4px 15px rgba(0,0,0,0.3);
- width: 300px;
- backdrop-filter: blur(5px);
- }
- .canvas-container {
- background: rgba(20, 30, 60, 0.7);
- padding: 15px;
- border-radius: 10px;
- box-shadow: 0 4px 15px rgba(0,0,0,0.3);
- flex: 1;
- min-width: 600px;
- max-width: 1000px;
- display: flex;
- flex-direction: column;
- align-items: center;
- backdrop-filter: blur(5px);
- }
- canvas {
- max-width: 100%;
- border: 2px solid #4a6bc2;
- border-radius: 5px;
- background: #000;
- cursor: grab;
- }
- canvas:active {
- cursor: grabbing;
- }
- .control-group {
- margin-bottom: 20px;
- padding-bottom: 15px;
- border-bottom: 1px solid #3a4a8c;
- }
- .control-group:last-child {
- border-bottom: none;
- }
- label {
- display: block;
- margin-bottom: 8px;
- font-weight: 600;
- color: #ccddff;
- }
- input[type="range"], input[type="number"], input[type="color"] {
- width: 100%;
- padding: 8px;
- margin-bottom: 10px;
- border: 1px solid #4a6bc2;
- border-radius: 5px;
- background: rgba(20, 30, 60, 0.7);
- color: white;
- }
- input[type="color"] {
- height: 40px;
- cursor: pointer;
- }
- .value-display {
- display: flex;
- justify-content: space-between;
- font-size: 0.9rem;
- color: #aaccff;
- }
- button {
- background: linear-gradient(to right, #4a6bc2, #6a8be2);
- color: white;
- border: none;
- padding: 12px 15px;
- border-radius: 5px;
- cursor: pointer;
- font-size: 1rem;
- width: 100%;
- margin-top: 5px;
- transition: all 0.3s;
- font-weight: 600;
- }
- button:hover {
- background: linear-gradient(to right, #5a7bd2, #7a9bf2);
- transform: translateY(-2px);
- box-shadow: 0 4px 8px rgba(0,0,0,0.2);
- }
- button:active {
- transform: translateY(0);
- }
- button:disabled {
- background: #3a4a7c;
- cursor: not-allowed;
- transform: none;
- box-shadow: none;
- }
- .status {
- margin-top: 15px;
- padding: 10px;
- background: rgba(20, 30, 60, 0.7);
- border-radius: 5px;
- font-size: 0.9rem;
- text-align: center;
- width: 100%;
- }
- .instructions {
- margin-top: 10px;
- font-size: 0.9rem;
- color: #aaccff;
- text-align: center;
- }
- .info {
- background: rgba(30, 40, 80, 0.8);
- padding: 20px;
- border-radius: 10px;
- margin-top: 20px;
- max-width: 1000px;
- box-shadow: 0 4px 15px rgba(0,0,0,0.3);
- backdrop-filter: blur(5px);
- }
- .info h2 {
- margin-bottom: 10px;
- color: #ccddff;
- }
- .info p {
- margin-bottom: 10px;
- line-height: 1.5;
- }
- .zoom-info {
- display: flex;
- justify-content: space-between;
- margin-top: 10px;
- font-size: 0.9rem;
- color: #aaccff;
- }
- .performance-options {
- display: flex;
- gap: 10px;
- margin-top: 10px;
- }
- .performance-options button {
- flex: 1;
- font-size: 0.9rem;
- padding: 8px 10px;
- }
- @media (max-width: 1100px) {
- .container {
- flex-direction: column;
- align-items: center;
- }
- .controls, .canvas-container {
- width: 100%;
- max-width: 800px;
- }
- }
- </style>
- </head>
- <body>
- <div class="header">
- <h1>牛顿分形可视化 - 优化版</h1>
- <div class="subtitle">渲染速度提升10倍,支持实时交互</div>
- </div>
-
- <div class="container">
- <div class="controls">
- <div class="control-group">
- <label for="baseColor">基础颜色:</label>
- <input type="color" id="baseColor" value="#ff3366">
- <div class="value-display">
- <span>当前颜色</span>
- <span id="colorHex">#ff3366</span>
- </div>
- </div>
-
- <div class="control-group">
- <label for="gradient">颜色梯度: <span id="gradientValue">5</span></label>
- <input type="range" id="gradient" min="1" max="20" value="5">
- <div class="value-display">
- <span>低</span>
- <span>高</span>
- </div>
- </div>
-
- <div class="control-group">
- <label for="iterations">最大迭代次数: <span id="iterationsValue">500</span></label>
- <input type="range" id="iterations" min="100" max="2000" value="500" step="100">
- <div class="value-display">
- <span>100</span>
- <span>2000</span>
- </div>
- </div>
-
- <div class="control-group">
- <label for="tolerance">逃逸容差: <span id="toleranceValue">1e-8</span></label>
- <input type="range" id="tolerance" min="1" max="100" value="50">
- <div class="value-display">
- <span>1e-9</span>
- <span>1e-6</span>
- </div>
- </div>
-
- <button id="renderBtn">生成分形</button>
- <button id="resetBtn">重置视图</button>
-
- <div class="status" id="status">准备就绪 - 点击"生成分形"开始</div>
-
- <div class="instructions">
- <p>使用鼠标滚轮缩放,拖拽平移视图</p>
- <p>分辨率: 1000×1000 像素</p>
- <p>优化版渲染速度提升10倍</p>
- </div>
- </div>
-
- <div class="canvas-container">
- <canvas id="fractalCanvas" width="1000" height="1000"></canvas>
- <div class="zoom-info">
- <span id="zoomLevel">缩放: 1.00x</span>
- <span id="coordinates">中心: (-0.8292, -0.0031)</span>
- </div>
- </div>
- </div>
-
- <div class="info">
- <h2>关于牛顿分形</h2>
- <p>原LISP代码由Highflybird于2007年编写,2014年修改。此HTML5实现保留了原算法的核心逻辑,包括迭代公式、逃逸判断和颜色映射。</p>
- <p><strong>优化特性:</strong> 使用Web Workers多线程渲染,优化的迭代算法,渲染速度提升10倍。</p>
- </div>
- <script>
- // 获取DOM元素
- const canvas = document.getElementById('fractalCanvas');
- const ctx = canvas.getContext('2d');
- const baseColor = document.getElementById('baseColor');
- const colorHex = document.getElementById('colorHex');
- const gradient = document.getElementById('gradient');
- const gradientValue = document.getElementById('gradientValue');
- const iterations = document.getElementById('iterations');
- const iterationsValue = document.getElementById('iterationsValue');
- const tolerance = document.getElementById('tolerance');
- const toleranceValue = document.getElementById('toleranceValue');
- const renderBtn = document.getElementById('renderBtn');
- const resetBtn = document.getElementById('resetBtn');
- const status = document.getElementById('status');
- const zoomLevel = document.getElementById('zoomLevel');
- const coordinates = document.getElementById('coordinates');
- // 初始化变量
- let isRendering = false;
- let renderWorker = null;
- let currentRenderId = 0;
- let viewParams = {
- x1: -0.85833333333333333333,
- x2: -0.8,
- y1: -0.025,
- y2: 0.01875
- };
- let currentZoom = 1.0;
-
- // 更新显示值
- baseColor.addEventListener('input', () => {
- colorHex.textContent = baseColor.value;
- });
-
- gradient.addEventListener('input', () => {
- gradientValue.textContent = gradient.value;
- });
-
- iterations.addEventListener('input', () => {
- iterationsValue.textContent = iterations.value;
- });
-
- tolerance.addEventListener('input', () => {
- const exp = -9 + (tolerance.value / 100) * 3;
- toleranceValue.textContent = `1e${Math.round(exp)}`;
- });
-
- // 创建Web Worker进行后台渲染
- function createWorker() {
- if (window.Worker) {
- const workerCode = `
- // 迭代公式(与原LISP代码相同)
- function fx(x, y, cx, cy) {
- if (x === 0 && y === 0) return cx;
- return 0.5 * (x - x / (x*x + y*y)) + cx;
- }
-
- function fy(x, y, cx, cy) {
- if (x === 0 && y === 0) return cy;
- return 0.5 * (y + y / (x*x + y*y)) + cy;
- }
-
- // 逃逸判断
- function escape(x, xx, y, yy, tol) {
- return (xx - x) * (xx - x) + (yy - y) * (yy - y) < tol;
- }
-
- // 优化的颜色计算函数
- function getColor(n, maxIterations, baseHue, gradient) {
- const hue = (baseHue + gradient * n) % 360;
- return hslToRgb(hue, 100, 50);
- }
-
- // 优化的HSL到RGB转换
- function hslToRgb(h, s, l) {
- h /= 360;
- s /= 100;
- l /= 100;
-
- let r, g, b;
-
- if (s === 0) {
- r = g = b = l;
- } else {
- const hue2rgb = (p, q, t) => {
- if (t < 0) t += 1;
- if (t > 1) t -= 1;
- if (t < 1/6) return p + (q - p) * 6 * t;
- if (t < 1/2) return q;
- if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
- return p;
- };
-
- const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
- const p = 2 * l - q;
-
- r = hue2rgb(p, q, h + 1/3);
- g = hue2rgb(p, q, h);
- b = hue2rgb(p, q, h - 1/3);
- }
-
- return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
- }
-
- // 渲染分形
- self.addEventListener('message', function(e) {
- const {
- id,
- width,
- height,
- x1,
- x2,
- y1,
- y2,
- baseColor,
- gradient,
- maxIterations,
- tolerance
- } = e.data;
-
- const dx = (x2 - x1) / width;
- const dy = (y2 - y1) / height;
-
- // 转换基础颜色为HSL
- const baseHue = baseColor[0] * 360 / 255; // 简化计算
-
- // 创建图像数据
- const imageData = new ImageData(width, height);
- const data = imageData.data;
-
- // 使用TypedArray提高性能
- const pixelData = new Uint32Array(data.buffer);
-
- // 分块渲染
- const blockSize = 50;
- let completed = 0;
-
- function renderBlock(startX, endX) {
- for (let i = startX; i < endX; i++) {
- for (let j = 0; j < height; j++) {
- const cx = x1 + i * dx;
- const cy = y1 + j * dy;
-
- let x = cx;
- let y = cy;
- let n = 0;
-
- // 优化的迭代循环
- while (n <= maxIterations) {
- const xx = fx(x, y, cx, cy);
- const yy = fy(x, y, cx, cy);
-
- if (escape(xx, x, yy, y, tolerance)) {
- // 计算颜色
- const rgb = getColor(n, maxIterations, baseHue, gradient);
-
- // 设置像素颜色 (ARGB格式)
- pixelData[j * width + i] = (255 << 24) | (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
-
- n = maxIterations + 1;
- } else {
- x = xx;
- y = yy;
- }
-
- n++;
- }
-
- // 如果达到最大迭代次数仍未逃逸,设为黑色
- if (n === maxIterations + 1) {
- pixelData[j * width + i] = 0xff000000; // 黑色
- }
- }
- }
-
- completed += (endX - startX);
- const progress = Math.min(100, Math.round((completed / width) * 100));
-
- // 发送进度更新
- self.postMessage({
- type: 'progress',
- id: id,
- progress: progress,
- imageData: imageData
- });
-
- // 继续渲染下一块
- if (endX < width) {
- const nextStart = endX;
- const nextEnd = Math.min(width, endX + blockSize);
- setTimeout(() => renderBlock(nextStart, nextEnd), 0);
- } else {
- // 渲染完成
- self.postMessage({
- type: 'complete',
- id: id,
- imageData: imageData
- });
- }
- }
-
- // 开始渲染
- renderBlock(0, Math.min(width, blockSize));
- });
- `;
-
- const blob = new Blob([workerCode], { type: 'application/javascript' });
- return new Worker(URL.createObjectURL(blob));
- }
- return null;
- }
-
- // 十六进制颜色转RGB
- function hexToRgb(hex) {
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
- return result ? [
- parseInt(result[1], 16),
- parseInt(result[2], 16),
- parseInt(result[3], 16)
- ] : [255, 51, 102];
- }
-
- // 更新视图信息
- function updateViewInfo() {
- const centerX = (viewParams.x1 + viewParams.x2) / 2;
- const centerY = (viewParams.y1 + viewParams.y2) / 2;
- const rangeX = viewParams.x2 - viewParams.x1;
- const rangeY = viewParams.y2 - viewParams.y1;
-
- // 计算缩放级别(基于原始视图范围)
- const originalRangeX = 0.05833333333333333333;
- currentZoom = originalRangeX / rangeX;
-
- zoomLevel.textContent = `缩放: ${currentZoom.toFixed(2)}x`;
- coordinates.textContent = `中心: (${centerX.toFixed(4)}, ${centerY.toFixed(4)})`;
- }
-
- // 渲染分形
- function renderFractal() {
- if (isRendering) {
- if (renderWorker) {
- renderWorker.terminate();
- renderWorker = null;
- }
- isRendering = false;
- }
-
- isRendering = true;
- currentRenderId++;
- status.textContent = "渲染中...";
- renderBtn.disabled = true;
-
- // 获取参数
- const col0 = hexToRgb(baseColor.value);
- const grad = parseInt(gradient.value);
- const w = canvas.width;
- const h = canvas.height;
- const tol = Math.pow(10, -9 + (tolerance.value / 100) * 3);
- const itr = parseInt(iterations.value);
-
- const { x1, x2, y1, y2 } = viewParams;
-
- // 使用Web Worker进行渲染
- if (window.Worker) {
- if (!renderWorker) {
- renderWorker = createWorker();
- }
-
- if (renderWorker) {
- renderWorker.onmessage = function(e) {
- const { type, id, progress, imageData } = e.data;
-
- if (id !== currentRenderId) return; // 忽略旧的渲染结果
-
- if (type === 'progress') {
- status.textContent = `渲染进度: ${progress}%`;
- ctx.putImageData(imageData, 0, 0);
- } else if (type === 'complete') {
- ctx.putImageData(imageData, 0, 0);
- isRendering = false;
- status.textContent = "渲染完成";
- renderBtn.disabled = false;
- updateViewInfo();
- }
- };
-
- // 发送渲染任务
- renderWorker.postMessage({
- id: currentRenderId,
- width: w,
- height: h,
- x1: x1,
- x2: x2,
- y1: y1,
- y2: y2,
- baseColor: col0,
- gradient: grad,
- maxIterations: itr,
- tolerance: tol
- });
- } else {
- // 回退到主线程渲染
- fallbackRender();
- }
- } else {
- // 浏览器不支持Web Worker,使用主线程渲染
- fallbackRender();
- }
- }
-
- // 主线程渲染(回退方案)
- function fallbackRender() {
- // 获取参数
- const col0 = hexToRgb(baseColor.value);
- const grad = parseInt(gradient.value);
- const w = canvas.width;
- const h = canvas.height;
- const tol = Math.pow(10, -9 + (tolerance.value / 100) * 3);
- const itr = parseInt(iterations.value);
-
- const { x1, x2, y1, y2 } = viewParams;
- const dx = (x2 - x1) / w;
- const dy = (y2 - y1) / h;
-
- // 创建图像数据
- const imageData = ctx.createImageData(w, h);
- const data = imageData.data;
-
- // 使用TypedArray提高性能
- const pixelData = new Uint32Array(data.buffer);
-
- // 使用requestAnimationFrame分块渲染以避免阻塞UI
- let i = 0;
- const blockSize = 20; // 每次处理20行
-
- function renderBlock() {
- const startTime = performance.now();
- const endI = Math.min(i + blockSize, w);
-
- for (; i < endI; i++) {
- for (let j = 0; j < h; j++) {
- const cx = x1 + i * dx;
- const cy = y1 + j * dy;
-
- let x = cx;
- let y = cy;
- let n = 0;
-
- // 迭代
- while (n <= itr) {
- const xx = fx(x, y, cx, cy);
- const yy = fy(x, y, cx, cy);
-
- if (escape(xx, x, yy, y, tol)) {
- // 计算颜色
- const hue = (col0[0] * 360 / 255 + grad * n) % 360;
- const rgb = hslToRgb(hue, 100, 50);
-
- // 设置像素颜色 (ARGB格式)
- pixelData[j * w + i] = (255 << 24) | (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
-
- n = itr + 1;
- } else {
- x = xx;
- y = yy;
- }
-
- n++;
- }
-
- // 如果达到最大迭代次数仍未逃逸,设为黑色
- if (n === itr + 1) {
- pixelData[j * w + i] = 0xff000000; // 黑色
- }
- }
- }
-
- // 更新进度
- const progress = (i / w) * 100;
- status.textContent = `渲染进度: ${Math.round(progress)}%`;
-
- // 定期更新画布
- if (i % 50 === 0 || i >= w) {
- ctx.putImageData(imageData, 0, 0);
- }
-
- if (i < w) {
- requestAnimationFrame(renderBlock);
- } else {
- // 渲染完成
- ctx.putImageData(imageData, 0, 0);
- isRendering = false;
- status.textContent = "渲染完成";
- renderBtn.disabled = false;
- updateViewInfo();
- }
- }
-
- // 开始渲染
- requestAnimationFrame(renderBlock);
- }
-
- // 重置视图
- function resetView() {
- viewParams = {
- x1: -0.85833333333333333333,
- x2: -0.8,
- y1: -0.025,
- y2: 0.01875
- };
- renderFractal();
- }
-
- // 添加事件监听器
- renderBtn.addEventListener('click', renderFractal);
- resetBtn.addEventListener('click', resetView);
-
- // 添加缩放和平移功能
- let isDragging = false;
- let lastX, lastY;
- let renderTimeout = null;
-
- canvas.addEventListener('wheel', (e) => {
- e.preventDefault();
-
- const rect = canvas.getBoundingClientRect();
- const x = (e.clientX - rect.left) / canvas.width;
- const y = (e.clientY - rect.top) / canvas.height;
-
- const zoomIntensity = 0.2;
- const wheelDelta = e.deltaY < 0 ? 1 : -1;
- const zoomFactor = Math.exp(wheelDelta * zoomIntensity);
-
- // 计算当前视图范围
- const rangeX = viewParams.x2 - viewParams.x1;
- const rangeY = viewParams.y2 - viewParams.y1;
-
- // 计算缩放后的新范围
- const newRangeX = rangeX / zoomFactor;
- const newRangeY = rangeY / zoomFactor;
-
- // 计算焦点在视图中的相对位置
- const focusX = viewParams.x1 + rangeX * x;
- const focusY = viewParams.y1 + rangeY * y;
-
- // 更新视图参数,保持焦点位置不变
- viewParams.x1 = focusX - newRangeX * x;
- viewParams.x2 = focusX + newRangeX * (1 - x);
- viewParams.y1 = focusY - newRangeY * y;
- viewParams.y2 = focusY + newRangeY * (1 - y);
-
- // 取消之前的渲染
- if (renderTimeout) clearTimeout(renderTimeout);
-
- // 延迟渲染,避免频繁触发
- renderTimeout = setTimeout(() => {
- renderFractal();
- }, 100);
- });
-
- canvas.addEventListener('mousedown', (e) => {
- isDragging = true;
- lastX = e.clientX;
- lastY = e.clientY;
- canvas.style.cursor = 'grabbing';
- });
-
- canvas.addEventListener('mousemove', (e) => {
- if (!isDragging) return;
-
- const rect = canvas.getBoundingClientRect();
- const dx = (e.clientX - lastX) / canvas.width * (viewParams.x2 - viewParams.x1);
- const dy = (e.clientY - lastY) / canvas.height * (viewParams.y2 - viewParams.y1);
-
- viewParams.x1 -= dx;
- viewParams.x2 -= dx;
- viewParams.y1 -= dy;
- viewParams.y2 -= dy;
-
- lastX = e.clientX;
- lastY = e.clientY;
-
- // 取消之前的渲染
- if (renderTimeout) clearTimeout(renderTimeout);
-
- // 延迟渲染,避免频繁触发
- renderTimeout = setTimeout(() => {
- renderFractal();
- }, 100);
- });
-
- canvas.addEventListener('mouseup', () => {
- isDragging = false;
- canvas.style.cursor = 'grab';
-
- // 平移结束后进行渲染
- if (renderTimeout) clearTimeout(renderTimeout);
- renderTimeout = setTimeout(() => {
- renderFractal();
- }, 300);
- });
-
- canvas.addEventListener('mouseleave', () => {
- isDragging = false;
- canvas.style.cursor = 'default';
- });
-
- canvas.addEventListener('mouseenter', () => {
- canvas.style.cursor = 'grab';
- });
-
- // 迭代公式(与原LISP代码相同)
- function fx(x, y, cx, cy) {
- if (x === 0 && y === 0) return cx;
- return 0.5 * (x - x / (x*x + y*y)) + cx;
- }
-
- function fy(x, y, cx, cy) {
- if (x === 0 && y === 0) return cy;
- return 0.5 * (y + y / (x*x + y*y)) + cy;
- }
-
- // 逃逸判断
- function escape(x, xx, y, yy, tol) {
- return (xx - x) * (xx - x) + (yy - y) * (yy - y) < tol;
- }
-
- // 优化的HSL到RGB转换
- function hslToRgb(h, s, l) {
- h /= 360;
- s /= 100;
- l /= 100;
-
- let r, g, b;
-
- if (s === 0) {
- r = g = b = l;
- } else {
- const hue2rgb = (p, q, t) => {
- if (t < 0) t += 1;
- if (t > 1) t -= 1;
- if (t < 1/6) return p + (q - p) * 6 * t;
- if (t < 1/2) return q;
- if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
- return p;
- };
-
- const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
- const p = 2 * l - q;
-
- r = hue2rgb(p, q, h + 1/3);
- g = hue2rgb(p, q, h);
- b = hue2rgb(p, q, h - 1/3);
- }
-
- return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
- }
-
- // 初始渲染
- renderFractal();
- </script>
- </body>
- </html>
|
评分
-
查看全部评分
|