highflybird 发表于 2014-8-22 10:34:43

CAD中的美与乐

本帖最后由 highflybird 于 2014-8-22 11:01 编辑

我是属于爱折腾的那一类人的。
CAD是我这个专业的必备工具。一天到晚都离不开它。
当然,有时候画图累了,就想开开小差。偶然的一次,我在用CAD的夹点拉伸的时候,竟然发现了一个很有意思的现象。
用过CAD的人基本都知道,一段弧有四个夹点 :弧的两个端点、弧段的中点、弧心。当我们把弧的中间的那个夹点拉到弧心的那个夹点位置后,弧心的那个夹点位置却跑走了,然后我又把弧中间的夹点拉到新的弧心处,当我这样重复下去,我开始以为整个弧会最后定在一个位置,但实际上没有,无论我这样做多少次,弧的位置永远也不会固定。
这个偶然的发现引起了我的注意。我觉得不是一个普通的问题,我感觉它是发散的。QJchen博士用Maple佐证了我的感觉–它的确是发散的!但是,发散的,并不意味这这些夹点的位置是毫无规律的。那么,我就开始折腾了:
首先,我用公式找出它们之间的关系,通过一番简单的数学推理,得到如下图的公式:
http://highflybird.mjtd.com/blog/wp-content/uploads/2014/08/zzzf-300x300.jpg
这个关系看起来比我想象的要简单多了,跟求开平方的公式仅仅相差一个正负号,如此的简洁而美妙的公式。当然这个公式是对于实数集的,我又展开联想了,如果把这个公式扩展到复数集呢,按照这样的迭代,是怎样的效果呢?
很好,那么稍加推理,对某个位置(Cx ,Cy)的迭代得到如下公式:
Xn+1= (xn-xn/(xn*xn+yn*yn))/2+Cx;
Yn+1= (yn+yn/(xn*xn+yn*yn))/2+Cy;
到此,我想到了曼德布罗特的分形集,说做就做,我利用我编写的一段程序,加入了一点代码,然后在CAD上运行,得到了如下的效果:


结果证明了,它果然具有分形的性质。像啥?有的说是蝴蝶,有的说是猴子的脸,究竟像什么,就凭你的想象了。
我把这个图形叫做高飞鸟集。因为我没在其他人中或者其他地方看到过类似的图片。这也算是我的一个发现吧。

是啊,CAD中竟然蕴藏了这么多的乐趣和美,是我以前从未领略过的。
美是无处不在的,只不过我们还未发觉。
附注:多年前我就想把写下来,直至今日才付诸实现。

2014年8月21日 Highflybird于深圳

yangyangyixia 发表于 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, input, input {
            width: 100%;
            padding: 8px;
            margin-bottom: 10px;
            border: 1px solid #4a6bc2;
            border-radius: 5px;
            background: rgba(20, 30, 60, 0.7);
            color: white;
      }
      input {
            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 ;
                  }
                  
                  // 渲染分形
                  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 * 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 = (255 << 24) | (rgb << 16) | (rgb << 8) | rgb;
                                          
                                          n = maxIterations + 1;
                                        } else {
                                          x = xx;
                                          y = yy;
                                        }
                                       
                                        n++;
                                    }
                                    
                                    // 如果达到最大迭代次数仍未逃逸,设为黑色
                                    if (n === maxIterations + 1) {
                                        pixelData = 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(, { type: 'application/javascript' });
                return new Worker(URL.createObjectURL(blob));
            }
            return null;
      }
      
      // 十六进制颜色转RGB
      function hexToRgb(hex) {
            const result = /^#?({2})({2})({2})$/i.exec(hex);
            return result ? [
                parseInt(result, 16),
                parseInt(result, 16),
                parseInt(result, 16)
            ] : ;
      }
      
      // 更新视图信息
      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 * 360 / 255 + grad * n) % 360;
                              const rgb = hslToRgb(hue, 100, 50);
                              
                              // 设置像素颜色 (ARGB格式)
                              pixelData = (255 << 24) | (rgb << 16) | (rgb << 8) | rgb;
                              
                              n = itr + 1;
                            } else {
                              x = xx;
                              y = yy;
                            }
                           
                            n++;
                        }
                        
                        // 如果达到最大迭代次数仍未逃逸,设为黑色
                        if (n === itr + 1) {
                            pixelData = 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 ;
      }
      
      // 初始渲染
      renderFractal();
    </script>
</body>
</html>

highflybird 发表于 2025-11-11 23:08:04

yangyangyixia 发表于 2025-11-11 09:35
膜拜大师,lisp有点慢,用html看了看效果还行

佩服佩服!测试运行了,效果很不错!

香远益清 发表于 2020-1-3 14:20:45

highflybird大师才是真正的高高手!!!

highflybird 发表于 2014-8-22 10:41:55

本帖最后由 highflybird 于 2014-8-22 15:05 编辑

