1

Here is a test program. I started with two random dots and the line connecting them. Now, I want to take a given image (with x,y dimensions of 79 x 1080) and blit it on top of the guide line. I understand that arctan will give me the angle between the points on a cartesian grid, but because y is backwards the screen (x,y), I have to invert some values. I'm confused about the negating step.

If you run this repeatedly, you'll see the image is always parallel to the line, and sometimes on top, but not consistently.

import math
import pygame
import random
pygame.init()
screen = pygame.display.set_mode((600,600))

#target = (126, 270)
#start = (234, 54)
target = (random.randrange(600), random.randrange(600))
start = (random.randrange(600), random.randrange(600))
BLACK = (0,0,0)
BLUE = (0,0,128)
GREEN = (0,128,0)

pygame.draw.circle(screen, GREEN, start, 15)
pygame.draw.circle(screen, BLUE, target, 15)
pygame.draw.line(screen, BLUE, start, target, 5)                        
route = pygame.Surface((79,1080))
route.set_colorkey(BLACK)
BMP = pygame.image.load('art/trade_route00.png').convert()
(bx, by, bwidth, bheight) = route.get_rect()
route.blit(BMP, (0,0), area=route.get_rect())
# get distance within screen in pixels
dist = math.sqrt((start[0] - target[0])**2 + (start[1] - target[1])**2)
# scale to fit: use distance between points, and make width extra skinny.
route = pygame.transform.scale(route, (int(bwidth * dist/bwidth * 0.05), int( bheight * dist/bheight)))
# and rotate... (invert, as negative is for clockwise)
angle = math.degrees(math.atan2(-1*(target[1]-start[1]), target[0]-start[0]))
route = pygame.transform.rotate(route, angle + 90 )
position = route.get_rect()
HERE = (abs(target[0] - position[2]), target[1]) # - position[3]/2)
print(HERE)
screen.blit(route, HERE)
pygame.display.update()
print(start, target, dist, angle, position)

enter image description here

Valentino
  • 7,291
  • 6
  • 18
  • 34
Marc Maxmeister
  • 4,191
  • 4
  • 40
  • 54

1 Answers1

2

The main problem

The error is not due to the inverse y coordinates (0 at top, max at bottom) while rotating as you seems to think. That part is correct. The error is here:

HERE = (abs(target[0] - position[2]), target[1]) # - position[3]/2)

HERE must be the coordinates of the top-left corner of the rectangle inscribing your green and blue dots connected by the blue line. At those coordinates, you need to place the Surface route after rescaling.

You can get this vertex by doing:

HERE = (min(start[0], target[0]), min(start[1], target[1])) 

This should solve the problem, and your colored dots should lay on the blue line.

A side note

Another thing you might wish to fix is the scaling parameter of route:

route = pygame.transform.scale(route, (int(bwidth * dist/bwidth * 0.05), int( bheight * dist/bheight)))

If my guess is correct and you want to preserve the original widht/height ratio in the rescaled route (since your original image is not a square) this should be:

route = pygame.transform.scale(route, (int(dist* bwidth/bheight), int(dist)))

assuming that you want height (the greater size in the original) be scaled to dist. So you may not need the 0.05, or maybe you can use a different shrinking parameter (probably 0.05 will shrink it too much).

Community
  • 1
  • 1
Valentino
  • 7,291
  • 6
  • 18
  • 34
  • Thanks! I'll test this out and award if it works. On the scaling thing, your intuition is correct, though I was taking an image and making more of a fancy styled line (pygame does NOT style its `.line` functions at all) so the 0.05 on width achieved that. I'll try your scale approach too. Mine was just an intuitive first guess. – Marc Maxmeister Sep 26 '19 at 17:00
  • pygame is quite low level. If you want a styled line, you must draw all the elements by yourself. Like instead of drawing a line, draw a series of circles in different colors, calculating by yourself the coordinates of each circle. If using the image, to mimic a line you may scale it to a fixed widht of 5 or 10 (depending on the width of your line) instead of using shrinking factor of the original width. – Valentino Sep 26 '19 at 17:13
  • Or just import line-like images and rotate them, as I did here. Seems less work than vectorizing your art. A fixed width of 5-10 pixels would be good idea too. – Marc Maxmeister Sep 26 '19 at 17:34
  • Excellent! Your code worked perfectly! for the scaling issue, I went with capping the max width, like this: ```route = pygame.transform.scale(route, (min([15,int(dist * bwidth/bheight)]), int(dist)))``` – Marc Maxmeister Sep 26 '19 at 17:44