3b1b / manim

Animation engine for explanatory math videos
MIT License
62.93k stars 5.83k forks source link

Arrow for vectors in 3D #774

Open DarkRinzler opened 4 years ago

DarkRinzler commented 4 years ago

Hi guys, I have this weird issue to which I have not found a solution. When I try to draw a 3D vector the arrow of the vector does not point in the same direction of the vector. The code I am trying is class Example(ThreeDScene): def construct(self): axes=ThreeDAxes() vector=Vector(direction=0.5(UP+RIGHT+OUT)) self.add(axes) self.add(vector) self.move_camera(phi=60DEGREES,theta=-45*DEGREES,run_time=6) self.begin_ambient_camera_rotation(rate=0.2) self.wait(5) self.stop_ambient_camera_rotation()

Any suggestions?

NickLennonLiu commented 4 years ago

Hi, I encountered this issue too. The reason why the arrow doesn't go in the "correct" direction is that when creating a TipableVMobject, the positioning of the tip does not include rotating along phi direction, therefore I added some code by myself: At geometry.py line 97 is the method position_tip(), right after the tip.rotate() I added:

    angle = angle_of_vector(handle - anchor) + PI/2

    a = np.array((np.cos(angle),np.sin(angle),0))

    tip.rotate(-phi_of_vector(handle - anchor),a)

By doing so the vectors we create in 3D scene will look right but they all seem to be raised from xy plane, since even though the rear of the triangle connects to the end of the line as expected, the triangle itself still has a free degree of rotation, which is arbitrary.

jaybonthius commented 4 years ago

@NickLennonLiu what is phi_of_vector? That's undefined for me.

NickLennonLiu commented 4 years ago

@jaybonthius Oops sorry I forgot that. It is a new method I defined in space_ops.py to calculate the phi angle of a vecter.

def phi_of_vector(vector):

xy = complex(*vector[:2])

if xy == 0:

    return 0;

a = ((vector[:1])**2 + (vector[1:2])**2)**(1/2)

vector[0] = a

vector[1] = vector[2]

return np.angle(complex(*vector[:2]))
vikramsingh111 commented 4 years ago

