Recursive Surfaces in Pygame - recursion

Suppose you have an app with a big gaping hole of negative space, so you want to fill it up with something... so why not fill the void with a copy of the app itself?
I have an app. It will have nested apps. Some apps are normal (i.e., rectangular), but (most) others are elliptical, or even triangular. There are connector layers for nesting the various combinations of app geometries.
While still in progress, there is a notion of somehow managing the ratio of positive space vs. negative space caused by such nesting. One idea I'm working on is subclassing those connectors to create more areas where there will be positive space, and dynamically selecting a subclass.
This will be able to give me a number of extra positive spaces-to-be, which need to be filled with something.
Based on:
Pygame. How do I resize a surface and keep all objects within proportionate to the new window size?

At a high level, we need to:
take a copy of whatever is going to be fractalized
compute where the fractalized image can be repeated
(the bounding rectangles, here called "recursion points")
create a temporary surface for drawing
scale said copy to the size of the temp surface
draw the scaled copy onto the temp surface
In other words:
def draw_background (self, temp):
App.draw_background (self, temp) # default background for apps is to show a default image
self.child.draw_scene (temp) # draw whatever is going to be fractalized
def draw_foreground (self, temp): # fractalization function
TR = temp.get_rect () # bounding rect for parent
X, Y, W, H = TR
ts = pygame.Surface ((W, H)) # get a fresh surface for working
pic = temp.copy () # could be anything
for rp in self.recursion_points (temp):
x, y, w, h = rp
w, h = tr ((w, h)) # round coordinates
trans = pygame.transform.scale (pic, (w, h)) # scale fake screen to bounding rect
ts.blit (trans, (x, y)) # blit fake screen onto working surface
temp.blit (ts, (X, Y)) # blit working-surface onto real surface
Client code:
d = None # default behavior for no app is to not display a foreground
# these adapters handle differing window geometries
c = CircledSquare (d, rotation=STRAIGHT) # middle circle, inner square
b = SquaredCircle (c, background=SECONDARY_BACKGROUND) # outer square, middle circle
a = RecursiveComposite (b) # without this layer, the inner square is not visible... boring!
Geometry functions:
def recursive_affine (rect, dx, dy, rw, rh, n):
x, y, w, h = rect
for k in range (1, n + 1):
dx, dy = dx * rw, dy * rh
x, y = x + dx, y + dy
w, h = w * rw, h * rh
yield x, y, w, h
def recurse_point (rect, rp, minsz):
X, Y, W, H = rect
x, y, w, h = rp
# get scale and offset for recursion point
dx, dy = x - X, y - Y
rw, rh = w / W, h / H
# get number of recursions until < minsz
f = lambda a, b, c: (log (a) - log (b)) / log (c)
xmin, ymin = minsz
xn, yn = f (xmin, w, rw), f (ymin, h, rh)
n = min (xn, yn)
n = ceil (n)
# recursively apply scale and offset
tail = recursive_affine (rp, dx, dy, rw, rh, n)
return rp, *tail

Related

Positioning objects parallel with a mesh

