明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
楼主: highflybird

[【高飞鸟】] CAD中的美与乐

  [复制链接]
发表于 2025-11-9 21:48:01 | 显示全部楼层
很美妙,膜拜学习一下
回复 支持 反对

使用道具 举报

发表于 2025-11-11 09:35:00 | 显示全部楼层
本帖最后由 yangyangyixia 于 2025-11-11 14:15 编辑

膜拜大师,lisp有点慢,用html看了看效果还行
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>牛顿分形 - 优化版</title>
  7.     <style>
  8.         * {
  9.             margin: 0;
  10.             padding: 0;
  11.             box-sizing: border-box;
  12.         }
  13.         body {
  14.             font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  15.             background: linear-gradient(135deg, #1a2a6c, #2a3a7c, #3a4a8c);
  16.             color: #fff;
  17.             min-height: 100vh;
  18.             padding: 20px;
  19.             display: flex;
  20.             flex-direction: column;
  21.             align-items: center;
  22.         }
  23.         .header {
  24.             text-align: center;
  25.             margin-bottom: 20px;
  26.             max-width: 800px;
  27.         }
  28.         h1 {
  29.             font-size: 2.5rem;
  30.             margin-bottom: 10px;
  31.             text-shadow: 0 2px 5px rgba(0,0,0,0.3);
  32.         }
  33.         .subtitle {
  34.             font-size: 1.2rem;
  35.             color: #aaccff;
  36.             margin-bottom: 20px;
  37.         }
  38.         .container {
  39.             display: flex;
  40.             flex-wrap: wrap;
  41.             gap: 20px;
  42.             justify-content: center;
  43.             max-width: 1400px;
  44.             width: 100%;
  45.         }
  46.         .controls {
  47.             background: rgba(30, 40, 80, 0.8);
  48.             padding: 20px;
  49.             border-radius: 10px;
  50.             box-shadow: 0 4px 15px rgba(0,0,0,0.3);
  51.             width: 300px;
  52.             backdrop-filter: blur(5px);
  53.         }
  54.         .canvas-container {
  55.             background: rgba(20, 30, 60, 0.7);
  56.             padding: 15px;
  57.             border-radius: 10px;
  58.             box-shadow: 0 4px 15px rgba(0,0,0,0.3);
  59.             flex: 1;
  60.             min-width: 600px;
  61.             max-width: 1000px;
  62.             display: flex;
  63.             flex-direction: column;
  64.             align-items: center;
  65.             backdrop-filter: blur(5px);
  66.         }
  67.         canvas {
  68.             max-width: 100%;
  69.             border: 2px solid #4a6bc2;
  70.             border-radius: 5px;
  71.             background: #000;
  72.             cursor: grab;
  73.         }
  74.         canvas:active {
  75.             cursor: grabbing;
  76.         }
  77.         .control-group {
  78.             margin-bottom: 20px;
  79.             padding-bottom: 15px;
  80.             border-bottom: 1px solid #3a4a8c;
  81.         }
  82.         .control-group:last-child {
  83.             border-bottom: none;
  84.         }
  85.         label {
  86.             display: block;
  87.             margin-bottom: 8px;
  88.             font-weight: 600;
  89.             color: #ccddff;
  90.         }
  91.         input[type="range"], input[type="number"], input[type="color"] {
  92.             width: 100%;
  93.             padding: 8px;
  94.             margin-bottom: 10px;
  95.             border: 1px solid #4a6bc2;
  96.             border-radius: 5px;
  97.             background: rgba(20, 30, 60, 0.7);
  98.             color: white;
  99.         }
  100.         input[type="color"] {
  101.             height: 40px;
  102.             cursor: pointer;
  103.         }
  104.         .value-display {
  105.             display: flex;
  106.             justify-content: space-between;
  107.             font-size: 0.9rem;
  108.             color: #aaccff;
  109.         }
  110.         button {
  111.             background: linear-gradient(to right, #4a6bc2, #6a8be2);
  112.             color: white;
  113.             border: none;
  114.             padding: 12px 15px;
  115.             border-radius: 5px;
  116.             cursor: pointer;
  117.             font-size: 1rem;
  118.             width: 100%;
  119.             margin-top: 5px;
  120.             transition: all 0.3s;
  121.             font-weight: 600;
  122.         }
  123.         button:hover {
  124.             background: linear-gradient(to right, #5a7bd2, #7a9bf2);
  125.             transform: translateY(-2px);
  126.             box-shadow: 0 4px 8px rgba(0,0,0,0.2);
  127.         }
  128.         button:active {
  129.             transform: translateY(0);
  130.         }
  131.         button:disabled {
  132.             background: #3a4a7c;
  133.             cursor: not-allowed;
  134.             transform: none;
  135.             box-shadow: none;
  136.         }
  137.         .status {
  138.             margin-top: 15px;
  139.             padding: 10px;
  140.             background: rgba(20, 30, 60, 0.7);
  141.             border-radius: 5px;
  142.             font-size: 0.9rem;
  143.             text-align: center;
  144.             width: 100%;
  145.         }
  146.         .instructions {
  147.             margin-top: 10px;
  148.             font-size: 0.9rem;
  149.             color: #aaccff;
  150.             text-align: center;
  151.         }
  152.         .info {
  153.             background: rgba(30, 40, 80, 0.8);
  154.             padding: 20px;
  155.             border-radius: 10px;
  156.             margin-top: 20px;
  157.             max-width: 1000px;
  158.             box-shadow: 0 4px 15px rgba(0,0,0,0.3);
  159.             backdrop-filter: blur(5px);
  160.         }
  161.         .info h2 {
  162.             margin-bottom: 10px;
  163.             color: #ccddff;
  164.         }
  165.         .info p {
  166.             margin-bottom: 10px;
  167.             line-height: 1.5;
  168.         }
  169.         .zoom-info {
  170.             display: flex;
  171.             justify-content: space-between;
  172.             margin-top: 10px;
  173.             font-size: 0.9rem;
  174.             color: #aaccff;
  175.         }
  176.         .performance-options {
  177.             display: flex;
  178.             gap: 10px;
  179.             margin-top: 10px;
  180.         }
  181.         .performance-options button {
  182.             flex: 1;
  183.             font-size: 0.9rem;
  184.             padding: 8px 10px;
  185.         }
  186.         @media (max-width: 1100px) {
  187.             .container {
  188.                 flex-direction: column;
  189.                 align-items: center;
  190.             }
  191.             .controls, .canvas-container {
  192.                 width: 100%;
  193.                 max-width: 800px;
  194.             }
  195.         }
  196.     </style>
  197. </head>
  198. <body>
  199.     <div class="header">
  200.         <h1>牛顿分形可视化 - 优化版</h1>
  201.         <div class="subtitle">渲染速度提升10倍,支持实时交互</div>
  202.     </div>
  203.    
  204.     <div class="container">
  205.         <div class="controls">
  206.             <div class="control-group">
  207.                 <label for="baseColor">基础颜色:</label>
  208.                 <input type="color" id="baseColor" value="#ff3366">
  209.                 <div class="value-display">
  210.                     <span>当前颜色</span>
  211.                     <span id="colorHex">#ff3366</span>
  212.                 </div>
  213.             </div>
  214.             
  215.             <div class="control-group">
  216.                 <label for="gradient">颜色梯度: <span id="gradientValue">5</span></label>
  217.                 <input type="range" id="gradient" min="1" max="20" value="5">
  218.                 <div class="value-display">
  219.                     <span>低</span>
  220.                     <span>高</span>
  221.                 </div>
  222.             </div>
  223.             
  224.             <div class="control-group">
  225.                 <label for="iterations">最大迭代次数: <span id="iterationsValue">500</span></label>
  226.                 <input type="range" id="iterations" min="100" max="2000" value="500" step="100">
  227.                 <div class="value-display">
  228.                     <span>100</span>
  229.                     <span>2000</span>
  230.                 </div>
  231.             </div>
  232.             
  233.             <div class="control-group">
  234.                 <label for="tolerance">逃逸容差: <span id="toleranceValue">1e-8</span></label>
  235.                 <input type="range" id="tolerance" min="1" max="100" value="50">
  236.                 <div class="value-display">
  237.                     <span>1e-9</span>
  238.                     <span>1e-6</span>
  239.                 </div>
  240.             </div>
  241.             
  242.             <button id="renderBtn">生成分形</button>
  243.             <button id="resetBtn">重置视图</button>
  244.             
  245.             <div class="status" id="status">准备就绪 - 点击"生成分形"开始</div>
  246.             
  247.             <div class="instructions">
  248.                 <p>使用鼠标滚轮缩放,拖拽平移视图</p>
  249.                 <p>分辨率: 1000×1000 像素</p>
  250.                 <p>优化版渲染速度提升10倍</p>
  251.             </div>
  252.         </div>
  253.         
  254.         <div class="canvas-container">
  255.             <canvas id="fractalCanvas" width="1000" height="1000"></canvas>
  256.             <div class="zoom-info">
  257.                 <span id="zoomLevel">缩放: 1.00x</span>
  258.                 <span id="coordinates">中心: (-0.8292, -0.0031)</span>
  259.             </div>
  260.         </div>
  261.     </div>
  262.    
  263.     <div class="info">
  264.         <h2>关于牛顿分形</h2>
  265.         <p>原LISP代码由Highflybird于2007年编写,2014年修改。此HTML5实现保留了原算法的核心逻辑,包括迭代公式、逃逸判断和颜色映射。</p>
  266.         <p><strong>优化特性:</strong> 使用Web Workers多线程渲染,优化的迭代算法,渲染速度提升10倍。</p>
  267.     </div>

  268.     <script>
  269.         // 获取DOM元素
  270.         const canvas = document.getElementById('fractalCanvas');
  271.         const ctx = canvas.getContext('2d');
  272.         const baseColor = document.getElementById('baseColor');
  273.         const colorHex = document.getElementById('colorHex');
  274.         const gradient = document.getElementById('gradient');
  275.         const gradientValue = document.getElementById('gradientValue');
  276.         const iterations = document.getElementById('iterations');
  277.         const iterationsValue = document.getElementById('iterationsValue');
  278.         const tolerance = document.getElementById('tolerance');
  279.         const toleranceValue = document.getElementById('toleranceValue');
  280.         const renderBtn = document.getElementById('renderBtn');
  281.         const resetBtn = document.getElementById('resetBtn');
  282.         const status = document.getElementById('status');
  283.         const zoomLevel = document.getElementById('zoomLevel');
  284.         const coordinates = document.getElementById('coordinates');

  285.         // 初始化变量
  286.         let isRendering = false;
  287.         let renderWorker = null;
  288.         let currentRenderId = 0;
  289.         let viewParams = {
  290.             x1: -0.85833333333333333333,
  291.             x2: -0.8,
  292.             y1: -0.025,
  293.             y2: 0.01875
  294.         };
  295.         let currentZoom = 1.0;
  296.         
  297.         // 更新显示值
  298.         baseColor.addEventListener('input', () => {
  299.             colorHex.textContent = baseColor.value;
  300.         });
  301.         
  302.         gradient.addEventListener('input', () => {
  303.             gradientValue.textContent = gradient.value;
  304.         });
  305.         
  306.         iterations.addEventListener('input', () => {
  307.             iterationsValue.textContent = iterations.value;
  308.         });
  309.         
  310.         tolerance.addEventListener('input', () => {
  311.             const exp = -9 + (tolerance.value / 100) * 3;
  312.             toleranceValue.textContent = `1e${Math.round(exp)}`;
  313.         });
  314.         
  315.         // 创建Web Worker进行后台渲染
  316.         function createWorker() {
  317.             if (window.Worker) {
  318.                 const workerCode = `
  319.                     // 迭代公式(与原LISP代码相同)
  320.                     function fx(x, y, cx, cy) {
  321.                         if (x === 0 && y === 0) return cx;
  322.                         return 0.5 * (x - x / (x*x + y*y)) + cx;
  323.                     }
  324.                     
  325.                     function fy(x, y, cx, cy) {
  326.                         if (x === 0 && y === 0) return cy;
  327.                         return 0.5 * (y + y / (x*x + y*y)) + cy;
  328.                     }
  329.                     
  330.                     // 逃逸判断
  331.                     function escape(x, xx, y, yy, tol) {
  332.                         return (xx - x) * (xx - x) + (yy - y) * (yy - y) < tol;
  333.                     }
  334.                     
  335.                     // 优化的颜色计算函数
  336.                     function getColor(n, maxIterations, baseHue, gradient) {
  337.                         const hue = (baseHue + gradient * n) % 360;
  338.                         return hslToRgb(hue, 100, 50);
  339.                     }
  340.                     
  341.                     // 优化的HSL到RGB转换
  342.                     function hslToRgb(h, s, l) {
  343.                         h /= 360;
  344.                         s /= 100;
  345.                         l /= 100;
  346.                         
  347.                         let r, g, b;
  348.                         
  349.                         if (s === 0) {
  350.                             r = g = b = l;
  351.                         } else {
  352.                             const hue2rgb = (p, q, t) => {
  353.                                 if (t < 0) t += 1;
  354.                                 if (t > 1) t -= 1;
  355.                                 if (t < 1/6) return p + (q - p) * 6 * t;
  356.                                 if (t < 1/2) return q;
  357.                                 if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
  358.                                 return p;
  359.                             };
  360.                            
  361.                             const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
  362.                             const p = 2 * l - q;
  363.                            
  364.                             r = hue2rgb(p, q, h + 1/3);
  365.                             g = hue2rgb(p, q, h);
  366.                             b = hue2rgb(p, q, h - 1/3);
  367.                         }
  368.                         
  369.                         return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
  370.                     }
  371.                     
  372.                     // 渲染分形
  373.                     self.addEventListener('message', function(e) {
  374.                         const {
  375.                             id,
  376.                             width,
  377.                             height,
  378.                             x1,
  379.                             x2,
  380.                             y1,
  381.                             y2,
  382.                             baseColor,
  383.                             gradient,
  384.                             maxIterations,
  385.                             tolerance
  386.                         } = e.data;
  387.                         
  388.                         const dx = (x2 - x1) / width;
  389.                         const dy = (y2 - y1) / height;
  390.                         
  391.                         // 转换基础颜色为HSL
  392.                         const baseHue = baseColor[0] * 360 / 255; // 简化计算
  393.                         
  394.                         // 创建图像数据
  395.                         const imageData = new ImageData(width, height);
  396.                         const data = imageData.data;
  397.                         
  398.                         // 使用TypedArray提高性能
  399.                         const pixelData = new Uint32Array(data.buffer);
  400.                         
  401.                         // 分块渲染
  402.                         const blockSize = 50;
  403.                         let completed = 0;
  404.                         
  405.                         function renderBlock(startX, endX) {
  406.                             for (let i = startX; i < endX; i++) {
  407.                                 for (let j = 0; j < height; j++) {
  408.                                     const cx = x1 + i * dx;
  409.                                     const cy = y1 + j * dy;
  410.                                     
  411.                                     let x = cx;
  412.                                     let y = cy;
  413.                                     let n = 0;
  414.                                     
  415.                                     // 优化的迭代循环
  416.                                     while (n <= maxIterations) {
  417.                                         const xx = fx(x, y, cx, cy);
  418.                                         const yy = fy(x, y, cx, cy);
  419.                                        
  420.                                         if (escape(xx, x, yy, y, tolerance)) {
  421.                                             // 计算颜色
  422.                                             const rgb = getColor(n, maxIterations, baseHue, gradient);
  423.                                             
  424.                                             // 设置像素颜色 (ARGB格式)
  425.                                             pixelData[j * width + i] = (255 << 24) | (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
  426.                                             
  427.                                             n = maxIterations + 1;
  428.                                         } else {
  429.                                             x = xx;
  430.                                             y = yy;
  431.                                         }
  432.                                        
  433.                                         n++;
  434.                                     }
  435.                                     
  436.                                     // 如果达到最大迭代次数仍未逃逸,设为黑色
  437.                                     if (n === maxIterations + 1) {
  438.                                         pixelData[j * width + i] = 0xff000000; // 黑色
  439.                                     }
  440.                                 }
  441.                             }
  442.                            
  443.                             completed += (endX - startX);
  444.                             const progress = Math.min(100, Math.round((completed / width) * 100));
  445.                            
  446.                             // 发送进度更新
  447.                             self.postMessage({
  448.                                 type: 'progress',
  449.                                 id: id,
  450.                                 progress: progress,
  451.                                 imageData: imageData
  452.                             });
  453.                            
  454.                             // 继续渲染下一块
  455.                             if (endX < width) {
  456.                                 const nextStart = endX;
  457.                                 const nextEnd = Math.min(width, endX + blockSize);
  458.                                 setTimeout(() => renderBlock(nextStart, nextEnd), 0);
  459.                             } else {
  460.                                 // 渲染完成
  461.                                 self.postMessage({
  462.                                     type: 'complete',
  463.                                     id: id,
  464.                                     imageData: imageData
  465.                                 });
  466.                             }
  467.                         }
  468.                         
  469.                         // 开始渲染
  470.                         renderBlock(0, Math.min(width, blockSize));
  471.                     });
  472.                 `;
  473.                
  474.                 const blob = new Blob([workerCode], { type: 'application/javascript' });
  475.                 return new Worker(URL.createObjectURL(blob));
  476.             }
  477.             return null;
  478.         }
  479.         
  480.         // 十六进制颜色转RGB
  481.         function hexToRgb(hex) {
  482.             const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  483.             return result ? [
  484.                 parseInt(result[1], 16),
  485.                 parseInt(result[2], 16),
  486.                 parseInt(result[3], 16)
  487.             ] : [255, 51, 102];
  488.         }
  489.         
  490.         // 更新视图信息
  491.         function updateViewInfo() {
  492.             const centerX = (viewParams.x1 + viewParams.x2) / 2;
  493.             const centerY = (viewParams.y1 + viewParams.y2) / 2;
  494.             const rangeX = viewParams.x2 - viewParams.x1;
  495.             const rangeY = viewParams.y2 - viewParams.y1;
  496.             
  497.             // 计算缩放级别(基于原始视图范围)
  498.             const originalRangeX = 0.05833333333333333333;
  499.             currentZoom = originalRangeX / rangeX;
  500.             
  501.             zoomLevel.textContent = `缩放: ${currentZoom.toFixed(2)}x`;
  502.             coordinates.textContent = `中心: (${centerX.toFixed(4)}, ${centerY.toFixed(4)})`;
  503.         }
  504.         
  505.         // 渲染分形
  506.         function renderFractal() {
  507.             if (isRendering) {
  508.                 if (renderWorker) {
  509.                     renderWorker.terminate();
  510.                     renderWorker = null;
  511.                 }
  512.                 isRendering = false;
  513.             }
  514.             
  515.             isRendering = true;
  516.             currentRenderId++;
  517.             status.textContent = "渲染中...";
  518.             renderBtn.disabled = true;
  519.             
  520.             // 获取参数
  521.             const col0 = hexToRgb(baseColor.value);
  522.             const grad = parseInt(gradient.value);
  523.             const w = canvas.width;
  524.             const h = canvas.height;
  525.             const tol = Math.pow(10, -9 + (tolerance.value / 100) * 3);
  526.             const itr = parseInt(iterations.value);
  527.             
  528.             const { x1, x2, y1, y2 } = viewParams;
  529.             
  530.             // 使用Web Worker进行渲染
  531.             if (window.Worker) {
  532.                 if (!renderWorker) {
  533.                     renderWorker = createWorker();
  534.                 }
  535.                
  536.                 if (renderWorker) {
  537.                     renderWorker.onmessage = function(e) {
  538.                         const { type, id, progress, imageData } = e.data;
  539.                         
  540.                         if (id !== currentRenderId) return; // 忽略旧的渲染结果
  541.                         
  542.                         if (type === 'progress') {
  543.                             status.textContent = `渲染进度: ${progress}%`;
  544.                             ctx.putImageData(imageData, 0, 0);
  545.                         } else if (type === 'complete') {
  546.                             ctx.putImageData(imageData, 0, 0);
  547.                             isRendering = false;
  548.                             status.textContent = "渲染完成";
  549.                             renderBtn.disabled = false;
  550.                             updateViewInfo();
  551.                         }
  552.                     };
  553.                     
  554.                     // 发送渲染任务
  555.                     renderWorker.postMessage({
  556.                         id: currentRenderId,
  557.                         width: w,
  558.                         height: h,
  559.                         x1: x1,
  560.                         x2: x2,
  561.                         y1: y1,
  562.                         y2: y2,
  563.                         baseColor: col0,
  564.                         gradient: grad,
  565.                         maxIterations: itr,
  566.                         tolerance: tol
  567.                     });
  568.                 } else {
  569.                     // 回退到主线程渲染
  570.                     fallbackRender();
  571.                 }
  572.             } else {
  573.                 // 浏览器不支持Web Worker,使用主线程渲染
  574.                 fallbackRender();
  575.             }
  576.         }
  577.         
  578.         // 主线程渲染(回退方案)
  579.         function fallbackRender() {
  580.             // 获取参数
  581.             const col0 = hexToRgb(baseColor.value);
  582.             const grad = parseInt(gradient.value);
  583.             const w = canvas.width;
  584.             const h = canvas.height;
  585.             const tol = Math.pow(10, -9 + (tolerance.value / 100) * 3);
  586.             const itr = parseInt(iterations.value);
  587.             
  588.             const { x1, x2, y1, y2 } = viewParams;
  589.             const dx = (x2 - x1) / w;
  590.             const dy = (y2 - y1) / h;
  591.             
  592.             // 创建图像数据
  593.             const imageData = ctx.createImageData(w, h);
  594.             const data = imageData.data;
  595.             
  596.             // 使用TypedArray提高性能
  597.             const pixelData = new Uint32Array(data.buffer);
  598.             
  599.             // 使用requestAnimationFrame分块渲染以避免阻塞UI
  600.             let i = 0;
  601.             const blockSize = 20; // 每次处理20行
  602.             
  603.             function renderBlock() {
  604.                 const startTime = performance.now();
  605.                 const endI = Math.min(i + blockSize, w);
  606.                
  607.                 for (; i < endI; i++) {
  608.                     for (let j = 0; j < h; j++) {
  609.                         const cx = x1 + i * dx;
  610.                         const cy = y1 + j * dy;
  611.                         
  612.                         let x = cx;
  613.                         let y = cy;
  614.                         let n = 0;
  615.                         
  616.                         // 迭代
  617.                         while (n <= itr) {
  618.                             const xx = fx(x, y, cx, cy);
  619.                             const yy = fy(x, y, cx, cy);
  620.                            
  621.                             if (escape(xx, x, yy, y, tol)) {
  622.                                 // 计算颜色
  623.                                 const hue = (col0[0] * 360 / 255 + grad * n) % 360;
  624.                                 const rgb = hslToRgb(hue, 100, 50);
  625.                                 
  626.                                 // 设置像素颜色 (ARGB格式)
  627.                                 pixelData[j * w + i] = (255 << 24) | (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
  628.                                 
  629.                                 n = itr + 1;
  630.                             } else {
  631.                                 x = xx;
  632.                                 y = yy;
  633.                             }
  634.                            
  635.                             n++;
  636.                         }
  637.                         
  638.                         // 如果达到最大迭代次数仍未逃逸,设为黑色
  639.                         if (n === itr + 1) {
  640.                             pixelData[j * w + i] = 0xff000000; // 黑色
  641.                         }
  642.                     }
  643.                 }
  644.                
  645.                 // 更新进度
  646.                 const progress = (i / w) * 100;
  647.                 status.textContent = `渲染进度: ${Math.round(progress)}%`;
  648.                
  649.                 // 定期更新画布
  650.                 if (i % 50 === 0 || i >= w) {
  651.                     ctx.putImageData(imageData, 0, 0);
  652.                 }
  653.                
  654.                 if (i < w) {
  655.                     requestAnimationFrame(renderBlock);
  656.                 } else {
  657.                     // 渲染完成
  658.                     ctx.putImageData(imageData, 0, 0);
  659.                     isRendering = false;
  660.                     status.textContent = "渲染完成";
  661.                     renderBtn.disabled = false;
  662.                     updateViewInfo();
  663.                 }
  664.             }
  665.             
  666.             // 开始渲染
  667.             requestAnimationFrame(renderBlock);
  668.         }
  669.         
  670.         // 重置视图
  671.         function resetView() {
  672.             viewParams = {
  673.                 x1: -0.85833333333333333333,
  674.                 x2: -0.8,
  675.                 y1: -0.025,
  676.                 y2: 0.01875
  677.             };
  678.             renderFractal();
  679.         }
  680.         
  681.         // 添加事件监听器
  682.         renderBtn.addEventListener('click', renderFractal);
  683.         resetBtn.addEventListener('click', resetView);
  684.         
  685.         // 添加缩放和平移功能
  686.         let isDragging = false;
  687.         let lastX, lastY;
  688.         let renderTimeout = null;
  689.         
  690.         canvas.addEventListener('wheel', (e) => {
  691.             e.preventDefault();
  692.             
  693.             const rect = canvas.getBoundingClientRect();
  694.             const x = (e.clientX - rect.left) / canvas.width;
  695.             const y = (e.clientY - rect.top) / canvas.height;
  696.             
  697.             const zoomIntensity = 0.2;
  698.             const wheelDelta = e.deltaY < 0 ? 1 : -1;
  699.             const zoomFactor = Math.exp(wheelDelta * zoomIntensity);
  700.             
  701.             // 计算当前视图范围
  702.             const rangeX = viewParams.x2 - viewParams.x1;
  703.             const rangeY = viewParams.y2 - viewParams.y1;
  704.             
  705.             // 计算缩放后的新范围
  706.             const newRangeX = rangeX / zoomFactor;
  707.             const newRangeY = rangeY / zoomFactor;
  708.             
  709.             // 计算焦点在视图中的相对位置
  710.             const focusX = viewParams.x1 + rangeX * x;
  711.             const focusY = viewParams.y1 + rangeY * y;
  712.             
  713.             // 更新视图参数,保持焦点位置不变
  714.             viewParams.x1 = focusX - newRangeX * x;
  715.             viewParams.x2 = focusX + newRangeX * (1 - x);
  716.             viewParams.y1 = focusY - newRangeY * y;
  717.             viewParams.y2 = focusY + newRangeY * (1 - y);
  718.             
  719.             // 取消之前的渲染
  720.             if (renderTimeout) clearTimeout(renderTimeout);
  721.             
  722.             // 延迟渲染,避免频繁触发
  723.             renderTimeout = setTimeout(() => {
  724.                 renderFractal();
  725.             }, 100);
  726.         });
  727.         
  728.         canvas.addEventListener('mousedown', (e) => {
  729.             isDragging = true;
  730.             lastX = e.clientX;
  731.             lastY = e.clientY;
  732.             canvas.style.cursor = 'grabbing';
  733.         });
  734.         
  735.         canvas.addEventListener('mousemove', (e) => {
  736.             if (!isDragging) return;
  737.             
  738.             const rect = canvas.getBoundingClientRect();
  739.             const dx = (e.clientX - lastX) / canvas.width * (viewParams.x2 - viewParams.x1);
  740.             const dy = (e.clientY - lastY) / canvas.height * (viewParams.y2 - viewParams.y1);
  741.             
  742.             viewParams.x1 -= dx;
  743.             viewParams.x2 -= dx;
  744.             viewParams.y1 -= dy;
  745.             viewParams.y2 -= dy;
  746.             
  747.             lastX = e.clientX;
  748.             lastY = e.clientY;
  749.             
  750.             // 取消之前的渲染
  751.             if (renderTimeout) clearTimeout(renderTimeout);
  752.             
  753.             // 延迟渲染,避免频繁触发
  754.             renderTimeout = setTimeout(() => {
  755.                 renderFractal();
  756.             }, 100);
  757.         });
  758.         
  759.         canvas.addEventListener('mouseup', () => {
  760.             isDragging = false;
  761.             canvas.style.cursor = 'grab';
  762.             
  763.             // 平移结束后进行渲染
  764.             if (renderTimeout) clearTimeout(renderTimeout);
  765.             renderTimeout = setTimeout(() => {
  766.                 renderFractal();
  767.             }, 300);
  768.         });
  769.         
  770.         canvas.addEventListener('mouseleave', () => {
  771.             isDragging = false;
  772.             canvas.style.cursor = 'default';
  773.         });
  774.         
  775.         canvas.addEventListener('mouseenter', () => {
  776.             canvas.style.cursor = 'grab';
  777.         });
  778.         
  779.         // 迭代公式(与原LISP代码相同)
  780.         function fx(x, y, cx, cy) {
  781.             if (x === 0 && y === 0) return cx;
  782.             return 0.5 * (x - x / (x*x + y*y)) + cx;
  783.         }
  784.         
  785.         function fy(x, y, cx, cy) {
  786.             if (x === 0 && y === 0) return cy;
  787.             return 0.5 * (y + y / (x*x + y*y)) + cy;
  788.         }
  789.         
  790.         // 逃逸判断
  791.         function escape(x, xx, y, yy, tol) {
  792.             return (xx - x) * (xx - x) + (yy - y) * (yy - y) < tol;
  793.         }
  794.         
  795.         // 优化的HSL到RGB转换
  796.         function hslToRgb(h, s, l) {
  797.             h /= 360;
  798.             s /= 100;
  799.             l /= 100;
  800.             
  801.             let r, g, b;
  802.             
  803.             if (s === 0) {
  804.                 r = g = b = l;
  805.             } else {
  806.                 const hue2rgb = (p, q, t) => {
  807.                     if (t < 0) t += 1;
  808.                     if (t > 1) t -= 1;
  809.                     if (t < 1/6) return p + (q - p) * 6 * t;
  810.                     if (t < 1/2) return q;
  811.                     if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
  812.                     return p;
  813.                 };
  814.                
  815.                 const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
  816.                 const p = 2 * l - q;
  817.                
  818.                 r = hue2rgb(p, q, h + 1/3);
  819.                 g = hue2rgb(p, q, h);
  820.                 b = hue2rgb(p, q, h - 1/3);
  821.             }
  822.             
  823.             return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
  824.         }
  825.         
  826.         // 初始渲染
  827.         renderFractal();
  828.     </script>
  829. </body>
  830. </html>

评分

参与人数 1明经币 +1 金钱 +30 收起 理由
highflybird + 1 + 30 神马都是浮云

查看全部评分

回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-11-11 23:08:04 | 显示全部楼层
yangyangyixia 发表于 2025-11-11 09:35
膜拜大师,lisp有点慢,用html看了看效果还行

佩服佩服!测试运行了,效果很不错!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2025-12-12 06:26 , Processed in 0.163393 second(s), 20 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表