如何检测旋转Line与固定Circle的碰撞并改变Circle颜色?
问题描述
我有一个旋转的Line和一个固定的Circle,希望检测两者发生碰撞时,对应改变Circle的颜色。我尝试使用Intersection()函数,但怀疑由于Line不是VMobject导致该函数无效(我并不完全确定这一点)。
请问该如何实现这一需求?以下是我的简化代码:
class Test(Scene): def construct(self): b1_l = Line([-1.,-1.,0.],[3,3,0]).set_color(YELLOW).set_angle(57.5) obj_1 = Circle(radius=.1, color=RED, fill_opacity=1).move_to([0,1.5,0]) def updateObject(obj: Mobject): if(Intersection(obj, b1_l).has_points()): obj.set_color(BLUE) # Never here else: obj.set_color(YELLOW) # Always here obj_1.add_updater(updateObject) self.add(obj_1) self.play(b1_l.animate.set_angle(57.5*DEGREES + 20*DEGREES)) self.wait(0.05) self.play(b1_l.animate.set_angle(57.5*DEGREES))
目前代码运行后仅显示黄色Circle,两者碰撞时没有任何颜色变化。
解决方案
问题根源
Intersection函数对Line这类1D的VMobject(Line实际是VMobject子类)检测精度极低,因为它依赖Mobject的填充区域或采样点,而Line本身无填充,仅靠线条采样点很难与Circle区域产生有效交集检测。
两种可行实现方式
方式一:几何公式判断(精度高,推荐)
直接通过几何计算判断线段与圆是否相交:
from manim import * class Test(Scene): def construct(self): b1_l = Line([-1.,-1.,0.],[3,3,0]).set_color(YELLOW).set_angle(57.5 * DEGREES) obj_1 = Circle(radius=.1, color=RED, fill_opacity=1).move_to([0,1.5,0]) def updateObject(obj: Mobject): # 获取线段端点与圆心坐标 start, end = b1_l.get_start_and_end() center = obj.get_center() # 计算线段方向向量与圆心到起点的向量 line_vec = end - start center_to_start = center - start # 计算圆心投影在线段上的位置,限制在线段范围内 t = np.dot(center_to_start, line_vec) / np.dot(line_vec, line_vec) t_clamped = max(0, min(1, t)) # 线段上离圆心最近的点 closest_point = start + t_clamped * line_vec # 判断距离是否小于等于圆半径 if np.linalg.norm(center - closest_point) <= obj.radius: obj.set_color(BLUE) else: obj.set_color(YELLOW) obj_1.add_updater(updateObject) self.add(b1_l, obj_1) # 必须将Line添加到场景中 self.play(b1_l.animate.set_angle(57.5*DEGREES + 20*DEGREES)) self.wait(0.05) self.play(b1_l.animate.set_angle(57.5*DEGREES))
方式二:给Line添加碰撞代理(实现简单)
创建一个极窄的Rectangle作为Line的碰撞检测代理,利用Intersection检测矩形与圆的交集:
from manim import * class Test(Scene): def construct(self): b1_l = Line([-1.,-1.,0.],[3,3,0]).set_color(YELLOW).set_angle(57.5 * DEGREES) # 生成Line的碰撞代理:极窄矩形,与Line重合 line_collider = Rectangle( width=b1_l.get_length(), height=0.02, fill_opacity=1 ).align_to(b1_l, LEFT).set_angle(b1_l.get_angle()) line_collider.set_opacity(0) # 隐藏代理,不显示在画面中 obj_1 = Circle(radius=.1, color=RED, fill_opacity=1).move_to([0,1.5,0]) def updateObject(obj: Mobject): # 保持代理与Line角度同步 line_collider.set_angle(b1_l.get_angle()) if Intersection(obj, line_collider).has_points(): obj.set_color(BLUE) else: obj.set_color(YELLOW) obj_1.add_updater(updateObject) self.add(b1_l, line_collider, obj_1) self.play(b1_l.animate.set_angle(57.5*DEGREES + 20*DEGREES)) self.wait(0.05) self.play(b1_l.animate.set_angle(57.5*DEGREES))
关键修正点
- 原代码未将
b1_l添加到场景,导致看不到Line的旋转,必须加入self.add(b1_l)。 set_angle参数需传入弧度值,原代码中57.5会被当作弧度,应改为57.5 * DEGREES转换为角度对应的弧度。
内容的提问来源于stack exchange,提问作者Ardi
相关产品推荐
相关产品推荐

