查询 UIBezierCurve 上的特定点以进行动画?
我最近为 Maya 编写了一个脚本,该脚本导出一个包含 Bezier 路径(一系列 xy 点和 xy 控制点)数据的文件。
这条贝塞尔曲线路径旨在表示我的角色将在应用程序内以恒定速度行进的 3D“轨道”或路径。
我了解如何构造 UIBezierCurve,但我似乎找不到任何关于是否可能/如何在给定沿曲线行进的距离的情况下获取曲线上点的 x / y 位置的可靠信息。
我在苹果上找到了这个列表:
http://lists.apple。 com/archives/cocoa-dev/2002/Feb/msg01806.html
但我不太明白该函数返回的内容以及我将如何使用它来完成我的目标 为了。
任何帮助/建议将不胜感激,
谢谢, - 亚当·艾斯菲尔德
I have recently finished writing a script for Maya that exports a file containing data for a Bezier path (a series of xy points and xy control points).
This bezier path is meant to represent the 3D "rail" or path that my character will travel along at a constant speed inside of the app.
I understand how to construct a UIBezierCurve, but I cant seem to find any solid information on if its possible / how to get the x / y position of a point on the curve, given a distance to travel along the curve.
I found this list on apple:
http://lists.apple.com/archives/cocoa-dev/2002/Feb/msg01806.html
But I dont quite understand what that function is returning and how I would use it to accomplish what Im aiming for.
Any help / advice would be greatly appreciated,
Thanks,
- Adam Eisfeld
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
好吧,这将是一个很长的答案。这是我所做的:
我编写了一个 MEL 脚本,允许您在 Maya 中绘制贝塞尔曲线,然后 - 选择该曲线 - 运行我的脚本,该脚本将遍历曲线,分析曲线的每个贝塞尔曲线部分,计算每个部分以及曲线点/控制点的位置。一旦计算完所有这些数据,它就会将所有内容导出到结构如下的 .bezier 文件:
第 1 行:整个贝塞尔路径中包含的单独贝塞尔曲线的数量
第 2 行:第一条贝塞尔曲线的长度
...
X线:最后一条贝塞尔曲线的长度
第一个曲线点的第一个控制点的X位置
第一个曲线点的第一个控制点的 Y 位置
Z 第一个曲线点的第一个控制点的位置
第一个曲线点的 X 位置
第一个曲线点的 Y 位置
第一个曲线点的Z位置
第一个曲线点的第二个控制点的 X 位置
第一个曲线点的第二个控制点的 Y 位置
Z 第一个曲线点的第二个控制点的位置
...
最后一个曲线点的第一个控制点的X位置
最后一个曲线点的第一个控制点的 Y 位置
Z 最后一个曲线点的第一个控制点的位置
最后一个曲线点的X位置
最后一个曲线点的 Y 位置
最后一个曲线点的Z位置
最后一个曲线点的第二个控制点的X位置
最后一个曲线点的第二个控制点的 Y 位置
最后一个曲线点的第二个控制点的 Z 位置
因此,为了使这组类正常工作,您需要一个类似结构的文件。
以下是我编写的用于处理 .bezier 文件的三个类:
AEBezierPath:
.h 文件:
.m 文件:
AEBezierVertex:
.h 文件:
.m 文件:
AEBezierLine:
.h 文件:
.m 文件:
工作原理:
确保您已创建适合结构的 .bezier 文件我已在上面显示,并将其放入您的应用程序包中。
通过以下方式实例化一个新的 AEBezierPath 实例:
-(id) initFromFile: (NSString*) file;
这将从名为 *file 的 .bezier 文件中读取所有数据,并从中构造一个 UIBezierPath,并将必要的长度信息存储到 AEBezierPath 中。
通过向 AEBezierPath 发送从路径起点开始行驶的距离值,以 CGPoint 的形式查询 AEBezierPath 的 x/y 位置,使用以下方法:
-(CGPoint)positionFromDistance: (float)fromDistance;
此方法将首先使用先前从 .bezier 文件检索的每个贝塞尔曲线段的长度来确定该距离位于哪个贝塞尔曲线段。此后,该方法将使用之前关于此 SO Question 的帖子中提到的 bezierInterpolation 函数来计算该距离的贝塞尔曲线路径上的 x/y 位置,并将其作为 CGPoint 返回。
它并不完美,在长贝塞尔曲线与短急角上行驶的距离仍然存在一些明显的差异,但它肯定比根本不使用该系统而是依赖于沿贝塞尔曲线行驶的百分比值要明显得多。
我知道代码当然可以优化,这只是让一切正常工作的第一次运行,但我认为它现在足以作为答案发布。
Alright so this'll be a long answer. Here's what Ive done:
Ive programmed a MEL script that allows you to draw a bezier curve within Maya and then - selecting that curve - run my script which will go through the curve analyzing each bezier section of the curve calculating the length of each section and the positions of the curve points / control points. Once it has all of this data calculated, it exports everything to a .bezier file that is structured like this:
Line 1: Number of individual bezier curves contained in the entire bezier path
Line 2: Length of first bezier curve
...
Line X: Length of last bezier curve
X Position of the first control point of the first curve point
Y Position of the first control point of the first curve point
Z Position of the first control point of the first curve point
X Position of the first curve point
Y Position of the first curve point
Z Position of the first curve point
X Position of the second control point of the first curve point
Y Position of the second control point of the first curve point
Z Position of the second control point of the first curve point
...
X Position of the first control point of the last curve point
Y Position of the first control point of the last curve point
Z Position of the first control point of the last curve point
X Position of the last curve point
Y Position of the last curve point
Z Position of the last curve point
X Position of the second control point of the last curve point
Y Position of the second control point of the last curve point
Z Position of the second control point of the last curve point
So for this set of classes to work you'll need a file structured like that.
Here are the three classes Ive then programmed to handle .bezier files:
AEBezierPath:
.h file:
.m file:
AEBezierVertex:
.h file:
.m file:
AEBezierLine:
.h file:
.m file:
How It Works:
Ensure you have created a .bezier file suiting the structure I've shown above and have it in your app's bundle.
Instantiate a new AEBezierPath instance via:
-(id) initFromFile: (NSString*) file;
This will read in all of the data from the .bezier file named *file and construct a UIBezierPath from it, as well as store the necessary length information into the AEBezierPath.
Query the AEBezierPath for an x/y position in the form of a CGPoint, by sending it a distance value to travel from the start of the path, using the method:
-(CGPoint) positionFromDistance: (float) fromDistance;
This method will first determine which bezier segment that distance lies on by using the lengths of each bezier segment previously retrieved from the .bezier file. After this the method will use the bezierInterpolation function mentioned in the previous posts on this SO Question to calculate the x/y position on the bezier path at this distance, and return it as a CGPoint.
Its not perfect, there still is some noticeable differences in the distance traveled over long bezier curves vs short tight corners, but it is certainly far less noticeable than not using this system at all and instead relying on a percentage value to travel along the bezier curve.
I know the code can certainly be optimized, this is just a first run through to get everything working, but I think its good enough to post as an answer for now.
您引用的链接暗示的是贝塞尔曲线的每一段都描绘出一条路径 (x(t), y(t)),其中 t 从 0 到 1。
我不熟悉 UIBezierCurve ,但我敢打赌您可以从中获取
NSBezierPath
,然后您可以从那里手动迭代这些段。每个段都是 moveTo、lineTo、curveTo 或 close(相当于最后 moveTo 位置的 lineTo)。唯一重要的路径类型是 curveTo,您可以在此处阅读更多信息:http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B.C3.A9zier_curves
如果您只想制作沿着曲线移动的动画,给出每个段固定的时间,这很简单;您可以迭代这些段,并在每个段中,逐渐从 0 到 1 运行 t 并代入方程。
棘手的一点是以恒定的速度移动。为此,您需要实际测量每个段的长度并将该长度分成每个帧的部分。您可以在这个问题中阅读更多相关信息:
Bezier 曲线上的等距点
我没有使用 Cocoa 一段时间了,但我有一些代码 这里在 Java 中,您可能可以相当轻松地移植(这只是数学,在任何语言中都是相同的):
What the link you cite is alluding to is that each segment of a bezier curve traces out a path (x(t), y(t)), where t goes from 0 to 1.
I'm not familiar with
UIBezierCurve
, but I would bet you can get anNSBezierPath
from it, and from there you can iterate through the segments manually. Each segment is either a moveTo, lineTo, curveTo, or close (equivalent to a lineTo that last moveTo location). The only nontrivial path type is curveTo, which you can read more about here:http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B.C3.A9zier_curves
If you just wanted to animate moving along the curve giving each segment a fixed amount of time, it would be simple; you could just iterate through the segments and within each one, run t gradually from 0 to 1 and plug into the equation.
The tricky bit is going to be moving at a constant speed. For that, you need to actually measure the length of each segment and split that length into parts for each frame. You can read more about that in this question:
Equidistant points across Bezier curves
I haven't worked with Cocoa in a while, but I have some code here in Java that you can probably port fairly easily (it's all just math, which is the same in any language):