Hermite curve function [solved]

Started by
0 comments, last by JHiemstra 13 years, 6 months ago
Hello,

I'm working on a game that includes a base path (vec array) that is turned into an interpolated curved path using a hermit function. I created this function based on multiple examples in different code languages than I needed. They were also for 2D paths only, and I need it for 3D. I'm using the Imgtec PVR SDK on the iPhone.

The problem is that it isn't working entirely as it should. For some reason it smooths only about half the path (and not exactly the first half of the node list but half on the X-axis I think) and makes a mess of the rest. I've spent a couple of days now trying to get this to work perfectly, checking my path, the order of the nodes in the basepath etc. My math skills are not so good, I get what the function is supposed to do, but I'm kinda lost on how to troubleshoot/fix this function.

Following is the code (posting it entirely for clarity but also because I figure it could be useful to others when it actually works):
////  ThePath.mm//#import "ThePath.h"@implementation ThePath@synthesize basePath;@synthesize curvedPath;@synthesize STEP, startTangent, endTangent;- (id) init { 	[super init];		basePath = [[NSMutableArray alloc] init];	return self;}- (void) setStartEndTangent { 	//first two nodes	PathNode* startN0 = [self.basePath objectAtIndex:0];	PathNode* startN1 = [self.basePath objectAtIndex:1];	//last two nodes	PathNode* startN2 = [self.basePath objectAtIndex:basePath.count-1];	PathNode* startN3 = [self.basePath objectAtIndex:basePath.count-2];		PVRTVec4 startTangentTemp ((startN1.pos.x - startN0.pos.x) *2,0,0,0);	startTangent = startTangentTemp;	PVRTVec4 endTangentTemp((startN2.pos.x - startN3.pos.x)*2), 0,0,0);	endTangent = endTangentTemp;}- (int) countVerts:(int)num{		int numOutVerts = 1;	for(int i=0; i < num-1; i++) {	  numOutVerts += (STEP+1);	}	return numOutVerts;}- (void) makeCurve{	int numInVerts = basePath.count;	int numOutVerts = [self countVerts:numInVerts]; 	//NSLog(@"numInVerts: %d numOutVerts: %d ", numInVerts, numOutVerts);		//alloc all outnodes in curvedpath	curvedPath = [[NSMutableArray alloc] init];	for (int it=0; it<numOutVerts; it++) {		PathNode* newNode = [[PathNode alloc] init];		[curvedPath addObject:newNode];	}		PathNode* inNode = [self.basePath objectAtIndex:0];	PathNode* outNode = [self.curvedPath objectAtIndex:0];		PVRTVec4 outPos(inNode.pos.x, inNode.pos.y, inNode.pos.z,0);	outNode.pos = outPos;	int startPosn = 1;		// tangents for the current curve segment between two points	PVRTVec4 t0(0.0f);	PVRTVec4 t1(0.0f);		for (int i=0; i < numInVerts-1; i++) {        if (i == 0) {			t0.x = startTangent.x;			t0.y = startTangent.y;			t0.z = startTangent.z;		} else  { // use previous t1 tangent			t0.x = t1.x;			t0.y = t1.y;			t0.z = t1.z;						if (i == numInVerts-2) {   // next point is the last one				t1.x = endTangent.x;				t1.y = endTangent.y;				t1.z = endTangent.z;			} else {								PathNode*  prevNode = [basePath objectAtIndex:i-1];				PathNode*  nextNode = [basePath objectAtIndex:i+1];								double xLen = nextNode.pos.x - prevNode.pos.x;				double yLen = nextNode.pos.y - prevNode.pos.y;				double zLen = nextNode.pos.z - prevNode.pos.z;								t1.x = xLen/2;				t1.y = yLen/2;				t1.z = zLen/2;							}		}		PathNode* thisInNode = [self.basePath objectAtIndex:i];		PathNode* nextInNode = [self.basePath objectAtIndex:i+1];		PathNode* thisStartPosNode = [self.curvedPath objectAtIndex:startPosn];				[self makeHermit:i :startPosn :t0 :t1];		startPosn += (STEP+1);         	}}- (void) makeHermit:(int)i:(int)startPosn:(PVRTVec4)t0:(PVRTVec4)t1 {		double x0, y0, z0, x1,  y1, z1;	PathNode* thisNode = [self.basePath objectAtIndex:i];	PathNode* nextNode = [self.basePath objectAtIndex:i+1];	x0 = thisNode.pos.x;	y0 = thisNode.pos.y;	z0 = thisNode.pos.z;	x1 = nextNode.pos.x;	y1 = nextNode.pos.y;	z1 = nextNode.pos.z;		double xCoord, yCoord, zCoord;	double tStep = 1.0/(STEP+1);	double t;		for(int i=0; i < STEP; i++) {		t = tStep * (i+1);		PathNode* thisOutNode = [self.curvedPath objectAtIndex:startPosn+i];		xCoord = ([self fh1:t] * x0) + ([self fh2:t] * x1) + ([self fh3:t] * t0.x) + ([self fh4:t] * t1.x);				yCoord = ([self fh1:t] * y0) + ([self fh2:t] * y1) + ([self fh3:t] * t0.y) + ([self fh4:t] * t1.y);				zCoord = ([self fh1:t]  * z0) + ([self fh2:t]  * z1) + ([self fh3:t]  * t0.z) + ([self fh4:t]  * t1.z);				PVRTVec4 thisOutPos(xCoord, yCoord, zCoord, 0);		thisOutNode.pos = thisOutPos;	}		PathNode* thislastOutNode = [self.curvedPath objectAtIndex:startPosn+STEP];	PVRTVec4 thisLastOutPos(x1, y1, z1, 0);	thislastOutNode.pos = thisLastOutPos;		}- (double) fh1:(double)t {  return (2.0)*pow(t,3) - (3.0*t*t) + 1;  }- (double) fh2:(double)t {  return (-2.0)*pow(t,3) + (3.0*t*t); }- (double) fh3:(double)t {  return pow(t,3) - (2.0*t*t) + t; }- (double) fh4:(double)t {  return pow(t,3) - (t*t);  }@end


