I'm trying to find a way to compare two colors to find out how much they are alike. I can't seem to find any resources about the subject so I'm hoping to get some pointers here.
Idealy, I would like to get a score that tells how much they are alike. For example, 0 to 100, where 100 would be equal and 0 would be totally different.
Thanks!
Edit:
Getting to know a bit more about colors from the answers I understand my question was a bit vague. I will try to explain what I needed this for.
I have pixeldata (location and color) of an application window at 800x600 size so I can find out if a certain window is open or not by checking every x-interval.
However, this method fails as soon as the application is resized (the contents are scaled, not moved). I can calculate where the pixels move, but because of rounding and antialising the color can be slightly different.
Pieter's solution was good enough for me in this case, although all other responses were extremely helpfull as well, so I just upvoted everyone. I do think that ColorEye's answer is the most accurate when looking at this from a professional way, so I marked it as the answer.
What you are looking for is called Delta-E.
http://www.colorwiki.com/wiki/Delta_E:_The_Color_Difference
It is the distance between two colors in LAB color space. It is said that the human eye cannot distinguish colors below 1 DeltaE (I find that my eyes can find differences in colors below 1 DeltaE, each person is different.)
There are 4 formulas for 'color difference'.
Delta E (CIE 1976)
Delta E (CIE 1994)
Delta E (CIE 2000)
Delta E (CMC)
Check the math link on this site:
http://www.brucelindbloom.com/
So the proper answer is to convert your RGB to LAB using the formula given, then use DeltaE 1976 to determine the 'difference' in your colors. A result of 0 would indicate identical colors. Any value higher than 0 could be judged by the rule 'A delta e of 1 or less is indistinguishable by most people'.
There's an open-source .net library that lets you do this easily: https://github.com/hvalidi/ColorMine
The most common method for comparing colors is CIE76:
var a = new Rgb { R = 149, G = 13, B = 12 }
var b = new Rgb { R = 255, G = 13, B = 12 }
var deltaE = a.Compare(b,new Cie1976Comparison());
Colors have different weights affecting human eye.
So convert the colors to grayscale using their calculated weights:
Gray Color =
.11 * B +
.59 * G +
.30 * R
And your difference will be
difference = (GrayColor1 - GrayColor2) * 100.0 / 255.0
with difference ranging from 0-100.
This is actually commonly used and very simple approach thats used calculating image differences in image procesing.
-edit
this is the very simple and still usable formula - even in commercial applications.
If you want to go deep you should check out the color difference methods called: CIE1976, CIE1994, CIE2000 and CMC
Here you can find some more detailed info:
http://en.wikipedia.org/wiki/Color_difference
Something like this:
public static int CompareColors(Color a, Color b)
{
return 100 * (int)(
1.0 - ((double)(
Math.Abs(a.R - b.R) +
Math.Abs(a.G - b.G) +
Math.Abs(a.B - b.B)
) / (256.0 * 3))
);
}
Converting the RGB color to the HSL color space often produces good results. Check wikipedia for the conversion formula. It is up to you to assign weights to the differences in H, the color, S, how 'deep' the color is and L, how bright it is.
I found a interesting approach called Colour metric and adapted it to C#
public static double ColourDistance(Color e1, Color e2)
{
long rmean = ((long)e1.R + (long)e2.R) / 2;
long r = (long)e1.R - (long)e2.R;
long g = (long)e1.G - (long)e2.G;
long b = (long)e1.B - (long)e2.B;
return Math.Sqrt((((512 + rmean) * r * r) >> 8) + 4 * g * g + (((767 - rmean) * b * b) >> 8));
}
Colour perception depends on many factors and similarity can be measured in many ways. Just comparing how similar the R, G and B components are generally gives results humans won't agree with.
There's some general material on colour comparisons in wikipedia, and on working with natural colour spaces in C# in this question.
I've translated the code for DeltaE2000 on Bruce Lindbloom's page into C.
Here:
//
// deltae2000.c
//
// Translated by Dr Cube on 10/1/16.
// Translated to C from this javascript code written by Bruce LindBloom:
// http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE2000.html
// http://www.brucelindbloom.com/javascript/ColorDiff.js
#include <stdio.h>
#include <math.h>
#define Lab2k struct Lab2kStruct
Lab2k
{
float L;
float a;
float b;
};
// function expects Lab where: 0 >= L <=100.0 , -100 >=a <= 100.0 and -100 >= b <= 100.0
float
DeltaE2000(Lab2k Lab1,Lab2k Lab2)
{
float kL = 1.0;
float kC = 1.0;
float kH = 1.0;
float lBarPrime = 0.5 * (Lab1.L + Lab2.L);
float c1 = sqrtf(Lab1.a * Lab1.a + Lab1.b * Lab1.b);
float c2 = sqrtf(Lab2.a * Lab2.a + Lab2.b * Lab2.b);
float cBar = 0.5 * (c1 + c2);
float cBar7 = cBar * cBar * cBar * cBar * cBar * cBar * cBar;
float g = 0.5 * (1.0 - sqrtf(cBar7 / (cBar7 + 6103515625.0))); /* 6103515625 = 25^7 */
float a1Prime = Lab1.a * (1.0 + g);
float a2Prime = Lab2.a * (1.0 + g);
float c1Prime = sqrtf(a1Prime * a1Prime + Lab1.b * Lab1.b);
float c2Prime = sqrtf(a2Prime * a2Prime + Lab2.b * Lab2.b);
float cBarPrime = 0.5 * (c1Prime + c2Prime);
float h1Prime = (atan2f(Lab1.b, a1Prime) * 180.0) / M_PI;
float dhPrime; // not initialized on purpose
if (h1Prime < 0.0)
h1Prime += 360.0;
float h2Prime = (atan2f(Lab2.b, a2Prime) * 180.0) / M_PI;
if (h2Prime < 0.0)
h2Prime += 360.0;
float hBarPrime = (fabsf(h1Prime - h2Prime) > 180.0) ? (0.5 * (h1Prime + h2Prime + 360.0)) : (0.5 * (h1Prime + h2Prime));
float t = 1.0 -
0.17 * cosf(M_PI * ( hBarPrime - 30.0) / 180.0) +
0.24 * cosf(M_PI * (2.0 * hBarPrime ) / 180.0) +
0.32 * cosf(M_PI * (3.0 * hBarPrime + 6.0) / 180.0) -
0.20 * cosf(M_PI * (4.0 * hBarPrime - 63.0) / 180.0);
if (fabsf(h2Prime - h1Prime) <= 180.0)
dhPrime = h2Prime - h1Prime;
else
dhPrime = (h2Prime <= h1Prime) ? (h2Prime - h1Prime + 360.0) : (h2Prime - h1Prime - 360.0);
float dLPrime = Lab2.L - Lab1.L;
float dCPrime = c2Prime - c1Prime;
float dHPrime = 2.0 * sqrtf(c1Prime * c2Prime) * sinf(M_PI * (0.5 * dhPrime) / 180.0);
float sL = 1.0 + ((0.015 * (lBarPrime - 50.0) * (lBarPrime - 50.0)) / sqrtf(20.0 + (lBarPrime - 50.0) * (lBarPrime - 50.0)));
float sC = 1.0 + 0.045 * cBarPrime;
float sH = 1.0 + 0.015 * cBarPrime * t;
float dTheta = 30.0 * expf(-((hBarPrime - 275.0) / 25.0) * ((hBarPrime - 275.0) / 25.0));
float cBarPrime7 = cBarPrime * cBarPrime * cBarPrime * cBarPrime * cBarPrime * cBarPrime * cBarPrime;
float rC = sqrtf(cBarPrime7 / (cBarPrime7 + 6103515625.0));
float rT = -2.0 * rC * sinf(M_PI * (2.0 * dTheta) / 180.0);
return(sqrtf(
(dLPrime / (kL * sL)) * (dLPrime / (kL * sL)) +
(dCPrime / (kC * sC)) * (dCPrime / (kC * sC)) +
(dHPrime / (kH * sH)) * (dHPrime / (kH * sH)) +
(dCPrime / (kC * sC)) * (dHPrime / (kH * sH)) * rT
)
);
}
Related
I'm trying to parse the values given from a device with a LSM6DSL chip (gyroscopic and acc.) and I'm having a hard time parsing the data properly for positioning and angle.
From the vendor I've received the information that the unit is running on a resolution of 2000 for the gyro, 8g for the acc.
I receive the data in bytes that are converted by the following to shorts;
public int[] BufferToMotionData(byte[] buffer, int segments = 2)
{
int[] motionDataArray = new int[segments * 3];
int offset = Constants.BufferSizeImage + Constants.CommandLength;
for (int i = 0; i < 6; i++)
{
motionDataArray[i] = BitConverter.ToInt16(buffer, offset + (i * 2));
if (motionDataArray[i] >= Int16.MaxValue)
motionDataArray[i] -= 65535;
}
return motionDataArray;
}
(Edit; Cleaned up version)
This returns values in the range of (example) 961, -16223, -1635, 664, -269, -597.
According to the spec sheet I'm supposed to multiply each vector with it's corresponding value.. * 70f for gyro, .448f for acc.
From the documentation I understand that for the G forces these are in milliG's and gyro in millidegrees per sec?
// Gyro X,Y,Z
gx = Mathf.Deg2Rad * (motionData[0] * 70f / 1000f);
gy = Mathf.Deg2Rad * (motionData[1] * 70f / 1000f);
gz = Mathf.Deg2Rad * (motionData[2] * 70f / 1000f);
// Acc X,Y,Z
ax = motionData[3] * 0.488f / 1000f;
ay = motionData[4] * 0.488f / 1000f;
az = motionData[5] * 0.488f / 1000f;
Update(gx, gy, gz, ax, ay, az);
Update(..) is Madgwick's quaternion formul, although for velocity I use the acceleration vectors.
G force values that I'm getting at this moment after calculation;
X 0.047824 Y -0.320128 Z 0.006344
X 0.07076 Y -0.2562 Z 0.020008
X 0.099552 Y -0.063928 Z -0.13664
These look awfully low, and if applied as velocity it just runs off in a given direction, I know I'm missing a gravity correct although not entirely sure how to apply this.
I'm under the assumption that I do not need to apply drag to my velocity vector since values should be negated by the acceleration values received?
Anyone with experience with this type of chip and actually applying the values to yaw/pitch/roll (or quaternion) and applying the G forces as linear acceleration.
By looking on existing code on GitHub, it's looks like the sensitivity factor for 8g is 244 µg/digit and not 488 µg/digit as you coded it.
Also it look's like raw values are shifted and are in [-r/2,r/2] instead of [0, r]. So you have to add 500µg or 500µdps to it. (But maybe it's linked to a uint/int issue, anyway are you sure about the endianness?)
See here for acc data and here for gyro data.
Based on that, the code should look likes this:
// Gyro X,Y,Z (in rad/s)
gx = Mathf.Deg2Rad * (motionData[0] * 70000f + 500) / 1000000;
gy = Mathf.Deg2Rad * (motionData[1] * 70000f + 500) / 1000000;
gz = Mathf.Deg2Rad * (motionData[2] * 70000f + 500) / 1000000;
// Acc X,Y,Z (in g)
ax = (motionData[3] * 244f + 500) / 1000000;
ay = (motionData[4] * 244f + 500) / 1000000;
az = (motionData[5] * 244f + 500) / 1000000;
Update(gx, gy, gz, ax, ay, az);
I am struggling with a situation, where I want to draw a circle with GDI+ and some points on it (drawn as smaller circles), but the circle seems to be noncircular. By implementing scaling and zero point shifting, I zoom into the points and the circle, and find the points not lying exactly on the circle.
When I add a 'discrete' circle drawn with line segments, this circle does fit the points very well, if enough segments are used. Since the math is the same, I think that roundoff errors in my code can not be the cause of the circle deviations (although probably in the implementation of DrawEllipse).
In fact the deviations are biggest on 45/135/225/315 degrees.
I made a small project reproducing the effect with a slightly different setup: I draw multiple circles with their origins lying on an other circle with its center on the center of the form with the same radius. If everything goes well all circles shoud touch the center of the form. But with big radii like 100'000, they dont pass throug the center anymore, but miss it by a screendistance of maybe 15 pixel.
To reproduce the situation make a new c# project, put on form1 button1, and call the function Draw() from it:
private void Draw()
{
Graphics g = this.CreateGraphics();
g.Clear(this.BackColor );
double hw = this.Width / 2;
double hh = this.Height / 2;
double scale = 100000;
double R = 1;
for (int i = 0; i < 12; i++)
{
double angle = i * 30;
double cx = R * Math.Cos(angle * Math.PI / 180);
double cy = R * Math.Sin(angle * Math.PI / 180);
g.DrawEllipse(new Pen(Color.Blue, 1f), (float)(hw - scale * (cx + R)), (float)(hh + scale * (cy - R)), (float)(2 * R * scale), (float)(2 * R * scale));
g.DrawLine(Pens.Black, new Point(0, (int)hh), new Point(this.Width, (int)hh));
g.DrawLine(Pens.Black, new Point((int)hw, 0), new Point((int)hw, this.Height));
double r = 3;
g.DrawEllipse(new Pen(Color.Green, 1f), (float)(hw - r), (float)(hh - r), (float)(2 * r), (float)(2 * r));
//Pen magpen = new Pen(Color.Magenta, 1);
//double n = 360d / 1000;
//for (double j = 0; j < 360; j += n)
//{
// double p1x = R * Math.Cos(j * Math.PI / 180) + cx;
// double p1y = R * Math.Sin(j * Math.PI / 180) + cy;
// double p2x = R * Math.Cos((j + n) * Math.PI / 180) + cx;
// double p2y = R * Math.Sin((j + n) * Math.PI / 180) + cy;
// g.DrawLine(magpen, (float)(hw - scale * p1x), (float)(hh + scale * p1y), (float)(hw - scale * p2x), (float)(hh + scale * p2y));
//}
}
}
use the variable scale from 100 to 100'000 to see the effect: The circles don't touch the origin anymore, but wobble around it. If you uncomment the commented lines you can see, that the magenta 'discrete' circle performs much better.
Since using 'discrete' circles and arcs is a performance killer, I am looking for a way to draw better circles with GDI+.
Any ideas, suggestions?
GDI+ (at least in its native version) doesn't draw ellipses: it uses the cubic Bezier approximation, as described here and here.
That is, instead of
c = cos(angle);
s = sin(angle);
x = c * radius;
y = s * radius;
or, for an ellipse (a & b as semi major and semi minor axes in some order)
x = c * a;
y = s * b;
it uses
K = (sqrt(2) - 1) * 4.0 / 3;
t = angle * 0.5 / pi;
u = 1 - t;
c = (u * u * u) * 1 + (3 * u * u * t) * 1 + (3 * u * t * t) * K + (t * t * t) * 0;
s = (u * u * u) * 0 + (3 * u * u * t) * K + (3 * u * t * t) * 1 + (t * t * t) * 1;
(for angle in the range [0, pi/2]: other quadrants can be calculated by symmetry).
g.DrawArc(new Pen(Color.Blue, 1f), (float)((hw) - R * scale), -(float)((2 * R * scale) - hh), (float)(2 * R * scale), (float)(2 * R * scale), 0, 360);
Well I got somewhat precise results with this method.However this is only a sample.
I need to find a point where a line (its origin is ellipse' center) intersects an ellipse in 2D... I can easily find a point on a circle, because I know an angle F and the circle' radius (R):
x = x0 + R * cosF
y = y0 + R * sinF
However I just can't figure how am I supposed to deal with an ellipse... I know it's dimensions (A & B), but what is the way of finding parameter T?!
x = x0 + A * cosT
y = y0 + B * sinT
From what I understand the parameter T (T angle) is not far from the F angle (approximately +-15 degrees in some cases), but I just can't figure how to calculate it!!!
If there is a kind hearted soul, please help me with this problem...
The standard equation of an ellipse, stationed at 0,0, is:
1 = (x)^2 / (a) + (y)^2 / (b)
Where a is 1/2 the diameter on the horizontal axis, and b is 1/2 the diameter on the vertical axis.
you have a line, assuming an equation:
y = (m)(x - x0) + y0
So, let us plug-and-play!
1 = (x)^2 / (a) + (m(x - x0) + y0)^2 / (b)
1 = x^2 / a + (mx + (y0 - mx0))^2 / b
1 = x^2 / a + (m^2 * x^2 + 2mx*(y0 - mx0) + (y0 - mx0)^2) / b
1 = x^2 / a + (m^2 x^2) / b + (2mx*(y0 - mx0) + (y0^2 - 2y0mx0 + m^2*x0^2)) / b
1 = ((x^2 * b) / (a * b)) + ((m^2 * x^2 * a) / (a * b)) + (2mxy0 - 2m^2xx0)/b + (y0^2 - 2y0mx0 + m^2*x0^2)/b
1 = ((bx^2 + am^2x^2)/(ab)) + (x*(2my0 - 2m^2x0))/b + (y0^2 - 2y0mx0 + m^2*x0^2)/b
0 = x^2*((b + a*m^2)/(ab)) + x*((2my0 - 2m^2x0)/b) + (((y0^2 - 2y0mx0 + m^2*x0^2)/b) - 1)
That last equation follows the form of a standard quadratic equation.
So just use the quadratic formula, with:
((b + a*m^2)/(ab))
((2my0 - 2m^2x0)/b)
and
(((y0^2 - 2y0mx0 + m^2*x0^2)/b) - 1)
to get the X values at the intersections; Then, plug in those values into your original line equation to get the Y values.
Good luck!
Don't do it this way. Instead check the equation that forms an ellipse and that forming a line and solve the set:
The ellipse: (x/a)^2 + (y/b)^2 = 1
Your line: y = cx
You know a, b and c, so finding a solution is going to be easy. You'll find two solutions, because the line crosses the ellipse twice.
EDIT: Note I moved your ellipse's center to (0,0). It makes everything easier. Just add (x0,y0) to the solution.
public Hits<float2> EllipseLineIntersection ( float rx , float ry , float2 p1 , float2 p2 )
{
Hits<float2> hits = default(Hits<float2>);
float2 p3, p4;
Rect rect = default(Rect);
{
rect.xMin = math.min(p1.x,p2.x);
rect.xMax = math.max(p1.x,p2.x);
rect.yMin = math.min(p1.y,p2.y);
rect.yMax = math.max(p1.y,p2.y);
}
float s = ( p2.y - p1.y )/( p2.x - p1.x );
float si = p2.y - ( s * p2.x );
float a = ( ry*ry )+( rx*rx * s*s );
float b = 2f * rx*rx * si * s;
float c = rx*rx * si*si - rx*rx * ry*ry;
float radicand_sqrt = math.sqrt( ( b*b )-( 4f * a * c) );
p3.x = ( -b - radicand_sqrt )/( 2f*a );
p4.x = ( -b + radicand_sqrt )/( 2f*a );
p3.y = s*p3.x + si;
p4.y = s*p4.x + si;
if( rect.Contains(p3) ) hits.Push( p3 );
if( rect.Contains(p4) ) hits.Push( p4 );
return hits;
}
public struct Hits<T>
{
public byte count;
public T point0, point1;
public void Push ( T val )
{
if( count==0 ) { point0 = val; count ++; }
else if( count==1 ) { point1 = val; count ++; }
else print("This structure can only fit 2 values");
}
}
I wrote a C# code for your problem and I hope you can find it helpful. the distance function inside this code calculates euclidean distance between two points in space.
wX denotes horizontal radios of ellipse and wY denotes vertical radios.
private PointF LineIntersectEllipse(PointF A, PointF B, float wX, float wY)
{
double dx = B.X - A.X;
double dy = B.Y - A.Y;
double theta = Math.Atan2(dy, dx);
double r = distance(A, B) - ((wX * wY) / Math.Sqrt(Math.Pow(wY * Math.Cos(theta), 2) + Math.Pow(wX * Math.Sin(theta), 2)));
return PointF((float)(A.X + r * Math.Cos(theta)), (float)(A.Y + r * Math.Sin(theta)));
}
Andrew Łukasik posted a good and useful answer, however it is not using regular C# types. As I wrote in the comments, I converted the code using System.Drawing objects PointF and RectangleF. I found out that if the points given as parameters are aligned as a vertical or horizontal line, then "rect" will have a width or a height equal to 0. Then, rect.Contains(point) will return false even if the point is on this line.
I also modified the "Hits" structure to check if the point pushed is not already existing, which is the case if the line is perfectly tangent, then p3 and p4 will have same coordinates, as the exact tangent point is the only crossing point.
Here is the new code taking care of all the cases :
public static Hits<PointF> EllipseLineIntersection0(float rx, float ry, PointF p1, PointF p2)
{
Hits<PointF> hits = default(Hits<PointF>);
PointF p3 = new PointF();
PointF p4 = new PointF();
var rect = default(RectangleF);
rect.X = Math.Min(p1.X, p2.X);
rect.Width = Math.Max(p1.X, p2.X) - rect.X;
rect.Y = Math.Min(p1.Y, p2.Y);
rect.Height = Math.Max(p1.Y, p2.Y) - rect.Y;
float s = (p2.Y - p1.Y) / (p2.X - p1.X);
float si = p2.Y - (s * p2.X);
float a = (ry * ry) + (rx * rx * s * s);
float b = 2f * rx * rx * si * s;
float c = rx * rx * si * si - rx * rx * ry * ry;
float radicand_sqrt = (float)Math.Sqrt((b * b) - (4f * a * c));
p3.X = (-b - radicand_sqrt) / (2f * a);
p4.X = (-b + radicand_sqrt) / (2f * a);
p3.Y = s * p3.X + si;
p4.Y = s * p4.X + si;
if (rect.Width == 0)
{
if (p3.Y >= rect.Y && p3.Y <= rect.Y + rect.Height) hits.Push(p3);
if (p4.Y >= rect.Y && p4.Y <= rect.Y + rect.Height) hits.Push(p4);
}
else if (rect.Height == 0)
{
if (p3.X >= rect.X && p3.X <= rect.X + rect.Width) hits.Push(p3);
if (p4.X >= rect.X && p4.X <= rect.X + rect.Width) hits.Push(p4);
}
else
{
if (rect.Contains(p3)) hits.Push(p3);
if (rect.Contains(p4)) hits.Push(p4);
}
return hits;
}
public struct Hits<T>
{
public byte Count;
public T P0, P1;
public void Push(T val)
{
if (Count == 0) { P0 = val; Count++; }
else if (Count == 1) { if (!P0.Equals(val)) { P1 = val; Count++; } }
else throw new OverflowException("Structure Hits can only fit 2 values.");
}
}
I need to render a torus in OpenGL, without using GLUT. I'm using C# and Tao Framework bindings. I have the following code, which I got from here.
private void DrawTorus() {
int numc = 100, numt = 100;
double TWOPI = 2 * Math.PI;
for (int i = 0; i < numc; i++) {
Gl.glBegin(Gl.GL_QUAD_STRIP);
for (int j = 0; j <= numt; j++) {
for (int k = 1; k >= 0; k--) {
double s = (i + k) % numc + 0.5;
double t = j % numt;
double x = (1 + 0.1 * Math.Cos(s * TWOPI / numc)) * Math.Cos(t * TWOPI / numt);
double y = (1 + 0.1 * Math.Cos(s * TWOPI / numc)) * Math.Sin(t * TWOPI / numt);
double z = 0.1 * Math.Sin(s * TWOPI / numc);
Gl.glVertex3d(2 * x, 2 * y, 2 * z);
}
}
Gl.glEnd();
}
}
This code draws a torus, but now I need to put a texture on it. I'm trying to use these formulas for the texture coordinates, but I can't figure out what to use for R and r (inner and outer radius respectively).
v = arccos (Y/R)/2pi
u = [arccos ((X/(R + r*cos(2pi * v))] * 2pi
Having some trouble understanding that code, I would appreciate an explanation of it or perhaps an alternative, more intuitive code with comments. Any help will be much appreciated.
If we compare the formula
X = (R + r cos (2 pv)) cos (2 pu)
Y = r sin (2 pv)
Z = (R + r cos (2 pv)) sin (2 pu)
with the code
double x = (1 + 0.1 * Math.Cos(s * TWOPI / numc)) * Math.Cos(t * TWOPI / numt);
double y = (1 + 0.1 * Math.Cos(s * TWOPI / numc)) * Math.Sin(t * TWOPI / numt);
double z = 0.1 * Math.Sin(s * TWOPI / numc);
Clearly, X = x, Y = z, Z = y, R = 1, r = 0.1, 2 pv = s * TWOPI / numc and 2 pu = t * TWOPI / numt. Then
v = arccos (Y/R)/2p
u = [arccos ((X/(R + r*cos(2 pv))]2p
gives
v = arcos (z/1)/TWOPI
u = [arcos ((x/(1 + 0.1*cos(s * TWOPI / numc)]/TWOPI
EDIT: To be honest, I didn't try hard to understand the formula... Reading your code, I think this should do the trick:
u = (i + k) / (float)numc;
v = t / (float)numt;
(You may have to swap u and v.)
I have to make a program that uses C# Generated Graphics to make a replica of my name that I wrote in cursive. Twist is, I have to use Bezier Curves. I've already called a function to make Bezier Curves using 4 points and a gravity concept. My question to you is, What would be the easiest way to make around 10 curves.
Here is my function for a Bezier Curve.
public static void bezierCurve(
Graphics g,
double p1x, double p1y,
double p2x, double p2y,
double p3x, double p3y,
double p4x, double p4y)
{
double t, r1x, r4x, r1y, r4y;
float x, y;
Pen black = new Pen(Color.Black);
r1x = 3 * (p2x - p1x);
r4x = 3 * (p4x - p3x);
r1y = 3 * (p2y - p1y);
r4y = 3 * (p4y - p3y);
t = 0;
while (t <= 1)
{
x = (float) ((2 * Math.Pow(t, 3) - 3 * Math.Pow(t, 2) + 1) * p1x
+ (-2 * Math.Pow(t, 3) + 3 * Math.Pow(t, 2)) * p4x
+ (Math.Pow(t, 3) - 2 * Math.Pow(t, 2) + t) * r1x
+ (Math.Pow(t, 3) - Math.Pow(t, 2)) * r4x);
y = (float) ((2 * Math.Pow(t, 3) - 3 * Math.Pow(t, 2) + 1) * p1y
+ (-2 * Math.Pow(t, 3) + 3 * Math.Pow(t, 2)) * p1y
+ (Math.Pow(t, 3) - 2 * Math.Pow(t, 2) + t) * r1y
+ (Math.Pow(t, 3) - Math.Pow(t, 2)) * r4y);
g.DrawRectangle(black, x, y, 1, 1);
t = t + 0.01;
}
}
I would suggest taking some vector editing software, e.g. InkScape or Corel, draw your name with beziers using that software, then save as .SVG. The SVG format is easy to understand, here is an example of encoding a bezier path. Copy the coordinates from the path into your program. Alternatively, use a piece of graph paper to get the coordinates by hand.
C# already has a function for drawing Beziers, see Graphics.DrawBezier, that is going to be much more efficient (and producing better-looking results) than your implementation.