This is my first pass at plotting tangents and normals.
Starting with a sine wave and will modify to work with any curves focusing on mathematical plots.
I have yet to deal with length of tangent and length of normal; just using a an “offset” for now and lengths will vary depending on slope.
Suggestions welcome.
I prefer hints and tips to a “canned” solution with complete code.
I do not want to use a library but may want to glean code for insight.
float theta = 0;
int cycles = 4; // # cycles to display
float x = 0, y = 0;
float w = width;
float xScale; //used to scale across x axis; only used for plotting!
float yScale; //these will distort tangents and normals if no the same
float m1, m2;
float offset;
float amp;
float x1, y1, xa, ya, xb, yb;
void setup()
{
size(1280, 1024, P3D);
background(0);
// ortho();
}
void draw()
{
background(0);
translate(0, height/2);
stroke(64);
strokeWeight(2);
line(0, 0, width, 0);
xScale = 100;
yScale = 100;
amp = 3;
// Sine wave plot
for (x = 0; x <= cycles*TWO_PI; x+=TWO_PI/width)
{
stroke(255, 0, 0);
y = sin(x);
point(xScale*x, yScale*y);
}
for (x = 0; x <= cycles*TWO_PI; x+=TAU/16) // Try /8 /16 etc. here
{
x1 = x;
y1 = sin(x);
offset = PI/32;
// Tangent ******************************************************************
m1 = cos(x);
// First point
xa = x + offset;
ya = m1*(xa-x1) + y1;
// Second point
xb = x - offset;
yb = m1*(xb-x1) + y1;
stroke(0, 255, 0);
line(xScale*xa, yScale*ya, xScale*xb, yScale*yb);
// Normal *******************************************************************
m2 = -1/m1;
// First point
xa = x + offset;
ya = m2*(xa-x1) + y1;
// Second point
xb = x - offset;
yb = m2*(xb-x1) + y1;
stroke(255, 255, 0);
line(xScale*xa, yScale*ya, xScale*xb, yScale*yb);
}
}
// Normal *******************************************************************
m2 = -1/m1;
// First point
xa = x + cos(atan(m2))*offset;
ya = m2*(xa-x1) + y1;
// Second point
xb = x - cos(atan(m2))*offset;
yb = m2*(xb-x1) + y1;
First of all I suggest that you investigate the PVector class since it can store
a position on the curve
the tangent at a position on the curve
the normal at a position on the curve
as single entities and it will reduce the code complexity.
Calculating the position, tangent and normal for a continuous curve in 2D space is simple enough and I suggest you create separate functions that return PVector objects for each of these actions e.g.
/**
Calculate the position of a point on the curve
y = sin(x) + cos(2*x)
*/
PVector fX(float x) {
float y = sin(x) + cos(2*x);
return new PVector(x, y);
}
/**
Calculate the tangent to the curve y=f(x) at the
given position.
The returned tangent will be of unit length so would
need to be scaled for display.
*/
PVector tan_fX(float x, float deltaX) {
PVector tan = PVector.sub(fX(x + deltaX), fX(x));
tan.normalize();
return tan;
}
/**
Calculate the normal to the curve y=f(x) at the
given position.
The returned normal will be of unit length so would
need to be scaled for display.
Note there are two normals for any point on a 2D curve
and this function returns one of them. To get the other
simply multiple the returned vector by -1
*/
PVector norm_fX(float x, float deltaX) {
PVector norm = PVector.sub(fX(x + deltaX), fX(x));
norm.set(-norm.y, norm.x);
norm.normalize();
return norm;
}
I notice that you are using P3D and I suggest you change this to P2D if you are only interested in 2D curves. If you decide to work in 3D then you can use the same approach for the position and the tangent to the curve but not for the normal.For a 3D continuous curve there are an infinite number of normals for any point on the curve.
EDIT:
I should have said the parameter deltaX is a very small number e.g. 0.001
The method used in this post uses approximation to calculate the tangent and normal. The calculation is more accurate as deltaX approaches zero but deltaX must not be so small that the value PVector.sub(fX(x + deltaX), fX(x)); creates a zero length vector.
Final project was a success!
I challenged myself to do this with basic math operators, accomplished that and learned so much along the way.
PVectors were not used.
This is all my hand stitched code.
You might also be interested in the bundled ArcLengthParameterization example – it specifically addresses the two ways that you could approach vertex distribution (and so, whether / how your animated figure slows down on the curves).