无论如何,在pygame中递归地执行此操作?

时间:2019-12-23 12:47:11

标签: python pygame

我想做一个自定义的分形

this is my fractal

我要做的是创建一个半径为R的圆,假设我们内部有3个较小的圆,它们碰到了较大的圆,它们的大小都相同,我想根据大圆的半径

我可以计算出公式并使用一个找到半径的函数:

def formula(R):
    return ((sqrt(3)*R)/(sqrt(3)+2))

如果有人需要我可以解释

我现在要做的是编写一个递归函数,用pygame再次为每个小圆圈执行此操作

2 个答案:

答案 0 :(得分:4)

定义归一化向量,从原点(0,0)到Equilateral triangle的拐角点:

sin60 = math.sin(math.pi/3) # math.sqrt(3) / 2
tri_pt = [[-sin60, 0.5], [sin60, 0.5], [0, -1]]

使用"Radius of inner circles given radius of outer circle and number of inner circles in circular fractal"中给出的公式来计算内圆的半径以及从中心到内圆的中心点的距离:

in_r     = radius * math.sin(math.pi/3) / (math.sin(math.pi/3)+1)
in_cpt_d = radius - in_r

计算循环中内圆的中心点:

colors = [(255, 0, 0), (0, 0, 255), (255, 255, 0)]

for pt, color in zip(tri_pt, colors):
    in_cpt = (cpt[0] + pt[0] * in_cpt_d), (cpt[1] + pt[1] * in_cpt_d)

    pygame.draw.circle(window, color, (round(in_cpt[0]), round(in_cpt[1])), round(in_r), 1)

请参见使用递归函数的示例:

import pygame, math

def innerCircles(cpt, radius, depth):
    if depth == 4:
        return

    sin60    = math.sin(math.pi/3) # math.sqrt(3) / 2
    tri_pt   = [[-sin60, 0.5], [sin60, 0.5], [0, -1]]
    in_r     = radius * sin60 / (sin60+1)
    in_cpt_d = radius - in_r

    colors = [(255, 0, 0), (0, 0, 255), (255, 255, 0)]
    for pt, color in zip(tri_pt, colors):
        in_cpt = (cpt[0] + pt[0] * in_cpt_d), (cpt[1] + pt[1] * in_cpt_d)
        pygame.draw.circle(wnd, color, (round(in_cpt[0]), round(in_cpt[1])), round(in_r), 1)
        innerCircles(in_cpt, in_r, depth+1)

pygame.init()
wnd = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()
run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    wnd.fill(0)
    cpt = (250, 250)
    radius = 200
    pygame.draw.circle(wnd, (255, 255, 255), cpt, radius+1, 3)
    innerCircles(cpt, radius, 0)
    pygame.display.flip()

答案 1 :(得分:2)

作为一种零食/运动,我将其实现为与React互动的玩具(使用@Yevhenii M.的答案公式进行半径计算)。 (向下滚动并点击“运行”以查看其运行情况!)

(自然)分形的肉在drawFractal函数中;您可能应该能够将其改编成Python / PyGame,用drawCircle替换框架中所有画出圆圈的东西。

const nextRadius = (r1, n) =>
  (r1 * Math.sin(Math.PI / n)) / (1 + Math.sin(Math.PI / n));

function drawFractal(
  circleStyle,
  levels,
  branches,
  startRadius,
  anglePerLevel
) {
  const circles = [];
  function drawCircle(cx, cy, r, transform = null) {
    circles.push(
      <circle cx={cx} cy={cy} r={r} style={circleStyle} key={circles.length} />
    );
  }
  function drawLevel(centerX, centerY, lastRadius, n, levelsLeft) {
    const radius = nextRadius(lastRadius, n);
    const distance = lastRadius - radius;
    const level = levels - levelsLeft;
    for (var i = 0; i < n; i++) {
      const angle = (i / n + level * anglePerLevel) * Math.PI * 2;
      const x = Math.sin(angle) * distance;
      const y = Math.cos(angle) * distance;
      drawCircle(centerX + x, centerY + y, radius);
      if (levelsLeft > 0) {
        drawLevel(centerX + x, centerY + y, radius, n, levelsLeft - 1);
      }
    }
  }
  drawCircle(0, 0, startRadius);
  drawLevel(0, 0, startRadius, branches, levels);
  return circles;
}

function App() {
  const [branches, setBranches] = React.useState(3);
  const [levels, setLevels] = React.useState(3);
  const [twist, setTwist] = React.useState(0);
  const contents = drawFractal(
    {
      fill: "none",
      stroke: "red",
      strokeWidth: 1
    },
    levels,
    branches,
    250,
    twist
  );

  return (
    <div className="App">
      <label>
        Branches
        <input
          type="range"
          min={1}
          max={15}
          value={branches}
          onInput={e => setBranches(e.target.valueAsNumber)}
        />
      </label>
      <label>
        Levels
        <input
          type="range"
          min={1}
          max={7}
          value={levels}
          onInput={e => setLevels(e.target.valueAsNumber)}
        />
      </label>
      <label>
        Twist
        <input
          type="range"
          min={-1}
          max={1}
          step={0.001}
          value={twist}
          onInput={e => setTwist(e.target.valueAsNumber)}
        />
      </label>
      <hr />
      <svg
        viewbox="0 0 1000 1000"
        width={500}
        height={500}
        style={{ border: "1px solid black" }}
      >
        <g transform="translate(250 250)">{contents}</g>
      </svg>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>