////  ThePath.h//#import <Foundation/Foundation.h>#include "OGLES2Tools.h"#import "PathNode.h"@interface ThePath : NSObject {	NSMutableArray* basePath;	NSMutableArray* curvedPath;	NSInteger		STEP;	PVRTVec4		startTangent;	PVRTVec4		endTangent;}@property(nonatomic, retain) NSMutableArray* basePath;@property(nonatomic, retain) NSMutableArray* curvedPath;@property(nonatomic, assign) NSInteger		 STEP;@property(nonatomic, assign) PVRTVec4		startTangent;@property(nonatomic, assign) PVRTVec4		endTangent;- (int) countVerts:(int)num;- (void) makeHermit:(int)i:(int)startPosn:(PVRTVec4)t0:(PVRTVec4)t1;- (void) makeCurve;- (double) fh1:(double)t;- (double) fh2:(double)t;- (double) fh3:(double)t;- (double) fh4:(double)t;- (void) setStartEndTangent;@end


STEP is set first, then setStartEndTangent is called, and then makeCurve. My basepath has 84 nodes, with STEP 30 that results in 1314 nodes in the curvedpath. It seems the curved path is about 50% correct and the rest is sorta folded onto the other half. I think I'm abs()-ing or flipping a value too much.

Any help would be appreciated. :)

[Edited by - JHiemstra on October 23, 2010 11:35:30 AM]
Advertisement
I made some progress but still not as it should be. All paths where sort of folded horizontally, at the center of the screen. I think one of my 2D examples was meant for a screen with 0,0 being at the left bottom corner instead of 0,0,0 at the center, and to avoid a portion of the path to be outside the screen it flipped the negative values to positive.

So I kinda answered it myself in the end of my post above, however, it's still not perfect. The problem is at and near the start and the end and at some corners it stutters. I changed some abs into fabs and removed some negations and it's now creating a path along the entire basepath, but it has some weird deviations so is only partly smoothed. The weird thing is that some segments are perfect and others are not. I think I can get it to work, just got yet another math book, but if you can help please let me know.

And yeah, I know it's Hermite, not hermit... let's see if I can change the thread title.

[EDIT: updated the code above, it's working now :) ]

[Edited by - JHiemstra on October 23, 2010 11:28:37 AM]

This topic is closed to new replies.

Advertisement