程序的LISP代码:
;;;*********************************************
;;; 迭代公式(用户可以自行修改)               
;;; x,xx复数的实数部分,y,yy复数的虚数部分      
;;; xx = (x-x/(x*x+y*y))/2+cx                  
;;; yy = (y+y/(x*x+y*y))/2+cy                  
;;;*********************************************
(defun fx (x y cx cy)
(if (and (zerop x) (zerop y))
    cx
    (+ (* 0.5 (- x (/ x (+ (* x x) (* y y))))) cx)
)
)
(defun fy (x y cx cy)
(if (and (zerop x) (zerop y))
    cy
    (+ (* 0.5 (+ y (/ y (+ (* x x) (* y y))))) cy)
)
)

;;;*********************************************
;;; 逃逸判断:                                 
;;; 当两个复数相接近的时候停止迭代            
;;;*********************************************
(defun Escape (x xx y yy tol)
(< (+ (* (- xx x) (- xx x)) (* (- yy y) (- yy y))) tol)
)
            
;;;*********************************************
;;; Highflybird分形的绘制主函数               
;;; 参数: col0 初始颜色值                     
;;;       X1,X2,Y1,Y2,点的取值范围            
;;;       W,H图像的宽和高                     
;;;       tol逃逸半径                        
;;;       Itr迭代最大次数                     
;;;       Grad 颜色梯度                        
;;;*********************************************
(defun HFB_fractal (col0 X1 X2 Y1 Y2 W H tol Itr grad /
      i j dx dy HSL0 cx cy x y n xx yy)
(setq HSL0 (apply 'RGB->HSL col0))
(setq dx (/ (- X2 X1 0.0) W))
(setq dy (/ (- Y2 Y1 0.0) H))
(setq i 0)            
(repeat W                                     ;图像的宽
    (setq j 0)
    (repeat H                                 ;图像的高
      (setq cx (+ X1 (* i dx)))               ;实数部分
      (setq cy (+ Y1 (* j dy)))               ;虚数部分
      (setq x cx y cy)                        ;开始迭代位置
      (setq n 0)                              ;迭代次数置零
      (while (<= n Itr)                         ;开始迭代
(setq xx (fx x y cx cy))                ;得到新的实数部分
(setq yy (fy x y cx cy))                ;得到新的虚数部分
(if (Escape xx x yy y tol)            ;如果满足逃逸函数
    (progn                                 
      (PutColor HSL0 i j n grad)          ;着色这点
      (setq n Itr)                        ;中断循环
    )                                          
    (setq x xx y yy)                      ;否则继续迭代
)
(setq n (1+ n))
      )
      (setq j (1+ j))
    )
    (setq i (1+ i))
)
)
当然用LISP速度比较慢,最后我用了ARX编程,才满足了所见即所得的效果。
顺便推销一下我的博客地址:http://highflybird.mjtd.com/
欢迎大家有空转转哦!

lucas_3333 发表于 2014-8-22 10:45:56

高飞鸟神一般的存在!飞在云端看不见啊
太高深,我等这一辈子都不法理解啊

masterlong 发表于 2014-8-22 11:04:21

老实说没看明白
不是指后面那些涉及数学的内容
虽然那些确实也没去看

不知高飞鸟用的什么版本的CAD
我用的2004
弧的夹点只显示3个
两端点+弧中点
2010也只显示3夹点

假如还显示弧的圆心的话
那在拖动弧中点的时候
圆心应该是一直在变化的
怎么也没可能
让弧中点与圆心无限接近

前置条件没明白
后面也就没去看了
难道说
4夹点的系统里
拖动弧中点的过程中
圆心的那个夹点是“静止”的
弧中心接近圆心时才发生变化?

highflybird 发表于 2014-8-22 11:35:15

本帖最后由 highflybird 于 2014-8-22 11:38 编辑

masterlong 发表于 2014-8-22 11:04 static/image/common/back.gif
老实说没看明白
不是指后面那些涉及数学的内容
虽然那些确实也没去看

我没用过2004版本,但2006版本的确是有4个夹点的。
你选中的时候,出现4个蓝色的夹点。
在2006版本的时候,拖动中点的那个夹点,弧心的那个夹点位置不变,只有确定后才发生改变。
哈哈,幸好我用的是2006,不然的话,还发现不了这一规律。

smartstar 发表于 2014-8-22 14:18:03

highflybird 是我们的偶像,真心想佩服!说胡话,前辈发内容我没有一个完全看懂的(其实最多只能看懂一点点),但是我真的很佩服前辈。一个小小的问题,您能会阐述出这么多的理论,佩服前辈这种“钻”的精神,是值得我们后辈学习!向highflybird致敬!!

自贡黄明儒 发表于 2014-8-22 14:55:37

能看懂LZ的帖子就能成为大师了,尽管只看懂了一点点,也使我在黑暗中少摸索了N多年,真心感谢

fan_zh 发表于 2014-8-22 15:18:05

前排仰视大神,晚辈向高老师学习!

邹锋 发表于 2014-8-22 23:35:03

不明白你那图片是怎么弄出来的

lidaxiu 发表于 2014-8-23 02:03:57

向highflybird大师学习,看来不仅是编程高手,而且是个数学天材,膜拜中,望尘莫及啊
页: [1] 2 3 4
查看完整版本: CAD中的美与乐