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.
Related
This question already has answers here:
Is there any way to draw an image to use 4 points rather than 3 (perspective warp)
(4 answers)
Perspective Image Transformation with tiling
(2 answers)
Closed 4 years ago.
I want to rotate a bitmap image constantly but the array of four elements of points that I created doesn't fit in this method. drawimage(image,points,srcrectangle,Graphicsunit).
I was reading microsft document about drawimage method and It says that it will work for 3 points(paralellogram). So I tried with 3 points, and It works as microsoft document says. But I need these four elements, perhaps I'm wrong , can someone just tell me how this method works?
public override void dibujar(Graphics area, Bitmap imagen)
{
radio = Math.Sqrt(Math.Pow(ancho, 2) + Math.Pow(largo, 2)) / 2;
Rectangle porcion = new Rectangle(indicex * ancho, indicey * largo, ancho, largo);
Point p1 = new Point(x + (int)(radio * Math.Sin(Math.PI * angulo / 180)), y+(int)(radio * Math.Cos(Math.PI * angulo / 180)));
Point p2 = new Point(x + ancho + (int)(radio * Math.Sin(Math.PI * (angulo-90) / 180)), y + (int)(radio * Math.Cos(Math.PI * (angulo - 90) / 180)));
Point p3 = new Point(x + (int)(radio * Math.Sin(Math.PI * (angulo - 180) / 180)), y - largo + (int)(radio * Math.Cos(Math.PI * (angulo - 180) / 180)));
Point p4 = new Point(x + ancho + (int)(radio * Math.Sin(Math.PI * (angulo - 270) / 180)), y - largo + (int)(radio * Math.Cos(Math.PI * (angulo - 270) / 180)));
Point[] points = { p2, p3, p4 }; // Get all points in one array
area.DrawImage(imagen, points, porcion, GraphicsUnit.Pixel);
if (angulo == 0)
{
angulo = 360;
}
else
{
angulo--;
}
x += dx;
}
This method throw NotimplementedException when my array is of four elements
I need to find the shortest distance between a point and a line segment (point defined as x y, line defined using endpoints ax ay and bx by).
I have written some code but it doesn't seem to work for some of the parameters. I'm not sure if there's just some small mistake somewhere or (more likely) the way it works is fundamentally wrong.
public static double GetDist(double ax, double ay, double bx, double by, double x, double y)
{
double Perpendicular = Math.Abs((by - ay) * x - (bx - ax) * y + bx * ay - by * ax) /
Math.Sqrt((ay - by) * (ay - by) + (ax - bx) * (ax - bx));
double to_axay = Math.Sqrt((x - ax) * (x - ax) + (y - ay) * (y - ay));
double to_bxby = Math.Sqrt((x - bx) * (x - bx) + (y - by) * (y - by));
double dist1 = Math.Sqrt((x - ax) * (x - ax) + (y - ay) * (y - ay));
double dist2 = Math.Sqrt((bx - ax) * (bx - ax) + (by - ay) * (by - ay));
double theta1 = Math.Atan(dist1);
double theta2 = Math.Atan(dist2);
double angle = Math.Abs(theta2 - theta1);
if (angle > Math.PI)
angle = 2 * Math.PI - angle;
if (angle <= (1 / 2 * Math.PI))
return Perpendicular;
else
{
if (to_axay >= to_bxby)
return to_bxby;
else
return to_axay;
}
}
Example when it doesn't work:
x=0, y=30
ax=-30, ay=0
bx=30, by=0
The expected result is 30. My program instead returns 42.4264.
You have some mistake in angle part, so result is calculated for one of the ends (30*sqrt(2)). (Note thet you calculate atan for distance, but argument should be ratio of two distances. Moreover, such approach suffers from issues with atan range and so on).
But you don't need to make trigonometric calculations. Just find scalar product of ba and bp vectors and scalar product of ab and ap vectors.
If the first one is negative (angle abp is obtuse), get distance to b end. If the second one is negative, get distance to a end.
If both are negative, use distance calculated through cross product (you already have got it). Note that the last distance might be calculated after all comparisons.
Checked code:
public static double GetDist(double ax, double ay, double bx,
double by, double x, double y) {
if ((ax-bx)*(x-bx)+(ay-by)*(y-by) <= 0)
return Math.Sqrt((x - bx) * (x - bx) + (y - by) * (y - by));
if ((bx-ax)*(x-ax)+(by-ay)*(y-ay) <= 0)
return Math.Sqrt((x - ax) * (x - ax) + (y - ay) * (y - ay));
return Math.Abs((by - ay)*x - (bx - ax)*y + bx*ay - by*ax) /
Math.Sqrt((ay - by) * (ay - by) + (ax - bx) * (ax - bx));
}
public static void Main()
{
Console.WriteLine(GetDist(0, 2, 2, 0, 0, 0));
Console.WriteLine(GetDist(0, 2, 2, 0, 0, 3));
}
1.41421356237309
1
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 am trying to optimize a bezier curve implementation by using the formula used in this wikipedia article. I have a horribly slow implentation now but at least it should be accurate. Using the following:
p0 = (0, 256) //Violet dot
p1 = (70, 223) //Green dot
p2 = (24, 472) //Blue dot
p3 = (255, 256) //Yellow dot
t = 0.5
Drawn with my current code below, the point at T = 0.5 is (67.125, 324.625)
Trying the formula for the X-axis, I do a calculation like this:
var x = Math.Pow(1 - t, 3) * p0.X + 3 * Math.Pow(1 - t, 2) * t * p1.X + 3
* (1 - t) * Math.Pow(t, 2) * p2.X + Math.Pow(t, 3) + p3.X;
But this gives me an X coordinate of 290.375 which is obviously not right. What am I missing here?
Duh! Looking at my own question now, I see the obvious. The last bit Math.Pow(t, 3) + p3.X; should have been Math.Pow(t, 3) * p3.X;. Now it works.
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
)
);
}