Advertisement
Geometrian

Rotation of Surface Around a Point

Feb 19th, 2019
264
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.24 KB | None | 0 0
  1. #Test parameters
  2.  
  3. #   Resolution of screen
  4. screen_res = (1024,768)
  5.  
  6. #   Path to test surface (if `None`, one will be created procedurally)
  7. image_path = None
  8. #   Resolution of the image (resized as-necessary)
  9. image_testres = (400,100)
  10.  
  11. #   Blit position of un-rotated image (marked with an unfilled light-blue dot).
  12. image_blitpos = (400,300)
  13.  
  14. #   If `True`, uses different function that supports rotating around
  15. #       an arbitrary point, instead of the center of the image.  Either way,
  16. #       the rotation point is marked with a red dot.
  17. use_arbitrary_centers = True
  18. if use_arbitrary_centers:
  19.     #       Coordinates of rotation center in [0,1] space over the image.
  20.     image_rotation_center_uv = (0.2,0.7)
  21.  
  22. #   If `True`, draws crosshairs at mouse position
  23. draw_crosshairs = False
  24.  
  25.  
  26.  
  27. #Test program
  28.  
  29. import os, sys
  30. import traceback
  31.  
  32. with open(os.devnull, "w") as f:
  33.     oldstdout=sys.stdout; sys.stdout=f; import pygame; sys.stdout=oldstdout
  34. from pygame.locals import *
  35.  
  36.  
  37. if sys.platform in ["win32","win64"]: os.environ["SDL_VIDEO_CENTERED"]="1"
  38.  
  39. pygame.display.init()
  40. pygame.font.init()
  41.  
  42. icon=pygame.Surface((1,1)); icon.set_alpha(0); pygame.display.set_icon(icon)
  43. pygame.display.set_caption("Rotation Around Point - Ian Mallett - 2019")
  44.  
  45. surfdisp = pygame.display.set_mode(screen_res)
  46.  
  47. font12 = pygame.font.SysFont("Times New Roman",12,bold=True)
  48. ##font25 = pygame.font.SysFont("Times New Roman",25)
  49.  
  50. def rndint(x): return int(round(x))
  51.  
  52.  
  53.  
  54. #   Setup test image
  55.  
  56. if use_arbitrary_centers:
  57.     image_rotation_center_pixels = ( image_testres[0]*image_rotation_center_uv[0], image_testres[1]*image_rotation_center_uv[1] )
  58. else:
  59.     image_rotation_center_pixels = ( image_testres[0]*0.5,                         image_testres[1]*0.5                         )
  60. tmp = (rndint(image_rotation_center_pixels[0]),rndint(image_rotation_center_pixels[1]))
  61. ##rounded_center_u = float(tmp[0])!=image_rotation_center_pixels[0]
  62. ##rounded_center_v = float(tmp[1])!=image_rotation_center_pixels[1]
  63. image_rotation_center_pixels = tmp
  64.  
  65. if image_path == None:
  66.     surftest = pygame.Surface(image_testres)
  67.     for j in range(image_testres[1]):
  68.         for i in range(image_testres[0]):
  69.             surftest.set_at((i,j),( rndint((255.0*i)/image_testres[0]), rndint((255.0*j)/image_testres[1]), 0 ))
  70.     pygame.draw.rect(surftest,(192,192,192),(0,0,image_testres[0],image_testres[1]),1)
  71. ##  surftest.blit(font25.render("Hello World",True,(0,0,0)),(5,5))
  72.     surftest.set_at((0,0),(128,0,128))
  73. else:
  74.     surftest = pygame.image.load(image_path).convert()
  75.     surftest = pygame.transform.smoothscale(surftest,image_testres)
  76.  
  77. pygame.draw.line(surftest,(255,255,255),(0,image_rotation_center_pixels[1]),image_rotation_center_pixels,1)
  78. pygame.draw.line(surftest,(255,255,255),(image_rotation_center_pixels[0],0),image_rotation_center_pixels,1)
  79. ##labelu = font12.render(u"u*w = (%g)(%d) %s %d"%(image_rotation_center_uv[0],image_testres[0],("=",u"≈")[int(rounded_center_u)],image_rotation_center_pixels[0]),True,(255,255,255))
  80. ##labelv = font12.render(u"v*h = (%g)(%d) %s %d"%(image_rotation_center_uv[1],image_testres[1],("=",u"≈")[int(rounded_center_v)],image_rotation_center_pixels[1]),True,(255,255,255))
  81. labelu = font12.render("u=%g"%image_rotation_center_uv[0],True,(255,255,255))
  82. labelv = font12.render("v=%g"%image_rotation_center_uv[1],True,(255,255,255))
  83. surftest.blit(labelu,(image_rotation_center_pixels[0]//2-labelu.get_width()//2,image_rotation_center_pixels[1]+5))
  84. surftest.blit(labelv,(image_rotation_center_pixels[0]+5,image_rotation_center_pixels[1]//2-labelu.get_height()//2))
  85.  
  86. pygame.draw.circle(surftest,(255,0,0),image_rotation_center_pixels,3)
  87.  
  88.  
  89.  
  90. def get_input():
  91.     keys_pressed = pygame.key.get_pressed()
  92.     mouse_buttons = pygame.mouse.get_pressed()
  93.     mouse_position = pygame.mouse.get_pos()
  94.     mouse_rel = pygame.mouse.get_rel()
  95.     for event in pygame.event.get():
  96.         if   event.type == QUIT: return False
  97.         elif event.type == KEYDOWN:
  98.             if   event.key == K_ESCAPE: return False
  99.     return True
  100.  
  101. import math
  102. def blit_rotated_center( dest, source,coord,angle_deg, area=None,special_flags=0 ):
  103.     """Draws surface `source` onto surface `dest` at coordinate `coord`, but as-if it had been
  104.     rotated by `angle_deg` degrees around its center first.  The `area` and `special_flags` are as
  105.     for ordinary blits."""
  106.  
  107.     x, y = coord
  108.     w, h = source.get_size()
  109.  
  110.     source_rotated = pygame.transform.rotate(source,angle_deg)
  111.  
  112.     angle_rad = math.radians(angle_deg)
  113.     costheta, sintheta = math.cos(angle_rad), math.sin(angle_rad)
  114.     abscostheta, abssintheta = abs(costheta), abs(sintheta)
  115.  
  116. ##  shift = (
  117. ##      0.5*w - 0.5*( w*abscostheta+h*abssintheta ),
  118. ##      0.5*h - 0.5*( w*abssintheta+h*abscostheta )
  119. ##  )
  120.     shift = (
  121.         0.5*(w - w*abscostheta - h*abssintheta ),
  122.         0.5*(h - h*abscostheta - w*abssintheta )
  123.     )
  124.     x_new = x + shift[0]
  125.     y_new = y + shift[1]
  126.  
  127.     dest.blit(source_rotated,(x_new,y_new),area=area,special_flags=special_flags)
  128.  
  129.     return (x_new,y_new)
  130. def blit_rotated_point ( dest, source,coord,angle_deg,center_uv=(0.5,0.5), area=None,special_flags=0 ):
  131.     """Draws surface `source` onto surface `dest` at coordinate `coord`, but as-if it had been
  132.     rotated first by `angle_deg` degrees around the rotation center defined by `center_uv`, a point
  133.     in the UV space of the texture (e.g. `(0,0)` is the top left, `(1,0)` is the top right, etc.).
  134.     The `area` and `special_flags` are as for ordinary blits."""
  135.  
  136.     x, y = coord
  137.     w, h = source.get_size()
  138.     u, v = center_uv[0], 1-center_uv[1]
  139.  
  140.     source_rotated = pygame.transform.rotate(source,angle_deg)
  141.  
  142.     angle_rad = math.radians(angle_deg)
  143.     costheta, sintheta = math.cos(angle_rad), math.sin(angle_rad)
  144.  
  145.     #This is perhaps more-descriptive, but the uncommented version is simpler and faster.
  146. ##  def transform(pt):
  147. ##      pt = ( pt[0]-u*w, pt[1]-v*h )
  148. ##      pt = (
  149. ##          pt[0]*costheta - pt[1]*sintheta,
  150. ##          pt[0]*sintheta + pt[1]*costheta
  151. ##      )
  152. ##      pt = ( pt[0]+u*w, pt[1]+v*h )
  153. ##      return pt
  154. ##  pt00 = transform((0,0))
  155. ##  ptw0 = transform((w,0))
  156. ##  pt0h = transform((0,h))
  157. ##  ptwh = transform((w,h))
  158. ##  shift = (
  159. ##      min( pt00[0], ptw0[0], pt0h[0], ptwh[0] ),
  160. ##      min( pt00[1], ptw0[1], pt0h[1], ptwh[1] )
  161. ##  )
  162.     shift = (
  163.         ( (min(-u*costheta,(1-u)*costheta)+u)*w + (min( v*sintheta,(v-1)*sintheta)  )*h ),
  164.         ( (min(-u*sintheta,(1-u)*sintheta)  )*w + (min(-v*costheta,(1-v)*costheta)+v)*h )
  165.     )
  166.     x_new =                     x +                              shift[0]
  167.     y_new = source.get_height()+y - (source_rotated.get_height()+shift[1])
  168.  
  169.     dest.blit(source_rotated,(x_new,y_new),area=area,special_flags=special_flags)
  170.  
  171.     return (x_new,y_new)
  172.  
  173.  
  174.  
  175. label1 = font12.render(u"Rotation center →",True,(255,255,255))
  176. label1 = pygame.transform.rotate(label1,90)
  177.  
  178. label2 = font12.render(u"Original blit position →",True,(255,255,255))
  179.  
  180. label3 = font12.render(u"New blit position →",True,(255,255,255))
  181.  
  182. angle_deg = 0
  183. def draw():
  184.     global angle_deg
  185.  
  186.  
  187.     surfdisp.fill((64,64,64))
  188.  
  189.  
  190.     if use_arbitrary_centers:
  191.         image_blitpos_transformed = blit_rotated_point ( surfdisp, surftest,image_blitpos,angle_deg,image_rotation_center_uv )
  192.     else:
  193.         image_blitpos_transformed = blit_rotated_center( surfdisp, surftest,image_blitpos,angle_deg                          )
  194.  
  195.  
  196.     surfdisp.blit(label1,(image_blitpos[0]+image_rotation_center_pixels[0]-label1.get_width()//2,image_blitpos[1]+image_rotation_center_pixels[1]+5))
  197.  
  198.     surfdisp.blit(label2,(image_blitpos[0]-label2.get_width()-5,image_blitpos[1]-label2.get_height()//2))
  199.     pygame.draw.circle(surfdisp, (0,192,255), image_blitpos, 3, 1)
  200.  
  201.     surfdisp.blit(label3,(image_blitpos_transformed[0]-label3.get_width()-5,image_blitpos_transformed[1]-label3.get_height()//2))
  202.     pygame.draw.circle(surfdisp, (192,0,192), (rndint(image_blitpos_transformed[0]),rndint(image_blitpos_transformed[1])), 3, 1)
  203.  
  204.     surfdisp.blit(font12.render(u"Angle: %3d°"%angle_deg,True,(255,255,255)),(15,15))
  205.  
  206.  
  207.     if draw_crosshairs:
  208.         x,y = pygame.mouse.get_pos()
  209.         pygame.draw.line(surfdisp,(192,192,192),(x,0),(x,screen_res[1]),1)
  210.         pygame.draw.line(surfdisp,(192,192,192),(0,y),(screen_res[0],y),1)
  211.  
  212.  
  213.     pygame.display.flip()
  214.  
  215.  
  216.     angle_deg = (angle_deg+1) % 360
  217.  
  218. def main():
  219.     clock = pygame.time.Clock()
  220.     while True:
  221.         if not get_input(): break
  222.         draw()
  223.         clock.tick(60)
  224.     pygame.quit()
  225.  
  226. if __name__ == "__main__":
  227.     try:
  228.         main()
  229.     except:
  230.         traceback.print_exc()
  231.         pygame.quit()
  232.         input()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement