明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 11304|回复: 32

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

  [复制链接]
发表于 2014-8-22 10:34:43 | 显示全部楼层 |阅读模式
本帖最后由 highflybird 于 2014-8-22 11:01 编辑

我是属于爱折腾的那一类人的。
CAD是我这个专业的必备工具。一天到晚都离不开它。
当然,有时候画图累了,就想开开小差。偶然的一次,我在用CAD的夹点拉伸的时候,竟然发现了一个很有意思的现象。
用过CAD的人基本都知道,一段弧有四个夹点 :弧的两个端点、弧段的中点、弧心。当我们把弧的中间的那个夹点拉到弧心的那个夹点位置后,弧心的那个夹点位置却跑走了,然后我又把弧中间的夹点拉到新的弧心处,当我这样重复下去,我开始以为整个弧会最后定在一个位置,但实际上没有,无论我这样做多少次,弧的位置永远也不会固定。
这个偶然的发现引起了我的注意。我觉得不是一个普通的问题,我感觉它是发散的。QJchen博士用Maple佐证了我的感觉–它的确是发散的!但是,发散的,并不意味这这些夹点的位置是毫无规律的。那么,我就开始折腾了:
首先,我用公式找出它们之间的关系,通过一番简单的数学推理,得到如下图的公式:

这个关系看起来比我想象的要简单多了,跟求开平方的公式仅仅相差一个正负号,如此的简洁而美妙的公式。当然这个公式是对于实数集的,我又展开联想了,如果把这个公式扩展到复数集呢,按照这样的迭代,是怎样的效果呢?
很好,那么稍加推理,对某个位置(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于深圳

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x

评分

参与人数 6明经币 +9 金钱 +160 收起 理由
redcat + 1 好人品 厚积薄发
lucas_3333 + 1 很给力!
【KAIXIN】 + 1 + 50 很给力!
yjr111 + 1 + 50 丘比特神箭射过~~
qjchen + 2 + 30 赞一个!
Gu_xl + 3 + 30 赞一个!

查看全部评分

"觉得好,就打赏"
还没有人打赏,支持一下

本帖被以下淘专辑推荐:

发表于 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看了看效果还行

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

使用道具 举报

发表于 2020-1-3 14:20:45 | 显示全部楼层
highflybird大师才是真正的高高手!!!
 楼主| 发表于 2014-8-22 10:41:55 | 显示全部楼层
本帖最后由 highflybird 于 2014-8-22 15:05 编辑

程序的LISP代码:
  1. ;;;*********************************************
  2. ;;; 迭代公式(用户可以自行修改)               
  3. ;;; x,xx复数的实数部分,y,yy复数的虚数部分      
  4. ;;; xx = (x-x/(x*x+y*y))/2+cx                  
  5. ;;; yy = (y+y/(x*x+y*y))/2+cy                  
  6. ;;;*********************************************
  7. (defun fx (x y cx cy)
  8.   (if (and (zerop x) (zerop y))
  9.     cx
  10.     (+ (* 0.5 (- x (/ x (+ (* x x) (* y y))))) cx)
  11.   )
  12. )
  13. (defun fy (x y cx cy)
  14.   (if (and (zerop x) (zerop y))
  15.     cy
  16.     (+ (* 0.5 (+ y (/ y (+ (* x x) (* y y))))) cy)
  17.   )
  18. )

  19. ;;;*********************************************
  20. ;;; 逃逸判断:                                 
  21. ;;; 当两个复数相接近的时候停止迭代              
  22. ;;;*********************************************
  23. (defun Escape (x xx y yy tol)
  24.   (< (+ (* (- xx x) (- xx x)) (* (- yy y) (- yy y))) tol)
  25. )
  26.             
  27. ;;;*********************************************
  28. ;;; Highflybird分形的绘制主函数                 
  29. ;;; 参数: col0 初始颜色值                       
  30. ;;;       X1,X2,Y1,Y2,点的取值范围              
  31. ;;;       W,H  图像的宽和高                     
  32. ;;;       tol  逃逸半径                        
  33. ;;;       Itr  迭代最大次数                     
  34. ;;;       Grad 颜色梯度                        
  35. ;;;*********************************************
  36. (defun HFB_fractal (col0 X1 X2 Y1 Y2 W H tol Itr grad /
  37.         i j dx dy HSL0 cx cy x y n xx yy)
  38.   (setq HSL0 (apply 'RGB->HSL col0))
  39.   (setq dx (/ (- X2 X1 0.0) W))
  40.   (setq dy (/ (- Y2 Y1 0.0) H))
  41.   (setq i 0)            
  42.   (repeat W                                     ;图像的宽
  43.     (setq j 0)
  44.     (repeat H                                   ;图像的高
  45.       (setq cx (+ X1 (* i dx)))                 ;实数部分
  46.       (setq cy (+ Y1 (* j dy)))                 ;虚数部分
  47.       (setq x cx y cy)                          ;开始迭代位置
  48.       (setq n 0)                                ;迭代次数置零
  49.       (while (<= n Itr)                         ;开始迭代
  50.   (setq xx (fx x y cx cy))                ;得到新的实数部分
  51.   (setq yy (fy x y cx cy))                ;得到新的虚数部分
  52.   (if (Escape xx x yy y tol)              ;如果满足逃逸函数
  53.     (progn                                 
  54.       (PutColor HSL0 i j n grad)          ;着色这点
  55.       (setq n Itr)                        ;中断循环
  56.     )                                          
  57.     (setq x xx y yy)                      ;否则继续迭代
  58.   )
  59.   (setq n (1+ n))
  60.       )
  61.       (setq j (1+ j))
  62.     )
  63.     (setq i (1+ i))
  64.   )
  65. )

当然用LISP速度比较慢,最后我用了ARX编程,才满足了所见即所得的效果。
顺便推销一下我的博客地址:http://highflybird.mjtd.com/
欢迎大家有空转转哦!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
发表于 2014-8-22 10:45:56 | 显示全部楼层
高飞鸟神一般的存在!飞在云端看不见啊
太高深,我等这一辈子都不法理解啊
发表于 2014-8-22 11:04:21 | 显示全部楼层
老实说没看明白
不是指后面那些涉及数学的内容
虽然那些确实也没去看

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

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

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

 楼主| 发表于 2014-8-22 11:35:15 | 显示全部楼层
本帖最后由 highflybird 于 2014-8-22 11:38 编辑
masterlong 发表于 2014-8-22 11:04
老实说没看明白
不是指后面那些涉及数学的内容
虽然那些确实也没去看

我没用过2004版本,但2006版本的确是有4个夹点的。
你选中的时候,出现4个蓝色的夹点。
在2006版本的时候,拖动中点的那个夹点,弧心的那个夹点位置不变,只有确定后才发生改变。
哈哈,幸好我用的是2006,不然的话,还发现不了这一规律。
发表于 2014-8-22 14:18:03 | 显示全部楼层
highflybird 是我们的偶像,真心想佩服!说胡话,前辈发内容我没有一个完全看懂的(其实最多只能看懂一点点),但是我真的很佩服前辈。一个小小的问题,您能会阐述出这么多的理论,佩服前辈这种“钻”的精神,是值得我们后辈学习!向highflybird致敬!!
发表于 2014-8-22 14:55:37 | 显示全部楼层
能看懂LZ的帖子就能成为大师了,尽管只看懂了一点点,也使我在黑暗中少摸索了N多年,真心感谢
发表于 2014-8-22 15:18:05 | 显示全部楼层
前排仰视大神,晚辈向高老师学习!

发表于 2014-8-22 23:35:03 | 显示全部楼层
不明白你那图片是怎么弄出来的

点评

大师不是发了源码吗?lisp速度慢只能生成类似的,分辨率不能太高,大师也说了是用的ARX才达到这种效果的  发表于 2014-8-23 08:15
发表于 2014-8-23 02:03:57 | 显示全部楼层
向highflybird大师学习,看来不仅是编程高手,而且是个数学天材,膜拜中,望尘莫及啊
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-11-26 22:47 , Processed in 0.277920 second(s), 36 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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