---------------------------after running the code i got these lines----------------------------- Traceback (most recent call last): File "/usr/lib/python3.8/runpy.py", line 193, in _run_module_as_main return _run_code(code, main_globals, None, File "/usr/lib/python3.8/runpy.py", line 86, in _run_code exec(code, run_globals) File "/home/vikram/manim/manim.py", line 2, in import manimlib File "/home/vikram/manim/manimlib/init.py", line 4, in import manimlib.extract_scene File "/home/vikram/manim/manimlib/extract_scene.py", line 9, in from manimlib.scene.scene import Scene File "/home/vikram/manim/manimlib/scene/scene.py", line 11, in from manimlib.camera.camera import Camera File "/home/vikram/manim/manimlib/camera/camera.py", line 13, in from manimlib.mobject.types.image_mobject import AbstractImageMobject File "/home/vikram/manim/manimlib/mobject/types/image_mobject.py", line 7, in from manimlib.mobject.shape_matchers import SurroundingRectangle File "/home/vikram/manim/manimlib/mobject/shape_matchers.py", line 2, in from manimlib.mobject.geometry import Line File "/home/vikram/manim/manimlib/mobject/geometry.py", line 107 a = np.array([np.cos(angle),np.sin(angle),0]) ^ SyntaxError: invalid syntax

csylvain commented 4 years ago

Hi, I encoutered this issue too. The reason why the arrow doesn't go in the "correct" direction is that when creating a TipableVMobject, the positioning of the tip does not include rotating along phi direction, therefore I added some code by myself: At geometry.py line 97 is the method position_tip(), right after the tip.rotate() I added:

    angle = angle_of_vector(handle - anchor) + PI/2

    a = np.array([np.cos(angle),np.sin(angle),0])

    tip.rotate(-phi_of_vector(handle - anchor),a)

By doing so the vectors we create in 3D scene will look right but they all seem to be raised from xy plane, since even though the rear of the triangle connects to the end of the line as expected, the triangle itself still has a free degree of rotation, which is arbitrary.

possible the syntax problem is fixed by using

      a = np.array((np.cos(angle),np.sin(angle),0))

instead? (parens for a list instead of square braces?)

Catchip commented 3 years ago

@jaybonthius Oops sorry I forgot that. It is a new method I defined in space_ops.py to calculate the phi angle of a vecter.

def phi_of_vector(vector):

xy = complex(*vector[:2])

if xy == 0:

    return 0;

a = ((vector[:1])**2 + (vector[1:2])**2)**(1/2)

vector[0] = a

vector[1] = vector[2]

return np.angle(complex(*vector[:2]))

I find that if the vector is something like [0,0,x], the tip doesn't go in the right direction. I delete part of the phi_of_vector and it goes well.

def phi_of_vector(vector):
    xy = complex(*vector[:2])
    # if xy == 0:
    # return 0
    a = ((vector[:1])**2 + (vector[1:2])**2)**(1/2)
    vector[0] = a
    vector[1] = vector[2]
    return np.angle(complex(*vector[:2]))
rpSebastian commented 3 years ago

I find that if the vector is something like [0,0,x], the tip doesn't go in the right direction. I delete part of the phi_of_vector and it goes well.

I encounter the same problem, but the tip doesn't help. I solve the problem by adding a statement.


def phi_of_vector(vector):
    vector += 1e-8
    xy = complex(*vector[:2])
    if xy == 0:
        return 0
    a = ((vector[:1])**2 + (vector[1:2])**2)**(1/2)
    vector[0] = a
    vector[1] = vector[2]
    return np.angle(complex(*vector[:2]))
csylvain commented 3 years ago

is this a "machine epsilon" problem? the value for epsilon depends on the floating point precision, and 1e-8 seems familiar to me.

On Mon, Jan 18, 2021 at 11:23 AM rpSebastian notifications@github.com wrote:

I find that if the vector is something like [0,0,x], the tip doesn't go in the right direction. I delete part of the phi_of_vector and it goes well.

I encounter the same problem, but the tip doesn't help. I solve the problem by adding a statement.

def phi_of_vector(vector): vector += 1e-8 xy = complex(*vector[:2]) if xy == 0: return 0 a = ((vector[:1])2 + (vector[1:2])2)*(1/2) vector[0] = a vector[1] = vector[2] return np.angle(complex(vector[:2]))

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/3b1b/manim/issues/774#issuecomment-762350875, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABRUYNL5C5OGOBFEBAEXU23S2ROAJANCNFSM4JDNBKEQ .

rpSebastian commented 3 years ago

I use this method to skip the if statement and avoid potential floating point precision problems.

3dfernando commented 3 years ago

I had the same issue, thanks csylvain for providing the code snippet. I decided to put it all together in the same function, if anyone wants an "easier way out". I had not tested the machine precision issues discussed afterwards, so I don't know if it's worth to suggest a push:

#Replace the position_tip function inside geometry.py
def position_tip(self, tip, at_start=False):
    # Last two control points, defining both
    # the end, and the tangency direction
    if at_start:
        anchor = self.get_start()
        handle = self.get_first_handle()
    else:
        handle = self.get_last_handle()
        anchor = self.get_end()
    tip.rotate(angle_of_vector(handle - anchor) - PI - tip.tip_angle)

    #####NEW CODE        
    angle = angle_of_vector(handle - anchor) + PI/2
    Ba = np.array((np.cos(angle),np.sin(angle),0))
    vector=handle - anchor
    xy = complex(*vector[:2])
    if xy == 0:
        return 0;
    Va = ((vector[:1])**2 + (vector[1:2])**2)**(1/2)
    vector[0] = Va
    vector[1] = vector[2]

    tip.rotate(-np.angle(complex(*vector[:2])),Ba)
    #####NEW CODE

    tip.shift(anchor - tip.tip_point)
    return tip