3D anims of LightSource fixed (everything faces the camera again)

This commit is contained in:
Ben Hambrecht
2018-02-20 17:44:33 +01:00
parent 2d93c45624
commit fe1c3ca0f9

View File

@ -21,7 +21,7 @@ from scipy.spatial import ConvexHull
from traceback import *
LIGHT_COLOR = GREEN
LIGHT_COLOR = YELLOW
SHADOW_COLOR = BLACK
SWITCH_ON_RUN_TIME = 1.5
FAST_SWITCH_ON_RUN_TIME = 0.1
@ -62,7 +62,7 @@ class LightSource(VMobject):
"opacity_function": inverse_quadratic(1,2,1),
"max_opacity_ambient": AMBIENT_FULL,
"max_opacity_spotlight": SPOTLIGHT_FULL,
"camera": None
"camera_mob": None
}
def generate_points(self):
@ -87,7 +87,7 @@ class LightSource(VMobject):
screen = self.screen,
opacity_function = self.opacity_function,
max_opacity = self.max_opacity_spotlight,
camera = self.camera
camera_mob = self.camera_mob
)
else:
self.spotlight = Spotlight()
@ -124,9 +124,9 @@ class LightSource(VMobject):
self.max_opacity_spotlight = new_opacity
self.spotlight.dimming(new_opacity)
def set_camera(self,new_cam):
self.camera = new_cam
self.spotlight.camera = new_cam
def set_camera_mob(self,new_cam_mob):
self.camera_mob = new_cam_mob
self.spotlight.camera_mob = new_cam_mob
def set_screen(self, new_screen):
@ -135,7 +135,7 @@ class LightSource(VMobject):
else:
# Note: See below
index = self.submobjects.index(self.spotlight)
camera = self.spotlight.camera
camera_mob = self.spotlight.camera_mob
self.remove(self.spotlight)
self.spotlight = Spotlight(
source_point = VectorizedPoint(location = self.get_source_point()),
@ -143,7 +143,7 @@ class LightSource(VMobject):
num_levels = self.num_levels,
radius = self.radius,
screen = new_screen,
camera = self.camera
camera_mob = self.camera_mob
)
self.spotlight.move_source_to(self.get_source_point())
@ -162,7 +162,6 @@ class LightSource(VMobject):
def move_source_to(self,point):
print_stack()
apoint = np.array(point)
v = apoint - self.get_source_point()
# Note: As discussed, things stand to behave better if source
@ -186,12 +185,64 @@ class LightSource(VMobject):
self.spotlight.radius = new_radius
def update(self):
self.update_lighthouse()
self.update_ambient()
self.spotlight.update_sectors()
self.update_shadow()
def update_lighthouse(self):
new_lh = Lighthouse()
new_lh.move_to(ORIGIN)
new_lh.apply_matrix(self.rotation_matrix())
new_lh.shift(self.get_source_point())
self.lighthouse.submobjects = new_lh.submobjects
def update_ambient(self):
new_ambient_light = AmbientLight(
source_point = VectorizedPoint(location = ORIGIN),
color = self.color,
num_levels = self.num_levels,
radius = self.radius,
opacity_function = self.opacity_function,
max_opacity = self.max_opacity_ambient
)
new_ambient_light.apply_matrix(self.rotation_matrix())
new_ambient_light.move_source_to(self.get_source_point())
self.ambient_light.submobjects = new_ambient_light.submobjects
def get_source_point(self):
return self.source_point.get_location()
def rotation_matrix(self):
if self.camera_mob == None:
return np.eye(3)
phi = self.camera_mob.get_center()[0]
theta = self.camera_mob.get_center()[1]
R1 = np.array([
[1, 0, 0],
[0, np.cos(phi), -np.sin(phi)],
[0, np.sin(phi), np.cos(phi)]
])
R2 = np.array([
[np.cos(theta + TAU/4), -np.sin(theta + TAU/4), 0],
[np.sin(theta + TAU/4), np.cos(theta + TAU/4), 0],
[0, 0, 1]
])
R = np.dot(R2, R1)
return R
def update_shadow(self):
point = self.get_source_point()
@ -209,11 +260,12 @@ class LightSource(VMobject):
np.reshape(projected_source,(1,3)),
axis = 0
)
rotation_matrix = z_to_vector(self.spotlight.projection_direction())
rotation_matrix = self.rotation_matrix() # z_to_vector(self.spotlight.projection_direction())
back_rotation_matrix = rotation_matrix.T # i. e. its inverse
rotated_point_cloud_3d = np.dot(projected_point_cloud_3d,back_rotation_matrix.T)
# these points now should all have z = 0
point_cloud_2d = rotated_point_cloud_3d[:,:2]
# now we can compute the convex hull
hull_2d = ConvexHull(point_cloud_2d) # guaranteed to run ccw
@ -247,6 +299,7 @@ class LightSource(VMobject):
ray1 = anchors[source_index - 1] - projected_source
ray1 = ray1/np.linalg.norm(ray1) * 100
ray2 = anchors[source_index] - projected_source
ray2 = ray2/np.linalg.norm(ray2) * 100
outpoint1 = anchors[source_index - 1] + ray1
@ -370,13 +423,11 @@ class AmbientLight(VMobject):
def dimming(self,new_alpha):
print "dimming"
old_alpha = self.max_opacity
self.max_opacity = new_alpha
for submob in self.submobjects:
old_submob_alpha = submob.fill_opacity
new_submob_alpha = old_submob_alpha * new_alpha / old_alpha
print old_submob_alpha, new_submob_alpha
submob.set_fill(opacity = new_submob_alpha)
@ -403,7 +454,7 @@ class Spotlight(VMobject):
"num_levels" : 10,
"radius" : 5.0,
"screen" : None,
"camera": None
"camera_mob": None
}
def projection_direction(self):
@ -411,11 +462,12 @@ class Spotlight(VMobject):
# need to be sure that any 3d scene including a spotlight
# somewhere assigns that spotlights "camera" attribute
# to be the camera associated with that scene.
if self.camera == None:
if self.camera_mob == None:
return OUT
else:
v = self.camera.get_cartesian_coords()
return v/np.linalg.norm(v)
[phi, theta, r] = self.camera_mob.get_center()
v = np.array([np.sin(phi)*np.cos(theta), np.sin(phi)*np.sin(theta), np.cos(phi)])
return v #/np.linalg.norm(v)
def project(self,point):
v = self.projection_direction()
@ -578,14 +630,6 @@ class Spotlight(VMobject):
submob.set_fill(opacity = alpha)
# Note: Stylistically, I typically keep all of the implementation for an
# update inside the relevant Animation or ContinualAnimation, rather than
# in the mobject. Things are fine the way you've done it, but sometimes
# I personally like a nice conceptual divide between all the things that
# determine how the mobject is displayed in a single moment (implement in
# the mobject's class itself) and all the things determining how that changes.
#
# Up to you though.
class ScreenTracker(ContinualAnimation):