Right now, I am working on something (in Python) to create an ellipse and display it on the screen (in the console). I have the ellipse creation already, but rotating the ellipse gives me problems.
Ellipse Method:
def ellipse(yc, xc, b, a, rotation=0):
yc_min_b = yc - b
# divide b to account for spacing in console
b = int(round(b / 2 + 0.01)) - 1
yc = yc_min_b + b
points = []
a2 = a*a
b2 = b*b
fa2 = 4 * a2
fb2 = 4 * b2
x = 0
y = b
sigma = 2 * b2 + a2 * (1 - 2 * b)
while b2 * x <= a2 * y:
points.append((xc + x, yc + y))
points.append((xc - x, yc + y))
points.append((xc + x, yc - y))
points.append((xc - x, yc - y))
if sigma >= 0:
sigma += fa2 * (1 - y)
y -= 1
sigma += b2 * ((4 * x) + 6)
x += 1 # INCREMENT
x = a
y = 0
sigma = 2 * a2 + b2 * (1 - 2 * a)
while a2 * y <= b2 * x:
points.append((xc + x, yc + y))
points.append((xc - x, yc + y))
points.append((xc + x, yc - y))
points.append((xc - x, yc - y))
if sigma >= 0:
sigma += fb2 * (1 - x)
x -= 1
sigma += a2 * ((4 * y) + 6)
y += 1 # INCREMENT
# now rotate points
sin = math.sin(rotation)
cos = math.cos(rotation)
rotated = []
for point in points:
x = point[0]
y = point[1]
'''
px -= xc
py -= yc
xnew = px * c - py * s
ynew = px * s + py * c
px = xnew + xc
py = ynew + yc
'''
#XRot := Round(XCenter + (X - XCenter) * CAngle - (Y - YCenter) * SAngle);
#YRot := Round(YCenter + (X - XCenter) * SAngle + (Y - YCenter) * CAngle);
x = round(xc + (x + xc) * cos - (y - yc) * sin)
y = round(yc + (x - xc) * sin + (y - yc) * cos)
rotated.append((int(x), int(y)))
points = rotated
print points
ell_matr = []
# set up empty matrix
maxx = 0
maxy = 0
for point in points:
y = point[1]
x = point[0]
if y > maxy:
maxy = y
if x > maxx:
maxx = x
for i in range(maxy + 1):
ell_matr.append([])
for j in range(maxx + 1):
ell_matr[i].append(' ')
for point in points:
y = point[1]
x = point[0]
ell_matr[y][x] = fill
return ell_matr
I would ignore the matrix part, as it is translating the points into a matrix to display on screen.
Here is the output of an ellipse without rotation.
And when I add a 45 degree rotation (converted to radians)
Is there a better way to rotate the points?
Related
I am trying to check if a point is inside a rotated rectangle.
I found: https://gamedev.stackexchange.com/questions/128598/collision-detection-point-hitting-a-rotating-rectangle
but it doesnt work...
this is my python code:
def collides(point_x, point_y, x_rect, y_rect, width_rect, height_rect, center_rect_x, center_rect_y, angle_rect):
radians = angle_rect / 180.0 * math.pi
angle_sin = math.sin(radians)
angle_cos = math.cos(radians)
x = ((point_x - center_rect_x) * angle_cos - (point_y - center_rect_y) * angle_sin) + center _rect_x
y = ((point_x - center_rect_x) * angle_sin + (point_y - center_rect_y) * angle_cos) + center_rect_y
return x > x_rect and x < x_rect + width_rect and y > y_rect and y < y_rect + height_rect
how can I check if a point collides a rotated rectangle?
I am trying to write a numerical integral calculator as my first Python project. The method takes a starting x value, a delta x value, and a set of y values and returns a lower and an upper bound. I am expecting the result of the numerical integration to converge as dx gets smaller, but it is getting larger.
I am trying to integrate an exponential from 0 to 1 and get the result to converge to 1/3 as dx gets smaller. Currently a dx of 0.1 gives me bounds of (0.14000000000000004, 0.17200000000000004). A dx of 0.01 has bounds of (0.30894500000000014, 0.313747).
Here is my code:
class Linear:
"""Creates a linear function from 2 points"""
def __init__(self, x0, y0, x1, y1):
self.__x0 = x0
self.__y0 = y0
self.__x1 = x1
self.__y1 = y1
self.__a = ((y1 - y0) / (x1 - x0))
self.__b = y0 - (self.__a * x0)
def fun(self, x):
return self.__a * x + self.__b
def integrate(x0, dx, data):
if len(data) < 3:
raise ValueError("data passed to integrate doesn't have enough data")
else:
i, lb, ub = 2, 0, 0
while i < len(data):
y0, y1, y2 = data[i - 2], data[i - 1], data[i]
lin = Linear(x0, y0, x0 + dx + dx, y2)
lin_delta = lin.fun(x0 + dx)
if y1 < lin_delta:
if y0 < y1:
lb += (y0 * dx)
ub += ((((y1 - y0) * dx) / 2) + (y0 * dx))
else:
lb += (y1 * dx)
ub += ((((y0 - y1) * dx) / 2) + (y1 * dx))
else:
if y0 < y1:
ub += (y1 * dx)
lb += ((((y1 - y0) * dx) / 2) + (y0 * dx))
else:
ub += (y0 * dx)
lb += ((((y0 - y1) * dx) / 2) + (y1 * dx))
i += 1
x0 += dx
return lb, ub
if __name__ == '__main__':
j = 0
d = []
dxj = 0.1
while j < (1/dxj):
d += [(j * dxj) * (j * dxj)]
j += 1
print(d)
print(integrate(0, dxj, d))
Also, what is the best way to avoid name collisions with counters like i?
I'm likely using the wrong terms but seeking some help.
I would like to generate an array of x,y values for a grid that sits within the perimeter of an ellipse shape.
There is code here: http://people.sc.fsu.edu/~jburkardt/c_src/ellipse_grid/ellipse_grid.html to accomplish this in Python.
However, for my purpose the ellipses have been rotated to a certain degree. The current equation does not account for this and need some help to account for this transformation, unsure how to change the code to do this?
I've been looking into np.meshrid function as well, so if there are better ways to do this, please say.
Many thanks.
Given an ellipse in the Euclidean plane in its most general form as quadratic curve in the form
f(x,y) = a x^2 + 2b x y + c y^2 + 2d x + 2f y + g,
one can compute the center (x0,y0) by
((cd-bf)/(b^2-ac), (af-bd)/(b^2-ac))
(see equations 19 and 20 at Ellipse on MathWorld). The length of the major axis a_m can be computed by equation 21 on the same page.
Now it suffices to find all grid points (x,y) inside the circle with center (x0,y0) and radius a_m with
sign(f(x,y)) = sign(f(x0,y0)).
To generate lattice points inside ellipse, we have to know where horizontal line intersects that ellipse.
Equation of zero-centered ellipse, rotated by angle Theta:
x = a * Cos(t) * Cos(theta) - b * Sin(t) * Sin(theta)
y = a * Cos(t) * Sin(theta) + b * Sin(t) * Cos(theta)
To simplify calculations, we can introduce pseudoangle Fi and magnitude M (constants for given ellipse)
Fi = atan2(a * Sin(theta), b * Cos(theta))
M = Sqrt((a * Sin(theta))^2 + (b * Cos(theta))^2)
so
y = M * Sin(Fi) * Cos(t) + M * Cos(Fi) * Sin(t)
y/M = Sin(Fi) * Cos(t) + Cos(Fi) * Sin(t)
y/M = Sin(Fi + t)
and solution for given horizontal line at position y are
Fi + t = ArcSin( y / M)
Fi + t = Pi - ArcSin( y / M)
t1 = ArcSin( y / M) - Fi //note two values
t2 = Pi - ArcSin( y / M) - Fi
Substitute both values of t in the first equation and get values of X for given Y, and generate one lattice point sequence
To get top and bottom coordinates, differentiate y
y' = M * Cos(Fi + t) = 0
th = Pi/2 - Fi
tl = -Pi/2 - Fi
find corresponding y's and use them as starting and ending Y-coordinates for lines.
import math
def ellipselattice(cx, cy, a, b, theta):
res = []
at = a * math.sin(theta)
bt = b * math.cos(theta)
Fi = math.atan2(at, bt)
M = math.hypot(at, bt)
ta = math.pi/2 - Fi
tb = -math.pi/2 - Fi
y0 = at * math.cos(ta) + bt *math.sin(ta)
y1 = at * math.cos(tb) + bt *math.sin(tb)
y0, y1 = math.ceil(cy + min(y0, y1)), math.floor(cy + max(y0, y1))
for y in range(y0, y1+1):
t1 = math.asin(y / M) - Fi
t2 = math.pi - math.asin(y / M) - Fi
x1 = a * math.cos(t1) * math.cos(theta) - b* math.sin(t1) * math.sin(theta)
x2 = a * math.cos(t2) * math.cos(theta) - b* math.sin(t2) * math.sin(theta)
x1, x2 = math.ceil(cx + min(x1, x2)), math.floor(cx + max(x1, x2))
line = [(x, y) for x in range(x1, x2 + 1)]
res.append(line)
return res
print(ellipselattice(0, 0, 4, 3, math.pi / 4))
Now that my perlin generator is 'working' I created noise, to find that it is nothing like what I see on the internets...
My noise:
Notice the streaks:
What I am aiming to get (obviously with corresponding colour):
1:
Why does mine look so noisy and nasty?
Code (sorry for no stub, the Perlin noise makes up most of the program so it's important to include the full program):
from PIL import Image
from tkinter import filedialog
from random import randint, random
#Initialise width / height
width = 625
height = 625
#Import gradient picture - 200*1 image used to texture perlin noise
#R,G,B,Alpha
gradient = Image.open("image.png")
gradlist = list(gradient.getdata())
#Create new image
img = Image.new('RGBA', (width, height), color=(255, 255, 255, 255))
#Perlin noise modules --------------------------------------------------------------------------------------------------------
#Modules
from random import sample
from math import floor
p = sample([x for x in range(0, (width * height))], (width * height)) * 2
#Antialising
def fade(t):
retval = 6*(t**5) - 15*(t**4) + 10*(t**3)
return retval
#Linear interpolation
def lerp(t,a,b):
retval = a + (t * (b - a))
return retval
#Clever bitwise hash stuff - picks a unit vector from 12 possible - (1,1,0),(-1,1,0),(1,-1,0),(-1,-1,0),(1,0,1),(-1,0,1),(1,0,-1),(-1,0,-1),(0,1,1),(0,-1,1),(0,1,-1),(0,-1,-1)
def grad(hash, x, y, z):
h = hash % 15
if h < 8:
u = x
else:
u = y
if h < 4:
v = y
elif h in (12, 14):
v = x
else:
v = z
return (u if (h & 1) == 0 else -u) + (v if (h & 2) == 0 else -v)
#Perlin function
def perlin(x,y,z):
ix = int(floor(x)) & 255
iy = int(floor(y)) & 255
iz = int(floor(z)) & 255
x -= int(floor(x))
y -= int(floor(y))
z -= int(floor(z))
u = fade(x)
v = fade(y)
w = fade(z)
#Complicated hash stuff
A = p[ix] + iy
AA = p[A] + iz
AB = p[A + 1] + iz
B = p[ix + 1] + iy
BA = p[B] + iz
BB = p[B + 1] + iz
return -lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z),grad(p[BA], x - 1, y, z)),lerp(u, grad(p[AB], x, y - 1, z),grad(p[BB], x - 1, y - 1, z))),lerp(v, lerp(u, grad(p[AA + 1], x, y, z - 1),grad(p[BA + 1], x - 1, y, z - 1)), lerp(u, grad(p[AB + 1], x, y - 1, z - 1),grad(p[BB + 1], x - 1, y - 1, z - 1))))
def octavePerlin(x,y,z,octaves,persistence):
total = 0
frequency = 1
amplitude = 1
maxValue = 0
for x in range(octaves):
total += perlin(x * frequency, y * frequency, z * frequency) * amplitude
maxValue += amplitude
amplitude *= persistence
frequency *= 2
return total / maxValue
z = random()
img.putdata([gradlist[int(octavePerlin((x + random() - 0.5) / 1000, (y + random() - 0.5) / 1000, z, 4, 2) * 100 + 100)] for x in range(width) for y in range(height)])
img.save(filedialog.asksaveasfilename() + ".png", "PNG")
I've taken the Wikipedia Perlin Noise Algorithm and implemented it in Python, here is the code:
import random
import math
from PIL import Image
from decimal import Decimal
IMAGE_SIZE = 200
PERLIN_RESOLUTION = 10
GRADIENT = []
for x in range(PERLIN_RESOLUTION + 1):
GRADIENT.append([])
for y in range(PERLIN_RESOLUTION + 1):
angle = random.random() * 2 * math.pi
vector = (
Decimal(math.cos(angle)),
Decimal(math.sin(angle))
)
GRADIENT[x].append(vector)
def lerp(a0, a1, w):
return (1 - w)*a0 + w*a1
def dotGridGradient(ix, iy, x, y):
dx = x - Decimal(ix)
dy = y - Decimal(iy)
return (dx*GRADIENT[iy][ix][0] + dy*GRADIENT[iy][ix][1])
def perlin(x, y):
if x > 0.0:
x0 = int(x)
else:
x0 = int(x) - 1
x1 = x0 + 1
if y > 0.0:
y0 = int(y)
else:
y0 = int(y) - 1
y1 = y0 + 1
sx = x - Decimal(x0)
sy = y - Decimal(y0)
n0 = dotGridGradient(x0, y0, x, y)
n1 = dotGridGradient(x1, y0, x, y)
ix0 = lerp(n0, n1, sx)
n0 = dotGridGradient(x0, y1, x, y)
n1 = dotGridGradient(x1, y1, x, y)
ix1 = lerp(n0, n1, sx)
value = lerp(ix0, ix1, sy)
return value
image = Image.new('RGB', (IMAGE_SIZE, IMAGE_SIZE))
pixels = image.load()
for i in range(IMAGE_SIZE):
x = Decimal(i) / IMAGE_SIZE
for j in range(IMAGE_SIZE):
y = Decimal(j) / IMAGE_SIZE
value = perlin(x * 10, y * 10)
greyscale = (value + 1) * 255 / 2
pixels[i, j] = (greyscale, greyscale, greyscale)
image.save('artifacts.png', 'PNG')
Here is the resulting image that is created by the script:
I must be missing something here, you can very clearly see the vertices. Can anyone let me know what is going wrong?
You need to use smoothstep instead of linear interpolation.
def smoothstep(a0, a1, w):
value = w*w*w*(w*(w*6 - 15) + 10)
return a0 + value*(a1 - a0)