Compute interception of line with bounding box - python
I am trying to cut a line to fit inside a bounding box.
I have a start and end point for the line, and the start and end dimensions of a bounding box.
I would like to have a function that to cut the part of the line that is outide the bounding box.
def intersection_with_BB(start_point, end_point, BB_bounds):
direction = end_point - start_point
length = np.linalg.norm(direction)
n = [np.array([1, 0, 0]), np.array([-1, 0, 0]), np.array([0, 1, 0]),
np.array([0, -1, 0]), np.array([0, 0, 1]), np.array([0, 0, -1])]
# something to check this
Sorry for the lack of code.
The end results would be.
P1 = np.array([-2, -2, 0.5])
P2 = np.array([2, 2, 0.5])
BB = np.array([[0, 0, 0], [1, 1, 1]])
result = intersection_with_BB(P1,P2,BB)
result should be the new start and end point that interpect the bounding box
Edit:
ChatGPT proposed this:
def compute_intersection(startPoint, endPoint, boundingBox):
# calculate the direction vector of the line
direction = endPoint - startPoint
# initialize t values
tmin = 0.0
tmax = 1.0
# loop over each axis of the bounding box
for i in range(3):
# check for parallelism
if np.abs(direction[i]) == 0:
# line is parallel to the plane
if startPoint[i] < boundingBox[0][i] or startPoint[i] > boundingBox[1][i]:
# line is outside the plane
return None
else:
# calculate t values for intersection with plane
t1 = (boundingBox[0][i] - startPoint[i]) / direction[i]
t2 = (boundingBox[1][i] - startPoint[i]) / direction[i]
# check ordering of t values
if t1 > t2:
t1, t2 = t2, t1
# update tmin and tmax
if t1 > tmin:
tmin = t1
if t2 < tmax:
tmax = t2
# check if intersection is empty
if tmin > tmax:
return None
# calculate intersection point(s)
intersection1 = startPoint + tmin * direction
intersection2 = startPoint + tmax * direction
# return intersection point(s)
return intersection1, intersection2, np.linalg.norm(intersection2- intersection1)
Can anyone vouche for this?
I think this should do it.
But, I would appreacite validation:
import numpy as np
INSIDE = 0
LEFT = 1
RIGHT = 2
BOTTOM = 4
TOP = 8
BACK = 16
FRONT = 32
def compute_outcode(point, bounds):
code = INSIDE
if point[0] < bounds[0, 0]:
code |= LEFT
elif point[0] > bounds[1, 0]:
code |= RIGHT
if point[1] < bounds[0, 1]:
code |= BOTTOM
elif point[1] > bounds[1, 1]:
code |= TOP
if point[2] < bounds[0, 2]:
code |= BACK
elif point[2] > bounds[1, 2]:
code |= FRONT
return code
def cohen_sutherland_3d(start, end, bounds):
outcode1 = compute_outcode(start, bounds)
outcode2 = compute_outcode(end, bounds)
while True:
if ( outcode1 == 0 and outcode2 == 0): # Both points inside the clipping region
return start, end
elif (outcode1 & outcode2) != 0: # Both points outside the same clipping region
return None, None
else: # compute interception point
x, y, z = 0, 0, 0
outcode_out = outcode1 if outcode1 else outcode2
if outcode_out & LEFT: # Clip against the left boundary
slope = (bounds[0, 0] - start[0]) / (end[0] - start[0])
x = bounds[0, 0]
y = start[1] + (end[1] - start[1]) * slope
z = start[2] + (end[2] - start[2]) * slope
elif outcode_out & RIGHT: # Clip against the right boundary
slope = (bounds[1, 0] - start[0]) / (end[0] - start[0])
x = bounds[1, 0]
y = start[1] + (end[1] - start[1]) * slope
z = start[2] + (end[2] - start[2]) * slope
elif outcode_out & TOP: # Clip against the top boundary
slope = (bounds[1, 1] - start[1])/(end[1] - start[1])
x = start[0] + (end[0] - start[0])*slope
y = bounds[1, 1]
z = start[2] + (end[2] - start[2]) *slope
elif outcode_out & BOTTOM: # Clip against the bottom boundary
slope = (bounds[0, 1] - start[1]) / (end[1] - start[1])
x = start[0] + (end[0] - start[0]) * slope
y = bounds[0, 1]
z = start[2] + (end[2] - start[2]) * slope
elif outcode_out & FRONT: # Clip against the front boundary
slope = (bounds[1, 2] - start[2]) / (end[2] - start[2])
x = start[0] + (end[0] - start[0]) * slope
y = start[1] + (end[1] - start[1]) * slope
z = bounds[1, 2]
elif outcode_out & BACK: # Clip against the back boundary
slope = (bounds[0, 2] - start[2]) / (end[2] - start[2])
x = start[0] + (end[0] - start[0]) * slope
y = start[1] + (end[1] - start[1]) * slope
z = bounds[0, 2]
if outcode1:
start=np.array([x,y,z])
outcode1 = compute_outcode(start, bounds)
else:
end=np.array([x,y,z])
outcode2 = compute_outcode(end, bounds)
# Example
p1 = np.array([0,0,0])
p2 = np.array([0.2,0.2,0])
bounds = np.array([[0,0,0],[0.1,0.1,0]])
p1, p2 = cohen_sutherland_3d(p1,p2,bounds)
Related
Fastest way to couple nodes to vectors
I all, I have a file with vectors to read, the data in the text file is stored in this way: '[start_time, x_start, y_start, z_start, laser_power]' Example: [8.830000, -9.8200, -3.16000, 4.20, 150.0] [8.830891, -9.7677, -2.49731, 4.20, 0.000] Here the vector would go from x[-9.82] to x[-9.7677] and y[-3.16] to y[-2.49] with 150.0 Watts. I also have a file with nodes that have temperatures stored in this way: '[node_id, x, y, z, temperature]' Example: '[63, 5.0, 24.0, 2.2727, 170.45]' The laser has approximately 7000 vectors over the z-layer, and 1200 nodes. What is the fastest way to couple the node coordinates to the vector coordinates? My try goes as follows: calculate the bounding box of the vector loop over all nodes and list those in the bounding box calculate the slope of the vector, calculate the perpendicular slope to that vector. intersect both slopes with the perpendicular line starting in a node in the list. if the distance between node coordinate and line intersect < 0.1, list that node with open(rf'Node_Temp_result.txt', 'r') as nodeFile: nodesInfo = nodeFile.readlines() with open(rf'laser.txt', 'r') as laserFile: laserInfo = laserFile.readlines() def cross_finder(x_start, x_end, y_start, y_end): line_slope = (y_end - y_start - 0.000000001) / (x_end - x_start - 0.000000001) point_slope = - 1 / line_slope line_extra = -x_end * line_slope + y_end point_extra = -node_search.node_x() * point_slope + node_search.node_y() cross_x = (point_extra - line_extra) / (line_slope - point_slope) cross_y = line_slope * cross_x + line_extra time_taken = math.sqrt((cross_x - laser_search.laser_x()) ** 2 + (cross_y - laser_search.laser_y()) ** 2) / laserVel distance = math.sqrt((cross_x - node_search.node_x()) ** 2 + (cross_y - node_search.node_y()) ** 2) return cross_x, cross_y, time_taken, distance for laser in tqdm(range(currentLaserMin, currentLaserNext)): local_laser = Split(laserInfo, laser) laser_search = Laser(local_laser.col0(), local_laser.col1(), local_laser.col2(), local_laser.col3(), local_laser.col4()) splits = 0 x_search_laser = laser_search.laser_x() y_search_laser = laser_search.laser_y() z_search_laser = laser_search.laser_layer() if layer - 1 < z_search_laser <= layer and laser_search.laser_power() > 0: laser_next = Split(laserInfo, laser + 1) laser_search_next = Laser(laser_next.col0(), laser_next.col1(), laser_next.col2(), laser_next.col3(), laser_next.col4()) x_start = x_search_laser x_end = laser_search_next.laser_x() y_start = y_search_laser y_end = laser_search_next.laser_y() num = 0 for node in range(currentNodeMin, currentNodeNext): local_node = Split(nodesInfo, node) node_search = Node(local_node.col0(), local_node.col1(), local_node.col2(), local_node.col3(), local_node.col4()) x_search_node = node_search.node_x() y_search_node = node_search.node_y() z_search_node = node_search.node_layer() if node_search.node_temp() > 250: if layer - 1 < z_search_node <= layer: if x_start <= x_search_node <= x_end and y_start <= y_search_node <= y_end: crossLocations = cross_finder(x_start, x_end, y_start, y_end) if crossLocations[3] < 0.1: splits += 1 if num == 0: # print(node_search.node_x(), node_search.node_y(), node_search.node_z()) laserlist.append([laser_search.laser_time(), laser_search.laser_x(), laser_search.laser_y(), laser_search.laser_z(), laser_search.laser_power() * 0.5]) num += 1 Is there a faster way to complete this? Eventually I'll have to do this for vector files of 203 000 vectors and node files of 30 000 nodes combined.
How to increase FPS in ursina python
I want to create survival games with infinite block terrain(like Minecraft). So i using ursina python game engine, you can see it here So i using perlin noise to create the terrain with build-in ursina block model. I test for first 25 block and it work pretty good with above 100 FPS, so i start increase to 250 block and more because I want a infinite terrain. But i ran to some problem, when i increase to 100 block or more, my FPS start to decrease below 30 FPS (With i create just one layer). Here is my code: #-------------------------------Noise.py(I got on the github)------------------------- # Copyright (c) 2008, Casey Duncan (casey dot duncan at gmail dot com) # see LICENSE.txt for details """Perlin noise -- pure python implementation""" __version__ = '$Id: perlin.py 521 2008-12-15 03:03:52Z casey.duncan $' from math import floor, fmod, sqrt from random import randint # 3D Gradient vectors _GRAD3 = ((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), (1,1,0),(0,-1,1),(-1,1,0),(0,-1,-1), ) # 4D Gradient vectors _GRAD4 = ((0,1,1,1), (0,1,1,-1), (0,1,-1,1), (0,1,-1,-1), (0,-1,1,1), (0,-1,1,-1), (0,-1,-1,1), (0,-1,-1,-1), (1,0,1,1), (1,0,1,-1), (1,0,-1,1), (1,0,-1,-1), (-1,0,1,1), (-1,0,1,-1), (-1,0,-1,1), (-1,0,-1,-1), (1,1,0,1), (1,1,0,-1), (1,-1,0,1), (1,-1,0,-1), (-1,1,0,1), (-1,1,0,-1), (-1,-1,0,1), (-1,-1,0,-1), (1,1,1,0), (1,1,-1,0), (1,-1,1,0), (1,-1,-1,0), (-1,1,1,0), (-1,1,-1,0), (-1,-1,1,0), (-1,-1,-1,0)) # A lookup table to traverse the simplex around a given point in 4D. # Details can be found where this table is used, in the 4D noise method. _SIMPLEX = ( (0,1,2,3),(0,1,3,2),(0,0,0,0),(0,2,3,1),(0,0,0,0),(0,0,0,0),(0,0,0,0),(1,2,3,0), (0,2,1,3),(0,0,0,0),(0,3,1,2),(0,3,2,1),(0,0,0,0),(0,0,0,0),(0,0,0,0),(1,3,2,0), (0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0), (1,2,0,3),(0,0,0,0),(1,3,0,2),(0,0,0,0),(0,0,0,0),(0,0,0,0),(2,3,0,1),(2,3,1,0), (1,0,2,3),(1,0,3,2),(0,0,0,0),(0,0,0,0),(0,0,0,0),(2,0,3,1),(0,0,0,0),(2,1,3,0), (0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0), (2,0,1,3),(0,0,0,0),(0,0,0,0),(0,0,0,0),(3,0,1,2),(3,0,2,1),(0,0,0,0),(3,1,2,0), (2,1,0,3),(0,0,0,0),(0,0,0,0),(0,0,0,0),(3,1,0,2),(0,0,0,0),(3,2,0,1),(3,2,1,0)) # Simplex skew constants _F2 = 0.5 * (sqrt(3.0) - 1.0) _G2 = (3.0 - sqrt(3.0)) / 6.0 _F3 = 1.0 / 3.0 _G3 = 1.0 / 6.0 class BaseNoise: """Noise abstract base class""" permutation = (151,160,137,91,90,15, 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, 190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, 88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166, 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, 102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,200,196, 135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,250,124,123, 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, 223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,172,9, 129,22,39,253,9,98,108,110,79,113,224,232,178,185,112,104,218,246,97,228, 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, 49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254, 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180) period = len(permutation) # Double permutation array so we don't need to wrap permutation = permutation * 2 randint_function = randint def __init__(self, period=None, permutation_table=None, randint_function=None): """Initialize the noise generator. With no arguments, the default period and permutation table are used (256). The default permutation table generates the exact same noise pattern each time. An integer period can be specified, to generate a random permutation table with period elements. The period determines the (integer) interval that the noise repeats, which is useful for creating tiled textures. period should be a power-of-two, though this is not enforced. Note that the speed of the noise algorithm is indpendent of the period size, though larger periods mean a larger table, which consume more memory. A permutation table consisting of an iterable sequence of whole numbers can be specified directly. This should have a power-of-two length. Typical permutation tables are a sequnce of unique integers in the range [0,period) in random order, though other arrangements could prove useful, they will not be "pure" simplex noise. The largest element in the sequence must be no larger than period-1. period and permutation_table may not be specified together. A substitute for the method random.randint(a, b) can be chosen. The method must take two integer parameters a and b and return an integer N such that a <= N <= b. """ if randint_function is not None: # do this before calling randomize() if not hasattr(randint_function, '__call__'): raise TypeError( 'randint_function has to be a function') self.randint_function = randint_function if period is None: period = self.period # enforce actually calling randomize() if period is not None and permutation_table is not None: raise ValueError( 'Can specify either period or permutation_table, not both') if period is not None: self.randomize(period) elif permutation_table is not None: self.permutation = tuple(permutation_table) * 2 self.period = len(permutation_table) def randomize(self, period=None): """Randomize the permutation table used by the noise functions. This makes them generate a different noise pattern for the same inputs. """ if period is not None: self.period = period perm = list(range(self.period)) perm_right = self.period - 1 for i in list(perm): j = self.randint_function(0, perm_right) perm[i], perm[j] = perm[j], perm[i] self.permutation = tuple(perm) * 2 class SimplexNoise(BaseNoise): """Perlin simplex noise generator Adapted from Stefan Gustavson's Java implementation described here: http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf To summarize: "In 2001, Ken Perlin presented 'simplex noise', a replacement for his classic noise algorithm. Classic 'Perlin noise' won him an academy award and has become an ubiquitous procedural primitive for computer graphics over the years, but in hindsight it has quite a few limitations. Ken Perlin himself designed simplex noise specifically to overcome those limitations, and he spent a lot of good thinking on it. Therefore, it is a better idea than his original algorithm. A few of the more prominent advantages are: * Simplex noise has a lower computational complexity and requires fewer multiplications. * Simplex noise scales to higher dimensions (4D, 5D and up) with much less computational cost, the complexity is O(N) for N dimensions instead of the O(2^N) of classic Noise. * Simplex noise has no noticeable directional artifacts. Simplex noise has a well-defined and continuous gradient everywhere that can be computed quite cheaply. * Simplex noise is easy to implement in hardware." """ def noise2(self, x, y): """2D Perlin simplex noise. Return a floating point value from -1 to 1 for the given x, y coordinate. The same value is always returned for a given x, y pair unless the permutation table changes (see randomize above). """ # Skew input space to determine which simplex (triangle) we are in s = (x + y) * _F2 i = floor(x + s) j = floor(y + s) t = (i + j) * _G2 x0 = x - (i - t) # "Unskewed" distances from cell origin y0 = y - (j - t) if x0 > y0: i1 = 1; j1 = 0 # Lower triangle, XY order: (0,0)->(1,0)->(1,1) else: i1 = 0; j1 = 1 # Upper triangle, YX order: (0,0)->(0,1)->(1,1) x1 = x0 - i1 + _G2 # Offsets for middle corner in (x,y) unskewed coords y1 = y0 - j1 + _G2 x2 = x0 + _G2 * 2.0 - 1.0 # Offsets for last corner in (x,y) unskewed coords y2 = y0 + _G2 * 2.0 - 1.0 # Determine hashed gradient indices of the three simplex corners perm = self.permutation ii = int(i) % self.period jj = int(j) % self.period gi0 = perm[ii + perm[jj]] % 12 gi1 = perm[ii + i1 + perm[jj + j1]] % 12 gi2 = perm[ii + 1 + perm[jj + 1]] % 12 # Calculate the contribution from the three corners tt = 0.5 - x0**2 - y0**2 if tt > 0: g = _GRAD3[gi0] noise = tt**4 * (g[0] * x0 + g[1] * y0) else: noise = 0.0 tt = 0.5 - x1**2 - y1**2 if tt > 0: g = _GRAD3[gi1] noise += tt**4 * (g[0] * x1 + g[1] * y1) tt = 0.5 - x2**2 - y2**2 if tt > 0: g = _GRAD3[gi2] noise += tt**4 * (g[0] * x2 + g[1] * y2) return noise * 70.0 # scale noise to [-1, 1] def noise3(self, x, y, z): """3D Perlin simplex noise. Return a floating point value from -1 to 1 for the given x, y, z coordinate. The same value is always returned for a given x, y, z pair unless the permutation table changes (see randomize above). """ # Skew the input space to determine which simplex cell we're in s = (x + y + z) * _F3 i = floor(x + s) j = floor(y + s) k = floor(z + s) t = (i + j + k) * _G3 x0 = x - (i - t) # "Unskewed" distances from cell origin y0 = y - (j - t) z0 = z - (k - t) # For the 3D case, the simplex shape is a slightly irregular tetrahedron. # Determine which simplex we are in. if x0 >= y0: if y0 >= z0: i1 = 1; j1 = 0; k1 = 0 i2 = 1; j2 = 1; k2 = 0 elif x0 >= z0: i1 = 1; j1 = 0; k1 = 0 i2 = 1; j2 = 0; k2 = 1 else: i1 = 0; j1 = 0; k1 = 1 i2 = 1; j2 = 0; k2 = 1 else: # x0 < y0 if y0 < z0: i1 = 0; j1 = 0; k1 = 1 i2 = 0; j2 = 1; k2 = 1 elif x0 < z0: i1 = 0; j1 = 1; k1 = 0 i2 = 0; j2 = 1; k2 = 1 else: i1 = 0; j1 = 1; k1 = 0 i2 = 1; j2 = 1; k2 = 0 # Offsets for remaining corners x1 = x0 - i1 + _G3 y1 = y0 - j1 + _G3 z1 = z0 - k1 + _G3 x2 = x0 - i2 + 2.0 * _G3 y2 = y0 - j2 + 2.0 * _G3 z2 = z0 - k2 + 2.0 * _G3 x3 = x0 - 1.0 + 3.0 * _G3 y3 = y0 - 1.0 + 3.0 * _G3 z3 = z0 - 1.0 + 3.0 * _G3 # Calculate the hashed gradient indices of the four simplex corners perm = self.permutation ii = int(i) % self.period jj = int(j) % self.period kk = int(k) % self.period gi0 = perm[ii + perm[jj + perm[kk]]] % 12 gi1 = perm[ii + i1 + perm[jj + j1 + perm[kk + k1]]] % 12 gi2 = perm[ii + i2 + perm[jj + j2 + perm[kk + k2]]] % 12 gi3 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1]]] % 12 # Calculate the contribution from the four corners noise = 0.0 tt = 0.6 - x0**2 - y0**2 - z0**2 if tt > 0: g = _GRAD3[gi0] noise = tt**4 * (g[0] * x0 + g[1] * y0 + g[2] * z0) else: noise = 0.0 tt = 0.6 - x1**2 - y1**2 - z1**2 if tt > 0: g = _GRAD3[gi1] noise += tt**4 * (g[0] * x1 + g[1] * y1 + g[2] * z1) tt = 0.6 - x2**2 - y2**2 - z2**2 if tt > 0: g = _GRAD3[gi2] noise += tt**4 * (g[0] * x2 + g[1] * y2 + g[2] * z2) tt = 0.6 - x3**2 - y3**2 - z3**2 if tt > 0: g = _GRAD3[gi3] noise += tt**4 * (g[0] * x3 + g[1] * y3 + g[2] * z3) return noise * 32.0 def lerp(t, a, b): return a + t * (b - a) def grad3(hash, x, y, z): g = _GRAD3[hash % 16] return x*g[0] + y*g[1] + z*g[2] class TileableNoise(BaseNoise): """Tileable implemention of Perlin "improved" noise. This is based on the reference implementation published here: http://mrl.nyu.edu/~perlin/noise/ """ def noise3(self, x, y, z, repeat, base=0.0): """Tileable 3D noise. repeat specifies the integer interval in each dimension when the noise pattern repeats. base allows a different texture to be generated for the same repeat interval. """ i = int(fmod(floor(x), repeat)) j = int(fmod(floor(y), repeat)) k = int(fmod(floor(z), repeat)) ii = (i + 1) % repeat jj = (j + 1) % repeat kk = (k + 1) % repeat if base: i += base; j += base; k += base ii += base; jj += base; kk += base x -= floor(x); y -= floor(y); z -= floor(z) fx = x**3 * (x * (x * 6 - 15) + 10) fy = y**3 * (y * (y * 6 - 15) + 10) fz = z**3 * (z * (z * 6 - 15) + 10) perm = self.permutation A = perm[i] AA = perm[A + j] AB = perm[A + jj] B = perm[ii] BA = perm[B + j] BB = perm[B + jj] return lerp(fz, lerp(fy, lerp(fx, grad3(perm[AA + k], x, y, z), grad3(perm[BA + k], x - 1, y, z)), lerp(fx, grad3(perm[AB + k], x, y - 1, z), grad3(perm[BB + k], x - 1, y - 1, z))), lerp(fy, lerp(fx, grad3(perm[AA + kk], x, y, z - 1), grad3(perm[BA + kk], x - 1, y, z - 1)), lerp(fx, grad3(perm[AB + kk], x, y - 1, z - 1), grad3(perm[BB + kk], x - 1, y - 1, z - 1)))) #--------------------------Math.py(For InverseLefp)-------------------------------- def Clamp(t: float, minimum: float, maximum: float): """Float result between a min and max values.""" value = t if t < minimum: value = minimum elif t > maximum: value = maximum return value def InverseLefp(a: float, b: float, value: float): if a != b: return Clamp((value - a) / (b - a), 0, 1) return 0 #-----------------------------Game.py(Main code)---------------------- from ursina import * from ursina.prefabs import * from ursina.prefabs.first_person_controller import * from Math import InverseLefp import Noise app = Ursina() #The maximum height of the terrain maxHeight = 10 #Control the width and height of the map mapWidth = 10 mapHeight = 10 #A class that create a block class Voxel(Button): def __init__(self, position=(0,0,0)): super().__init__( parent = scene, position = position, model = 'cube', origin_y = .5, texture = 'white_cube', color = color.color(0, 0, random.uniform(.9, 1.0)), highlight_color = color.lime, ) #Detect user key input def input(self, key): if self.hovered: if key == 'right mouse down': #Place block if user right click voxel = Voxel(position=self.position + mouse.normal) if key == 'left mouse down': #Break block if user left click destroy(self) if key == 'escape': #Exit the game if user press the esc key app.userExit() #Return perlin noise value between 0 and 1 with x, y position with scale = noiseScale def GeneratedNoiseMap(y: int, x: int, noiseScale: float): #Check if the noise scale was invalid or not if noiseScale <= 0: noiseScale = 0.001 sampleX = x / noiseScale sampleY = y / noiseScale #The Noise.SimplexNoise().noise2 will return the value between -1 and 1 perlinValue = Noise.SimplexNoise().noise2(sampleX, sampleY) #The InverseLefp will make the value scale to between 0 and 1 perlinValue = InverseLefp(-1, 1, perlinValue) return perlinValue for z in range(mapHeight): for x in range(mapWidth): #Calculating the height of the block and round it to integer height = round(GeneratedNoiseMap(z, x, 20) * maxHeight) #Place the block and make it always below the player block = Voxel(position=(x, height - maxHeight - 1, z)) #Set the collider of the block block.collider = 'mesh' #Character movement player = FirstPersonController() #Run the game app.run() All file in same folder. It was working fine but the FPS is very low, so can anyone help?
I'm not able to test this code at the moment but this should serve as a starting point: level_parent = Entity(model=Mesh(vertices=[], uvs=[])) for z in range(mapHeight): for x in range(mapWidth): height = round(GeneratedNoiseMap(z, x, 20) * maxHeight) block = Voxel(position=(x, height - maxHeight - 1, z)) level_parent.model.vertices.extend(block.model.vertices) level_parent.collider = 'mesh' # call this only once after all vertices are set up For texturing, you might have to add the block.uvs from each block to level_parent.model.uvs as well. Alternatively, call level_parent.model.project_uvs() after setting up the vertices.
On my version of ursina engine (5.0.0) only this code: ` level_parent = Entity(model=Mesh(vertices=[], uvs=[])) for z in range(mapHeight): for x in range(mapWidth): height = round(GeneratedNoiseMap(z, x, 20) * maxHeight) block = Voxel(position=(x, height - maxHeight - 1, z)) #level_parent.model.vertices.extend(block.model.vertices) level_parent.combine().vertices.extend(block.combine().vertices) level_parent.collider = 'mesh' ` is working.
Centroidal Voronoi tessellation
I am trying to build a bounded Voronoi diagram using the scipy package and in each iteration I compute the centroids of the Voronoi cells and move a bit say some delta towards the centroid and recompute the Voronoi diagram by updating the generator points. When I try to plot the updated points I get a weird error as in the point I plot is not where it is expected to be. Here's the code import matplotlib.pyplot as pl import numpy as np import scipy as sp import scipy.spatial import sys np.random.seed(1) eps = sys.float_info.epsilon n_robots = 10 robots = np.random.rand(n_robots, 2) #print(robots) bounding_box = np.array([0., 1., 0., 1.]) def in_box(robots, bounding_box): return np.logical_and(np.logical_and(bounding_box[0] <= robots[:, 0], robots[:, 0] <= bounding_box[1]), np.logical_and(bounding_box[2] <= robots[:, 1], robots[:, 1] <= bounding_box[3])) def voronoi(robots, bounding_box): i = in_box(robots, bounding_box) points_center = robots[i, :] points_left = np.copy(points_center) points_left[:, 0] = bounding_box[0] - (points_left[:, 0] - bounding_box[0]) points_right = np.copy(points_center) points_right[:, 0] = bounding_box[1] + (bounding_box[1] - points_right[:, 0]) points_down = np.copy(points_center) points_down[:, 1] = bounding_box[2] - (points_down[:, 1] - bounding_box[2]) points_up = np.copy(points_center) points_up[:, 1] = bounding_box[3] + (bounding_box[3] - points_up[:, 1]) points = np.append(points_center, np.append(np.append(points_left, points_right, axis=0), np.append(points_down, points_up, axis=0), axis=0), axis=0) # Compute Voronoi vor = sp.spatial.Voronoi(points) # Filter regions regions = [] ind = np.arange(points.shape[0]) ind = np.expand_dims(ind,axis= 1) for region in vor.regions: flag = True for index in region: if index == -1: flag = False break else: x = vor.vertices[index, 0] y = vor.vertices[index, 1] if not(bounding_box[0] - eps <= x and x <= bounding_box[1] + eps and bounding_box[2] - eps <= y and y <= bounding_box[3] + eps): flag = False break if region != [] and flag: regions.append(region) vor.filtered_points = points_center vor.filtered_regions = regions return vor def centroid_region(vertices): A = 0 C_x = 0 C_y = 0 for i in range(0, len(vertices) - 1): s = (vertices[i, 0] * vertices[i + 1, 1] - vertices[i + 1, 0] * vertices[i, 1]) A = A + s C_x = C_x + (vertices[i, 0] + vertices[i + 1, 0]) * s C_y = C_y + (vertices[i, 1] + vertices[i + 1, 1]) * s A = 0.5 * A C_x = (1.0 / (6.0 * A)) * C_x C_y = (1.0 / (6.0 * A)) * C_y return np.array([[C_x, C_y]]) def plot(r,index): vor = voronoi(r, bounding_box) fig = pl.figure() ax = fig.gca() # Plot initial points ax.plot(vor.filtered_points[:, 0], vor.filtered_points[:, 1], 'b.') print("initial",vor.filtered_points) # Plot ridges points for region in vor.filtered_regions: vertices = vor.vertices[region, :] ax.plot(vertices[:, 0], vertices[:, 1], 'go') # Plot ridges for region in vor.filtered_regions: vertices = vor.vertices[region + [region[0]], :] ax.plot(vertices[:, 0], vertices[:, 1], 'k-') # Compute and plot centroids centroids = [] for region in vor.filtered_regions: vertices = vor.vertices[region + [region[0]], :] centroid = centroid_region(vertices) centroids.append(list(centroid[0, :])) ax.plot(centroid[:, 0], centroid[:, 1], 'r.') centroids = np.asarray(centroids) rob = np.copy(vor.filtered_points) # the below code is for the plotting purpose the update happens in the update function interim_x = np.asarray(centroids[:,0] - rob[:,0]) interim_y = np.asarray(centroids[:,1] - rob[:,1]) magn = [np.linalg.norm(centroids[i,:] - rob[i,:]) for i in range(rob.shape[0])] x = np.copy(interim_x) x = np.asarray([interim_x[i]/magn[i] for i in range(interim_x.shape[0])]) y = np.copy(interim_y) y = np.asarray([interim_y[i]/magn[i] for i in range(interim_y.shape[0])]) nor = np.copy(rob) for i in range(x.shape[0]): nor[i,0] = x[i] nor[i,1] = y[i] temp = np.copy(rob) temp[:,0] = [rob[i,0] + 0.5*interim_x[i] for i in range(rob.shape[0])] temp[:,1] = [rob[i,1] + 0.5*interim_y[i] for i in range(rob.shape[0])] ax.plot(temp[:,0] ,temp[:,1], 'y.' ) ax.set_xlim([-0.1, 1.1]) ax.set_ylim([-0.1, 1.1]) pl.savefig("voronoi" + str(index) + ".png") return centroids def update(rob,centroids): interim_x = np.asarray(centroids[:,0] - rob[:,0]) interim_y = np.asarray(centroids[:,1] - rob[:,1]) magn = [np.linalg.norm(centroids[i,:] - rob[i,:]) for i in range(rob.shape[0])] x = np.copy(interim_x) x = np.asarray([interim_x[i]/magn[i] for i in range(interim_x.shape[0])]) y = np.copy(interim_y) y = np.asarray([interim_y[i]/magn[i] for i in range(interim_y.shape[0])]) nor = [np.linalg.norm([x[i],y[i]]) for i in range(x.shape[0])] temp = np.copy(rob) temp[:,0] = [rob[i,0] + 0.5*interim_x[i] for i in range(rob.shape[0])] temp[:,1] = [rob[i,1] + 0.5*interim_y[i] for i in range(rob.shape[0])] return np.asarray(temp) if __name__ == '__main__': for i in range(1): centroids = plot(robots,i) robots = update(robots,centroids) Also here is an image of what the code does. The blue points are the generator points, red are the centroids and yellow are supposed to be the midway points between the blue and red points. But as you can see the yellow points are not in between the blue and red points.
The problem is that your points when fed to Voronoi get inflated during the construction of the tessellation, and when you later filter them out the points are in the wrong order. Consequently when you set vor.filtered_points = points_center in voronoi(), the points are shuffled compared to the order of regions. So while you're computing the midpoints correctly, you're using the wrong pairs of points. I circled two correct pairings in green and an incorrect one in red here: As you can see from the red circle, the basis point in an edge cell is paired with the centroid of an adjacent cell. The solution is simple: when you're filtering the regions and find a region to keep, you need to gather the point which falls inside the corresponding region. You can do this by matching vor.points to vor.point_region and finding the corresponding region, for which you'll need to enumerate your regions: # Compute Voronoi vor = sp.spatial.Voronoi(points) # Filter regions and select corresponding points regions = [] points_to_filter = [] # we'll need to gather points too ind = np.arange(points.shape[0]) ind = np.expand_dims(ind,axis= 1) for i,region in enumerate(vor.regions): # enumerate the regions if not region: # nicer to skip the empty region altogether continue flag = True for index in region: if index == -1: flag = False break else: x = vor.vertices[index, 0] y = vor.vertices[index, 1] if not(bounding_box[0] - eps <= x and x <= bounding_box[1] + eps and bounding_box[2] - eps <= y and y <= bounding_box[3] + eps): flag = False break if flag: regions.append(region) # find the point which lies inside points_to_filter.append(vor.points[vor.point_region == i][0,:]) vor.filtered_points = np.array(points_to_filter) vor.filtered_regions = regions With these modifications the averaging works fine:
Circle and line collision detection in python tkinter
I writing a python program in which a circle bounces off of user drawn lines. There are multiple circles that bounce off the wall. For each one, the shortest distance from the center of the circle and the ball should be calculated. I would prefer if this code was very efficient because my current algorithm lags the computer a lot. If point a is the starting point ,and point b is the end point, and point c is the center, and r is the radius, how would I calculate the shortest distance between the ball? This algorithm should also work if the X coordinate of the ball is out of range of x coordinates in segment AB. Please post python code Any help would be appreciated! Here's what I have so far: lineList is a list with 4 values that contains beginning and end coordinates of the user drawn lines center is the center of the ball global lineList, numobjects if not(0 in lineList): beginCoord = [lineList[0],lineList[1]] endCoord = [lineList[2]-500,lineList[3]-500] center = [xCoordinate[i],yCoordinate[i]+15] distance1 = math.sqrt((lineList[1] - center[1])**2 + (lineList[0] - center[0])**2) slope1 = math.tan((lineList[1] - lineList[3]) / (lineList[0] - lineList[2])) try: slope2 = math.tan((center[1] - beginCoord[1])/(center[0]-beginCoord[0])) angle1 = slope2 + slope1 circleDistance = distance1 * math.sin(angle1) except: #If the circle is directly above beginCoord circleDistance = center[1] - lineList[1] global numbounces if circleDistance < 2 and circleDistance > -2: print(circleDistance) b = False b2=False if xCoordinate[i] < 0: xCoordinate[i] += 1 speed1[i] *= -1 b=True elif xCoordinate[i] > 0: xCoordinate[i] -= 1 speed1[i] *= -1 b=True if yCoordinate[i] < 0: yCoordinate[i] += 1 speed2[i] *= -1 b2=True elif yCoordinate[i] > 0: yCoordinate[i] -= 1 speed2[i] *= -1 b2=True if b and b2: #Only delete the line if the ball reversed directions numbounces += 1 #Add a ball after 5 bounces if numbounces % 5 == 0 and numbounces != 0: numobjects = 1 getData(numobjects) canvas.delete("line") lineList = [0,0,0,0]
I don't know what is the mean of shortest distance between the ball, but if you want to calculation the point where the circle will contact the line you can use sympy to figure the formula: from sympy import * from sympy.geometry import * x1, y1, x2, y2, xc, yc = symbols("x1,y1,x2,y2,xc,yc") p1 = Point(x1, y1) p2 = Point(x2, y2) pc = Point(xc, yc) line = Line(p1, p2) pline = line.perpendicular_line(pc) p = line.intersection(pline)[0] cse(p, symbols=numbered_symbols("t")) the output is : ([(t0, x1 - x2), (t1, y1 - y2), (t2, x1*y2 - x2*y1), (t3, t0**2 + t1**2)], [Point((t0**2*xc + t0*t1*yc - t1*t2)/t3, (t0*t1*xc + t0*t2 + t1**2*yc)/t3)]) this means that you can calculate the perpendicular point as: t0 = x1 - x2 t1 = y1 - y2 t2 = x1*y2 - x2*y1 t3 = t0**2 + t1**2 xp = (t0**2*xc + t0*t1*yc - t1*t2)/t3 yp = (t0*t1*xc + t0*t2 + t1**2*yc)/t3
Simplex Noise Function: Unexpected Results
I'm trying to adapt this noise module to a project I'm working on but I'm not getting the results I was expecting: https://pypi.python.org/pypi/noise/ Instead of a varied height-map, each row tends to have the exact same value. If I create something 250x250 it will generate the same value 250 times and then generate a new value 250 times until it ends. Here is the function I'm currently using. I understand this function fairly well but I'm just not sure how to get more "interesting" results. Thank you for your help. class SimplexNoise(BaseNoise): def noise2(self, x, y): """2D Perlin simplex noise. Return a floating point value from -1 to 1 for the given x, y coordinate. The same value is always returned for a given x, y pair unless the permutation table changes (see randomize above). """ # Skew input space to determine which simplex (triangle) we are in s = (x + y) * _F2 i = floor(x + s) j = floor(y + s) t = (i + j) * _G2 x0 = x - (i - t) # "Unskewed" distances from cell origin y0 = y - (j - t) if x0 > y0: i1 = 1; j1 = 0 # Lower triangle, XY order: (0,0)->(1,0)->(1,1) else: i1 = 0; j1 = 1 # Upper triangle, YX order: (0,0)->(0,1)->(1,1) x1 = x0 - i1 + _G2 # Offsets for middle corner in (x,y) unskewed coords y1 = y0 - j1 + _G2 x2 = x0 + _G2 * 2.0 - 1.0 # Offsets for last corner in (x,y) unskewed coords y2 = y0 + _G2 * 2.0 - 1.0 # Determine hashed gradient indices of the three simplex corners perm = BaseNoise.permutation ii = int(i) % BaseNoise.period jj = int(j) % BaseNoise.period gi0 = perm[ii + perm[jj]] % 12 gi1 = perm[ii + i1 + perm[jj + j1]] % 12 gi2 = perm[ii + 1 + perm[jj + 1]] % 12 # Calculate the contribution from the three corners tt = 0.5 - x0**2 - y0**2 if tt > 0: g = _GRAD3[gi0] noise = tt**4 * (g[0] * x0 + g[1] * y0) else: noise = 0.0 tt = 0.5 - x1**2 - y1**2 if tt > 0: g = _GRAD3[gi1] noise += tt**4 * (g[0] * x1 + g[1] * y1) tt = 0.5 - x2**2 - y2**2 if tt > 0: g = _GRAD3[gi2] noise += tt**4 * (g[0] * x2 + g[1] * y2) return noise * 70.0 # scale noise to [-1, 1] win = pygcurse.PygcurseWindow(85, 70, 'Generate') octaves = 2 ysize = 150 xsize = 150 freq = 32.0 * octaves for y in range(ysize): for x in range(xsize): tile = SimplexNoise.noise2(x / freq, y / freq, octaves) win.write(str(tile) + "\n")