mirror of
https://github.com/3b1b/manim.git
synced 2025-07-28 12:32:36 +08:00
update
This commit is contained in:
@ -68,48 +68,6 @@ class OpeningManimExample(Scene):
|
||||
self.wait(2)
|
||||
|
||||
|
||||
class InteractiveDevlopment(Scene):
|
||||
def construct(self):
|
||||
circle = Circle()
|
||||
circle.set_fill(BLUE, opacity=0.5)
|
||||
circle.set_stroke(BLUE_E, width=4)
|
||||
square = Square()
|
||||
|
||||
self.play(ShowCreation(square))
|
||||
self.wait()
|
||||
|
||||
# This opens an iPython termnial where you can keep writing
|
||||
# lines as if they were part of this construct method
|
||||
self.embed()
|
||||
# Try copying and pasting some of the lines below into
|
||||
# the interactive shell
|
||||
self.play(ReplacementTransform(square, circle))
|
||||
self.wait()
|
||||
self.play(circle.stretch, 4, 0)
|
||||
self.play(Rotate(circle, 90 * DEGREES))
|
||||
self.play(circle.shift, 2 * RIGHT, circle.scale, 0.25)
|
||||
|
||||
text = Text("""
|
||||
In general, using the interactive shell
|
||||
is very helpful when developing new scenes
|
||||
""")
|
||||
self.play(Write(text))
|
||||
|
||||
# In the interactive shell, you can just type
|
||||
# play, add, remove, clear, wait, save_state and restore,
|
||||
# instead of self.play, self.add, self.remove, etc.
|
||||
|
||||
# To interact with the window, type touch(). You can then
|
||||
# scroll in the window, or zoom by holding down 'z' while scrolling,
|
||||
# and change camera perspective by holding down 'd' while moving
|
||||
# the mouse. Press 'r' to reset to the standard camera position.
|
||||
# Press 'q' to stop interacting with the window and go back to
|
||||
# typing new commands into the shell.
|
||||
|
||||
# In principle you can customize a scene
|
||||
always(circle.move_to, self.mouse_point)
|
||||
|
||||
|
||||
class AnimatingMethods(Scene):
|
||||
def construct(self):
|
||||
grid = Tex(r"\pi").get_grid(10, 10, height=4)
|
||||
@ -122,7 +80,10 @@ class AnimatingMethods(Scene):
|
||||
# to the left, but the following line animates that motion.
|
||||
self.play(grid.shift, 2 * LEFT)
|
||||
# The same applies for any method, including those setting colors.
|
||||
self.play(grid.set_color, YELLOW)
|
||||
self.wait()
|
||||
self.play(grid.set_submobject_colors_by_gradient, BLUE, GREEN)
|
||||
self.wait()
|
||||
self.play(grid.set_height, TAU - MED_SMALL_BUFF)
|
||||
self.wait()
|
||||
|
||||
@ -362,6 +323,166 @@ class UpdatersExample(Scene):
|
||||
self.wait(4 * PI)
|
||||
|
||||
|
||||
class GraphExample(Scene):
|
||||
def construct(self):
|
||||
axes = Axes((-3, 10), (-1, 8))
|
||||
axes.add_coordinate_labels()
|
||||
|
||||
self.play(Write(axes, lag_ratio=0.01, run_time=1))
|
||||
|
||||
# Axes.get_graph will return the graph of a function
|
||||
sin_graph = axes.get_graph(
|
||||
lambda x: 2 * math.sin(x),
|
||||
color=BLUE,
|
||||
)
|
||||
# By default, it draws it so as to somewhat smoothly interpolate
|
||||
# between sampled points (x, f(x)). If the graph is meant to have
|
||||
# a corner, though, you can set use_smoothing to False
|
||||
relu_graph = axes.get_graph(
|
||||
lambda x: max(x, 0),
|
||||
use_smoothing=False,
|
||||
color=YELLOW,
|
||||
)
|
||||
# For discontinuous functions, you can specify the point of
|
||||
# discontinuity so that it does not try to draw over the gap.
|
||||
step_graph = axes.get_graph(
|
||||
lambda x: 2.0 if x > 3 else 1.0,
|
||||
discontinuities=[3],
|
||||
color=GREEN,
|
||||
)
|
||||
|
||||
# Axes.get_graph_label takes in either a string or a mobject.
|
||||
# If it's a string, it treats it as a LaTeX expression. By default
|
||||
# it places the label next to the graph near the right side, and
|
||||
# has it match the color of the graph
|
||||
sin_label = axes.get_graph_label(sin_graph, "\\sin(x)")
|
||||
relu_label = axes.get_graph_label(relu_graph, Text("ReLU"))
|
||||
step_label = axes.get_graph_label(step_graph, Text("Step"), x=4)
|
||||
|
||||
self.play(
|
||||
ShowCreation(sin_graph),
|
||||
FadeIn(sin_label, RIGHT),
|
||||
)
|
||||
self.wait(2)
|
||||
self.play(
|
||||
ReplacementTransform(sin_graph, relu_graph),
|
||||
FadeTransform(sin_label, relu_label),
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
ReplacementTransform(relu_graph, step_graph),
|
||||
FadeTransform(relu_label, step_label),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
parabola = axes.get_graph(lambda x: 0.25 * x**2)
|
||||
parabola.set_stroke(BLUE)
|
||||
self.play(
|
||||
FadeOut(step_graph),
|
||||
FadeOut(step_label),
|
||||
ShowCreation(parabola)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
# You can use axes.input_to_graph_point, abbreviated
|
||||
# to axes.i2gp, to find a particular point on a graph
|
||||
dot = Dot(color=RED)
|
||||
dot.move_to(axes.i2gp(2, parabola))
|
||||
self.play(FadeIn(dot, scale=0.5))
|
||||
|
||||
# A value tracker lets us animate a parameter, usually
|
||||
# with the intent of having other mobjects update based
|
||||
# on the parameter
|
||||
x_tracker = ValueTracker(2)
|
||||
f_always(
|
||||
dot.move_to,
|
||||
lambda: axes.i2gp(x_tracker.get_value(), parabola)
|
||||
)
|
||||
|
||||
self.play(x_tracker.set_value, 4, run_time=3)
|
||||
self.play(x_tracker.set_value, -2, run_time=3)
|
||||
self.wait()
|
||||
|
||||
|
||||
class CoordinateSystemExample(Scene):
|
||||
def construct(self):
|
||||
axes = Axes(
|
||||
# x-axis ranges from -1 to 10, with a default step size of 1
|
||||
x_range=(-1, 10),
|
||||
# y-axis ranges from -2 to 10 with a step size of 0.5
|
||||
y_range=(-2, 2, 0.5),
|
||||
# The axes will be stretched so as to match the specified
|
||||
# height and width
|
||||
height=6,
|
||||
width=10,
|
||||
# Axes is made of two NumberLine mobjects. You can specify
|
||||
# their configuration with axis_config
|
||||
axis_config={
|
||||
"stroke_color": GREY_A,
|
||||
"stroke_width": 2,
|
||||
},
|
||||
# Alternatively, you can specify configuration for just one
|
||||
# of them, like this.
|
||||
y_axis_config={
|
||||
"include_tip": False,
|
||||
}
|
||||
)
|
||||
# Keyword arguments of add_coordinate_labels can be used to
|
||||
# configure the DecimalNumber mobjects which it creates and
|
||||
# adds to the axes
|
||||
axes.add_coordinate_labels(
|
||||
font_size=20,
|
||||
num_decimal_places=1,
|
||||
)
|
||||
self.add(axes)
|
||||
|
||||
# Axes descends from the CoordinateSystem class, meaning
|
||||
# you can call call axes.coords_to_point, abbreviated to
|
||||
# axes.c2p, to associate a set of coordinates with a point,
|
||||
# like so:
|
||||
dot = Dot(color=RED)
|
||||
dot.move_to(axes.c2p(0, 0))
|
||||
self.play(FadeIn(dot, scale=0.5))
|
||||
self.play(dot.move_to, axes.c2p(3, 2))
|
||||
self.wait()
|
||||
self.play(dot.move_to, axes.c2p(5, 0.5))
|
||||
self.wait()
|
||||
|
||||
# Similarly, you can call axes.point_to_coords, or axes.p2c
|
||||
# print(axes.p2c(dot.get_center()))
|
||||
|
||||
# We can draw lines from the axes to better mark the coordinates
|
||||
# of a given point.
|
||||
# Here, the always_redraw command means that on each new frame
|
||||
# the lines will be redrawn
|
||||
h_line = always_redraw(lambda: axes.get_h_line(dot.get_left()))
|
||||
v_line = always_redraw(lambda: axes.get_v_line(dot.get_bottom()))
|
||||
|
||||
self.play(
|
||||
ShowCreation(h_line),
|
||||
ShowCreation(v_line),
|
||||
)
|
||||
self.play(dot.move_to, axes.c2p(3, -2))
|
||||
self.wait()
|
||||
self.play(dot.move_to, axes.c2p(1, 1))
|
||||
self.wait()
|
||||
|
||||
# If we tie the dot to a particular set of coordinates, notice
|
||||
# that as we move the axes around it respects the coordinate
|
||||
# system defined by them.
|
||||
f_always(dot.move_to, lambda: axes.c2p(1, 1))
|
||||
self.play(
|
||||
axes.scale, 0.75,
|
||||
axes.to_corner, UL,
|
||||
run_time=2,
|
||||
)
|
||||
self.wait()
|
||||
self.play(FadeOut(VGroup(axes, dot, h_line, v_line)))
|
||||
|
||||
# Other coordinate systems you can play around with include
|
||||
# ThreeDAxes, NumberPlane, and ComplexPlane.
|
||||
|
||||
|
||||
class SurfaceExample(Scene):
|
||||
CONFIG = {
|
||||
"camera_class": ThreeDCamera,
|
||||
@ -453,6 +574,52 @@ class SurfaceExample(Scene):
|
||||
self.wait()
|
||||
|
||||
|
||||
class InteractiveDevlopment(Scene):
|
||||
def construct(self):
|
||||
circle = Circle()
|
||||
circle.set_fill(BLUE, opacity=0.5)
|
||||
circle.set_stroke(BLUE_E, width=4)
|
||||
square = Square()
|
||||
|
||||
self.play(ShowCreation(square))
|
||||
self.wait()
|
||||
|
||||
# This opens an iPython termnial where you can keep writing
|
||||
# lines as if they were part of this construct method.
|
||||
# In particular, 'square', 'circle' and 'self' will all be
|
||||
# part of the local namespace in that terminal.
|
||||
self.embed()
|
||||
|
||||
# Try copying and pasting some of the lines below into
|
||||
# the interactive shell
|
||||
self.play(ReplacementTransform(square, circle))
|
||||
self.wait()
|
||||
self.play(circle.stretch, 4, 0)
|
||||
self.play(Rotate(circle, 90 * DEGREES))
|
||||
self.play(circle.shift, 2 * RIGHT, circle.scale, 0.25)
|
||||
|
||||
text = Text("""
|
||||
In general, using the interactive shell
|
||||
is very helpful when developing new scenes
|
||||
""")
|
||||
self.play(Write(text))
|
||||
|
||||
# In the interactive shell, you can just type
|
||||
# play, add, remove, clear, wait, save_state and restore,
|
||||
# instead of self.play, self.add, self.remove, etc.
|
||||
|
||||
# To interact with the window, type touch(). You can then
|
||||
# scroll in the window, or zoom by holding down 'z' while scrolling,
|
||||
# and change camera perspective by holding down 'd' while moving
|
||||
# the mouse. Press 'r' to reset to the standard camera position.
|
||||
# Press 'q' to stop interacting with the window and go back to
|
||||
# typing new commands into the shell.
|
||||
|
||||
# In principle you can customize a scene to be responsive to
|
||||
# mouse and keyboard interactions
|
||||
always(circle.move_to, self.mouse_point)
|
||||
|
||||
|
||||
class ControlsExample(Scene):
|
||||
def setup(self):
|
||||
self.textbox = Textbox()
|
||||
|
@ -5,6 +5,7 @@ from manimlib.constants import *
|
||||
from manimlib.mobject.functions import ParametricCurve
|
||||
from manimlib.mobject.geometry import Arrow
|
||||
from manimlib.mobject.geometry import Line
|
||||
from manimlib.mobject.geometry import DashedLine
|
||||
from manimlib.mobject.geometry import Rectangle
|
||||
from manimlib.mobject.number_line import NumberLine
|
||||
from manimlib.mobject.svg.tex_mobject import Tex
|
||||
@ -24,8 +25,8 @@ class CoordinateSystem():
|
||||
"""
|
||||
CONFIG = {
|
||||
"dimension": 2,
|
||||
"x_range": [-8, 8, 1],
|
||||
"y_range": [-4, 4, 1],
|
||||
"x_range": np.array([-8, 8, 1]),
|
||||
"y_range": np.array([-4, 4, 1]),
|
||||
"width": None,
|
||||
"height": None,
|
||||
"num_sampled_graph_points_per_tick": 5,
|
||||
@ -88,12 +89,26 @@ class CoordinateSystem():
|
||||
)
|
||||
return self.axis_labels
|
||||
|
||||
def get_line_from_axis_to_point(self, index, point,
|
||||
line_func=DashedLine,
|
||||
color=GREY_A,
|
||||
stroke_width=2):
|
||||
axis = self.get_axis(index)
|
||||
line = line_func(axis.get_projection(point), point)
|
||||
line.set_stroke(color, stroke_width)
|
||||
return line
|
||||
|
||||
def get_v_line(self, point, **kwargs):
|
||||
return self.get_line_from_axis_to_point(0, point, **kwargs)
|
||||
|
||||
def get_h_line(self, point, **kwargs):
|
||||
return self.get_line_from_axis_to_point(1, point, **kwargs)
|
||||
|
||||
# Useful for graphing
|
||||
def get_graph(self, function, x_range=None, **kwargs):
|
||||
t_range = list(self.x_range)
|
||||
t_range = np.array(self.x_range, dtype=float)
|
||||
if x_range is not None:
|
||||
for i in range(len(x_range)):
|
||||
t_range[i] = x_range[i]
|
||||
t_range[:len(x_range)] = x_range
|
||||
# For axes, the third coordinate of x_range indicates
|
||||
# tick frequency. But for functions, it indicates a
|
||||
# sample frequency
|
||||
@ -134,7 +149,7 @@ class CoordinateSystem():
|
||||
else:
|
||||
return None
|
||||
|
||||
def itgp(self, x, graph):
|
||||
def i2gp(self, x, graph):
|
||||
"""
|
||||
Alias for input_to_graph_point
|
||||
"""
|
||||
@ -147,6 +162,7 @@ class CoordinateSystem():
|
||||
direction=RIGHT,
|
||||
buff=MED_SMALL_BUFF,
|
||||
color=None):
|
||||
if isinstance(label, str):
|
||||
label = Tex(label)
|
||||
if color is None:
|
||||
label.match_color(graph)
|
||||
@ -154,8 +170,10 @@ class CoordinateSystem():
|
||||
# Searching from the right, find a point
|
||||
# whose y value is in bounds
|
||||
max_y = FRAME_Y_RADIUS - label.get_height()
|
||||
for x0 in np.arange(*self.x_range)[-1::-1]:
|
||||
if abs(self.itgp(x0, graph)[1]) < max_y:
|
||||
max_x = FRAME_X_RADIUS - label.get_width()
|
||||
for x0 in np.arange(*self.x_range)[::-1]:
|
||||
pt = self.i2gp(x0, graph)
|
||||
if abs(pt[0]) < max_x and abs(pt[1]) < max_y:
|
||||
x = x0
|
||||
break
|
||||
if x is None:
|
||||
@ -170,11 +188,11 @@ class CoordinateSystem():
|
||||
label.shift_onto_screen()
|
||||
return label
|
||||
|
||||
def get_vertical_line_to_graph(self, x, graph, line_func=Line):
|
||||
return line_func(
|
||||
self.coords_to_point(x, 0),
|
||||
self.input_to_graph_point(x, graph),
|
||||
)
|
||||
def get_v_line_to_graph(self, x, graph, **kwargs):
|
||||
return self.get_v_line(self.i2gp(x, graph), **kwargs)
|
||||
|
||||
def get_h_line_to_graph(self, x, graph, **kwargs):
|
||||
return self.get_h_line(self.i2gp(x, graph), **kwargs)
|
||||
|
||||
# For calculus
|
||||
def angle_of_tangent(self, x, graph, dx=EPSILON):
|
||||
@ -221,7 +239,7 @@ class CoordinateSystem():
|
||||
else:
|
||||
raise Exception("Invalid input sample type")
|
||||
height = get_norm(
|
||||
self.itgp(sample, graph) - self.c2p(sample, 0)
|
||||
self.i2gp(sample, graph) - self.c2p(sample, 0)
|
||||
)
|
||||
rect = Rectangle(width=x1 - x0, height=height)
|
||||
rect.move_to(self.c2p(x0, 0), DL)
|
||||
@ -244,21 +262,25 @@ class Axes(VGroup, CoordinateSystem):
|
||||
CONFIG = {
|
||||
"axis_config": {
|
||||
"include_tip": True,
|
||||
"numbers_to_exclude": [0],
|
||||
},
|
||||
"x_axis_config": {},
|
||||
"y_axis_config": {
|
||||
"line_to_number_direction": LEFT,
|
||||
},
|
||||
"height": FRAME_HEIGHT - 2,
|
||||
"width": FRAME_WIDTH - 2,
|
||||
}
|
||||
|
||||
def __init__(self, x_range=None, y_range=None, **kwargs):
|
||||
VGroup.__init__(self, **kwargs)
|
||||
def __init__(self,
|
||||
x_range=None,
|
||||
y_range=None,
|
||||
**kwargs):
|
||||
super().__init__(**kwargs)
|
||||
if x_range is not None:
|
||||
for i in range(len(x_range)):
|
||||
self.x_range[i] = x_range[i]
|
||||
self.x_range[:len(x_range)] = x_range
|
||||
if y_range is not None:
|
||||
for i in range(len(y_range)):
|
||||
self.y_range[i] = y_range[i]
|
||||
self.y_range[:len(x_range)] = y_range
|
||||
|
||||
self.x_axis = self.create_axis(
|
||||
self.x_range, self.x_axis_config, self.width,
|
||||
@ -300,24 +322,21 @@ class Axes(VGroup, CoordinateSystem):
|
||||
def add_coordinate_labels(self,
|
||||
x_values=None,
|
||||
y_values=None,
|
||||
excluding=[0],
|
||||
**kwargs):
|
||||
axes = self.get_axes()
|
||||
self.coordinate_labels = VGroup()
|
||||
for axis, values in zip(axes, [x_values, y_values]):
|
||||
numbers = axis.add_numbers(
|
||||
values, excluding=excluding, **kwargs
|
||||
)
|
||||
self.coordinate_labels.add(numbers)
|
||||
labels = axis.add_numbers(values, **kwargs)
|
||||
self.coordinate_labels.add(labels)
|
||||
return self.coordinate_labels
|
||||
|
||||
|
||||
class ThreeDAxes(Axes):
|
||||
CONFIG = {
|
||||
"dimension": 3,
|
||||
"x_range": (-6, 6, 1),
|
||||
"y_range": (-5, 5, 1),
|
||||
"z_range": (-4, 4, 1),
|
||||
"x_range": np.array([-6, 6, 1]),
|
||||
"y_range": np.array([-5, 5, 1]),
|
||||
"z_range": np.array([-4, 4, 1]),
|
||||
"z_axis_config": {},
|
||||
"z_normal": DOWN,
|
||||
"depth": None,
|
||||
@ -365,6 +384,8 @@ class NumberPlane(Axes):
|
||||
"stroke_width": 2,
|
||||
"stroke_opacity": 1,
|
||||
},
|
||||
"height": None,
|
||||
"width": None,
|
||||
# Defaults to a faded version of line_config
|
||||
"faded_line_style": None,
|
||||
"faded_line_ratio": 1,
|
||||
|
@ -9,6 +9,7 @@ class ParametricCurve(VMobject):
|
||||
"epsilon": 1e-8,
|
||||
# TODO, automatically figure out discontinuities
|
||||
"discontinuities": [],
|
||||
"use_smoothing": True,
|
||||
}
|
||||
|
||||
def __init__(self, t_func, t_range=None, **kwargs):
|
||||
@ -39,6 +40,7 @@ class ParametricCurve(VMobject):
|
||||
points = np.array([self.t_func(t) for t in t_range])
|
||||
self.start_new_path(points[0])
|
||||
self.add_points_as_corners(points[1:])
|
||||
if self.use_smoothing:
|
||||
self.make_approximately_smooth()
|
||||
return self
|
||||
|
||||
|
@ -472,6 +472,14 @@ class Line(TipableVMobject):
|
||||
def get_angle(self):
|
||||
return angle_of_vector(self.get_vector())
|
||||
|
||||
def get_projection(self, point):
|
||||
"""
|
||||
Return projection of a point onto the line
|
||||
"""
|
||||
unit_vect = self.get_unit_vector()
|
||||
start = self.get_start()
|
||||
return start + np.dot(point - start, unit_vect) * unit_vect
|
||||
|
||||
def get_slope(self):
|
||||
return np.tan(self.get_angle())
|
||||
|
||||
|
@ -149,17 +149,22 @@ class NumberLine(Line):
|
||||
num_mob.shift(num_mob[0].get_width() * LEFT / 2)
|
||||
return num_mob
|
||||
|
||||
def add_numbers(self, x_values=None, excluding=None, **kwargs):
|
||||
def add_numbers(self, x_values=None, excluding=None, font_size=24, **kwargs):
|
||||
if x_values is None:
|
||||
x_values = self.get_tick_range()
|
||||
if excluding is not None:
|
||||
x_values = list_difference_update(x_values, excluding)
|
||||
|
||||
self.numbers = VGroup()
|
||||
kwargs["font_size"] = font_size
|
||||
|
||||
numbers = VGroup()
|
||||
for x in x_values:
|
||||
self.numbers.add(self.get_number_mobject(x, **kwargs))
|
||||
self.add(self.numbers)
|
||||
return self.numbers
|
||||
if x in self.numbers_to_exclude:
|
||||
continue
|
||||
if excluding is not None and x in excluding:
|
||||
continue
|
||||
numbers.add(self.get_number_mobject(x, **kwargs))
|
||||
self.add(numbers)
|
||||
self.numbers = numbers
|
||||
return numbers
|
||||
|
||||
|
||||
class UnitInterval(NumberLine):
|
||||
|
@ -156,10 +156,6 @@ class Square3D(Surface):
|
||||
|
||||
class Cube(SGroup):
|
||||
CONFIG = {
|
||||
# "fill_color": BLUE,
|
||||
# "fill_opacity": 1,
|
||||
# "stroke_width": 1,
|
||||
# "stroke_color": BLACK,
|
||||
"color": BLUE,
|
||||
"opacity": 1,
|
||||
"gloss": 0.5,
|
||||
@ -174,7 +170,6 @@ class Cube(SGroup):
|
||||
face.apply_matrix(z_to_vector(vect))
|
||||
self.add(face)
|
||||
self.set_height(self.side_length)
|
||||
# self.set_color(self.color, self.opacity, self.gloss)
|
||||
|
||||
|
||||
class Prism(Cube):
|
||||
|
@ -101,37 +101,51 @@ class Surface(Mobject):
|
||||
return normalize_along_axis(normals, 1)
|
||||
|
||||
def pointwise_become_partial(self, smobject, a, b, axis=None):
|
||||
assert(isinstance(smobject, Surface))
|
||||
if axis is None:
|
||||
axis = self.prefered_creation_axis
|
||||
assert(isinstance(smobject, Surface))
|
||||
if a <= 0 and b >= 1:
|
||||
self.match_points(smobject)
|
||||
return self
|
||||
|
||||
nu, nv = smobject.resolution
|
||||
self.set_points(np.vstack([
|
||||
self.get_partial_points_array(arr, a, b, (nu, nv, 3), axis=axis)
|
||||
self.get_partial_points_array(arr.copy(), a, b, (nu, nv, 3), axis=axis)
|
||||
for arr in smobject.get_surface_points_and_nudged_points()
|
||||
]))
|
||||
return self
|
||||
|
||||
def get_partial_points_array(self, points, a, b, resolution, axis):
|
||||
if len(points) == 0:
|
||||
return points
|
||||
nu, nv = resolution[:2]
|
||||
points = points.reshape(resolution)
|
||||
max_index = resolution[axis] - 1
|
||||
lower_index, lower_residue = integer_interpolate(0, max_index, a)
|
||||
upper_index, upper_residue = integer_interpolate(0, max_index, b)
|
||||
if axis == 0:
|
||||
points[:lower_index] = interpolate(points[lower_index], points[lower_index + 1], lower_residue)
|
||||
points[upper_index:] = interpolate(points[upper_index], points[upper_index + 1], upper_residue)
|
||||
points[:lower_index] = interpolate(
|
||||
points[lower_index],
|
||||
points[lower_index + 1],
|
||||
lower_residue
|
||||
)
|
||||
points[upper_index + 1:] = interpolate(
|
||||
points[upper_index],
|
||||
points[upper_index + 1],
|
||||
upper_residue
|
||||
)
|
||||
else:
|
||||
tuples = [
|
||||
(points[:, :lower_index], lower_index, lower_residue),
|
||||
(points[:, upper_index:], upper_index, upper_residue),
|
||||
]
|
||||
for to_change, index, residue in tuples:
|
||||
col = interpolate(points[:, index], points[:, index + 1], residue)
|
||||
to_change[:] = col.reshape((nu, 1, *resolution[2:]))
|
||||
shape = (nu, 1, resolution[2])
|
||||
points[:, :lower_index] = interpolate(
|
||||
points[:, lower_index],
|
||||
points[:, lower_index + 1],
|
||||
lower_residue
|
||||
).reshape(shape)
|
||||
points[:, upper_index + 1:] = interpolate(
|
||||
points[:, upper_index],
|
||||
points[:, upper_index + 1],
|
||||
upper_residue
|
||||
).reshape(shape)
|
||||
return points.reshape((nu * nv, *resolution[2:]))
|
||||
|
||||
def sort_faces_back_to_front(self, vect=OUT):
|
||||
|
@ -56,7 +56,7 @@ mat4 get_xyz_to_uv(vec3 b0, vec3 b1, vec3 unit_normal){
|
||||
// float get_reduced_control_points(vec3 b0, vec3 b1, vec3 b2, out vec3 new_points[3]){
|
||||
float get_reduced_control_points(in vec3 points[3], out vec3 new_points[3]){
|
||||
float length_threshold = 1e-6;
|
||||
float angle_threshold = 1e-3;
|
||||
float angle_threshold = 5e-2;
|
||||
|
||||
vec3 p0 = points[0];
|
||||
vec3 p1 = points[1];
|
||||
|
Reference in New Issue
Block a user