Change meaning of resolution for surfaces to be the number of posts, not fences

This commit is contained in:
Grant Sanderson 2020-06-06 13:22:23 -07:00
parent 58fe0c79d8
commit b8a1853692

View file

@ -14,7 +14,9 @@ class ParametricSurface(Mobject):
CONFIG = { CONFIG = {
"u_range": (0, 1), "u_range": (0, 1),
"v_range": (0, 1), "v_range": (0, 1),
"resolution": (100, 100), # Resolution counts number of points sampled, which is off by
# 1 from the number of actual approximating squares seen
"resolution": (101, 101),
"color": GREY, "color": GREY,
"opacity": 1.0, "opacity": 1.0,
"gloss": 0.3, "gloss": 0.3,
@ -33,20 +35,15 @@ class ParametricSurface(Mobject):
] ]
} }
def __init__(self, function=None, **kwargs): def __init__(self, uv_func, **kwargs):
self.passed_function = function self.uv_func = uv_func
super().__init__(**kwargs) super().__init__(**kwargs)
def uv_func(self, u, v):
# Typically to be implemented by a subclass
if self.passed_function is not None:
return self.passed_function(u, v)
return [u, v, 0]
def init_points(self): def init_points(self):
dim = self.dim
nu, nv = self.resolution nu, nv = self.resolution
u_range = np.linspace(*self.u_range, nu + 1) u_range = np.linspace(*self.u_range, nu)
v_range = np.linspace(*self.v_range, nv + 1) v_range = np.linspace(*self.v_range, nv)
# Get three lists: # Get three lists:
# - Points generated by pure uv values # - Points generated by pure uv values
@ -56,28 +53,33 @@ class ParametricSurface(Mobject):
for (du, dv) in [(0, 0), (self.epsilon, 0), (0, self.epsilon)]: for (du, dv) in [(0, 0), (self.epsilon, 0), (0, self.epsilon)]:
uv_grid = np.array([[[u + du, v + dv] for v in v_range] for u in u_range]) uv_grid = np.array([[[u + du, v + dv] for v in v_range] for u in u_range])
point_grid = np.apply_along_axis(lambda p: self.uv_func(*p), 2, uv_grid) point_grid = np.apply_along_axis(lambda p: self.uv_func(*p), 2, uv_grid)
point_lists.append(self.get_triangle_ready_array_from_grid(point_grid)) point_lists.append(point_grid.reshape((nu * nv, dim)))
# Rather than tracking normal vectors, the points list will hold on to the # Rather than tracking normal vectors, the points list will hold on to the
# infinitesimal nudged values alongside the original values. This way, one # infinitesimal nudged values alongside the original values. This way, one
# can perform all the manipulations they'd like to the surface, and normals # can perform all the manipulations they'd like to the surface, and normals
# are still easily recoverable. # are still easily recoverable.
self.points = np.vstack(point_lists) self.points = np.vstack(point_lists)
def get_triangle_ready_array_from_grid(self, grid): def get_triangle_ready_array(self, array):
# Given a grid, say of points or normals, this returns an Nx3 array # Given an array of points representing a flattened grid, say of points or
# whose rows are elements from this grid in such such a way that successive # normals, this returns an Nx3 array whose rows are elements from this grid
# triplets of points form triangles covering the grid. # in such such a way that successive triplets of points form triangles covering
nu, nv, dim = grid.shape # the grid.
nu -= 1 dim = array.shape[1]
nv -= 1 nu, nv = self.resolution
arr = np.zeros((nu * nv * 6, dim)) assert(array.shape == (nu * nv, dim))
grid = array.reshape((nu, nv, dim))
new_length = (nu - 1) * (nv - 1)
arr = np.zeros((6 * new_length, dim))
shape = (new_length, dim)
# To match the triangles covering this surface # To match the triangles covering this surface
arr[0::6] = grid[:-1, :-1].reshape((nu * nv, dim)) # Top left arr[0::6] = grid[:-1, :-1].reshape(shape) # Top left
arr[1::6] = grid[+1:, :-1].reshape((nu * nv, dim)) # Bottom left arr[1::6] = grid[+1:, :-1].reshape(shape) # Bottom left
arr[2::6] = grid[:-1, +1:].reshape((nu * nv, dim)) # Top right arr[2::6] = grid[:-1, +1:].reshape(shape) # Top right
arr[3::6] = grid[:-1, +1:].reshape((nu * nv, dim)) # Top right arr[3::6] = grid[:-1, +1:].reshape(shape) # Top right
arr[4::6] = grid[+1:, :-1].reshape((nu * nv, dim)) # Bottom left arr[4::6] = grid[+1:, :-1].reshape(shape) # Bottom left
arr[5::6] = grid[+1:, +1:].reshape((nu * nv, dim)) # Bottom right arr[5::6] = grid[+1:, +1:].reshape(shape) # Bottom right
return arr return arr
def init_colors(self): def init_colors(self):
@ -120,7 +122,10 @@ class ParametricSurface(Mobject):
return self return self
def get_shader_data(self): def get_shader_data(self):
s_points, du_points, dv_points = self.get_surface_points_and_nudged_points() s_points, du_points, dv_points = [
self.get_triangle_ready_array(array)
for array in self.get_surface_points_and_nudged_points()
]
data = self.get_blank_shader_data_array(len(s_points)) data = self.get_blank_shader_data_array(len(s_points))
data["point"] = s_points data["point"] = s_points
data["du_point"] = du_points data["du_point"] = du_points
@ -163,17 +168,22 @@ class TexturedSurface(ParametricSurface):
path = get_full_raster_image_path(filename) path = get_full_raster_image_path(filename)
self.image = Image.open(path) self.image = Image.open(path)
self.texture_path = path self.texture_path = path
self.uv_surface = uv_surface self.uv_surface = uv_surface
super().__init__(**kwargs) self.uv_func = uv_surface.uv_func
self.u_range = uv_surface.u_range
self.v_range = uv_surface.v_range
self.resolution = uv_surface.resolution
super().__init__(self.uv_func, **kwargs)
def init_points(self): def init_points(self):
self.points = self.uv_surface.points self.points = self.uv_surface.points
# Init im_coords # Init im_coords
nu, nv = self.uv_surface.resolution nu, nv = self.uv_surface.resolution
u_range = np.linspace(0, 1, nu + 1) u_range = np.linspace(0, 1, nu)
v_range = np.linspace(1, 0, nv + 1) # Reverse y-direction v_range = np.linspace(1, 0, nv) # Reverse y-direction
uv_grid = np.array([[[u, v] for v in v_range] for u in u_range]) uv_grid = np.array([[u, v] for u in u_range for v in v_range])
self.im_coords = self.uv_surface.get_triangle_ready_array_from_grid(uv_grid) self.im_coords = uv_grid
def init_colors(self): def init_colors(self):
self.opacity = self.uv_surface.rgbas[:, 3] self.opacity = self.uv_surface.rgbas[:, 3]
@ -187,7 +197,7 @@ class TexturedSurface(ParametricSurface):
return self return self
def fill_in_shader_color_info(self, data): def fill_in_shader_color_info(self, data):
data["im_coords"] = self.im_coords data["im_coords"] = self.get_triangle_ready_array(self.im_coords)
data["opacity"] = self.opacity data["opacity"] = self.opacity
data["gloss"] = self.gloss data["gloss"] = self.gloss
return data return data