I want to be able to lock the angle of the wheels relative to the car's chassis. In between the wheels, there are springs, that should allow the car to suspend, but right now, the angle is not locked. I am using pymunk's function "RotaryLimitJoint"
A behavior like this is the goal (gif)
Right now it looks like this:
My code:
car_pos = Vec2d(100,500)
mass = 30
radius = 10
moment = pymunk.moment_for_circle(mass, 20, radius)
wheel1_b = pymunk.Body(mass, moment)
wheel1_s = pymunk.Circle(wheel1_b, radius)
wheel1_s.friction = 1.5
wheel1_s.color = wheel_color
space.add(wheel1_b, wheel1_s)
mass = 30
radius = 10
moment = pymunk.moment_for_circle(mass, 20, radius)
wheel2_b = pymunk.Body(mass, moment)
wheel2_s = pymunk.Circle(wheel2_b, radius)
wheel2_s.friction = 1.5
wheel2_s.color = wheel_color
space.add(wheel2_b, wheel2_s)
mass = 100
size = (80,25)
moment = pymunk.moment_for_box(mass, size)
chassi_b = pymunk.Body(mass, moment)
chassi_s = pymunk.Poly.create_box(chassi_b, size)
chassi_s.color = chassi_color
space.add(chassi_b, chassi_s)
#Positions
chassi_b.position = car_pos + (0,-15)
wheel1_b.position = car_pos + (-25,0)
wheel2_b.position = car_pos + (25,0)
#Joints
spring1 = pymunk.DampedSpring(chassi_b, wheel1_b, (-25,0), (0,0), 20, 100000, 1)
spring1.collide_bodies = False
spring2 = pymunk.DampedSpring(chassi_b, wheel2_b, (25,0), (0,0), 20, 100000, 1)
spring2.collide_bodies = False
wheelAngle1 = pymunk.RotaryLimitJoint(wheel1_b, chassi_b, 0, 0)
wheelAngle1.collide_bodies = False
wheelAngle2 = pymunk.RotaryLimitJoint(chassi_b, wheel2_b, 0, 0)
wheelAngle2.collide_bodies = False
space.add(
spring1,
spring2,
wheelAngle1,
wheelAngle2
)
speed = 20
space.add(
pymunk.SimpleMotor(wheel1_b, chassi_b, speed),
pymunk.SimpleMotor(wheel2_b, chassi_b, speed)
)
First off credits to #viblo.
What makes the following code work, is that the GrooveJoint (see docs) is created perpendicular to the car's chassis. The GrooveJoint is defining a line, where a body is free to slide on. Defining the GrooveJoint it is attached to the car's chassis and to the wheel (for both the front and back wheel).
It looks like this now:
I converted the code (from #viblo) to python and here it is:
def car(space, speed, add_car):
car_pos = Vec2d(100,500)
#bodies
wheel_color = 0,0,0
chassi_color = 255,0,0
wheelCon_color = 0,255,255
#Wheel 1
mass = 25
radius = 10
moment = pymunk.moment_for_circle(mass, 20, radius)
wheel1_b = pymunk.Body(mass, moment)
wheel1_s = pymunk.Circle(wheel1_b, radius)
wheel1_s.friction = 1.5
wheel1_s.color = wheel_color
#Wheel 2
mass = 25
radius = 10
moment = pymunk.moment_for_circle(mass, 20, radius)
wheel2_b = pymunk.Body(mass, moment)
wheel2_s = pymunk.Circle(wheel2_b, radius)
wheel2_s.friction = 1.5
wheel2_s.color = wheel_color
#Chassi
mass = 30
size = (80,25)
moment = pymunk.moment_for_box(mass, size)
chassi_b = pymunk.Body(mass, moment)
chassi_s = pymunk.Poly.create_box(chassi_b, size)
chassi_s.color = chassi_color
#Positions
chassi_b.position = car_pos + (0,-15)
wheel1_b.position = car_pos + (-25,0)
wheel2_b.position = car_pos + (25,0)
#Joints
spring1 = pymunk.constraint.DampedSpring(chassi_b, wheel1_b, (-25,0), (0,0), 15, 5000, 250)
spring1.collide_bodies = False
spring2 = pymunk.constraint.DampedSpring(chassi_b, wheel2_b, (25,0), (0,0), 15, 5000, 250)
spring2.collide_bodies = False
groove1 = pymunk.constraint.GrooveJoint(chassi_b, wheel1_b, (-25,0), (-25,25), (0, 0))
groove1.collide_bodies = False
groove2 = pymunk.constraint.GrooveJoint(chassi_b, wheel2_b, (25,0), (25,25), (0,0))
groove2.collide_bodies = False
if add_car:
motor1 = pymunk.SimpleMotor(wheel1_b, chassi_b, speed)
motor2 = pymunk.SimpleMotor(wheel2_b, chassi_b, speed)
space.add(
spring1,
spring2,
groove1,
groove2,
motor1,
motor2,
chassi_b,
chassi_s,
wheel2_b,
wheel2_s,
wheel1_b,
wheel1_s
)
The RotaryLimitJoint is not what you need. It will constrain the angle between the wheel and chassis, but the wheel needs to rotate so it wont work.
What you can try with instead is a GrooveJoint.
This is how the code looks like in c code, it should be fairly easy to convert it to python:
Full source: https://github.com/slembcke/Chipmunk2D/blob/master/demo/Joints.c#L263
The relevant part:
boxOffset = cpv(0, 0);
cpBody *wheel1 = addWheel(space, posA, boxOffset);
cpBody *wheel2 = addWheel(space, posB, boxOffset);
cpBody *chassis = addChassis(space, cpv(80, 100), boxOffset);
cpSpaceAddConstraint(space, cpGrooveJointNew(chassis, wheel1, cpv(-30, -10), cpv(-30, -40), cpvzero));
cpSpaceAddConstraint(space, cpGrooveJointNew(chassis, wheel2, cpv( 30, -10), cpv( 30, -40), cpvzero));
cpSpaceAddConstraint(space, cpDampedSpringNew(chassis, wheel1, cpv(-30, 0), cpvzero, 50.0f, 20.0f, 10.0f));
cpSpaceAddConstraint(space, cpDampedSpringNew(chassis, wheel2, cpv( 30, 0), cpvzero, 50.0f, 20.0f, 10.0f));
If you cant figure it out I can try to convert it to python, but unfortunately Im not on a computer with Python & Pymunk setup for that now.
Related
I'm trying to straiting out a ring using Manim Community. I want to acheive an animation exactly like this straiting a ring. I followed the code source of this video but the out put is not exactly the same (I used Manim Community and manimgl). Here is the code
class CircleScene(Scene):
def get_ring(self, radius=1, dR=0.1, color = GREEN):
ring = Circle(radius = radius + dR)
inner_ring = Circle(radius = radius)
inner_ring.rotate(np.pi,RIGHT)
ring.append_vectorized_mobject(inner_ring)
ring.set_stroke(width = 0)
ring.set_fill(color,1)
ring.R = radius
ring.dR = dR
return ring
def get_unwrapped(self, ring, to_edge = LEFT, **kwargs):
R = ring.R
R_plus_dr = ring.R + ring.dR
n_anchors = ring.get_num_curves()
result = VMobject()
points=[
interpolate(np.pi*R_plus_dr*LEFT, np.pi*R_plus_dr*RIGHT, a)
for a in np.linspace(0, 1, n_anchors // 2)
]+[
interpolate(np.pi*R*RIGHT+ring.dR*UP, np.pi*R*LEFT+ring.dR*UP, a)
for a in np.linspace(0, 1, n_anchors//2)
]
result.set_points_as_corners(points)
result.set_stroke(color=ring.get_fill_color(), width= ring.get_stroke_width()),
result.set_fill( color=ring.get_fill_color() , opacity=ring.get_fill_opacity()),
return result
class Example(CircleScene):
def construct(self,**kwargs):
circle=self.get_ring(1.5,0.1).rotate(PI/2);
line=self.get_unwrapped(circle).to_edge(DOWN)
self.play(Transform(circle,line))
I know that we can strait a circle to a line using the following code
circle=Circle(1).rotate(PI/2);
line=Line((-PI,-2,0),(PI,-2,0))
self.play(Transform(circle,line))
I am trying to draw a rectangle which would have an edge next to each of its sides (it should represent a building with a fence) for one building it goes quite well, but when I am trying to add a new building it messes itself up completely.
I have this code to create buildings:
class Building():
def __init__(self, Box_2_World,shape, position, sensor= None):
self.Box_2_World = Box_2_World
self.shape = shape
self.position = position
if sensor == None:
sensor = False
self.sensor = sensor
self.footprint = self.Box_2_World.CreateStaticBody(position = position,
angle = 0.0,
fixtures = b2FixtureDef(
shape = b2PolygonShape(box=(self.shape)),
density = 1000,
friction = 1000))
self.Lower_Fence = self.Box_2_World.CreateStaticBody(position=(self.footprint.position[0],self.footprint.position[1] -1.75*self.shape[1]))
self.Lower_Fence.CreateEdgeChain([(self.Lower_Fence.position[0]-4.25*self.shape[0],self.Lower_Fence.position[1]),
(self.Lower_Fence.position[0]-2.25*self.shape[0],self.Lower_Fence.position[1]),
])
self.Right_Fence = self.Box_2_World.CreateStaticBody(position=(self.footprint.position[0]-1*self.shape[0],self.footprint.position[1]))
self.Right_Fence.CreateEdgeChain([(self.Right_Fence.position[0],self.Right_Fence.position[1] - 1.25*self.shape[1]),
(self.Right_Fence.position[0],self.Right_Fence.position[1]-3.25*self.shape[1]),
])
self.Upper_Fence = self.Box_2_World.CreateStaticBody(position=(self.footprint.position[0],self.footprint.position[1] -0.45* self.shape[1]))
self.Upper_Fence.CreateEdgeChain([(self.Upper_Fence.position[0] - 4.25* self.shape[0],self.Upper_Fence.position[1]),
(self.Upper_Fence.position[0]- 3.25* self.shape[0]+ self.shape[0],self.Upper_Fence.position[1]),
])
self.Left_Fence = self.Box_2_World.CreateStaticBody(position=(self.footprint.position[0]-2.25*self.shape[0],self.footprint.position[1]))
self.Left_Fence.CreateEdgeChain([(self.Left_Fence.position[0],self.Left_Fence.position[1] - 1.25*self.shape[1]),
(self.Left_Fence.position[0],self.Left_Fence.position[1]-3*self.shape[1]),
])
Skyscrapers = []
Rectangles = [(pos_X-5, pos_Y-5),(pos_X+15, pos_Y -5),(pos_X - 5,pos_Y + 15),(pos_X+15, pos_Y + 15)]
for i in range(4):
Skyscrapers.append(Building(Box_2_World,shape = (5,5), position = Rectangles[i]))
and these functions to draw it using PyGame:
SCREEN_OFFSETX, SCREEN_OFFSETY = SCREEN_WIDTH/16, SCREEN_HEIGHT
def fix_vertices(vertices):
return [(int(SCREEN_OFFSETX + v[0]), int(SCREEN_OFFSETY - v[1])) for v in vertices]
def _draw_polygon(polygon, screen, body, fixture):
transform = body.transform
vertices = fix_vertices([transform * v * PPM for v in polygon.vertices])
pygame.draw.polygon(
screen, [c / 2.0 for c in colors[body.type]], vertices, 0)
pygame.draw.polygon(screen, colors[body.type], vertices, 1)
polygonShape.draw = _draw_polygon
def _draw_edge(edge, screen, body, fixture):
vertices = fix_vertices(
[body.transform * edge.vertex1 * PPM, body.transform * edge.vertex2 * PPM])
pygame.draw.line(screen, colors[body.type], vertices[0], vertices[1])
edgeShape.draw = _draw_edge
And the output is this: Blue rectangles represent buildings, blue lines are fences, first building fits quite nice, but the others are for some reason out of desired positions
Also, if you find out a way how to create the fences using for loop, it would be great (that's the reason why I put for-loop tag into this question)
Any help appreciated
The coordinates which are passed to CreateEdgeChain have to be relative to the body.
The position of the body is set when the object is constructed e.g:
self.Lower_Fence = self.Box_2_World.CreateStaticBody(
position=(self.footprint.position[0], self.footprint.position[1] -1.75*self.shape[1]))
And the edges have to be relative to this position rather than an absolute position.e.g:
self.Lower_Fence.CreateEdgeChain([(-4.25*self.shape[0], 0), (-2.25*self.shape[0], 0)])
To create the fences in a for-loop, you've to define a list of the edges. With the list of the edges you can create a list of fences:
fence_edges = [
[(-4.25, -1.75), (-2.25, -1.75)],
[(-1.00, -1.25), (-1.00, -3.25)],
[(-4.25, -0.45), (-2.25, -0.45)],
[(-2.25, -1.25), (-2.25, -3.25)]
]
self.Fences = []
for edge in fence_edges:
p1, p2 = edge
fence = self.Box_2_World.CreateStaticBody(position=self.footprint.position[:])
fence.CreateEdgeChain(
[(p1[0] * self.shape[0], p1[1] * self.shape[1]),
(p2[0] * self.shape[0], p2[1] * self.shape[1])])
self.Fences.append(fence)
self.Lower_Fence, self.Right_Fence, self.Upper_Fence, self.Left_Fence = self.Fences
We start building an Enviroment, based on cartpole-v0. We are trying to achieve a similar behaviour of our pole, so it doesn't rotate in the centerpoint, but at the Bottom. We are using set_rotation function from gym.classic_control.rendering. But there is no opportunity to set an anchorpoint.
We tried to translate the filledPolygon before rotation in different directions, but the anchorpoint remains in centerpoint.
import math
import gym
from gym import spaces, logger
from gym.utils import seeding
import numpy as np
from os import path
class THT_Env(gym.Env):
'''
I shorten the code to the render. The other parts of the code is working fine.
'''
def render(self, mode='human'):
screen_width = 600
screen_height = 600
lead_width = 6 #Lead diameter = 0.6mm
lead_length = 103 #Lead lenght ca. 10.3 mm
lead_spacing = 50 #Lead spacing 5 mm
body_height = 80 #Body height 8 mm
body_width = 65 #Body width 6.5 mm
pcb_thickness = 16 #1.6 mm
#pcb_hole_diameter = 9 #0.9mm
pcb_side = 270.5
pcb_middle = 41
if self.viewer is None:
from gym.envs.classic_control import rendering
self.viewer = rendering.Viewer(screen_width, screen_height)
# Initialize Body
fname = path.join(path.dirname(__file__), "assets/WYO_1nM.png")
body = rendering.Image(fname,body_width, body_height)
self.bodytrans = rendering.Transform()
body.add_attr(self.bodytrans)
self.viewer.add_geom(body)
# Initialize Lead 1
l, r, t, b = -lead_width/2, lead_width/2, lead_length/2, -lead_length/2
lead_1 = rendering.FilledPolygon([(l,b),(l,t),(r,t),(r,b)])
lead_1.set_color(.4, .4, .4)
self.lead_1_trans = rendering.Transform(translation=(lead_spacing/2, (-lead_length-body_height)/2))
lead_1.add_attr(self.lead_1_trans)
lead_1.add_attr(self.bodytrans)
self.viewer.add_geom(lead_1)
lead_2 = rendering.FilledPolygon([(l,b),(l,t),(r,t),(r,b)])
lead_2.set_color(.4, .4, .4)
#self.lead_2_trans = rendering.Transform(translation=(-lead_spacing/2, (-lead_length-body_height)/2))
self.lead_2_trans = rendering.Transform(translation=(0, (-lead_length-body_height)/2),rotation=np.pi/2)
lead_2.add_attr(self.lead_2_trans)
lead_2.add_attr(self.bodytrans)
self.viewer.add_geom(lead_2)
l, r, t, b = -pcb_side/2, pcb_side/2, pcb_thickness/2, -pcb_thickness/2
pcb_1 = rendering.FilledPolygon([(l,b),(l,t),(r,t),(r,b)])
pcb_1.set_color(.0, .42, .0)
self.pcb_1_trans = rendering.Transform(translation=(0+pcb_side/2, 110))
pcb_1.add_attr(self.pcb_1_trans)
self.viewer.add_geom(pcb_1)
pcb_2 = rendering.FilledPolygon([(l,b),(l,t),(r,t),(r,b)])
pcb_2.set_color(.0, .42, .0)
self.pcb_2_trans = rendering.Transform(translation=(screen_width-pcb_side/2, 110))
pcb_2.add_attr(self.pcb_2_trans)
self.viewer.add_geom(pcb_2)
l, r, t, b = -pcb_middle/2, pcb_middle/2, pcb_thickness/2, -pcb_thickness/2
self.pcb_mid = rendering.FilledPolygon([(l,b),(l,t),(r,t),(r,b)])
self.pcb_mid.set_color(.0, .42, .0)
self.pcb_mid_trans = rendering.Transform(translation=(screen_width/2, 110))
self.pcb_mid.add_attr(self.pcb_mid_trans)
self.viewer.add_geom(self.pcb_mid)
if self.state is None: return None
x = self.state
body_x = x[0]+screen_width/2
body_y = x[1]+screen_height/2+200
self.bodytrans.set_translation(body_x, body_y)
#self.lead_1_trans.set_translation(0,-lead_length/2)
#self.lead_1_trans.set_rotation(np.pi/2)
return self.viewer.render(return_rgb_array= mode == 'rgb_array')
def close(self):
if self.viewer:
self.viewer.close()
self.viewer = None
The rotation on the right image results in the circumstance the the pole(lead) is no longer connected to the body. What I expect is that the Anchor lies in the lower part of the body.
We were thinking the wrong wayfrom start of. Before we tryed to work with gym we build something with pyglet and learned that pyglet change anchor of images. When building our own gym we centered our leads and thought that we also could change the anchors. After a short pause I realized that the pole of cartpole is centred on the bottom with l,r,t,b.
So the simple solution in our situation was to change this line
l, r, t, b = -lead_width/2, lead_width/2, lead_length/2, -lead_length/2
into this line
l, r, t, b = -lead_width/2, lead_width/2, 0, -lead_length
So the result of this:
Anchor points of gyms pollygons are controlled by vertexes.
I am trying to draw a rectangle which would have an edge next to each of its sides (it should represent a building with a fence) for one building it goes quite well, but when I am trying to add a new building it messes itself up completely.
I have this code to create buildings:
class Building():
def __init__(self, Box_2_World,shape, position, sensor= None):
self.Box_2_World = Box_2_World
self.shape = shape
self.position = position
if sensor == None:
sensor = False
self.sensor = sensor
self.footprint = self.Box_2_World.CreateStaticBody(position = position,
angle = 0.0,
fixtures = b2FixtureDef(
shape = b2PolygonShape(box=(self.shape)),
density = 1000,
friction = 1000))
self.Lower_Fence = self.Box_2_World.CreateStaticBody(position=(self.footprint.position[0],self.footprint.position[1] -1.75*self.shape[1]))
self.Lower_Fence.CreateEdgeChain([(self.Lower_Fence.position[0]-4.25*self.shape[0],self.Lower_Fence.position[1]),
(self.Lower_Fence.position[0]-2.25*self.shape[0],self.Lower_Fence.position[1]),
])
self.Right_Fence = self.Box_2_World.CreateStaticBody(position=(self.footprint.position[0]-1*self.shape[0],self.footprint.position[1]))
self.Right_Fence.CreateEdgeChain([(self.Right_Fence.position[0],self.Right_Fence.position[1] - 1.25*self.shape[1]),
(self.Right_Fence.position[0],self.Right_Fence.position[1]-3.25*self.shape[1]),
])
self.Upper_Fence = self.Box_2_World.CreateStaticBody(position=(self.footprint.position[0],self.footprint.position[1] -0.45* self.shape[1]))
self.Upper_Fence.CreateEdgeChain([(self.Upper_Fence.position[0] - 4.25* self.shape[0],self.Upper_Fence.position[1]),
(self.Upper_Fence.position[0]- 3.25* self.shape[0]+ self.shape[0],self.Upper_Fence.position[1]),
])
self.Left_Fence = self.Box_2_World.CreateStaticBody(position=(self.footprint.position[0]-2.25*self.shape[0],self.footprint.position[1]))
self.Left_Fence.CreateEdgeChain([(self.Left_Fence.position[0],self.Left_Fence.position[1] - 1.25*self.shape[1]),
(self.Left_Fence.position[0],self.Left_Fence.position[1]-3*self.shape[1]),
])
Skyscrapers = []
Rectangles = [(pos_X-5, pos_Y-5),(pos_X+15, pos_Y -5),(pos_X - 5,pos_Y + 15),(pos_X+15, pos_Y + 15)]
for i in range(4):
Skyscrapers.append(Building(Box_2_World,shape = (5,5), position = Rectangles[i]))
and these functions to draw it using PyGame:
SCREEN_OFFSETX, SCREEN_OFFSETY = SCREEN_WIDTH/16, SCREEN_HEIGHT
def fix_vertices(vertices):
return [(int(SCREEN_OFFSETX + v[0]), int(SCREEN_OFFSETY - v[1])) for v in vertices]
def _draw_polygon(polygon, screen, body, fixture):
transform = body.transform
vertices = fix_vertices([transform * v * PPM for v in polygon.vertices])
pygame.draw.polygon(
screen, [c / 2.0 for c in colors[body.type]], vertices, 0)
pygame.draw.polygon(screen, colors[body.type], vertices, 1)
polygonShape.draw = _draw_polygon
def _draw_edge(edge, screen, body, fixture):
vertices = fix_vertices(
[body.transform * edge.vertex1 * PPM, body.transform * edge.vertex2 * PPM])
pygame.draw.line(screen, colors[body.type], vertices[0], vertices[1])
edgeShape.draw = _draw_edge
And the output is this: Blue rectangles represent buildings, blue lines are fences, first building fits quite nice, but the others are for some reason out of desired positions
Also, if you find out a way how to create the fences using for loop, it would be great (that's the reason why I put for-loop tag into this question)
Any help appreciated
The coordinates which are passed to CreateEdgeChain have to be relative to the body.
The position of the body is set when the object is constructed e.g:
self.Lower_Fence = self.Box_2_World.CreateStaticBody(
position=(self.footprint.position[0], self.footprint.position[1] -1.75*self.shape[1]))
And the edges have to be relative to this position rather than an absolute position.e.g:
self.Lower_Fence.CreateEdgeChain([(-4.25*self.shape[0], 0), (-2.25*self.shape[0], 0)])
To create the fences in a for-loop, you've to define a list of the edges. With the list of the edges you can create a list of fences:
fence_edges = [
[(-4.25, -1.75), (-2.25, -1.75)],
[(-1.00, -1.25), (-1.00, -3.25)],
[(-4.25, -0.45), (-2.25, -0.45)],
[(-2.25, -1.25), (-2.25, -3.25)]
]
self.Fences = []
for edge in fence_edges:
p1, p2 = edge
fence = self.Box_2_World.CreateStaticBody(position=self.footprint.position[:])
fence.CreateEdgeChain(
[(p1[0] * self.shape[0], p1[1] * self.shape[1]),
(p2[0] * self.shape[0], p2[1] * self.shape[1])])
self.Fences.append(fence)
self.Lower_Fence, self.Right_Fence, self.Upper_Fence, self.Left_Fence = self.Fences
I'm trying to achieve the pattern below.
Got as far as doing the first line, then I have no clue how to code the rest of the pattern.
Here's what I've done so far:
#Timothy Shek
from graphics import*
#open Graph Window
def main():
win = GraphWin("Example",100,100)
x = 7
y = 7
radius = 5
while x<=30 :
centre = Point(x,y)
circle1 = Circle(centre,radius)
circle1.setFill("red")
circle1.draw(win)
x = x+10
while x>=35 and x<=65 :
centre = Point(x+5,y)
circle2 = Circle(centre,radius)
circle2.setFill("red")
circle2.draw(win)
x = x+10
print(x)
while x>=67:
centre = Point(x+10,y)
circle1 = Circle(centre,radius)
circle1.setFill("red")
circle1.draw(win)
x = x+10
main()
I got it guys, thanks
Heres the solution
#Timothy Shek
from graphics import*
#open Graph Window
def main():
win = GraphWin("Patch2" ,100,100)
for x in (5, 15, 25, 40,50,60,75,85,95):
for y in (5, 15, 25, 40,50,60,75,85,95):
c = Circle(Point(x+2,y), 5)
d = Circle(Point(x+2,y), 5)
c.draw(win)
d.draw(win)
c.setFill("Red")
d.setFill("Red")
if x==15 or x==50 or x== 85:
if y==15 or y==50 or y== 85:
c2 = Circle(Point(x+2,y),5)
c2.draw(win)
c2.setFill("White")
main()
While there is nothing wrong with your solution, this is a bit more performant
from graphics import *
def main():
win = GraphWin("Patch2" ,100,100)
coords = [5, 15, 25, 40, 50, 60, 75, 85, 95]
centers = set([coords[i] for i in range(1, len(coords), 3)])
for i in xrange(len(coords)):
for j in xrange(i+1):
x, y = (coords[i], coords[j])
c1 = Circle(Point(x+2,y), 5)
c2 = Circle(Point(y+2,x), 5)
c1.draw(win)
c2.draw(win)
if x in centers and y in centers:
c1.setFill("White")
c2.setFill("White")
else:
c1.setFill("Red")
c2.setFill("Red")
main()
Update: "Better" version
And since I got bored and I liked this problem (yes, I program when I'm bored) I made a fully parameter-ized version which you can do some fun stuff with, like.
Probably over your head :) But maybe you learn something from it, so I'm posting it.
from graphics import *
def drawPattern(scale):
# Inner method: Draw a square of circles given the top-left point
def drawSquare(win, xCoord, yCoord, squareSize=30, numCircles=3, scale=1, scaleCircles=False, outer_color="Red", inner_color="White"):
# Overwrite the default scaling
if scale > 1:
squareSize *= scale
if scaleCircles:
numCircles *= scale
radius = squareSize/(numCircles*2) # Divide by 2 since it's the radius
from math import sqrt, floor
centerDiff = (2*radius) * floor(sqrt(numCircles)) # Used for drawing off-color circles
# xrange uses an exclusive stop value, so go one value past to make inclusive
for x in xrange(radius, squareSize+radius, radius*2):
for y in xrange(squareSize-radius, x-radius, -radius*2):
c1 = Circle(Point(x+xCoord+2,y+yCoord), radius)
c2 = Circle(Point(y+yCoord+2,x+xCoord), radius)
c1.draw(win)
c2.draw(win)
if (centerDiff < x < squareSize - centerDiff) and (centerDiff < y < squareSize - centerDiff):
c1.setFill(inner_color)
c2.setFill(inner_color)
else:
c1.setFill(outer_color)
c2.setFill(outer_color)
win = GraphWin("Patch2 (x{})".format(scale), 100*scale,100*scale)
coords = [0, 35, 70]
for x in coords:
for y in coords:
drawSquare(win, x*scale, y*scale, scale=scale) # normal (boring) version
# drawSquare(win, x*scale, y*scale, scale=scale, scaleCircles=True, outer_color="Blue") # Picture version
def main():
drawPattern(3)
main()