Using a PS4 controller in pygame, I already figured out how to capture axis rotation which can vary to a -1 or 1, but I don't know how to convert those numbers to a color ring like scale in order to turn it into a hex triplet number.
The values mimicking that of a color ring is more important than anything as I don't want the joystick capturing a color while it's not in motion. Picture
( Since that was a bit confusing, essentially I want to be able to move my joystick around and capture an accurate hex triplet number based on where it has moved )
This is my code so far:
import pygame
# Define some colors
BLACK = ( 0, 0, 0)
WHITE = ( 62, 210, 255)
# This is a simple class that will help us print to the screen
# It has nothing to do with the joysticks, just outputting the
# information.
class TextPrint:
def __init__(self):
self.reset()
self.font = pygame.font.Font(None, 25)
def print(self, screen, textString):
textBitmap = self.font.render(textString, True, BLACK)
screen.blit(textBitmap, [self.x, self.y])
self.y += self.line_height
def reset(self):
self.x = 25
self.y = 25
self.line_height = 30
def indent(self):
self.x += 10
def unindent(self):
self.x -= 10
pygame.init()
# Set the width and height of the screen [width,height]
size = [900, 1080]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("PS4Testing")
#Loop until the user clicks the close button.
done = False
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
# Initialize the joysticks
pygame.joystick.init()
# Get ready to print
textPrint = TextPrint()
# -------- Main Program Loop -----------
while done==False:
# EVENT PROCESSING STEP
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
done=True # Flag that we are done so we exit this loop
screen.fill(WHITE)
textPrint.reset()
# Get count of joysticks
joystick_count = pygame.joystick.get_count()
# For each joystick:
for i in range(joystick_count):
joystick = pygame.joystick.Joystick(i)
joystick.init()
# Usually axis run in pairs, up/down for one, and left/right for
# the other.
axes = joystick.get_numaxes()
for i in range( axes ):
axis = joystick.get_axis( i )
textPrint.print(screen, "Axis {} value: {:>6.3f}".format(i, axis) )
textPrint.unindent()
# ALL CODE TO DRAW SHOULD GO ABOVE THIS COMMENT
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Limit to 20 frames per second
clock.tick(20)
# Close the window and quit.
# If you forget this line, the program will 'hang'
# on exit if running from IDLE.
pygame.quit ()
Modified code from official pygame documentation
Any help would be greatly appreciated
My plan:
First, we need to find the joystick's angle. We can do this using the law of cosines and axis statements as lengths of the sides of a triangle (because they are from one point/center).
Store axis statements in this block:
for i in range( axes ):
axis = joystick.get_axis( i )
#Storing axis statement
if i == 0:
Xaxis = axis
elif i == 1:
Yaxis = axis
textPrint.print(screen, "Axis {} value: {:>6.3f}".format(i, axis) )
We store them because in for loop we can take only one statement at a time.
Then define a function that will return the angle of the stick. We need to use math
module for pi
and acos
.
def get_angle(Xaxis,Yaxis):
#To avoid ZeroDivisionError
#P.S. - you can improve it a bit.
if Xaxis == 0:
Xaxis = 0.001
if Yaxis == 0:
Yaxis = 0.001
#defining the third side of a triangle using the Pythagorean theorem
b = ((Xaxis)**2 + (Yaxis)**2)**0.5
c = Xaxis
a = Yaxis
#Using law of cosines we'll find angle using arccos of cos
#math.acos returns angles in radians, so we need to multiply it by 180/math.pi
angle = math.acos((b**2 + c**2 - a**2) / (2*b*c)) * 180/math.pi
#It'll fix angles to be in range of 0 to 360
if Yaxis > 0:
angle = 360 - angle
return angle
Now get_angle(Xaxis,Yaxis)
will return stick's angle. East is 0°, north is 90°, west is 180°, south is 270°.
We have stick's angle and we can use it to make an HSV color and then convert it to RGB, because Color Wheel is based on hue of the colors.
Hue has the same range as the stick's angle, so we can use stick's angle as hue.
Using the formulas from Wikipedia, define a function that converts HSV to RGB.
def hsv_to_rgb(H,S,V):
#0 <= H <= 360
#0 <= S <= 1
#0 <= V <= 1
C = V * S
h = H/60
X = C * (1 - abs(h % 2 -1))
#Yes, Python can compare like "0 <= 2 > 1"
if 0 <= h <= 1:
r = C; g = X; b = 0
elif 1 <= h <= 2:
r = X; g = C; b = 0
elif 2 <= h <= 3:
r = 0; g = C; b = X
elif 3 <= h <= 4:
r = 0; g = X; b = C
elif 4 <= h <= 5:
r = X; g = 0; b = C
elif 5 <= h < 6:
r = C; g = 0; b = X
m = V - C
#Final computing and converting from 0 - 1 to 0 - 255
R = int((r+m)*255)
G = int((g+m)*255)
B = int((b+m)*255)
return [R,G,B]
Now hsv_to_rgb(get_angle(Xaxis,Yaxis),1,1)
will return list of red, green and blue in range of 0 - 255 (for an easier conversion in hex). Saturation and Value are not necessary in this case.
The easiest part. We need to get the list of RGB and convert values to hex.
colors = hsv_to_rgb(get_angle(Xaxis,Yaxis),1,1)
#Converting to hex
lst = list(map(hex,colors))
#Cutting the "0x" part
for i in range(len(lst)):
lst[i] = lst[i][2:]
#If one of the colors has only one digit, extra 0 will be added for a better look
if len(lst[i]) == 1:
lst[i] = "0"+str(lst[i])
print(get_angle(Xaxis,Yaxis))
Something like #ff0000
, #00ff00
, and #0000ff
will be printed.
You also can make your program to show color change in real time, simply add WHITE = colors
in this block. DO NOT use it if you have photosensitive epilepsy.
Place both functions from first and second points somewhere at the beginning.
Add the storing from the first point to the block
for i in range( axes ):
axis = joystick.get_axis( i )
textPrint.print(screen, "Axis {} value: {:>6.3f}".format(i, axis) )
Add the conversion from the third point after the block. I recommend to make a death zone feature.
death_zone = 0.1
if abs(Xaxis) > death_zone or abs(Yaxis) > death_zone:
#If you prefer HSV color wheel, use hsv_to_rgb(get_angle(Xaxis,Yaxis),1,1)
#Else if you prefer RGB color wheel, use hsv_to_rgb(360-get_angle(Xaxis,Yaxis),1,1)
colors = hsv_to_rgb(get_angle(Xaxis,Yaxis),1,1)
#Converting to hex
lst = list(map(hex,colors))
#Cutting the "0x" part
for i in range(len(lst)):
lst[i] = lst[i][2:]
#If one of the colors has only one digit, extra 0 will be added for a better look
if len(lst[i]) == 1:
lst[i] = "0"+str(lst[i])
print("#"+"".join(lst))
That's how your code may look after all:
P.S. - you will probably have to change some code, because I thing my joystick's axis weren't properly captured.
import pygame
import math
# Define some colors
BLACK = ( 0, 0, 0)
WHITE = ( 62, 210, 255)
def hsv_to_rgb(H,S,V):
#Accirding to https://en.wikipedia.org/wiki/HSL_and_HSV#From_HSV
#0 <= H <= 360
#0 <= S <= 1
#0 <= V <= 1
C = V * S
h = H/60
X = C * (1 - abs(h % 2 -1))
#Yes, Python can compare like "0 <= 2 > 1"
if 0 <= h <= 1:
r = C; g = X; b = 0
elif 1 <= h <= 2:
r = X; g = C; b = 0
elif 2 <= h <= 3:
r = 0; g = C; b = X
elif 3 <= h <= 4:
r = 0; g = X; b = C
elif 4 <= h <= 5:
r = X; g = 0; b = C
elif 5 <= h < 6:
r = C; g = 0; b = X
m = V - C
#Final computing and converting from 0 - 1 to 0 - 255
R = int((r+m)*255)
G = int((g+m)*255)
B = int((b+m)*255)
return [R,G,B]
def get_angle(Xaxis,Yaxis):
#To avoid ZeroDivisionError
#P.S. - you can improve it a bit.
if Xaxis == 0:
Xaxis = 0.001
if Yaxis == 0:
Yaxis = 0.001
#defining the third side of a triangle using the Pythagorean theorem
b = ((Xaxis)**2 + (Yaxis)**2)**0.5
c = Xaxis
a = Yaxis
#Using law of cosines we'll fing angle using arccos of cos
#math.acos returns angles in radians, so we need to multiply it by 180/math.pi
angle = math.acos((b**2 + c**2 - a**2) / (2*b*c)) * 180/math.pi
#It'll fix angles to be in range of 0 to 360
if Yaxis > 0:
angle = 360 - angle
return angle
# This is a simple class that will help us print to the screen
# It has nothing to do with the joysticks, just outputting the
# information.
class TextPrint:
def __init__(self):
self.reset()
self.font = pygame.font.Font(None, 25)
def print(self, screen, textString):
textBitmap = self.font.render(textString, True, BLACK)
screen.blit(textBitmap, [self.x, self.y])
self.y += self.line_height
def reset(self):
self.x = 25
self.y = 25
self.line_height = 30
def indent(self):
self.x += 10
def unindent(self):
self.x -= 10
pygame.init()
# Set the width and height of the screen [width,height]
size = [900, 1080]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("PS4Testing")
#Loop until the user clicks the close button.
done = False
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
# Initialize the joysticks
pygame.joystick.init()
# Get ready to print
textPrint = TextPrint()
# -------- Main Program Loop -----------
while done==False:
# EVENT PROCESSING STEP
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
done=True # Flag that we are done so we exit this loop
screen.fill(WHITE)
textPrint.reset()
# Get count of joysticks
joystick_count = pygame.joystick.get_count()
# For each joystick:
for i in range(joystick_count):
joystick = pygame.joystick.Joystick(i)
joystick.init()
# Usually axis run in pairs, up/down for one, and left/right for
# the other.
axes = joystick.get_numaxes()
for i in range( axes ):
axis = joystick.get_axis( i )
#Storing axis statement
if i == 0:
Xaxis = axis
elif i == 1:
Yaxis = axis
textPrint.print(screen, "Axis {} value: {:>6.3f}".format(i, axis) )
textPrint.unindent()
#If joystick is not in the center
#Death zone is used to not capture joystick if it's very close to the center
death_zone = 0.1
if abs(Xaxis) > death_zone or abs(Yaxis) > death_zone:
#If you prefer HSV color wheel, use hsv_to_rgb(get_angle(Xaxis,Yaxis),1,1)
#Else if you prefer RGB color wheel, use hsv_to_rgb(360-get_angle(Xaxis,Yaxis),1,1)
colors = hsv_to_rgb(get_angle(Xaxis,Yaxis),1,1)
#Converting to hex
lst = list(map(hex,colors))
#Cutting the "0x" part
for i in range(len(lst)):
lst[i] = lst[i][2:]
#If one of the colors has only one digit, extra 0 will be added for a better look
if len(lst[i]) == 1:
lst[i] = "0"+str(lst[i])
print("#"+"".join(lst))
#You can use it to see color change in real time.
#But I don't recomend to use it if you have photosensitive epilepsy.
#WHITE = colors
# ALL CODE TO DRAW SHOULD GO ABOVE THIS COMMENT
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Limit to 20 frames per second
clock.tick(20)
# Close the window and quit.
# If you forget this line, the program will 'hang'
# on exit if running from IDLE.
pygame.quit ()