Yaw rotation of gear object in vpython - animation

I was wondering how to yaw gear object about Y-axis in vpython. I have been following some tutorials and I have managed to rotate the gear. The gear rotates about Z axis in vpython. I want the gear to remain rotating about Z axis but I would like to yaw the gear around Y-axis. When xxx.rotate(axis=vector(0,0,1), it works as it should do. As soon as I alter anything it goes crazy. I am not sure what xxx.rotate(origin) is meant to do. In my case, I have messed around with the origin vector but it doesn't make any sense to me. I am not sure if the origin vector expects parameter values in x,y,z or some other sequence. I hope I am making makes sense.
The code below is a working code and you can see what I am trying to do here. I have been debugging for a while so please ignore unnecessary comments.
'
# https://www.youtube.com/watch?v=0Sw8sxsN984
# https://www.youtube.com/watch?v=MJiVtz4Uj7M
from vpython import *
import numpy as np
axisOpac = 0.4
def makeAxes(length):
global axes
xaxis = arrow(pos=vector(0, 0, 0), axis=length*vector(1,0,0), shaftwidth = 0.1*(length), color=color.red, opacity = axisOpac)
negXaxis = xaxis.clone()
negXaxis.axis *= -1
yaxis = arrow(pos=vector(0, 0, 0), axis=length*vector(0,1,0), color=color.green, opacity = axisOpac)
negYaxis = yaxis.clone()
negYaxis.axis *= -1
zaxis = arrow(pos=vector(0, 0, 0), axis=length*vector(0,0,1), color=color.magenta, opacity = axisOpac)
negZaxis = zaxis.clone()
negZaxis.axis *= -1
fudge = 0.01*length
xlabel = label(text = "x", color = xaxis.color, pos=xaxis.axis+vector(0, fudge, 0),box = False)
ylabel = label(text = "y", color = yaxis.color, pos=yaxis.axis+vector(0, fudge, 0),box = False)
zlabel = label(text = "-z", color = zaxis.color, pos=zaxis.axis+vector(0, fudge, 0),box = False)
# To make axes visible or invisible
axes = [xaxis, negXaxis, yaxis, negYaxis, zaxis, negZaxis, xlabel, ylabel, zlabel]
return
makeAxes(1)
g = shapes.gear(n = 20)
gear1 = extrusion (path = [vector(0, 0, 0), vector(0, 0, 0.1)], shape=g, opacity = 0.4)
# gear1.axis = vector(0, 0, 0)
debugArrow=arrow(length=6,shaftwidth=.1,color=color.white,axis=vector(0,0,0))
gearAxisArrow=arrow(shaftwidth=.1, color=color.red)
inputYawAngleYaxis = 0 # int(input("Enter an angle: "))
while True:
for cir in np.arange(0, 2*np.pi, 0.01):
rate(10)
# sleep(1)
pitch=0
# k = vector(cos(yaw)*cos(pitch), sin(pitch),sin(yaw)*cos(pitch)) ''' with pitch input '''
k = vector(cos(cir), 0 ,sin(cir)) # ''' without pitch input '''
y=vector(0,0,0)
s=cross(k,y)
v=cross(s,k)
rad = radians(45)
# gear1.axis = vector(1.23, -1.77418, 0) #vector(cos(inputYawAngleYaxis * pi/180), sin(pitch),sin(inputYawAngleYaxis* pi/180)*cos(pitch))
# gear1.pos = vector(-2.94957, 0, 2.70185)
# Gear Pos0: <0.707107, 0, 0.707107> Gear Axis0: <2.15647, 0.101243, -0.00237659>
gear1.pos = vector(0.707107, 0, 0.707107)
# gear1.pos = debugArrow.axis #frontArrow.axis # k # vector(0.707107, 0, 0.707107)
gear1.rotate(angle = -0.1, axis = vector(0, 0, 1), origin = vector(0, 0, 1 ))
# gear1.axis = vector(cos(radians(-45)), 0, sin(radians(-45)))
''' DebugArrow represents the position of the gear's center '''
debugArrow.axis = vector(cos(rad), 0, sin(rad))
''' Arrow inside the gear to track rotational axis of gear - This is to debug the yaw issue facing with gear object'''
gearAxisArrow.pos = gear1.pos
gearAxisArrow.axis = gear1.axis # vector(cos(radians(-45)), 0, sin(radians(-45))) # gear1.axis
gearAxisArrow.length = gear1.length/2
## Debugg
# print("Front Arrow Axis: ", frontArrow.axis, " K vec: ", k, " Ball pos: ", ball.pos )
print("Gear Pos0: ", gear1.pos, " Gear Axis0: ", gear1.axis, "gearAxisArrow: ", gearAxisArrow.pos, "gearAxisArrow: ", gearAxisArrow.axis)
'

I don't understand "I want the gear to remain rotating about Z axis but I would like to yaw the gear around Y-axis". If by "yaw" you mean that the axis of the gear should rotate about the y axis (while the gear itself rotates about that rotating axis), then obviously the axis of the gear can't remain in the direction of the z axis. Instead, the axis of the gear has to rotate. If it's of any help, here is the relevant page of the documentation:
https://www.glowscript.org/docs/VPythonDocs/rotation.html

Related

How do I select a line by clicking in Makie?

I want to be able to select a line on the plot by clicking on it. Ideally, when I click on any line, the number of that line will appear on the screen.
I wrote my code based on the tutorial, but there is no example with lines, so I did what I did. https://docs.makie.org/v0.19/documentation/events/index.html#point_picking
At the moment I have no idea what these numbers are telling me and why. They are not even coordinates of clicked points.
P.S. Actually it is just a starting point. I want to create event interaction on series and topoplots. But for now it would be great to find out the basics.
f = Figure(backgroundcolor = RGBf(0.98, 0.98, 0.98), resolution = (1500, 700))
ax = Axis(f[1, 1], xlabel = "Time [s]", ylabel = "Voltage amplitude [µV]")
N = 1:length(pos)
positions = Observable(rand(Point2f, 10))
xs = 0:0.01:10
ys = 0.5 .* sin.(xs)
lines!(xs, ys)
lines!(xs, ys * 2)
hidedecorations!(ax, label = false, ticks = false, ticklabels = false)
hidespines!(ax, :t, :r)
hlines!(0, color = :gray, linewidth = 1)
vlines!(0, color = :gray, linewidth = 1)
i = Observable(0)
on(events(f).mousebutton, priority = 2) do event
if event.button == Mouse.left && event.action == Mouse.press
plt, i[] = pick(f)
str = lift(i -> "$(i)", i)
text!(ax, 1, -0.5, text = str, align = (:center, :center))
end
end
f
Below are some examples of the interaction between clicking and the number displayed (the red dot is where I click).
Check out the mouseposition variable here:
https://docs.makie.org/stable/api/#Events
or the registerInteraction! function here:
https://docs.makie.org/v0.19/examples/blocks/axis/index.html#registering_and_deregistering_interactions
You can use them both as below:
using GLMakie
f = Figure(backgroundcolor = RGBf(0.98, 0.98, 0.98), resolution = (1500, 700))
ax = Axis(f[1, 1], xlabel = "Time [s]", ylabel = "Voltage amplitude [µV]")
#N = 1:length(pos)
positions = Observable(rand(Point2f, 10))
xs = 0:0.01:10
ys = 0.5 .* sin.(xs)
lines!(xs, ys)
lines!(xs, ys * 2)
hidedecorations!(ax, label = false, ticks = false, ticklabels = false)
hidespines!(ax, :t, :r)
hlines!(0, color = :gray, linewidth = 1)
vlines!(0, color = :gray, linewidth = 1)
register_interaction!(ax, :my_interaction) do event, axis
if event.type === MouseEventTypes.leftclick
println("Graph axis position: $(event.data)")
end
end
i = Observable(0)
on(events(f).mousebutton, priority = 2) do event
if event.button == Mouse.left && event.action == Mouse.press
plt, i[] = pick(f)
str = lift(i -> "$(i)", i)
text!(ax, 1, -0.5, text = str, align = (:center, :center))
#show mouseposition(f)
end
end
f
Note that for some reason (perhaps it sees the first click as a selection?) Makie does not start registering the interaction on the graph until the first click within the graph, unlike the clicks on the figure which are all shown even the first one.

How to change slider position in Makie animation?

I want to create gif animation on my topoplot and I almost succeeded. The only problem is that slider position does not move. How to make it moving?
Here is my code:
xs = range(-0.3, length=size(dat_e, 2), step=1 ./ 128)
sg = SliderGrid(f[2, 1],
(label="time", range=xs, format = "{:.3f} ms", startvalue = 0),
)
time = sg.sliders[1].value
str = lift(t -> "[$(round(t, digits = 3)) ms]", time)
topo_slice = lift((t, data) -> mean(data[1:30, indexin(t, xs), :], dims=2)[:,1], time, dat_e)
topo_axis = Axis(f[1, 1], aspect = DataAspect(), title = "Interactive topoplot")
topo = eeg_topoplot!(topo_axis, topo_slice, # averaging all trial of 30 participants on Xth msec
raw.ch_names[1:30];
positions=pos, # produced automatically from ch_names
#interpolation=DelaunayMesh(),
enlarge=1,
extrapolation=GeomExtrapolation(enlarge=1.0, geometry=Circle),
label_text=true) # aspect ratio, correlation of height and width
text!(topo_axis, 1, 1, text = str, align = (:center, :center))
xlims!(-0.2, 1.2)
ylims!(-0.2, 1.2)
hidedecorations!(topo_axis)
hidespines!(topo_axis)
framerate = 1
timestamps = [-0.3, 0.0828125, 0.1609375, 0.2390625]
record(f, "animations/time_animation.gif", timestamps;
framerate = framerate) do z
sg.sliders[1].value[] = z
time[] = z
Maybe this will do the trick:
Makie.set_close_to!(sg.sliders[1], z)
(use this to set the value to z)

add top and Right spine to joint plot(regplot) in seaborn

This is the current code I have
sns.set_style("whitegrid",{'axes.spines.top': True, 'axes.spines.right': True,'axes.linewidth': 2, 'axes.edgecolor':'black'})
g = sns.JointGrid(x=station1,y=station2,data = df, xlim = xlim, ylim = ylim,space =1)
sns.set_style("whitegrid",{'axes.spines.top': True, 'axes.spines.right': True,'axes.linewidth': 2, 'axes.edgecolor':'black'})
g.plot_joint(sns.regplot, fit_reg = False, truncate = False, robust = False, label = 'regression', color = 'darkorange',)
g.plot_marginals(sns.histplot, kde = True, color = 'darkorange')
# g.ax_joint.legend({'Pearson_R: %.4f'%r, 'x_axis mean:%.3f; std:%.3f'%(d1.df[par_x][idx_1].mean(),d1.df[par_x][idx_1].std()),'y_axis mean:%.3f; std:%.3f'%(d2.df[par_y][idx_2].mean(),d2.df[par_y][idx_2].std())})
# g.ax_joint.legend({'y = %.3f x + %.3f \nR^2: %.4f'%(popt[0],popt[1],r**2)})
g.ax_joint.legend({'$R^2$:%.4f'%(r**2)}, loc = 'upper right')
g.ax_joint.plot(np.linspace(xlim[0],xlim[1],100),linear(np.linspace(xlim[0],xlim[1],100),1,0), linewidth = 0.7, ls = '-.', color = 'black')
I am trying to add spines to Top and left of a joint grid plot in seaborn, However, the best I am able to do is
2 sided plot
is there a way to add a boarder to the top and Right side of the main plot and add right top and left of the sub plots? Thanks.

Pygame window doesn't show on Mac OS Catalina

Running:
MacOS Catalina 10.15.3
Python 3.7.6.
Pygame 1.9.6
I just started programming and I am trying to run a reinforcement learning Pygame code (link: https://github.com/harvitronix/reinforcement-learning-car). When I run python3.7 -m pygame.examples.aliens I see the test window + sound and everything works.
However when I try to run the code for the game I am trying to get working I at first only saw the loading wheel, I fixed the loading wheel by putting in the following loop. `
pygame.display.update()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
When I try to run it now, I only see a black pygame window pop-up, so no loading wheel but also not the game, it also seems like the game doesn't run in the background (this was the case without the above loop). See the complete original code below:
import random
import math
import numpy as np
import pygame
from pygame.color import THECOLORS
import sys
import pymunk
from pymunk.vec2d import Vec2d
from pymunk.pygame_util import draw
# PyGame init
width = 1000
height = 700
pygame.init()
screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()
# Turn off alpha since we don't use it.
screen.set_alpha(None)
# Showing sensors and redrawing slows things down.
show_sensors = True
draw_screen = True
class GameState:
def __init__(self):
# Global-ish.
self.crashed = False
# Physics stuff.
self.space = pymunk.Space()
self.space.gravity = pymunk.Vec2d(0., 0.)
# Create the car.
self.create_car(100, 100, 0.5)
# Record steps.
self.num_steps = 0
# Create walls.
static = [
pymunk.Segment(
self.space.static_body,
(0, 1), (0, height), 1),
pymunk.Segment(
self.space.static_body,
(1, height), (width, height), 1),
pymunk.Segment(
self.space.static_body,
(width-1, height), (width-1, 1), 1),
pymunk.Segment(
self.space.static_body,
(1, 1), (width, 1), 1)
]
for s in static:
s.friction = 1.
s.group = 1
s.collision_type = 1
s.color = THECOLORS['red']
self.space.add(static)
# Create some obstacles, semi-randomly.
# We'll create three and they'll move around to prevent over-fitting.
self.obstacles = []
self.obstacles.append(self.create_obstacle(200, 350, 100))
self.obstacles.append(self.create_obstacle(700, 200, 125))
self.obstacles.append(self.create_obstacle(600, 600, 35))
# Create a cat.
self.create_cat()
def create_obstacle(self, x, y, r):
c_body = pymunk.Body(pymunk.inf, pymunk.inf)
c_shape = pymunk.Circle(c_body, r)
c_shape.elasticity = 1.0
c_body.position = x, y
c_shape.color = THECOLORS["blue"]
self.space.add(c_body, c_shape)
return c_body
def create_cat(self):
inertia = pymunk.moment_for_circle(1, 0, 14, (0, 0))
self.cat_body = pymunk.Body(1, inertia)
self.cat_body.position = 50, height - 100
self.cat_shape = pymunk.Circle(self.cat_body, 30)
self.cat_shape.color = THECOLORS["orange"]
self.cat_shape.elasticity = 1.0
self.cat_shape.angle = 0.5
direction = Vec2d(1, 0).rotated(self.cat_body.angle)
self.space.add(self.cat_body, self.cat_shape)
def create_car(self, x, y, r):
inertia = pymunk.moment_for_circle(1, 0, 14, (0, 0))
self.car_body = pymunk.Body(1, inertia)
self.car_body.position = x, y
self.car_shape = pymunk.Circle(self.car_body, 25)
self.car_shape.color = THECOLORS["green"]
self.car_shape.elasticity = 1.0
self.car_body.angle = r
driving_direction = Vec2d(1, 0).rotated(self.car_body.angle)
self.car_body.apply_impulse(driving_direction)
self.space.add(self.car_body, self.car_shape)
def frame_step(self, action):
if action == 0: # Turn left.
self.car_body.angle -= .2
elif action == 1: # Turn right.
self.car_body.angle += .2
# Move obstacles.
if self.num_steps % 100 == 0:
self.move_obstacles()
# Move cat.
if self.num_steps % 5 == 0:
self.move_cat()
driving_direction = Vec2d(1, 0).rotated(self.car_body.angle)
self.car_body.velocity = 100 * driving_direction
# Update the screen and stuff.
screen.fill(THECOLORS["black"])
draw(screen, self.space)
self.space.step(1./10)
if draw_screen:
pygame.display.flip()
clock.tick()
# Get the current location and the readings there.
x, y = self.car_body.position
readings = self.get_sonar_readings(x, y, self.car_body.angle)
normalized_readings = [(x-20.0)/20.0 for x in readings]
state = np.array([normalized_readings])
# Set the reward.
# Car crashed when any reading == 1
if self.car_is_crashed(readings):
self.crashed = True
reward = -500
self.recover_from_crash(driving_direction)
else:
# Higher readings are better, so return the sum.
reward = -5 + int(self.sum_readings(readings) / 10)
self.num_steps += 1
return reward, state
def move_obstacles(self):
# Randomly move obstacles around.
for obstacle in self.obstacles:
speed = random.randint(1, 5)
direction = Vec2d(1, 0).rotated(self.car_body.angle + random.randint(-2, 2))
obstacle.velocity = speed * direction
def move_cat(self):
speed = random.randint(20, 200)
self.cat_body.angle -= random.randint(-1, 1)
direction = Vec2d(1, 0).rotated(self.cat_body.angle)
self.cat_body.velocity = speed * direction
def car_is_crashed(self, readings):
if readings[0] == 1 or readings[1] == 1 or readings[2] == 1:
return True
else:
return False
def recover_from_crash(self, driving_direction):
"""
We hit something, so recover.
"""
while self.crashed:
# Go backwards.
self.car_body.velocity = -100 * driving_direction
self.crashed = False
for i in range(10):
self.car_body.angle += .2 # Turn a little.
screen.fill(THECOLORS["grey7"]) # Red is scary!
draw(screen, self.space)
self.space.step(1./10)
if draw_screen:
pygame.display.flip()
clock.tick()
def sum_readings(self, readings):
"""Sum the number of non-zero readings."""
tot = 0
for i in readings:
tot += i
return tot
def get_sonar_readings(self, x, y, angle):
readings = []
"""
Instead of using a grid of boolean(ish) sensors, sonar readings
simply return N "distance" readings, one for each sonar
we're simulating. The distance is a count of the first non-zero
reading starting at the object. For instance, if the fifth sensor
in a sonar "arm" is non-zero, then that arm returns a distance of 5.
"""
# Make our arms.
arm_left = self.make_sonar_arm(x, y)
arm_middle = arm_left
arm_right = arm_left
# Rotate them and get readings.
readings.append(self.get_arm_distance(arm_left, x, y, angle, 0.75))
readings.append(self.get_arm_distance(arm_middle, x, y, angle, 0))
readings.append(self.get_arm_distance(arm_right, x, y, angle, -0.75))
if show_sensors:
pygame.display.update()
return readings
def get_arm_distance(self, arm, x, y, angle, offset):
# Used to count the distance.
i = 0
# Look at each point and see if we've hit something.
for point in arm:
i += 1
# Move the point to the right spot.
rotated_p = self.get_rotated_point(
x, y, point[0], point[1], angle + offset
)
# Check if we've hit something. Return the current i (distance)
# if we did.
if rotated_p[0] <= 0 or rotated_p[1] <= 0 \
or rotated_p[0] >= width or rotated_p[1] >= height:
return i # Sensor is off the screen.
else:
obs = screen.get_at(rotated_p)
if self.get_track_or_not(obs) != 0:
return i
if show_sensors:
pygame.draw.circle(screen, (255, 255, 255), (rotated_p), 2)
# Return the distance for the arm.
return i
def make_sonar_arm(self, x, y):
spread = 10 # Default spread.
distance = 20 # Gap before first sensor.
arm_points = []
# Make an arm. We build it flat because we'll rotate it about the
# center later.
for i in range(1, 40):
arm_points.append((distance + x + (spread * i), y))
return arm_points
def get_rotated_point(self, x_1, y_1, x_2, y_2, radians):
# Rotate x_2, y_2 around x_1, y_1 by angle.
x_change = (x_2 - x_1) * math.cos(radians) + \
(y_2 - y_1) * math.sin(radians)
y_change = (y_1 - y_2) * math.cos(radians) - \
(x_1 - x_2) * math.sin(radians)
new_x = x_change + x_1
new_y = height - (y_change + y_1)
return int(new_x), int(new_y)
def get_track_or_not(self, reading):
if reading == THECOLORS['black']:
return 0
else:
return 1
if __name__ == "__main__":
game_state = GameState()
while True:
game_state.frame_step((random.randint(0, 2)))
I don't think the issue is with my python version because the test runs normal. Anybody see the issue?
Thanks!
Problem solved. I put the loop in the beginning of the code but it should have gone beneath:
if draw_screen:
pygame.display.flip()

Bouncing text animation issue in Pygame

I'm trying to code a program that can take text and animate it to bounce on a loop, like a ball bouncing to the floor. I used a similar piece of code I found a starting point as I'm still fairly new to Pygame (thank you Pete Shinners, whoever you are), but after updating the code and playing with it for a long time I still can't get it to blit to the screen correctly. The text starts above the rendered area and then gradually falls into view, but the top part of the text is cut off.
I've tried moving the blitted region around the window and resizing the rectangles and surface the program is using, but nothing seems to fix it.
import os, sys, math, pygame, pygame.font, pygame.image
from pygame.locals import *
def bounce():
# define constants
G = 0.98
FLOOR = 0
COEFFICIENT = 0.8
#define variables
ball = 500
direction = 'DOWN'
v = 0
count = 0
#create array to store data
array = [ball]
while True:
if count == 4:
return array
elif ball > FLOOR and direction == 'DOWN':
v += G
if (ball - v) >= FLOOR:
ball = ball - v
array.append(round(ball,2))
else:
ball = FLOOR
array.append(round(ball,2))
direction = 'UP'
v *= COEFFICIENT
count += 1
elif ball >= FLOOR and direction == 'UP':
v -= G
if (ball + v) >= FLOOR:
ball = ball + v
array.append(round(ball,2))
if v <= 0:
direction = 'DOWN'
else:
ball = FLOOR
array.append(ball)
direction = 'UP'
v *= COEFFICIENT
class textBouncy:
array = bounce()
def __init__(self, font, message, fontcolor, amount=10):
# Render the font message
self.base = font.render(message, 0, fontcolor)
# bounce amount (height)
self.amount = amount
#size = rect of maximum height/width of text
self.size = self.base.get_rect().inflate(0, amount).size
#normalise array to meet height restriction
self.array = [round(-x/(500/amount),2) for x in array]
def animate(self):
# create window surface s
s = pygame.Surface(self.size)
# height = max inflated height
height = self.size[1]
# define a step-sized rectangle in the location of the step
src = Rect(0, 0, self.base.get_width(), height)
# moves the message according to the array list.
dst = src.move(0, self.array[i])
if (i + 1) == len(self.array):
global i
i = 0
# blits the information onto the screen
s.blit(self.base, dst, src)
return s
entry_info = 'Bouncing ball text'
if __name__ == '__main__':
pygame.init()
#create text renderer
i = 0
array = bounce()
bigfont = pygame.font.Font(None, 60)
white = 255, 255, 255
renderer = textBouncy(bigfont, entry_info, white, 16)
text = renderer.animate()
#create a window the correct size
win = pygame.display.set_mode(text.get_size())
win.blit(text, (0, 10))
pygame.display.flip()
#run animation loop
finished = 0
while True:
pygame.time.delay(10)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
text = renderer.animate()
i += 1
win.blit(text, (0, 10)) # blits the finished product from animate
pygame.display.flip()
(Quote) "it all comes down to math really" Kay so you need to - the y axis when you want to make it go up and + the x axis to make it go side ways you could make it go up and down will moveing it horizontally and then when it reaches a point it will stop moving horizontally and just bonce up and down +ing it more every time
That was my 100$ which took me 5 mins to write
After revisiting this I managed to work this out - I needed to add everything I blitted down to compensate for the bounce up. So in the __init__function:
self.array = [round(-x/(500/amount),2)**+self.amount** for x in array]
Works perfectly now :)

Resources