Using sin + cos to imitate a snake (help)

Hi all,
I am trying to use the sin function in order to create the motion of a snake. See attached video for an example snake.ogv.zip (81.9 KB)

As the snake moves following the pointer I would like it to move on the x+y axis a certain amount to imitate this snake-like movement. If the snake moved only on the x axis it would be easy because all I would have to do is call the sin function with a ofGetFrameNum() and have it return a number between -1 and +1. Same for if it moved only on the y axis. But what about cases where the snake is moving diagonally? In that case what is the amount of sin/cos I should add on the x and y axis respectively?

thanks

Here is my code:

``````void sceneShortMovie::draw()
{
ofPushStyle();
ofBackground(210,124,111);

dragSegment(0, posX, posY);

for(int i=0; i<20-1; i++)
{
dragSegment(i+1, x[i], y[i]);
}

filmStrip.setAnchorPercent(0.5, 1);
filmStrip.draw(hatCurrLoc+mustacheCurrLoc, mustacheW, mustacheH);

hatImg.draw(hatCurrLoc);

ofPopStyle();
}
//----------------------------------------------------------
void sceneShortMovie::dragSegment(int i, float xin, float yin)
{

float dx = xin - x[i];
float dy = yin - y[i];

float angle = atan2(dy, dx);
x[i] = xin - cos(angle) * segLength;
y[i] = yin - sin(angle) * segLength;
if (i==0) segment(x[i], y[i], ofRadToDeg(angle), true);
}
//-------------------------------------------------
void sceneShortMovie::segment(float x, float y, float a, bool isFirst)
{
ofPushMatrix();
ofTranslate(x, y);
ofRotate(a);
filmStrip.setAnchorPercent(0.5, 0.5);
ofLine(0,0,segLength, 0);
ofPopMatrix();
}``````

Hey @marinero, sounds like a cool effect.

In order to displace your snake, what you want is something called a normal vector. If you have a point along a curve, the normal vector is perpendicular to the curve at that point. It makes much more sense visually, so here are a bunch of normal vectors drawn at various points along curves:

That normal vector will tell you how much x-displacement and how much y-displacement you need to apply to get your sinewave. You can calculate a normal vector yourself, but the `ofPolyline` class calculates normals for you. Below is some code that 1) starts with a straight line, 2) rotates the line to point in a direction based on the mouse position and 3) displaces the points along the rotated line so that it forms a sinewave. That last part makes use of the normals.

It will give you something like this:

Hope this helps. Googling “normal vector” might help if you are feeling lost.

``````ofPolyline polyline;
ofPolyline rotatedPolyline;
ofPolyline displacedPolyline;

void testApp::setup(){
// Create a polyline where (0,0) is the center of the line
// This makes rotation operations simple
for (int i=-150; i<=150; i+=10) polyline.addVertex(ofVec2f(i, 0));

// Copy the polyline over
rotatedPolyline = polyline;
displacedPolyline = polyline;
}

void testApp::draw(){
ofBackground(0);

ofSetColor(255);

ofPushMatrix();
ofTranslate(ofGetWidth()*1.0/4.0, ofGetHeight()/2);
polyline.draw();
ofPopMatrix();

ofPushMatrix();
ofTranslate(ofGetWidth()*2.0/4.0, ofGetHeight()/2);
rotatedPolyline.draw();
ofPopMatrix();

ofPushMatrix();
ofTranslate(ofGetWidth()*3.0/4.0, ofGetHeight()/2);
displacedPolyline.draw();
ofPopMatrix();
}

//--------------------------------------------------------------
void testApp::update(){

// Use the mouse x position to control the orientation of the polyline
float polylineRotation = ofMap(mouseX, 0, ofGetWidth(), 0, PI);

// Use the elapsed time to determine the x-axis position on the sin curve
// of the first point in the polyline
// The rest of the points in the polyline will then be defined relative
// the first point's position
float speed = TWO_PI / 0.5; // time = seconds * radians/second
float startingDisplacementAngle = ofGetElapsedTimef() * speed;

for (int i=0; i<polyline.size(); i++) {

// Take the original polyline's point and rotate it around (0,0), the origin
//  We could have also used ofVec3f's rotate function
float a = polylineRotation;
ofVec3f rotatedPoint(0.0, 0.0, 0.0);
rotatedPoint.x = polyline[i].x * cos(a) - polyline[i].y * sin(a);
rotatedPoint.y = polyline[i].y * cos(a) + polyline[i].x * sin(a);
rotatedPolyline[i] = rotatedPoint;

// Now we take that rotated polyline and use it's normal to displace the points
// along the line according to a sinewave
// The angleOffset describes where point i lies along the sinewave compared
// to point 0. By multiplying by TWO_PI, we are saying that the last point
// in polyline is 360 degrees farther along the sinewave than the first point. Or
// in other words, it is a full cycle ahead of the first point.
ofVec3f rotatedNormal = rotatedPolyline.getNormalAtIndex(i);
float angleOffset = (float)i/(float)polyline.size() * TWO_PI;
float scale = 50.0 * sin(startingDisplacementAngle + angleOffset);
displacedPolyline[i] = rotatedPoint + (scale * rotatedNormal);
}
}``````
1 Like