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.
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 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.
I'm trying visualise the sine waves addition in python using tkinter, and I'm trying to build lines between each circles center, but what I've tried so far didnt work as I thought it would. is there a way to fix what I've tried (see code), or a way to move only one coordinates point of a line independantly from the other?
As you'll see in the code if you run it, I've tried a method where each iteration the previous line is erased and a new one is created. When I run the code there is actually a line between each center of the circles just like I want, but facts are those lines persist and won't erase themselves; for some reason it seems like the canvas.delete(line) doesnt work as I expected it to.
here's the full code. The interesting part is in the 'updateline' fonction, into 'act()' func.
import math
import tkinter as tk
##important to know! -- the way I'm creating the circles is by setting an object, the bounds of the circle, depending on amplitude asked by user.
##then the programs calculates the path of these bounds, depending on circles, amplitude, phase and frequency of the sine waves asked by the user from the tkinter GUI.
##finally, the program creates and moves along this path a circle, representing visually the sine wave.
top = tk.Tk()
top.title('Superposition')
choice = tk.Tk()
choice.title('Parametres')
f = tk.Frame(choice,bd=3)
f.pack(side='top')
g = tk.Frame(choice,bd=3)
g.pack(side='bottom')
tk.Label(f,text="nbre ondes:",width = 10).grid(row=0,column=0)
sines = tk.Spinbox(f,from_=1,to=50,width=10,textvariable=tk.DoubleVar(value=2))
sines.grid(row=0,column=1)
sines.delete(0,5)
sines.insert(0,2)
delai = tk.Scale(g, orient='vertical', from_=100, to=1,resolution=1, length=100,label='delai')
delai.grid(row=0,column=0)
hauteur = tk.Scale(g, orient='vertical', from_=1100, to=100,resolution=100, length=100,label='fenetre')
hauteur.grid(row=0,column=1)
taillec1 = tk.Scale(g, orient='vertical', from_=3.5, to=0.1,resolution=0.1, length=100,label='taille')
taillec1.grid(row=0,column=2)
delai.set(20)
hauteur.set(600)
taillec1.set(1.5)
def grilledechoix():
numberofsines = int(sines.get())
for i in f.grid_slaves():
if int(i.grid_info()["row"]) > numberofsines+2:
i.grid_forget()
for i in range(1,numberofsines+1):
tk.Label(f,text="phase n."+str(i),width = 10).grid(row=i+2,column=4)
phase = tk.Spinbox(f,from_=-180,to=180,width=10)
phase.grid(row=i+2,column=5)
phase.delete(0,5)
phase.insert(0, 0)
for i in range(1,numberofsines+1):
tk.Label(f,text="amp. n."+str(i),width = 10).grid(row=i+2,column=0)
ampli = tk.Spinbox(f,from_=1,to=10000000,width=10)
ampli.grid(row=i+2,column=1)
ampli.delete(0,5)
ampli.insert(0,10)
for i in range(1,numberofsines+1):
tk.Label(f,text="freq n."+str(i),width = 10).grid(row=i+2,column=2)
freq = tk.Spinbox(f,from_=-1000,to=1000,width=10)
freq.grid(row=i+2,column=3)
freq.delete(0,5)
freq.insert(0,5)
def act():
h = g.grid_slaves()[1].get()
delai = g.grid_slaves()[2].get()
taillec1 = g.grid_slaves()[0].get()
w = h
ampdict = {'box1':100 * ((h/700)*taillec1)}
frqdict = {}
aaadict = {}
fffdict = {}
phadict = {}
numberofsines = int(sines.get())
sin = lambda degs: math.sin(math.radians(degs))
cos = lambda degs: math.cos(math.radians(degs))
for i in range(1,numberofsines+1):
fffdict['box'+str(numberofsines-i+1)] = f.grid_slaves()[(2*i)-2].get()
aaadict['box'+str(numberofsines-i+1)] = f.grid_slaves()[(2*i)-2+2*numberofsines].get()
phadict['box'+str(numberofsines-i+1)] = f.grid_slaves()[(2*i)-2+4*numberofsines].get()
for i in range(1,numberofsines+1):
ampdict['box'+str(i)] = (float(ampdict['box1'])/float(aaadict['box1'])) * float(aaadict['box'+str(i)])
frqdict['box'+str(i)] = float(fffdict['box'+str(i)])/float(fffdict['box1'])
class obj(object):
cos0, cos180 = cos(0), cos(180)
sin90, sin270 = sin(90), sin(270)
def __init__(i, x, y, rayon):
i.x, i.y = x, y
i.rayon = rayon
def bounds(i):
return (i.x + i.rayon*i.cos0, i.y + i.rayon*i.sin270,
i.x + i.rayon*i.cos180, i.y + i.rayon*i.sin90)
def updateposition(canvas, id, cent, obj, path):
obj.x, obj.y = next(path)
x0, y0, x1, y1 = canvas.coords(id)
oldx, oldy = (x0+x1) // 2, (y0+y1) // 2
dx, dy = obj.x - oldx, obj.y - oldy
canvas.move(id, dx, dy)
canvas.move(cent, dx, dy)
canvas.after(delai, updateposition, canvas, id, cent, obj, path)
def updateline(canvas, line, robj0, cent0, robj1, cent1):
x00, y00, x01, y01 = canvas.coords(cent0) ##defining coords of the two ovals asked, representing centers of circles
x10, y10, x11, y11 = canvas.coords(cent1)
oldx0, oldy0 = (x00+x01) // 2, (y00+y01) // 2 ##defining center coords of the two ovals
oldx1, oldy1 = (x10+x11) // 2, (y10+y11) // 2
dx0, dy0 = robj0.x - oldx0, robj0.y - oldy0 ##defining the deltax and deltay, difference of movements between frames, of the two ovals
dx1, dy1 = robj1.x - oldx1, robj1.y - oldy1
canvas.after(delai, canvas.delete, line) ##deleting previous line, does not work and I don't know why. I've also tried 'canvas.delete(line)', giving same results
canvas.create_line(oldx0+dx0, oldy0+dy0, oldx1+dx1, oldy1+dy1) ##creating new line
canvas.after(delai, updateline, canvas, line, robj0, cent0, robj1, cent1) ##function invoking itself after delay 'delai'
def posobj(pt,ang,deltang):
while True:
yield pt.x + pt.rayon*cos(ang), pt.y + pt.rayon*sin(ang)
ang = (ang+deltang)%360
try:
top.pack_slaves()[0].destroy()
except:
pass
canvas = tk.Canvas(top, bg='white', height=h, width=w)
canvas.pack(side='right')
robj = {}
r = {}
posobjet = {}
line = {}
cent = {}
## the following 'for' loop creates a number of circles corresponding to sine waves, as much as the user asked.
for i in range(1,int(sines.get())+2):
if i != int(sines.get())+1:
if i == 1:
robj[str(i)] = obj(h/2,h/2,float(ampdict['box'+str(i)]))
r[str(i)] = canvas.create_oval(robj[str(i)].bounds(),fill='',outline='black')
cent[str(i)] = canvas.create_oval(h/2+h/200,h/2+h/200.,h/2-h/200,h/2-h/200, fill='white', outline='red')
posobjet[str(i)] = posobj(robj[str(i)],float(phadict['box'+str(i)]),float(frqdict['box'+str(i)]))
else:
robj[str(i)] = obj(robj[str(i-1)].x,robj[str(i-1)].y,float(ampdict['box'+str(i)]))
r[str(i)] = canvas.create_oval(robj[str(i)].bounds(),fill='',outline='black')
cent[str(i)] = canvas.create_oval(robj[str(i)].x+h/200,robj[str(i)].y+h/200,robj[str(i)].x-h/200,robj[str(i)].y-h/200, fill='white', outline='blue')
line[str(i)] = canvas.create_line(0,0,0,0)
posobjet[str(i)] = posobj(robj[str(i)],float(phadict['box'+str(i)]),float(frqdict['box'+str(i)]))
top.after(delai, updateposition, canvas, r[str(i)], cent[str(i)], robj[str(i)], posobjet[str(i-1)])
##here I'm invoking the updateline function using the constant 'delai', the line i, and objects defining the bounds of the center objects, the little blue/red dots appearing as the center of each circles(run the code, it'll be easier to understand)
top.after(delai, updateline, canvas, line[str(i)], robj[str(i-1)], cent[str(i-1)], robj[str(i)], cent[str(i)])
else:
robj[str(i)] = obj(robj[str(i-1)].x,robj[str(i-1)].y,h/200)
r[str(i)] = canvas.create_oval(robj[str(i)].bounds(),fill='white',outline='red')
cent[str(i)] = canvas.create_oval(robj[str(i)].x+h/200,robj[str(i)].y+h/200,robj[str(i)].x-h/200,robj[str(i)].y-h/200, fill='white', outline='red')
line[str(i)] = canvas.create_line(0,0,0,0)
top.after(delai, updateposition, canvas, r[str(i)], cent[str(i)], robj[str(i)], posobjet[str(i-1)])
##2nd and last time invoking the updateline function, for the line between the last circle's point and the final red point.
top.after(delai, updateline, canvas, line[str(i)], robj[str(i-1)], cent[str(i-1)], robj[str(i)], cent[str(i)])
top.mainloop()
ok = tk.Button(f,text='NBRE',command=grilledechoix)
ok.grid(row=0,column=2)
ac = tk.Button(f,text='APPLY',command=act)
ac.grid(row=0,column=3)
grilledechoix()
act()
I expected the lines to disappear once the updateline function called itself again, because of that 'canvas.delete(line)' line into updateline, and I can't really understand why it does that.
anyway if you have a solution to make the lines move, without creating and deleting them each time the function is called, feel free to tell me.
Thanks!
If I understand the problem correctly, I believe the issue is with this code:
canvas.after(delai, canvas.delete, line)
canvas.create_line(oldx0+dx0, oldy0+dy0, oldx1+dx1, oldy1+dy1)
canvas.after(delai, updateline, canvas, line, robj0, cent0, robj1, cent1)
It fails to reassign the new line to the line variable for the next call. Instead try:
canvas.after(delai, canvas.delete, line)
line = canvas.create_line(oldx0+dx0, oldy0+dy0, oldx1+dx1, oldy1+dy1)
canvas.after(delai, updateline, canvas, line, robj0, cent0, robj1, cent1)
Which gets rid of the extra lines when I run it. Let me know if I've missed the point.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I want to draw a triangle like this:
I have tried different ways of solving it, but I have not done it correctly. How to add median lines in the triangle? Could someone please help and explain this to me?
from turtle import *
import random
def allTriMedian (w=300):
speed (0)
vertices = []
point = turtle.Point(x,y)
for i in range (3):
x = random.randint(0,300)
y = random.randint(0,300)
vertices.append(trutle.Point(x,y))
point = turtle.Point(x,y)
triangle = turtle.Polygon(vertices)
a = triangle.side()
b = triangle.side()
c = triangle.side()
m1 = tirangle.median
m2 = triangle.median
m3 = triangle.median
I tried to put the equation directly
def Median (a, b, c):
m1 = sqrt((((2b^2)+(2c^2)-(a^2))))
m2 = sqrt((((2a^2)+(2c^2)-(b^2))))
m3 = sqrt((((2a^2)+(2b^2)-(c^2))))
triangle.setFill("yellow")
triangle.draw(allTriMedian)
Or I thought to find a midpoint and draw a line segment to connect the vertices and midpoints.
def getMid(p1,p2):
return ( (p1[0]+p2[0]) / 2, (p1[1] + p2[1]))
mid1 = Line((point(p1[0]+p2[0]) / 2),point(x))
mid2 = Line((point(p2[1]+p3[1]) / 2),point(y))
I hate doing math. Let's see if we can solve this by throwing turtles at the problem. Lots of turtles.
We'll randomly generate the verticies of the triangle. Taking pairs of verticies in turn, we'll start a turtle at each heading toward the other. When the turtles collide (at the midpoint), we'll eliminate one turtle and send the other toward the vertex not in the pair. Once we've done this three times (with six turtles), we should have the drawing in question. Well, mostly (no fill in my solution):
from turtle import Turtle, Screen
from random import seed, randint
WIDTH, HEIGHT = 640, 480
def meet_in_the_middle(turtle_1, turtle_2):
position_2 = turtle_2.position()
while True:
turtle_1.setheading(turtle_1.towards(turtle_2))
turtle_1.forward(1)
position_1 = turtle_1.position()
if int(position_1[0]) == int(position_2[0]) and int(position_1[1]) == int(position_2[1]):
break
turtle_2.setheading(turtle_2.towards(turtle_1))
turtle_2.forward(1)
position_2 = turtle_2.position()
if int(position_2[0]) == int(position_1[0]) and int(position_2[1]) == int(position_1[1]):
break
seed()
screen = Screen()
screen.setup(WIDTH * 1.25, HEIGHT * 1.25)
vertices = []
for _ in range(3):
x = randint(-WIDTH//2, WIDTH//2)
y = randint(-HEIGHT//2, HEIGHT//2)
vertices.append((x, y))
A, B, C = vertices
turtle_AtoB = Turtle(shape='turtle')
turtle_AtoB.penup()
turtle_AtoB.goto(A)
turtle_AtoB.pendown()
turtle_BtoA = Turtle(shape='turtle')
turtle_BtoA.penup()
turtle_BtoA.goto(B)
turtle_BtoA.pendown()
meet_in_the_middle(turtle_AtoB, turtle_BtoA)
turtle_BtoA.hideturtle()
turtle_AtoB.setheading(turtle_AtoB.towards(C))
turtle_AtoB.goto(C)
turtle_AtoB.hideturtle()
turtle_BtoC = Turtle(shape='turtle')
turtle_BtoC.penup()
turtle_BtoC.goto(B)
turtle_BtoC.pendown()
turtle_CtoB = Turtle(shape='turtle')
turtle_CtoB.penup()
turtle_CtoB.goto(C)
turtle_CtoB.pendown()
meet_in_the_middle(turtle_BtoC, turtle_CtoB)
turtle_CtoB.hideturtle()
turtle_BtoC.setheading(turtle_BtoC.towards(A))
turtle_BtoC.goto(A)
turtle_BtoC.hideturtle()
turtle_CtoA = Turtle(shape='turtle')
turtle_CtoA.penup()
turtle_CtoA.goto(C)
turtle_CtoA.pendown()
turtle_AtoC = Turtle(shape='turtle')
turtle_AtoC.penup()
turtle_AtoC.goto(A)
turtle_AtoC.pendown()
meet_in_the_middle(turtle_CtoA, turtle_AtoC)
turtle_AtoC.hideturtle()
turtle_CtoA.setheading(turtle_CtoA.towards(B))
turtle_CtoA.goto(B)
turtle_CtoA.hideturtle()
screen.exitonclick()
Turtles at work:
Finished drawing:
thanks to cdlane, I took his code and put some functionality into functions to make it a Little clearer (at least for me)
# -*- coding: cp1252 -*-
import turtle
from turtle import Turtle, Screen
from random import seed, randint
WIDTH, HEIGHT = 640, 480
def create_screen(width, height):
screen = Screen()
screen.setup(width * 1.25, height * 1.25)
return screen
def create_points(count,width = WIDTH, height = HEIGHT):
vertices = []
for _ in range(count):
x = randint(-width//2, width//2)
y = randint(-height//2, height//2)
vertices.append((x, y))
return vertices
def create_turtle_at_position(position):
turtle = Turtle(shape='turtle')
turtle.hideturtle()
turtle.penup()
turtle.goto(position)
turtle.showturtle()
turtle.pendown()
return turtle
def meet_in_the_middle(turtle_1, turtle_2):
position_2 = turtle_2.position()
while True:
turtle_1.setheading(turtle_1.towards(turtle_2))
turtle_1.forward(1)
position_1 = turtle_1.position()
if int(position_1[0]) == int(position_2[0]) and int(position_1[1]) == int(position_2[1]):
break
turtle_2.setheading(turtle_2.towards(turtle_1))
turtle_2.forward(1)
position_2 = turtle_2.position()
if int(position_2[0]) == int(position_1[0]) and int(position_2[1]) == int(position_1[1]):
break
turtle_1.hideturtle()
turtle_2.hideturtle()
return create_turtle_at_position(position_2)
def draw_median(P1st, P2nd, POpposite):
turtle_AtoB = create_turtle_at_position(P1st)
turtle_BtoA = create_turtle_at_position(P2nd)
turtle_AandBmiddle = meet_in_the_middle(turtle_AtoB, turtle_BtoA)
turtle_AandBmiddle.setheading(turtle_AandBmiddle.towards(POpposite))
turtle_AandBmiddle.goto(POpposite)
return turtle_AandBmiddle
seed()
sc = create_screen(WIDTH, HEIGHT)
for _ in range(5):
sc = create_screen(WIDTH, HEIGHT)
A, B, C = create_points(3)
draw_median(A,B,C)
draw_median(B,C,A)
draw_median(C,A,B)
sc.exitonclick()
mathematical it is the easiest way to calculate this by vector. Let me say you have a triangle ABC and want to draw a line from A to the middle of BC so your vector starts at A and ends on A + AB + 1/2 BC or A + AC + 1/2 CB (vectorial)
(ax) + (bx - ax) + 0.5 (cx - bx)
(ay) (by - ay) (cy - by)
that results in the coordinates for the opposite Point of
x = 0.5(cx + bx)
y = 0.5(cy + by)
I'm trying to make an animation with a sequence of datafiles in mayavi. Unfortunately i have noticed that camera doesn't lock (it is zooming and zooming out). I think it is happening because the Z componrnt of my mesh is changing and mayavi is trying to recalculate scales.
How can I fix it?
import numpy
from mayavi import mlab
mlab.figure(size = (1024,768),bgcolor = (1,1,1))
mlab.view(azimuth=45, elevation=60, distance=0.01, focalpoint=(0,0,0))
#mlab.move(forward=23, right=32, up=12)
for i in range(8240,8243):
n=numpy.arange(10,400,20)
k=numpy.arange(10,400,20)
[x,y] = numpy.meshgrid(k,n)
z=numpy.zeros((20,20))
z[:] = 5
M = numpy.loadtxt('B:\\Dropbox\\Master.Diploma\\presentation\\movie\\1disk_j9.5xyz\\'+'{0:05}'.format(i)+'.txt')
Mx = M[:,0]; My = M[:,1]; Mz = M[:,2]
Mx = Mx.reshape(20,20); My = My.reshape(20,20); Mz = Mz.reshape(20,20);
s = mlab.quiver3d(x,y,z,Mx, My, -Mz, mode="cone",resolution=40,scale_factor=0.016,color = (0.8,0.8,0.01))
Mz = numpy.loadtxt('B:\\Dropbox\\Master.Diploma\\presentation\\movie\\Mzi\\' + '{0:05}'.format(i) + '.txt')
n=numpy.arange(2.5,400,2)
k=numpy.arange(2.5,400,2)
[x,y] = numpy.meshgrid(k,n)
f = mlab.mesh(x, y, -Mz/1.5,representation = 'wireframe',opacity=0.3,line_width=1)
mlab.savefig('B:\\Dropbox\\Master.Diploma\\presentation\\movie\\figs\\'+'{0:05}'.format(i)+'.png')
mlab.clf()
#mlab.savefig('B:\\Dropbox\\Master.Diploma\\figures\\vortex.png')
print(i)
mlab.show()
for anyone still interested in this, you could try wrapping whatever work you're doing in this context, which will disable rendering and return the disable_render value and camera views to their original states after the context exits.
with constant_camera_view():
do_stuff()
Here's the class:
class constant_camera_view(object):
def __init__(self):
pass
def __enter__(self):
self.orig_no_render = mlab.gcf().scene.disable_render
if not self.orig_no_render:
mlab.gcf().scene.disable_render = True
cc = mlab.gcf().scene.camera
self.orig_pos = cc.position
self.orig_fp = cc.focal_point
self.orig_view_angle = cc.view_angle
self.orig_view_up = cc.view_up
self.orig_clipping_range = cc.clipping_range
def __exit__(self, t, val, trace):
cc = mlab.gcf().scene.camera
cc.position = self.orig_pos
cc.focal_point = self.orig_fp
cc.view_angle = self.orig_view_angle
cc.view_up = self.orig_view_up
cc.clipping_range = self.orig_clipping_range
if not self.orig_no_render:
mlab.gcf().scene.disable_render = False
if t != None:
print t, val, trace
ipdb.post_mortem(trace)
I do not really see the problem in your plot but to reset the view after each plotting instance insert your view point:
mlab.view(azimuth=45, elevation=60, distance=0.01, focalpoint=(0,0,0))
directly above your mlab.savefig callwithin your for loop .
You could just use the vmin and vmax function in your mesh command, if u do so the scale will not change with your data and your camera should stay where it is.
Like this:
f = mlab.mesh(x, y, -Mz/1.5,representation = 'wireframe',vmin='''some value''',vmax='''some value''',opacity=0.3,line_width=1)