I'm trying to align multiple line objects along a human body circumference depending on the orientation of the triangles from the mesh. I would like to put the lines parallel to the mesh. I correctly assign the position for the lines along the circumference, but I also need to add the rotation of the lines such that to be parallel with the body.
The body is a mesh formed by multiple triangles and every line is "linked" with a triangle.
All I have is:
3 points for the closest triangle from the mesh for every line
The normal of the triangle
The positions for the instantiated lines (2 points, start and end)
I need to calculate the angle for every X, Y, Z axes for the line such that the normal of the triangle is perpendicular with the line mesh. I don't know how to get the desired angle. I really appreciate if someone would like to help me.
input:
FVector TrianglePoints[3];
FVector Triangle_Normal; //Calculated as (B-A)^(C-A), where A,B,C are the points of the triangle
FVector linePosition; //I also have the start line and the endLine position if that helps
ouput:
//FRotator rotation(x,y,z), such that the triangle normal and the line object to be perpendicular.
An overview of the circumference line construction. Now the rotation is calculated using the Start position and End position for each line. When we cross some irregular parts of the mesh we want to rotate the lines correctly. Now the rotation is fixed, depending just on the line start and end position.
If I have understood correctly your goal, here is some related vector geometry:
A,B,C are the vertices of the triangle:
A = [xA, yA, zA],
B = [xB, yB, zB]
C = [xC, yC, zC]
K,L are the endpoints of the line-segment:
K = [xK, yK, zK]
L = [xL, yL, zL]
vectors are interpreted as row-vectors
by . I denote matrix multiplication
by x I denote cross product of 3D vectors
by t() I denote the transpose of a matrix
by | | I denote the norm (magnitude) of a vector
Goal: find the rotation matrix and rotation transformation of segment KL
around its midpoint, so that after rotation KL is parallel to the plane ABC
also, the rotation is the "minimal" angle rotation by witch we need to
rotate KL in order to make it parallel to ABC
AB = B - A
AC = C - A
KL = L - K
n = AB x AC
n = n / |n|
u = KL x n
u = u / |u|
v = n x u
cos = ( KL . t(v) ) / |KL|
sin = ( KL . t(n) ) / |KL|
U = [[ u[0], u[1], u[2] ],
[ v[0], v[1], v[2] ],
[ n[0], n[1], n[2] ],
R = [[1, 0, 0],
[0, cos, sin],
[0, -sin, cos]]
ROT = t(U).R.U
then, one can rotate the segment KL around its midpoint
M = (K + L)/2
Y = M + ROT (X - M)
Here is a python script version
A = np.array([0,0,0])
B = np.array([3,0,0])
C = np.array([2,3,0])
K = np.array([ -1,0,1])
L = np.array([ 2,2,2])
KL = L-K
U = np.empty((3,3), dtype=float)
U[2,:] = np.cross(B-A, C-A)
U[2,:] = U[2,:] / np.linalg.norm(U[2,:])
U[0,:] = np.cross(KL, U[2,:])
U[0,:] = U[0,:] / np.linalg.norm(U[0,:])
U[1,:] = np.cross(U[2,:], U[0,:])
norm_KL = np.linalg.norm(KL)
cos_ = KL.dot(U[1,:]) / norm_KL
sin_ = KL.dot(U[2,:]) / norm_KL
R = np.array([[1, 0, 0],
[0, cos_, sin_],
[0,-sin_, cos_]])
ROT = (U.T).dot(R.dot(U))
M = (K+L) / 2
K_rot = M + ROT.dot( K - M )
L_rot = M + ROT.dot( L - M )
print(L_rot)
print(K_rot)
print(L_rot-K_rot)
print((L_rot-K_rot).dot(U[2,:]))
A more inspired solution was to use a procedural mesh, generated at runtime, that have all the requirements that I need:
Continuously along multiple vertices
Easy to apply a UV map for texture tiling
Can be updated at runtime
Isn't hard to compute/work with it

Finding if a circle is fully contained within multiple triangles?

In a game, an area is defined by triangles that never overlap, and characters are defined by circles.
How can I know whether the full character's collision circle is contained within these triangles?
Example image:
Here, the red parts are outside triangles, so the circle isn't contained within them. Is there an algorithm that can detect this?
I've only came up with "non-perfect" solutions, like sampling points at the border of the circle, then testing if each is inside a triangle.
So basically, the triangles form a domain with polygonal boundary and you want to check if a disk, defined by a center point and a radius is contained inside the domain. So if you start with the triangles, you have to find a way to extract the polygonal boundary of your domain and represent it as a 2D array (matrix) of shape n rows and two columns so that every row is the two coordinates of a vertex point of the polygonal boundary line and the points are ordered so that they are consecutive order along the boundary in a counterclockwise position, i.e. when you walk in a direction from point of index i to the next point i+1 the domain stays on your left. For example, here is the representation of a polygonal boundary of a domain like yours:
a = 4/math.sqrt(3)
Pgon = np.array([[0,0],
[a,0],
[2*a,-1],
[2*a+4,0],
[2*a+4,4],
[2*a,4],
[2*a,2],
[a,1],
[a,4],
[0,0]])
Observe that the first and the last points are the same.
In such a scenario, maybe you can try the following algorithm:
import numpy as np
import math
def angle_and_dist(p1, p2, o):
p12 = p2 - p1
op1 = p1 - o
op2 = p2 - o
norm_p12 = math.sqrt(p12[0]**2 + p12[1]**2)
norm_op1 = math.sqrt(op1[0]**2 + op1[1]**2)
norm_op2 = math.sqrt(op2[0]**2 + op2[1]**2)
p12_perp = np.array([ - p12[1], p12[0] ])
h = - op1.dot(p12_perp)
theta12 = op1.dot(op2) / (norm_op1*norm_op2)
theta12 = math.acos( theta12 )
if h < 0:
theta12 = - theta12
if op1.dot(p12) > 0:
return theta12, norm_op1
elif op2.dot(p12) < 0:
return theta12, norm_op2
else:
return theta12, h/norm_p12
def is_in_polygon(p, disk):
o, r = disk
n_p = len(p)-1
index_o = 0
h_min = 400
for i in range(n_p):
theta, h = angle_and_dist(p[i,:], p[i+1,:], o)
index_o = index_o + theta
if 0 <= h and h < h_min:
h_min = h
if theta <= math.pi/100:
return 'center of disc is not inside polygon'
elif theta > math.pi/100:
if h_min > r:
return 'disc is inside polygon'
else:
return 'center of disc is inside polygon but disc is not'
a = 4/math.sqrt(3)
Pgon = np.array([[0,0],
[a,0],
[2*a,-1],
[2*a+4,0],
[2*a+4,4],
[2*a,4],
[2*a,2],
[a,1],
[a,4],
[0,0]])
# A test example:
#disc = (np.array([3*a/4, 2]), a/4-0.001)
disc = (np.array([3*a/4, 2]), math.sqrt(3)*a/8 - 0.0001)
print(is_in_polygon(Pgon, disc))

2D Lua - Bullet velocity is not correct nor predictable

I'm using some stuff I've put together from various methods around the internet to make this work yet my bullets fly off in seemingly random directions. I've tried throwing negative signs in front of stuff and switching up the trig but to no avail. I've tried using the rotation of the player's arm because that accurately points to the user's mouse but that failed to give me any more accuracy.
I've tried to determine if the bullets follow a pattern like how I needed to invert the Y variable for my arm, but I cannot find a pattern here.
local x, y = objects.PlayerArm:GetPos()
local bullet = createBullet( x + objects.Player:GetWide(), y )
local mX, mY = gui.MouseX(), gui.MouseY()
local shootAngle = math.atan2((mY - y), (mX - x))
shootAngle = math.deg( math.Clamp(shootAngle, -90, 90) )
--shootAngle = objects.PlayerArm.Rotation
bullet.VelocityX = math.cos(shootAngle) * 5
bullet.VelocityY = math.sin(shootAngle) * 5
--bullet.Rotation = shootAngle
print("Angle", shootAngle, "Velocity X and Y", bullet.VelocityX, bullet.VelocityY)
Here is some of what was printed in console each time I shot a bullet.
Angle 47.920721521 Velocity X and Y -3.4948799788437 -3.5757256513158
Angle 24.928474135461 Velocity X and Y 4.8960495864893 -1.0142477244922
Angle 16.837472625676 Velocity X and Y -2.1355174970471 -4.5210137159497
Angle 10.684912400003 Velocity X and Y -1.5284445365972 -4.7606572338855
Angle -1.029154804306 Velocity X and Y 2.5777162320797 -4.2843178018061
Angle -11.63363399894 Velocity X and Y 2.978190104641 4.0162648942293
Angle -22.671343621981 Velocity X and Y -3.8872502993046 3.1447233758403
http://i.gyazo.com/e8ed605098a91bd450b10fda7d484975.png
As #iamnotmaynard suspected, Lua uses C's math library and so all the trig functions use radians instead of degrees. It is best to store all angles in radians and just print them in degrees for a more friendly format. Otherwise you have a lot of conversions to and from radians every time an angle is used. Below is the code updated to only use radians and print in degrees.
local mX, mY = gui.MouseX(), gui.MouseY()
local shootAngle = math.atan2((mY - y), (mX - x))
shootAngle = math.max(-math.pi/2, math.min(shootAngle, math.pi/2))
bullet.VelocityX = math.cos(shootAngle) * 5
bullet.VelocityY = math.sin(shootAngle) * 5
print("Angle (radians)", shootAngle, "(degrees)", math.deg(shootAngle),
"Velocity X and Y", bullet.VelocityX, bullet.VelocityY)
However to compute velocity in the x and y directions angles are not necessary at all. The function below computes VelocityX and VelocityY using only the displacements and makes sure that the velocity is only in the lower right and upper right quadrants as well.
function shoot(x, y, dirx, diry, vel)
local dx = math.max(dirx - x, 0)
local dy = diry - y
local sdist = dx * dx + dy * dy
if sdist > 0 then
local m = vel / math.sqrt(sdist)
return dx * m, dy * m
end
end
bullet.VelocityX, bullet.VeclocityY = shoot(x, y, gui.MouseX(), gui.MouseY(), 5)

How to find the nearest point in the perimeter of a rectangle to a given point?

This is a language-agnostic question. Given a rectangle's dimensions with l,t,w,h (left, top, width, height) and a point x,y, how do I find the nearest point on the perimeter of the rectangle to that point?
I have tried to resolve it in Lua, but any other language would do. So far this is my best effort:
local function nearest(x, a, b)
if a <= x and x <= b then
return x
elseif math.abs(a - x) < math.abs(b - x) then
return a
else
return b
end
end
local function getNearestPointInPerimeter(l,t,w,h, x,y)
return nearest(x, l, l+w), nearest(y, t, t+h)
end
This works for a point outside of the perimeter or in the perimeter itself. But for points inside of the perimeter it fails (it just returns x,y)
My gut tells me that the solution should be simple, but I don't seem to find it.
This time I'm trying to catch the minimum distance of the point toward any side of the rectangle.
local abs, min, max = math.abs, math.min, math.max
local function clamp(x, lower, upper)
return max(lower, min(upper, x))
end
local function getNearestPointInPerimeter(l,t,w,h, x,y)
local r, b = l+w, t+h
x, y = clamp(x, l, r), clamp(y, t, b)
local dl, dr, dt, db = abs(x-l), abs(x-r), abs(y-t), abs(y-b)
local m = min(dl, dr, dt, db)
if m == dt then return x, t end
if m == db then return x, b end
if m == dl then return l, y end
return r, y
end
Let C1,C2,C3,C4 be the vertices of the rectangle.
From the given point A which you have, draw the 2 lines which are
perpendicular to the sides of the rectangle. Let B1, B2, B3, B4
be their intersecting points with the lines determined by the
sides of the rectangle (some of these Bk may coincide too
e.g. if A = Ck for some k). Your solution is one of the points Bk
or one of the points Ck, just brute-force check the 8 points
(again, some of these 8 points may coincide but that doesn't matter).
Another possible algorithm (similar to my 1st answer) can be found here - the one from Dinre.
Calculating the distance between polygon and point in R
Looks quite simple, actually it is a simplified (maybe better) version of my 1st answer here.
Find the two nearest rectangle vertices Ci and Cj to the given point A.
Find the point M where the perpendicular line from A to the line (Ci,Cj) crosses the line (Ci,Cj).
Your solution is either Ci or Cj or M.
Seems to me like this works for all cases (no matter where the point A lies in the plane).
Are you looking for something like this?
Inspired by Keeper's code:
local function getNearestPointInPerimeter(l,t,w,h, x,y)
-- x axis increases to the right
-- y axis increases down
local r = l + w
local b = t + h
local inside = true -- unless later proven otherwise
-- if the point (x,y) is outside the rectangle,
-- push it once to the nearest point on the perimeter, or
-- push it twice to the nearest corner.
if x < l then x = l; inside = false; end
if x > r then x = r; inside = false; end
if y < t then y = t; inside = false; end
if y > b then y = b; inside = false; end
-- if the point (x,y) is inside the rectangle,
-- push it once to the closest side.
if inside then
local dt = math.abs (y - t)
local db = math.abs (y - b)
local dl = math.abs (x - l)
local dr = math.abs (x - r)
if dt <= db and dt <= dl and dt <= dr then
y = t
elseif db <= dl and db <= dr then
y = b
elseif dl <= dr then
x = l
else
x = r
end
end
return x,y
end
Thanks for the question and answers! Here is my python-translated version of the chosen answer in case anyone needs it. The only custom part is the clamp in-line function definition using lambda.
I used this successfully in a GUI with Qt's QRect and QPoint to make sure something showed up in a QGraphcsView.
def getNearestPointInPerimeter(self, left, top, width, height, x, y):
right = left + width
bottom = top + height
clamp = lambda value, minv, maxv: max(min(value, maxv), minv)
x = clamp(x, left, right)
y = clamp(y, top, bottom)
dl = abs(x - left)
dr = abs(x - right)
dt = abs(y - top)
db = abs(y - bottom)
m = min(dl, dr, dt, db)
if m == dt:
result = (x, top)
elif m == db:
result = (x, bottom)
elif m == dl:
result = (left, y)
else:
result = (right, y)
return result
For those looking for the Keeper's answer in C#
public static Point GetNearestPointInPerimeter(Point point, Rectangle rectangle)
{
point.X = Math.Max(rectangle.Left, Math.Min(rectangle.Right, point.X));
point.Y = Math.Max(rectangle.Top, Math.Min(rectangle.Bottom, point.Y));
var dl = Math.Abs(point.X - rectangle.Left);
var dr = Math.Abs(point.X - rectangle.Right);
var dt = Math.Abs(point.Y - rectangle.Top);
var db = Math.Abs(point.Y - rectangle.Bottom);
var m = new[] { dl, dr, dt, db }.Min();
if (m == dt) return new Point(point.X, rectangle.Top);
if (m == db) return new Point(point.X, rectangle.Bottom);
if (m == dl) return new Point(rectangle.Left, point.Y);
return new Point(rectangle.Right, point.Y);
}

Draw Lines Over a Circle

There's a line A-B and C at the center between A and B. It forms a circle as in the figure. If we assume A-B line as a diameter of the circle and then C is it's center. My problem is I have no idea how to draw another three lines (in blue) each 45 degree away from AC or AB. No, this is not a homework, it's part of my complex geometry in a rendering.
alt text http://www.freeimagehosting.net/uploads/befcd84d8c.png
There are a few ways to solve this problem, one of which is to find the angle of the line and then add 45 degrees to this a few times. Here's an example, it's in Python, but translating the math should be easy (and I've tried to write the Python in a simplistic way).
Here's the output for a few lines:
The main function is calc_points, the rest is just to give it A and B that intersect the circle, and make the plots.
from math import atan2, sin, cos, sqrt, pi
from matplotlib import pyplot
def calc_points(A, B, C):
dx = C[0]-A[0]
dy = C[1]-A[1]
line_angle = atan2(dy, dx)
radius = sqrt(dy*dy + dx*dx)
new_points = []
# now go around the circle and find the points
for i in range(3):
angle = line_angle + (i+1)*45*(pi/180) # new angle in radians
x = radius*cos(angle) + C[0]
y = radius*sin(angle) + C[1]
new_points.append([x, y])
return new_points
# test this with some reasonable values
pyplot.figure()
for i, a in enumerate((-20, 20, 190)):
radius = 5
C = [2, 2]
# find an A and B on the circle and plot them
angle = a*(pi/180)
A = [radius*cos(pi+angle)+C[0], radius*sin(pi+angle)+C[1]]
B = [radius*cos(angle)+C[0], radius*sin(angle)+C[1]]
pyplot.subplot(1,3,i+1)
pyplot.plot([A[0], C[0]], [A[1], C[1]], 'r')
pyplot.plot([B[0], C[0]], [B[1], C[1]], 'r')
# now run these through the calc_points function and the new lines
new_points = calc_points(A, B, C)
for np in new_points:
pyplot.plot([np[0], C[0]], [np[1], C[1]], 'b')
pyplot.xlim(-8, 8)
pyplot.ylim(-8, 8)
for x, X in (("A", A), ("B", B), ("C", C)):
pyplot.text(X[0], X[1], x)
pyplot.show()
If you want to find coordinates of blue lines, may be you will find helpful some information about tranformations (rotations):
http://en.wikipedia.org/wiki/Rotation_matrix
You need to rotate for example vector AC and then you can find coordinate of end point of blue line.
start with this and add a button with code:
private void btnCircleLined_Click(object sender, System.EventArgs e)
{
Graphics graph = Graphics.FromImage(DrawArea);
int x = 100, y = 100, diameter = 50;
myPen.Color = Color.Green;
myPen.Width = 10;
graph.DrawEllipse(myPen, x, y, diameter, diameter);
myPen.Color = Color.Red;
double radian = 45 * Math.PI / 180;
int xOffSet = (int)(Math.Cos(radian) * diameter / 2);
int yOffSet = (int)(Math.Sin(radian) * diameter / 2);
graph.DrawLine(myPen, x, y + yOffSet + myPen.Width + diameter / 2, x + xOffSet + myPen.Width + diameter / 2, y);
graph.DrawLine(myPen, x, y, x + xOffSet + myPen.Width + diameter / 2, y + yOffSet + myPen.Width + diameter / 2);
graph.Dispose();
this.Invalidate();
}
edit: could not see your picture so I misinterpeted your question, but this should get you started.
Translate A with C at the origin (i.e. A-C), rotate CW 45°, then translate back. Repeat three more times.
If I were doing this I'd use polar co-ordinates (apologies for including the link if you are already well aware what they are) as an easy way of figuring out the co-ordinates of the points on the circumference that you need. Then draw lines to there from the centre of the circle.

Resources