我想做一个自定义的分形
我要做的是创建一个半径为R的圆,假设我们内部有3个较小的圆,它们碰到了较大的圆,它们的大小都相同,我想根据大圆的半径
我可以计算出公式并使用一个找到半径的函数:
def formula(R):
return ((sqrt(3)*R)/(sqrt(3)+2))
如果有人需要我可以解释
我现在要做的是编写一个递归函数,用pygame再次为每个小圆圈执行此操作
答案 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>