递归金三角,三角形趋近于哪个点?

Recursive golden triangle, which point does the triangles approach?

我正在尝试制作一个旋转缩放递归金三角。它绘制了一个金色三角形,然后在其中绘制了另一个,依此类推。这很容易,但挑战在于让它放大并围绕三角形接近的点旋转。

为了让它无限放大那个点,我需要想出公式来计算三角形接近哪个点。

运行 此时演示:https://waltari10.github.io/recursive-golden-triangle/index.html

存储库:https://github.com/Waltari10/recursive-golden-triangle

/**
 * 
 * @param {float[]} pivot
 * @param {float} angle 
 * @param {float[]} point 
 * @returns {float[]} point 
 */
function rotatePoint(pivot, angle, point)
{
  const s = Math.sin(angle);
  const c = Math.cos(angle);


  const pointOriginX = point[0] - pivot[0];
  const pointOriginY = point[1] - pivot[1];

  // rotate point
  const xNew = (pointOriginX * c) - (pointOriginY * s);
  const yNew = (pointOriginX * s) + (pointOriginY * c);

  const newPoint = [
    pivot[0] + xNew,
    pivot[1] + yNew,
  ]

  return newPoint;
}

// https://www.onlinemath4all.com/90-degree-clockwise-rotation.html
// 
// Position is half way between points B and C 72 and 72, because AB/BC is golden ratio
function drawGoldenTriangle(pos, height, rotation, color = [0,255,0,255], pivot) {

// golden triangle degrees 72, 72, 36
// golden gnomon 36, 36, 108
// AB/BC is the golden ratio number
// https://www.mathsisfun.com/algebra/sohcahtoa.html

  const baseLength = (Math.tan(degToRad(18)) * height) * 2;

  const pointA = rotatePoint(pos, rotation, [pos[0], pos[1] - height]); // sharpest angle
  const pointB = rotatePoint(pos, rotation, [pos[0] - (baseLength / 2), pos[1]]); 
  const pointC = rotatePoint(pos, rotation, [pos[0] + (baseLength / 2), pos[1]]);


  drawTriangle(pointA, pointB, pointC, [0,255,0,255]);

}

let i = 0;

function drawRecursiveGoldenTriangle(pos, height, rotation, pivot) {

  
  drawGoldenTriangle(pos, height, rotation, [0,255,0,255], pivot);
  i++;

  if (i > 10) {
    return;
  }


  const hypotenuseLength = height / Math.cos(degToRad(18));
  const baseLength = (Math.tan(degToRad(18)) * height) * 2;
  const goldenRatio = hypotenuseLength / baseLength;

  const newHeight = height / goldenRatio;

  const newRotation = rotation - 108 * Math.PI/180

  const newPointC = rotatePoint(pos, rotation, [pos[0] + (baseLength / 2), pos[1]]);

  // Go half baselength up CA direction from pointC to get new position
  const newHypotenuseLength = baseLength;
  const newBaseLength = newHypotenuseLength / goldenRatio;

  let newPosXRelative = Math.cos(newRotation) * (newBaseLength / 2)
  let newPosYRelative = Math.sin(newRotation) * (newBaseLength / 2)
  
  const newPos = [newPointC[0] + newPosXRelative, newPointC[1] + newPosYRelative];

  drawRecursiveGoldenTriangle(newPos, newHeight, newRotation, [0,255,0,255], pivot); 

  
}

let triangleHeight = height - 50;

let pivotPoint = [(width/2),(height/2) -50];
let triangleLocation = [width/2, height/2 + 300];


let triangleRotation = 0;

function loop() {
  i = 0;

  const startTime = Date.now()
  wipeCanvasData();

  // triangleHeight++; 
  // triangleRotation = triangleRotation + 0.005;


  // drawX(pivotPoint)
  // drawX(triangleLocation)

  
  // Pivot point determines the point which the recursive golden 
  // triangle rotates around. Should be the point that triangles 
  // approach.
  drawRecursiveGoldenTriangle(triangleLocation, triangleHeight, triangleRotation, pivotPoint); 

  updateCanvas()
  const renderTime = Date.now() - startTime
  timeDelta = renderTime < targetFrameDuration ? targetFrameDuration : renderTime
  this.setTimeout(() => {
    loop()
  }, targetFrameDuration - renderTime)
}

loop()

递归金三角逼近点的计算公式是什么?或者在这种情况下我可以做一些聪明的 hack 吗?

logarithmic spiral的起点由startingPoint(a,b,c)计算,其中a,b,c 是三角形的点:

片段中的三角形不正确'golden triangle'但计算应该是正确的...

const distance = (p1, p2) => Math.hypot(p2.x - p1.x, p2.y - p1.y);

const intersection = (p1, p2, p3, p4) => {
  const l1A = (p2.y - p1.y) / (p2.x - p1.x);
  const l1B = p1.y - l1A * p1.x;

  const l2A = (p4.y - p3.y) / (p4.x - p3.x);
  const l2B = p3.y - l2A * p3.x;
  
  const x = (l2B - l1B) / (l1A - l2A);
  const y = x * l1A + l1B;
  
  return {x, y};
}

const startingPoint = (a, b, c) => {
  const ac = distance(a, c);
  const ab = distance(a, b);
  const bc = distance(b, c);
  // Law of cosines
  const alpha = Math.acos((ab * ab + ac * ac - bc * bc) / (2 * ab * ac));
  const gamma = Math.acos((ac * ac + bc * bc - ab * ab) / (2 * ac * bc));
  const delta = Math.PI - alpha / 2 - gamma;
  // Law of sines
  const cd = ac * Math.sin(alpha / 2) / Math.sin(delta);
  const d = {
    x: cd * (b.x - c.x) / bc + c.x,
    y: cd * (b.y - c.y) / bc + c.y
  };
  const e = {
    x: (a.x + c.x) / 2,
    y: (a.y + c.y) / 2
  };
  const f = {
    x: (a.x + b.x) / 2,
    y: (a.y + b.y) / 2,
  };
  return intersection(c, f, d, e);
};

d3.select('svg').append('path')
  .attr('d', 'M 100,50 L150,200 H 50 Z')
  .style('fill', 'none')
  .style('stroke', 'blue')
  
const point = startingPoint({x: 50, y: 200},{x: 100, y: 50},{x: 150, y: 200});  
console.log(point);

d3.select('svg').append('circle')
  .attr('cx', point.x)  
  .attr('cy', point.y)
  .attr('r', 5)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="200" height="400"></svg>