わかりましたので、これは長い答えになります。これが私がやったことです:
Maya 内でベジェ曲線を描画できるようにする MEL スクリプトをプログラムし、その曲線を選択して、曲線を通過するスクリプトを実行し、曲線の各ベジェ セクションを分析して、各セクションの長さと曲線の位置を計算します。ポイント/コントロールポイント。このデータがすべて計算されると、次のような構造の .bezier ファイルにすべてがエクスポートされます。
行 1: ベジェ パス全体に含まれる個々のベジェ曲線の数 行 2: 最初のベジェ曲線の長さ ... 行 X: 最後のベジェ曲線の長さ
X 最初のカーブ ポイントの最初のコントロール ポイントの位置 Y 最初のカーブ ポイントの最初のコントロール ポイントの位置 Z 最初のカーブ ポイントの最初のコントロール ポイントの位置
X 最初のカーブ ポイントの位置 Y 最初のカーブ ポイントの位置 Z 最初のカーブ ポイントの位置
X 最初の曲線点の 2 番目の制御点の位置 Y 最初の曲線点の 2 番目の制御点の位置 Z 最初の曲線点の 2 番目の制御点の位置
...
X 最後のカーブ ポイントの最初のコントロール ポイントの位置 Y 最後のカーブ ポイントの最初のコントロール ポイントの位置 Z 最後のカーブ ポイントの最初のコントロール ポイントの位置
X 最後のカーブ ポイントの位置 Y 最後のカーブ ポイントの位置 Z 最後のカーブ ポイントの位置
X 最後のカーブ ポイントの 2 番目のコントロール ポイントの位置 Y 最後のカーブ ポイントの 2 番目のコントロール ポイントの位置 Z 最後のカーブ ポイントの 2 番目のコントロール ポイントの位置
したがって、この一連のクラスを機能させるには、そのような構造のファイルが必要です。
次に、.bezier ファイルを処理するようにプログラムした 3 つのクラスを示します。
AEBezierPath:
.h ファイル:
#import <Foundation/Foundation.h>
#import "AEBezierVertex.h"
#import "AEBezierLine.h"
@interface AEBezierPath : NSObject
{
NSMutableArray *vertices;
NSMutableArray *lines;
UIBezierPath *path;
}
@property (strong) NSMutableArray *vertices;
@property (strong) NSMutableArray *lines;
@property (strong) UIBezierPath *path;
-(id) initFromFile: (NSString*) file;
-(CGPoint) positionFromDistance: (float) fromDistance;
@end
.m ファイル:
#import "AEBezierPath.h"
CGFloat bezierInterpolation(CGFloat t, CGFloat a, CGFloat b, CGFloat c, CGFloat d) {
// see also below for another way to do this, that follows the 'coefficients'
// idea, and is a little clearer
CGFloat t2 = t * t;
CGFloat t3 = t2 * t;
return a + (-a * 3 + t * (3 * a - a * t)) * t
+ (3 * b + t * (-6 * b + b * 3 * t)) * t
+ (c * 3 - c * 3 * t) * t2
+ d * t3;
}
@implementation AEBezierPath
@synthesize vertices;
@synthesize lines;
@synthesize path;
-(id) initFromFile: (NSString*) file
{
self = [super init];
if (self) {
//Init file objects for reading
NSError *fileError;
NSStringEncoding *encoding;
vertices = [[NSMutableArray alloc] init];
lines = [[NSMutableArray alloc] init];
path = [[UIBezierPath alloc] init];
//Load the specified file's contents into an NSString
NSString *fileData = [[NSString alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"testcurve" ofType:@"bezier"] usedEncoding:&encoding error:&fileError];
NSScanner *scanner = [[NSScanner alloc] initWithString:fileData];
if(fileData == nil)
{
NSLog(@"Error reading bezier path file");
}
else
{
float x;
float y;
float cx;
float cy;
float cx2;
float cy2;
float temp;
CGPoint readPoint;
CGPoint readControlIn;
CGPoint readControlOut;
int curRead = 0;
int totalSegments = 0;
float length;
[scanner scanInt:&totalSegments];
for (int s = 0; s < totalSegments; s++) {
[scanner scanFloat:&length];
AEBezierLine *newLine = [[AEBezierLine alloc] initWithLength:length];
[lines addObject:newLine];
}
AEBezierVertex *vertex;
while ([scanner isAtEnd] == 0) {
if (curRead == 0) {
[scanner scanFloat:&x];
[scanner scanFloat:&temp];
[scanner scanFloat:&y];
[scanner scanFloat:&cx2];
[scanner scanFloat:&temp];
[scanner scanFloat:&cy2];
cx = x;
cy = y;
}
else{
[scanner scanFloat:&cx];
[scanner scanFloat:&temp];
[scanner scanFloat:&cy];
[scanner scanFloat:&x];
[scanner scanFloat:&temp];
[scanner scanFloat:&y];
if ([scanner isAtEnd] == 0) {
[scanner scanFloat:&cx2];
[scanner scanFloat:&temp];
[scanner scanFloat:&cy2];
}else
{
cx = x;
cy = y;
}
}
readPoint = CGPointMake(x, y);
readControlIn = CGPointMake(cx, cy);
readControlOut = CGPointMake(cx2, cy2);
vertex = [[AEBezierVertex alloc] initWithControl:readPoint In:readControlIn Out:readControlOut];
[vertices addObject:vertex];
curRead ++;
}
for (int c = 0; c < [vertices count]-1; c++) {
//Init CGPoints for single bezier curve segment
CGPoint p1, p2, p3, p4;
//Store starting bezier point and control point
AEBezierVertex *b1 = [vertices objectAtIndex:c];
p1 = b1.control;
p2 = b1.controlOut;
//Store ending bezier point and control point
AEBezierVertex *b2 = [vertices objectAtIndex:c+1];
p3 = b2.controlIn;
p4 = b2.control;
if (c == 0) {
[path moveToPoint:p1];
}
else
{
[path addCurveToPoint:p4 controlPoint1:p2 controlPoint2:p3];
}
}
}
}
return self;
}
-(CGPoint) positionFromDistance: (float) fromDistance
{
CGPoint position;
AEBezierLine *line;
float runningLength;
int seg = 0;
for (int c = 0; c < [lines count]; c++) {
seg = c;
line = [lines objectAtIndex:c];
runningLength += line.length;
if (runningLength > fromDistance) {
break;
}
}
CGPoint p1, p2, p3, p4;
AEBezierVertex *vert1 = [vertices objectAtIndex:seg];
p1 = vert1.control;
p2 = vert1.controlOut;
//Store ending bezier point and control point
AEBezierVertex *vert2 = [vertices objectAtIndex:seg+1];
p3 = vert2.controlIn;
p4 = vert2.control;
float travelDist;
travelDist = fromDistance;
travelDist = runningLength - travelDist;
travelDist = line.length - travelDist;
float t = travelDist / line.length;
//Create a new point to represent this position
position = CGPointMake(bezierInterpolation(t, p1.x, p2.x, p3.x, p4.x),
bezierInterpolation(t, p1.y, p2.y, p3.y, p4.y));
return position;
}
@end
AEBezier頂点:
.h ファイル:
#import <Foundation/Foundation.h>
@interface AEBezierVertex : NSObject
{
CGPoint controlIn;
CGPoint controlOut;
CGPoint control;
}
@property CGPoint controlIn;
@property CGPoint controlOut;
@property CGPoint control;
-(id) initWithControl: (CGPoint) setControl In: (CGPoint) setIn Out: (CGPoint) setOut;
@end
.m ファイル:
#import "AEBezierVertex.h"
@implementation AEBezierVertex
@synthesize controlIn;
@synthesize controlOut;
@synthesize control;
-(id) initWithControl: (CGPoint) setControl In: (CGPoint) setIn Out: (CGPoint) setOut
{
self = [super init];
if (self) {
//Init
control = setControl;
controlIn = setIn;
controlOut = setOut;
}
return self;
}
@end
AEBezierLine:
.h ファイル:
#import <Foundation/Foundation.h>
@interface AEBezierLine : NSObject
{
float length;
}
@property float length;
-(id) initWithLength: (float) setLength;
@end
.m ファイル:
#import "AEBezierLine.h"
@implementation AEBezierLine
@synthesize length;
-(id) initWithLength: (float) setLength
{
self = [super init];
if (self) {
//Init
length = setLength;
}
return self;
}
@end
使い方:
上に示した構造に適した .bezier ファイルを作成し、それをアプリのバンドルに含めていることを確認してください。
次の方法で、新しい AEBezierPath インスタンスをインスタンス化します。
-(id) initFromFile: (NSString*) ファイル;
これにより、*file という名前の .bezier ファイルからすべてのデータが読み取られ、そこから UIBezierPath が構築され、必要な長さ情報が AEBezierPath に格納されます。
次のメソッドを使用して、パスの始点から移動する距離値を送信することにより、AEBezierPath に CGPoint の形式で x/y 位置を照会します。
-(CGPoint) positionFromDistance: (float) fromDistance;
このメソッドは、まず、.bezier ファイルから以前に取得した各ベジェ セグメントの長さを使用して、距離が存在するベジェ セグメントを特定します。この後、メソッドは、この SO Question の以前の投稿で説明した bezierInterpolation 関数を使用して、この距離でのベジエ パス上の x/y 位置を計算し、CGPoint として返します。
完璧ではありません。長いベジェ曲線と短いタイトなコーナーを移動する距離にはまだいくつかの顕著な違いがありますが、このシステムをまったく使用せず、代わりにパーセンテージ値に依存してベジェ曲線に沿って移動するよりもはるかに目立たないことは確かです.
コードを確実に最適化できることはわかっています。これはすべてを機能させるための最初の実行にすぎませんが、今のところ回答として投稿するのに十分だと思います。