Find intersection of circle and line - c#

I am trying to find a line is intersecting a circle or not.
I write code but seems some issues with that code.
private Point2d[] IntersectionPoint(Point2d p1, Point2d p2, Point2d sc, double r)
{
Point2d[] sect = null;
double a, b, c;
double bb4ac;
double mu1, mu2;
Point2d dp;
dp = p2 - p1;
a = dp.X * dp.X + dp.Y * dp.Y;
b = 2 * (dp.X * (p1.X - sc.X) + dp.Y * (p1.Y - sc.Y));
c = sc.X * sc.X + sc.Y * sc.Y;
c += p1.X * p1.X + p1.Y * p1.Y;
c -= 2 * (sc.X * p1.X + sc.Y * p1.Y);
c -= r * r;
bb4ac = b * b - 4 * a * c;
if (Math.Abs(a) < Double.Epsilon || bb4ac < 0)
{
return new Point2d[0];
}
mu1 = (-b + Math.Sqrt(bb4ac)) / (2 * a);
mu2 = (-b - Math.Sqrt(bb4ac)) / (2 * a);
// no intersection
if ((mu1 < 0 || mu1 > 1) && (mu2 < 0 || mu2 > 1))
{
sect = new Point2d[0];
}
// one point on mu1
else if (mu1 > 0 && mu1 < 1 && (mu2 < 0 || mu2 > 1))
{
sect = new Point2d[1];
sect[0] = p1 + ((p2 - p1) * mu1);
}
// one point on mu2
else if (mu2 > 0 && mu2 < 1 && (mu1 < 0 || mu1 > 1))
{
sect = new Point2d[1];
sect[0] = p1 + ((p2 - p1) * mu2);
}
// one or two points
else if (mu1 > 0 && mu1 < 1 && mu2 > 0 && mu2 < 1)
{
// tangential
if (mu1 == mu2)
{
sect = new Point2d[1];
sect[0] = p1 + ((p2 - p1) * mu1);
}
// two points
else
{
sect = new Point2d[2];
sect[0] = p1 + ((p2 - p1) * mu1);
sect[1] = p1 + ((p2 - p1) * mu2);
}
}
else
{
// should NEVER get here
sect = new Point2d[0];
}
return sect;
}
And calling this function like
Point ptOld = points[oldPoint];
Point ptNew = points[newPoint];
Point2d p1 = new Point2d((float)ptOld.latitude, (float)ptOld.longitude);
Point2d p2 = new Point2d((float)ptNew.latitude, (float)ptNew.longitude);
Point2d sc = new Point2d((float)loc.latitude, (float)loc.longitude);
It fails when i am trying with these co-ordinates
30,-30
80,-40
10
https://www.dropbox.com/s/38r9eylt2p4xfvw/graph.png

You could do some linear algebra:
Express the line as an origin point P1 and a normalized direction vector N
Project the center C of the circle onto the line: PC = P1 + dot(C - P1, N) * N
Compute the distance squared dSquared between points C and PC.
If equal (with some small tolerance) to radiusSquared, PC is on the circle and is the single intersection point.
If greater than radiusSquared, no intersection.
Otherwise, the two intersections are given by
offset = sqrt(radiusSquared - dSquared).
Intersections = PC +/- offset * N.

Edit: Vectors are now available in C#.
There is my code for intersecting:
public static Vector3? IntersectRayCircle(Vector3 rayStart, Vector3 rayPoint, Vector3 circlePosition, float circleRadiusSquared)
{
if (rayStart == rayPoint || circleRadiusSquared <= 0)
{
return null;
}
Vector3 nearest = GetNearestPoint(circlePosition, rayStart, rayPoint, false, false);
float distanceSquared = Vector3.DistanceSquared(nearest, circlePosition);
if (distanceSquared > circleRadiusSquared)
{
return null;
}
Vector3 offset = Vector3.Normalize(rayPoint - rayStart) * (float)Math.Sqrt(circleRadiusSquared - distanceSquared);
if (Vector3.DistanceSquared(circlePosition, rayStart) < circleRadiusSquared)
{
return nearest + offset;
}
else
{
return nearest - offset;
}
}
public static Vector3 GetNearestPoint(Vector3 location, Vector3 segmentStart, Vector3 segmentEnd, bool trimStart, bool trimEnd)
{
if (segmentStart == segmentEnd)
{
throw new ArgumentException("segmentStart cannot be equal to segmentEnd.");
}
Vector3 AP = location - segmentStart;
Vector3 AB = segmentEnd - segmentStart;
float magnitudeAB = AB.LengthSquared();
float ABAPproduct = Vector3.Dot(AP, AB);
float distance = ABAPproduct / magnitudeAB;
return (distance < 0 && trimStart) ? segmentStart : (distance > 1 && trimEnd) ? segmentEnd : segmentStart + AB * distance;
}

Related

Why Bresenham Algorithm is not fast than DDA Algorithm in C#

I read in stackoverflow before about Bresenham algorithm use for a particular environment like low-language, but I have to compare it in my report so any ideal for my report. How can I prove Bresenham is faster than DDA. Now I created a simple paint on winform C# and the time when I draw with 2 different method is likely equal, sometime DDA is faster. Here is my code in both method
Bresenham Algorithm
List<Point> vertices = new List<Point>();
int deltaX = xN - x0; int signX = deltaX >= 0 ? 1 : -1;
int deltaY = yN - y0; int signY = deltaY >= 0 ? 1 : -1;
if (deltaX == deltaY && deltaX * deltaY == 0) return vertices;
// |dy|/|dx| < 1 => |dy| < |dx| => m < 1
if (Math.Abs(deltaX) > Math.Abs(deltaY))
{
int _2deltaX = deltaX * 2 * signX;
int _2deltaY = deltaY * 2 * signY;
int p0 = _2deltaY - deltaX * signX;
// create array to contain vertices
vertices.Add(new Point(x0, y0));
int xCurrent = x0;
int yCurrent = y0;
while (true)
{
if (count >= (Math.Abs(deltaX) + 1)) return vertices;
if (p0 < 0)
{
xCurrent += signX;
// pk + 1 = pk + 2.∆y.signX
p0 = p0 + _2deltaY;
}
else
{
xCurrent += signX;
yCurrent += signY;
// pk+1= pk + 2.∆y.signX - 2.∆x.signY
p0 = p0 + _2deltaY - _2deltaX;
}
vertices.Add(new Point(xCurrent, yCurrent));
}
}
// |dy|/|dx| > 1 => |dy| > |dx| => m > 1
else if (Math.Abs(deltaX) <= Math.Abs(deltaY))
{
int _2deltaX = deltaX * 2 * signX;
int _2deltaY = deltaY * 2 * signY;
int p0 = _2deltaX - deltaY * signY;
// create array to contain vertices
vertices.Add(new Point(x0, y0));
int xCurrent = x0;
int yCurrent = y0;
while (true)
{
if (count >= (Math.Abs(deltaY) + 1)) return vertices;
if (p0 < 0)
{
yCurrent += signY;
// pk + 1 = pk + 2.∆x.signY
p0 = p0 + _2deltaX;
}
else
{
xCurrent += signX;
yCurrent += signY;
// pk+1= pk + 2.∆x.signY - 2.∆y.signX
p0 = p0 + _2deltaX - _2deltaY;
}
vertices.Add(new Point(xCurrent, yCurrent));
}
}
return vertices;
DDA Algorithm
List<Point> vertices = new List<Point>();
int deltaX = xN - x0; int signX = deltaX >= 0 ? 1 : -1;
int deltaY = yN - y0; int signY = deltaY >= 0 ? 1 : -1;
if (deltaX == deltaY && deltaX * deltaY == 0) return vertices;
int step = Math.Abs(deltaX) > Math.Abs(deltaY) ? Math.Abs(deltaX) : Math.Abs(deltaY);
// x(k + 1) = xk + x'
double stepX = deltaX * 1.0 / step;
double stepY = deltaY * 1.0 / step;
vertices.Add(new Point(x0, y0));
double xCurrent = x0;
double yCurrent = y0;
for (int i = 0; i < step; i++)
{
xCurrent += stepX;
yCurrent += stepY;
vertices.Add(new Point((int)Math.Round(xCurrent), (int)Math.Round(yCurrent)));
}
return vertices;

Visual C# method calls using too much memory?

I'm writing a game using the XNA 4.0 framework. I've written a set of methods that translates the 2D mouse coordinates to a line in the 3d world, then checks to see if that line intersects a plane, and if the intersection point is within the bounds of a face in that plane.
The math works, but for some reason when I do these calculations over 500 times a frame it brings the program to a halt. I can watch the memory usage climb from starting at 15 MB to about 130 MB before garbage collection decides to clean things up. I know specifically it is in this code because when I comment it out, everything else runs smoothly.
I'll paste my code below, any insight would be helpful and thank you!
The Loop:
GraphicObject me = new GraphicObject();
Intersection intersect;
double? dist = null;
foreach (GraphicObject obj in GraphicObjects)
{
intersect = obj.intersectMe(line);
if (intersect.Distance != null)
{
if (intersect.Distance < dist || dist == null)
{
dist = intersect.Distance;
me = obj;
}
else
{
obj.Highlight(false);
}
}
else
{
obj.Highlight(false);
}
}
if (dist != null)
{
me.Highlight(true);
}
intersectMe:
public override Intersection intersectMe(Ray _line)
{
GraphicHelper.Intersects(_line, rect.Vertices[0].Normal, rect.Vertices[0].Position, intersect);
if (intersect.Distance != null)
{
if (!rect.PointOnMe(intersect.X - position.X, intersect.Y - position.Y, intersect.Z - position.Z))
{
intersect.Distance = null;
}
}
return intersect;
}
GraphicsHelper.Intersects:
// _l = line, _n = normal to plane, _p = point on the plane
public static void Intersects(Ray _l, Vector3 _n, Vector3 _p, Intersection _i)
{
_i.Distance = null;
float num = (_n.X * (_p.X - _l.Position.X) + _n.Y * (_p.Y - _l.Position.Y) + _n.Z * (_p.Z - _l.Position.Z));
float denom = (_n.X * _l.Direction.X + _n.Y * _l.Direction.Y + _n.Z * _l.Direction.Z);
if (denom != 0 && num != 0)
{
float t = num / denom;
if (t > 0)
{
_i.X = _l.Position.X + _l.Direction.X * t;
_i.Y = _l.Position.Y + _l.Direction.Y * t;
_i.Z = _l.Position.Z + _l.Direction.Z * t;
_i.Distance = _i.X * _i.X + _i.Y * _i.Y + _i.Z * _i.Z;
}
}
}
PointOnMe:
public bool PointOnMe(float _x, float _y, float _z)
{
float ex = _x - Vertices[3].Position.X;
float ey = _y - Vertices[3].Position.Y;
float ez = _z - Vertices[3].Position.Z;
float ae = a.X * ex + a.Y * ey + a.Z * ez;
float be = b.X * ex + b.Y * ey + b.Z * ez;
ex = _x - Vertices[1].Position.X;
ey = _y - Vertices[1].Position.Y;
ez = _z - Vertices[1].Position.Z;
float ce = c.X * ex + c.Y * ex + c.Z * ez;
float de = d.X * ex + d.Y * ey + d.Z * ez;
if (ae > 0 && be > 0 && ce > 0 && de > 0)
{
return true;
}
else
{
return false;
}
}
Thank you all for taking some time to look at this for me. The error was actually in how I handle obj.Highlight(), TaW's kick in the butt to get a profiler setup helped me to figure that out.
public override void Highlight(bool toggle)
{
if(toggle)
{
rect.Texture = new Texture2D(GraphicsManager.Graphics.GraphicsDevice, 1, 1);
rect.Texture.SetData<Color>(new Color[] { Color.Yellow });
}
else
{
rect.Texture = new Texture2D(GraphicsManager.Graphics.GraphicsDevice, 1, 1);
rect.Texture.SetData<Color>(new Color[] { squareColor });
}
}
Every frame all the obj's were having new textures generated. A terrible way to do things.

Translating concave hull algorithm to c#

So I am trying to translate the algorith found here for concave hulls: http://repositorium.sdum.uminho.pt/bitstream/1822/6429/1/ConcaveHull_ACM_MYS.pdf
(Page 65)
Ive read through the entire thing but I cant figure out how to implement sortByAngle and angle, im not to sure what method I should do inside of them. This is what I have so far:
//Main method
public static Vertex[] ConcaveHull(Vertex[] points, int k = 3)
{
if (k < 3)
throw new ArgumentException("K is required to be 3 or more", "k");
List<Vertex> hull = new List<Vertex>();
//Clean first, may have lots of duplicates
Vertex[] clean = RemoveDuplicates(points);
if (clean.Length < 3)
throw new ArgumentException("At least 3 dissimilar points reqired", "points");
if (clean.Length == 3)//This is the hull, its already as small as it can be.
return clean;
if (clean.Length < k)
throw new ArgumentException("K must be equal to or smaller then the amount of dissimilar points", "points");
Vertex firstPoint = clean[0]; //TODO find mid point
hull.Add(firstPoint);
Vertex currentPoint = firstPoint;
Vertex[] dataset = RemoveIndex(clean, 0);
double previousAngle = 0;
int step = 2;
int i;
while (((currentPoint != firstPoint) || (step == 2)) && (dataset.Length > 0))
{
if (step == 5)
dataset = Add(dataset, firstPoint);
Vertex[] kNearestPoints = nearestPoints(dataset, currentPoint, k);
Vertex[] cPoints = sortByAngle(kNearestPoints, currentPoint, previousAngle);
bool its = true;
i = 0;
while ((its) && (i < cPoints.Length))
{
i++;
int lastPoint = 0;
if (cPoints[0] == firstPoint)
lastPoint = 1;
int j = 2;
its = false;
while ((!its) && (j < hull.Count - lastPoint))
{
its = intersectsQ(hull[step - 1 - 1], cPoints[0], hull[step - i - j - 1], hull[step - j - 1]);
j++;
}
}
if (its)
{
return ConcaveHull(points, k + 1);
}
currentPoint = cPoints[0];
hull.Add(currentPoint);
previousAngle = angle(hull[step - 1], hull[step - 2]);
dataset = RemoveIndex(dataset, 0);
step++;
}
bool allInside = true;
i = dataset.Length;
while (allInside && i > 0)
{
allInside = new Polygon(dataset).Contains(currentPoint); //TODO havent finished ray casting yet.
i--;
}
if (!allInside)
return ConcaveHull(points, k + 1);
return hull.ToArray();
}
private static Vertex[] Add(Vertex[] vs, Vertex v)
{
List<Vertex> n = new List<Vertex>(vs);
n.Add(v);
return n.ToArray();
}
private static Vertex[] RemoveIndex(Vertex[] vs, int index)
{
List<Vertex> removed = new List<Vertex>();
for (int i = 0; i < vs.Length; i++)
if (i != index)
removed.Add(vs[i]);
return removed.ToArray();
}
private static Vertex[] RemoveDuplicates(Vertex[] vs)
{
List<Vertex> clean = new List<Vertex>();
VertexComparer vc = new VertexComparer();
foreach (Vertex v in vs)
{
if (!clean.Contains(v, vc))
clean.Add(v);
}
return clean.ToArray();
}
private static Vertex[] nearestPoints(Vertex[] vs, Vertex v, int k)
{
Dictionary<double, Vertex> lengths = new Dictionary<double, Vertex>();
List<Vertex> n = new List<Vertex>();
double[] sorted = lengths.Keys.OrderBy(d => d).ToArray();
for (int i = 0; i < k; i++)
{
n.Add(lengths[sorted[i]]);
}
return n.ToArray();
}
private static Vertex[] sortByAngle(Vertex[] vs, Vertex v, double angle)
{
//TODO
return new Vertex[]{};
}
private static bool intersectsQ(Vertex v1, Vertex v2, Vertex v3, Vertex v4)
{
return intersectsQ(new Edge(v1, v2), new Edge(v3, v4));
}
private static bool intersectsQ(Edge e1, Edge e2)
{
double x1 = e1.A.X;
double x2 = e1.B.X;
double x3 = e2.A.X;
double x4 = e2.B.X;
double y1 = e1.A.Y;
double y2 = e1.B.Y;
double y3 = e2.A.Y;
double y4 = e2.B.Y;
var x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4));
var y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4));
if (double.IsNaN(x) || double.IsNaN(y))
{
return false;
}
else
{
if (x1 >= x2)
{
if (!(x2 <= x && x <= x1)) { return false; }
}
else
{
if (!(x1 <= x && x <= x2)) { return false; }
}
if (y1 >= y2)
{
if (!(y2 <= y && y <= y1)) { return false; }
}
else
{
if (!(y1 <= y && y <= y2)) { return false; }
}
if (x3 >= x4)
{
if (!(x4 <= x && x <= x3)) { return false; }
}
else
{
if (!(x3 <= x && x <= x4)) { return false; }
}
if (y3 >= y4)
{
if (!(y4 <= y && y <= y3)) { return false; }
}
else
{
if (!(y3 <= y && y <= y4)) { return false; }
}
}
return true;
}
private static double angle(Vertex v1, Vertex v2)
{
// TODO fix
Vertex v3 = new Vertex(v1.X, 0);
if (Orientation(v3, v1, v2) == 0)
return 180;
double b = EuclideanDistance(v3, v1);
double a = EuclideanDistance(v1, v2);
double c = EuclideanDistance(v3, v2);
double angle = Math.Acos((Math.Pow(a, 2) + Math.Pow(b, 2) - Math.Pow(c, 2)) / (2 * a * b));
if (Orientation(v3, v1, v2) < 0)
angle = 360 - angle;
return angle;
}
private static double EuclideanDistance(Vertex v1, Vertex v2)
{
return Math.Sqrt(Math.Pow((v1.X - v2.X), 2) + Math.Pow((v1.Y - v2.Y), 2));
}
public static double Orientation(Vertex p1, Vertex p2, Vertex p)
{
double Orin = (p2.X - p1.X) * (p.Y - p1.Y) - (p.X - p1.X) * (p2.Y - p1.Y);
if (Orin > 0)
return -1;//Left
if (Orin < 0)
return 1;//Right
return 0;//Colinier
}
I know that there is a load of code here. But im not sure if I can show the context and what I have without it.
Other classes:
public class Polygon
{
private Vertex[] vs;
public Polygon(Vertex[] Vertexes)
{
vs = Vertexes;
}
public Polygon(Bounds bounds)
{
vs = bounds.ToArray();
}
public Vertex[] ToArray()
{
return vs;
}
public IEnumerable<Edge> Edges()
{
if (vs.Length > 1)
{
Vertex P = vs[0];
for (int i = 1; i < vs.Length; i++)
{
yield return new Edge(P, vs[i]);
P = vs[i];
}
yield return new Edge(P, vs[0]);
}
}
public bool Contains(Vertex v)
{
return RayCasting.RayCast(this, v);
}
}
public class Edge
{
public Vertex A = new Vertex(0, 0);
public Vertex B = new Vertex(0, 0);
public Edge() { }
public Edge(Vertex a, Vertex b)
{
A = a;
B = b;
}
public Edge(double ax, double ay, double bx, double by)
{
A = new Vertex(ax, ay);
B = new Vertex(bx, by);
}
}
public class Bounds
{
public Vertex TopLeft;
public Vertex TopRight;
public Vertex BottomLeft;
public Vertex BottomRight;
public Bounds() { }
public Bounds(Vertex TL, Vertex TR, Vertex BL, Vertex BR)
{
TopLeft = TL;
TopRight = TR;
BottomLeft = BL;
BottomRight = BR;
}
public Vertex[] ToArray()
{
return new Vertex[] { TopLeft, TopRight, BottomRight, BottomLeft };
}
}
public class Vertex
{
public double X = 0;
public double Y = 0;
public Vertex() { }
public Vertex(double x, double y)
{
X = x;
Y = y;
}
public static Vertex[] Convert(string vs)
{
vs = vs.Replace("[", "");
vs = vs.Replace("]", "");
string[] spl = vs.Split(';');
List<Vertex> nvs = new List<Vertex>();
foreach (string s in spl)
{
try
{
nvs.Add(new Vertex(s));
}
catch
{
}
}
return nvs.ToArray();
}
public static string Stringify(Vertex[] vs)
{
string res = "[";
foreach (Vertex v in vs)
{
res += v.ToString();
res += ";";
}
res = res.RemoveLastCharacter();
res += "]";
return res;
}
public static string ToString(Vertex[] array)
{
string res = "[";
foreach (Vertex v in array)
res += v.ToString() + ",";
return res.RemoveLastCharacter() + "]";
}
/*
//When x < y return -1
//When x == y return 0
//When x > y return 1
public static int Compare(Vertex x, Vertex y)
{
//To find lowest
if (x.X < y.X)
{
return -1;
}
else if (x.X == y.X)
{
if (x.Y < y.Y)
{
return -1;
}
else if (x.Y == y.Y)
{
return 0;
}
else
{
return 1;
}
}
else
{
return 1;
}
}
*/
public static int CompareY(Vertex a, Vertex b)
{
if (a.Y < b.Y)
return -1;
if (a.Y == b.Y)
return 0;
return 1;
}
public static int CompareX(Vertex a, Vertex b)
{
if (a.X < b.X)
return -1;
if (a.X == b.X)
return 0;
return 1;
}
public double distance (Vertex b){
double dX = b.X - this.X;
double dY = b.Y - this.Y;
return Math.Sqrt((dX*dX) + (dY*dY));
}
public double slope (Vertex b){
double dX = b.X - this.X;
double dY = b.Y - this.Y;
return dY / dX;
}
public static int Compare(Vertex u, Vertex a, Vertex b)
{
if (a.X == b.X && a.Y == b.Y) return 0;
Vertex upper = new Vertex();
Vertex p1 = new Vertex();
Vertex p2 = new Vertex();
upper.X = (u.X + 180) * 360;
upper.Y = (u.Y + 90) * 180;
p1.X = (a.X + 180) * 360;
p1.Y = (a.Y + 90) * 180;
p2.X = (b.X + 180) * 360;
p2.Y = (b.Y + 90) * 180;
if(p1 == upper) return -1;
if(p2 == upper) return 1;
double m1 = upper.slope(p1);
double m2 = upper.slope(p2);
if (m1 == m2)
{
return p1.distance(upper) < p2.distance(upper) ? -1 : 1;
}
if (m1 <= 0 && m2 > 0) return -1;
if (m1 > 0 && m2 <= 0) return -1;
return m1 > m2 ? -1 : 1;
}
public static Vertex UpperLeft(Vertex[] vs)
{
Vertex top = vs[0];
for (int i = 1; i < vs.Length; i++)
{
Vertex temp = vs[i];
if (temp.Y > top.Y || (temp.Y == top.Y && temp.X < top.X))
{
top = temp;
}
}
return top;
}
}
Just a note on convention: you should start function names with upper case, and variables with lower case. In the function sortByAngle, you have a reference to the parameter angle and the function angle simultaneously.
Assuming Angle(...) is simply meant to calculate the angle between two points:
private static double Angle(Vertex v1, Vertex v2)
{
return Math.Atan2(v2.Y - v1.Y, v2.X - v1.X);
}
will give you the angle from v1 to v2, in radians between -pi and +pi. Do not mix degrees and radians. My suggestion is to always use radians, and only convert to degrees if necessary for human-readable output.
private static Vertex[] SortByAngle(Vertex[] vs, Vertex v, double angle)
{
List<Vertex> vertList = new List<Vertex>(vs);
vertList.Sort((v1, v2) => AngleDifference(angle, Angle(v, v1)).CompareTo(AngleDifference(angle, Angle(v, v2))));
return vertList.ToArray();
}
uses List.Sort to sort the vertices from greatest to least angle difference between the vertices point and itself, and angle. The order of v1 and v2 are swapped in the input tuple to sort descending, that is, greatest difference first. The difference between angles is calculated like so:
private static double AngleDifference(double a, double b)
{
while (a < b - Math.PI) a += Math.PI * 2;
while (b < a - Math.PI) b += Math.PI * 2;
return Math.Abs(a - b);
}
The first two lines ensure that the angles are not more than 180 degrees apart.
You have error in
private static Vertex[] nearestPoints(Vertex[] vs, Vertex v, int k)
{
Dictionary<double, Vertex> lengths = new Dictionary<double, Vertex>();
List<Vertex> n = new List<Vertex>();
double[] sorted = lengths.Keys.OrderBy(d => d).ToArray();
for (int i = 0; i < k; i++)
{
n.Add(lengths[sorted[i]]);
}
return n.ToArray();
}
according to code if you have several vertexes at the same distance, function returns only one. Since Dictionary uses unique keys.
BTW, did anyone finish this?
I don't have the time right now to read the paper, but I assume from my knowledge of conVEX hull algorithms that you're going around the points in a particular direction looking for the next point to link to.
If that's the case, "angle" would be the angle of the most recent line segment of the hull, and you want to sort the points by their angle from that line. Therefore you want to calculate the angles between a line (on the hull) and a set of lines (from the current point to each other point being considered). Whether the angles calculated are positive or negative depends upon whether you're going clockwise or anticlockwise. To calculate the angles, look at something like this:
Calculating the angle between two lines without having to calculate the slope? (Java)
Then just sort by the angles.
What about that?
private List<Vector> sortClockwiseFromCentroid(List<Vector> points, Vector center)
{
points = points.OrderBy(x => Math.Atan2(x.X - center.X, x.Y - center.Y)).ToList();
return points;
}

Line clipping by LiangBarsky algorithm c#

I have a Liang Barsky algorithm implemented in c# but there's something wrong because it sometimes returns points outside the rectangle bounds like in this case:
p1=(516,546) and p2=(0,0)
R=0;
L=511;
T=511;
B=0;
also how can i indicate that the line is totally outside the rectangle
private void LiangBarsky( PointF P1,PointF P2,float R,float L,float T,float B)
{
try
{
float Tmin = 0;
float Tmax = 1;
float Tl, Tr, Tt, Tb;
Tl = (L - P1.X) / (P2.X - P1.X);
Tr = (R - P1.X) / (P2.X - P1.X);
Tt = (T - P1.Y) / (P2.Y - P1.Y);
Tb = (B - P1.Y) / (P2.Y - P1.Y);
if (0 < Tl && Tl < 1.0)
{
if (InnerProduct(P1, P2, new Point(0, 1)) == EnteringPoint)
Tmin = Tl;
else
Tmax = Tl;
test = true;
}
if (0 < Tr && Tr < 1)
{
if (InnerProduct(P1, P2, new Point(0, -1)) == EnteringPoint)
Tmin = Tr;
else
Tmax = Tr;
test = true;
}
if (0 < Tt && Tt < 1)
{
if (InnerProduct(P1, P2, new Point(1, 0)) == EnteringPoint)
Tmin = Tt;
else
Tmax = Tt;
test = true;
}
if (0 < Tb && Tb < 1)
{
if (InnerProduct(P1, P2, new Point(-1, 0)) == EnteringPoint)
Tmin = Tb;
else
Tmax = Tb;
test = true;
}
if ((Tmin < Tmax) /*&& (Tmin!=0 )&&(Tmax!=1)*/)
{
P1 = new PointF((P1.X + (P2.X - P1.X) * Tmin), (P1.Y + (P2.Y - P1.Y) * Tmin));
P2 = new PointF((P1.X + (P2.X - P1.X) * Tmax), (P1.Y + (P2.Y - P1.Y) * Tmax));
Indtxt.BackColor = Color.Blue;
}
else
{
if(P1.Y>T && P1.X>L && P2.X>L && P2.Y>T)
Indtxt.BackColor = Color.Red;
}
}
catch (Exception ex)
{
}
}
private int InnerProduct(PointF LineP1, PointF LineP2, Point NormalVector)
{
PointF P1_P2 = new PointF(LineP2.X - LineP1.X, LineP2.Y - LineP1.Y);
if (((P1_P2.X * NormalVector.X) + (P1_P2.Y * NormalVector.Y)) <= 0)
return EnteringPoint;//0 as entering point ans 1 as exiting point
else
return ExitingPoint;
}
i translated the algorith into the previous code so i might missed some concepts

How can I render curved text into a Bitmap?

I am currently dynamically creating a bitmap and using the graphics object from the bitmap to draw a string on it like so:
System.Drawing.Graphics graph = System.Drawing.Graphics.FromImage(bmp);
graph.DrawString(text, font, brush, new System.Drawing.Point(0, 0));
This returns a rectangular bitmap with the string written straight across from left to right.
I would like to also be able to draw the string in the shape of a rainbow.
How can I do this?
I recently had this problem (I was rendering text for printing onto CDs), so here's my solution:
private void DrawCurvedText(Graphics graphics, string text, Point centre, float distanceFromCentreToBaseOfText, float radiansToTextCentre, Font font, Brush brush)
{
// Circumference for use later
var circleCircumference = (float)(Math.PI * 2 * distanceFromCentreToBaseOfText);
// Get the width of each character
var characterWidths = GetCharacterWidths(graphics, text, font).ToArray();
// The overall height of the string
var characterHeight = graphics.MeasureString(text, font).Height;
var textLength = characterWidths.Sum();
// The string length above is the arc length we'll use for rendering the string. Work out the starting angle required to
// centre the text across the radiansToTextCentre.
float fractionOfCircumference = textLength / circleCircumference;
float currentCharacterRadians = radiansToTextCentre + (float)(Math.PI * fractionOfCircumference);
for (int characterIndex = 0; characterIndex < text.Length; characterIndex++)
{
char #char = text[characterIndex];
// Polar to cartesian
float x = (float)(distanceFromCentreToBaseOfText * Math.Sin(currentCharacterRadians));
float y = -(float)(distanceFromCentreToBaseOfText * Math.Cos(currentCharacterRadians));
using (GraphicsPath characterPath = new GraphicsPath())
{
characterPath.AddString(#char.ToString(), font.FontFamily, (int)font.Style, font.Size, Point.Empty,
StringFormat.GenericTypographic);
var pathBounds = characterPath.GetBounds();
// Transformation matrix to move the character to the correct location.
// Note that all actions on the Matrix class are prepended, so we apply them in reverse.
var transform = new Matrix();
// Translate to the final position
transform.Translate(centre.X + x, centre.Y + y);
// Rotate the character
var rotationAngleDegrees = currentCharacterRadians * 180F / (float)Math.PI - 180F;
transform.Rotate(rotationAngleDegrees);
// Translate the character so the centre of its base is over the origin
transform.Translate(-pathBounds.Width / 2F, -characterHeight);
characterPath.Transform(transform);
// Draw the character
graphics.FillPath(brush, characterPath);
}
if (characterIndex != text.Length - 1)
{
// Move "currentCharacterRadians" on to the next character
var distanceToNextChar = (characterWidths[characterIndex] + characterWidths[characterIndex + 1]) / 2F;
float charFractionOfCircumference = distanceToNextChar / circleCircumference;
currentCharacterRadians -= charFractionOfCircumference * (float)(2F * Math.PI);
}
}
}
private IEnumerable<float> GetCharacterWidths(Graphics graphics, string text, Font font)
{
// The length of a space. Necessary because a space measured using StringFormat.GenericTypographic has no width.
// We can't use StringFormat.GenericDefault for the characters themselves, as it adds unwanted spacing.
var spaceLength = graphics.MeasureString(" ", font, Point.Empty, StringFormat.GenericDefault).Width;
return text.Select(c => c == ' ' ? spaceLength : graphics.MeasureString(c.ToString(), font, Point.Empty, StringFormat.GenericTypographic).Width);
}
I needed to answer this question in raw C# and could not find any samples that do it so:
This solution requires a lot of Maths to solve. In summary it takes a set of points (2D vectors), aligns them to a baseline and then bends the points around a Spline. The code is fast enough to update in real time and handles loops, etc.
For ease the solution turns text into vectors using GraphicsPath.AddString and uses GraphicsPath.PathPoints/PathTypes for the points, however you can bend any shape or even bitmaps using the same functions. (I would not recommend doing 4k bitmaps in real time though).
The code has a simple Paint function followed by the Spline class. GraphicsPath is used in the Paint method to make the code hopefully easier to understand. ClickedPoints are the array of points you want the text bent around. I used a List as it was added to in Mouse events, use an array if you know the points beforehand.
public void Paint(System.Drawing.Graphics g)
{
List<System.Drawing.Point> clickedPoints = new List<System.Drawing.Point>();
Additional.Math.Beziers.BezierSplineCubic2D _Spline = new Additional.Math.Beziers.BezierSplineCubic2D();
// Create the spline, exit if no points to bend around.
System.Drawing.PointF[] cd = Additional.Math.Beziers.BezierSplineCubic2D.CreateCurve(ClickedPoints.ToArray(), 0, ClickedPoints.Count, 0);
_Spline = new Additional.Math.Beziers.BezierSplineCubic2D(cd);
if (_Spline.Beziers == null || _Spline.Length == 0) return;
// Start Optional: Remove if you only want the bent object
// Draw the spline curve the text will be put onto using inbuilt GDI+ calls
g.DrawCurve(System.Drawing.Pens.Blue, clickedPoints.ToArray());
// draw the control point data for the curve
for (int i = 0; i < cd.Length; i++)
{
g.DrawRectangle(System.Drawing.Pens.Green, cd[i].X - 2F, cd[i].Y - 2F, 4F, 4F);
}
// End Optional:
// Turn the text into points that can be bent - if no points then exit:
System.Drawing.Drawing2D.GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath();
path.AddString("Lorem ipsum dolor", new System.Drawing.FontFamily("Arial"), 0, 12.0F, new System.Drawing.Point(0, 0), new System.Drawing.StringFormat() { Alignment = System.Drawing.StringAlignment.Near });
textBounds = path.GetBounds();
curvedData = (System.Drawing.PointF[])path.PathPoints.Clone();
curvedTypes = (byte[])path.PathTypes.Clone();
dataLength = curvedData.Length;
if (dataLength == 0) return;
// Bend the shape(text) around the path (Spline)
_Spline.BendShapeToSpline(textBounds, dataLength, ref curvedData, ref curvedTypes);
// draw the transformed text path
System.Drawing.Drawing2D.GraphicsPath textPath = new System.Drawing.Drawing2D.GraphicsPath(curvedData, curvedTypes);
g.DrawPath(System.Drawing.Pens.Black, textPath);
}
And now for the spline class:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Additional.Math
{
namespace Vectors
{
public struct Vector2DFloat
{
public float X;
public float Y;
public void SetXY(float x, float y)
{
X = x;
Y = y;
}
public static Vector2DFloat Lerp(Vector2DFloat v0, Vector2DFloat v1, float t)
{
return v0 + (v1 - v0) * t;
}
public Vector2DFloat(Vector2DFloat value)
{
this.X = value.X;
this.Y = value.Y;
}
public Vector2DFloat(float x, float y)
{
this.X = x;
this.Y = y;
}
public Vector2DFloat Rotate90Degrees(bool positiveRotation)
{
return positiveRotation ? new Vector2DFloat(-Y, X) : new Vector2DFloat(Y, -X);
}
public Vector2DFloat Normalize()
{
float magnitude = (float)System.Math.Sqrt(X * X + Y * Y);
return new Vector2DFloat(X / magnitude, Y / magnitude);
}
public float Distance(Vector2DFloat target)
{
return (float)System.Math.Sqrt((X - target.X) * (X - target.X) + (Y - target.Y) * (Y - target.Y));
}
public float DistanceSquared(Vector2DFloat target)
{
return (X - target.X) * (X - target.X) + (Y - target.Y) * (Y - target.Y);
}
public double DistanceTo(Vector2DFloat target)
{
return System.Math.Sqrt(System.Math.Pow(target.X - X, 2F) + System.Math.Pow(target.Y - Y, 2F));
}
public System.Drawing.PointF ToPointF()
{
return new System.Drawing.PointF(X, Y);
}
public Vector2DFloat(System.Drawing.PointF value)
{
this.X = value.X;
this.Y = value.Y;
}
public static implicit operator Vector2DFloat(System.Drawing.PointF value)
{
return new Vector2DFloat(value);
}
public static Vector2DFloat operator +(Vector2DFloat first, Vector2DFloat second)
{
return new Vector2DFloat(first.X + second.X, first.Y + second.Y);
}
public static Vector2DFloat operator -(Vector2DFloat first, Vector2DFloat second)
{
return new Vector2DFloat(first.X - second.X, first.Y - second.Y);
}
public static Vector2DFloat operator *(Vector2DFloat first, float second)
{
return new Vector2DFloat(first.X * second, first.Y * second);
}
public static Vector2DFloat operator *(float first, Vector2DFloat second)
{
return new Vector2DFloat(second.X * first, second.Y * first);
}
public static Vector2DFloat operator *(Vector2DFloat first, int second)
{
return new Vector2DFloat(first.X * second, first.Y * second);
}
public static Vector2DFloat operator *(int first, Vector2DFloat second)
{
return new Vector2DFloat(second.X * first, second.Y * first);
}
public static Vector2DFloat operator *(Vector2DFloat first, double second)
{
return new Vector2DFloat((float)(first.X * second), (float)(first.Y * second));
}
public static Vector2DFloat operator *(double first, Vector2DFloat second)
{
return new Vector2DFloat((float)(second.X * first), (float)(second.Y * first));
}
public override bool Equals(object obj)
{
return this.Equals((Vector2DFloat)obj);
}
public bool Equals(Vector2DFloat p)
{
// If parameter is null, return false.
if (p == null)
{
return false;
}
// Optimization for a common success case.
if (this == p)
{
return true;
}
// If run-time types are not exactly the same, return false.
if (this.GetType() != p.GetType())
{
return false;
}
// Return true if the fields match.
// Note that the base class is not invoked because it is
// System.Object, which defines Equals as reference equality.
return (X == p.X) && (Y == p.Y);
}
public override int GetHashCode()
{
return (int)(System.Math.Round(X + Y, 4) * 10000);
}
public static bool operator ==(Vector2DFloat first, Vector2DFloat second)
{
// Check for null on left side.
if (first == null)
{
if (second == null)
{
// null == null = true.
return true;
}
// Only the left side is null.
return false;
}
// Equals handles case of null on right side.
return first.Equals(second);
}
public static bool operator !=(Vector2DFloat first, Vector2DFloat second)
{
return !(first == second);
}
}
}
namespace Beziers
{
public struct BezierCubic2D
{
public Vectors.Vector2DFloat P0;
public Vectors.Vector2DFloat P1;
public Vectors.Vector2DFloat P2;
public Vectors.Vector2DFloat P3;
public int ArcLengthDivisionCount;
public List<float> ArcLengths { get { if (_ArcLengths.Count == 0) CalculateArcLength(); return _ArcLengths; } }
public float ArcLength { get { if (_ArcLength == 0.0F) CalculateArcLength(); return _ArcLength; } }
private Vectors.Vector2DFloat A;
private Vectors.Vector2DFloat B;
private Vectors.Vector2DFloat C;
private List<float> _ArcLengths;
private float _ArcLength;
public BezierCubic2D(Vectors.Vector2DFloat p0, Vectors.Vector2DFloat p1, Vectors.Vector2DFloat p2, Vectors.Vector2DFloat p3)
{
P0 = p0;
P1 = p1;
P2 = p2;
P3 = p3;
// vt = At^3 + Bt^2 + Ct + p0
A = P3 - 3 * P2 + 3 * P1 - P0;
B = 3 * P2 - 6 * P1 + 3 * P0;
C = 3 * P1 - 3 * P0;
ArcLengthDivisionCount = 100;
_ArcLengths = new List<float>();
_ArcLength = 0.0F;
}
public BezierCubic2D(System.Drawing.PointF p0, System.Drawing.PointF p1, System.Drawing.PointF p2, System.Drawing.PointF p3)
{
P0 = p0;
P1 = p1;
P2 = p2;
P3 = p3;
// vt = At^3 + Bt^2 + Ct + p0
A = P3 - 3 * P2 + 3 * P1 - P0;
B = 3 * P2 - 6 * P1 + 3 * P0;
C = 3 * P1 - 3 * P0;
ArcLengthDivisionCount = 100;
_ArcLengths = new List<float>();
_ArcLength = 0.0F;
}
public BezierCubic2D(float p0X, float p0Y, float p1X, float p1Y, float p2X, float p2Y, float p3X, float p3Y)
{
P0 = new Vectors.Vector2DFloat(p0X, p0Y);
P1 = new Vectors.Vector2DFloat(p1X, p1Y);
P2 = new Vectors.Vector2DFloat(p2X, p2Y);
P3 = new Vectors.Vector2DFloat(p3X, p3Y);
// vt = At^3 + Bt^2 + Ct + p0
A = P3 - 3 * P2 + 3 * P1 - P0;
B = 3 * P2 - 6 * P1 + 3 * P0;
C = 3 * P1 - 3 * P0;
ArcLengthDivisionCount = 100;
_ArcLengths = new List<float>();
_ArcLength = 0.0F;
}
public Vectors.Vector2DFloat PointOnCurve(float t)
{
return A * System.Math.Pow(t, 3) + B * System.Math.Pow(t, 2) + C * t + P0;
}
public Vectors.Vector2DFloat PointOnCurveGeometric(float t)
{
Vectors.Vector2DFloat p4 = Vectors.Vector2DFloat.Lerp(P0, P1, t);
Vectors.Vector2DFloat p5 = Vectors.Vector2DFloat.Lerp(P1, P2, t);
Vectors.Vector2DFloat p6 = Vectors.Vector2DFloat.Lerp(P2, P3, t);
Vectors.Vector2DFloat p7 = Vectors.Vector2DFloat.Lerp(p4, p5, t);
Vectors.Vector2DFloat p8 = Vectors.Vector2DFloat.Lerp(p5, p6, t);
return Vectors.Vector2DFloat.Lerp(p7, p8, t);
}
public Vectors.Vector2DFloat PointOnCurveTangent(float t)
{
return 3 * A * System.Math.Pow(t, 2) + 2 * B * t + C;
}
public Vectors.Vector2DFloat PointOnCurvePerpendicular(float t, bool positiveRotation)
{
return (3 * A * System.Math.Pow(t, 2) + 2 * B * t + C).Rotate90Degrees(positiveRotation).Normalize() * 10F + PointOnCurve(t);
}
public Vectors.Vector2DFloat PointOnCurvePerpendicular(float t, bool positiveRotation, float pointHeight)
{
return (3 * A * System.Math.Pow(t, 2) + 2 * B * t + C).Rotate90Degrees(positiveRotation).Normalize() * pointHeight + PointOnCurve(t);
}
public float FindTAtPointOnBezier(float u)
{
float t;
int index = _ArcLengths.BinarySearch(u);
if (index >= 0)
t = index / (float)(_ArcLengths.Count - 1);
else if (index * -1 >= _ArcLengths.Count)
t = 1;
else if (index == 0)
t = 0;
else
{
index *= -1;
float lengthBefore = _ArcLengths[index - 1];
float lengthAfter = _ArcLengths[index];
float segmentLength = lengthAfter - lengthBefore;
float segmentFraction = (u - lengthBefore) / segmentLength;
// add that fractional amount to t
t = (index + segmentFraction) / (float)(_ArcLengths.Count - 1);
}
return t;
}
private void CalculateArcLength()
{
// calculate Arc Length through successive approximation. Use the squared version as it is faster.
_ArcLength = 0.0F;
int arrayMax = ArcLengthDivisionCount + 1;
_ArcLengths = new List<float>(arrayMax)
{
0.0F
};
Vectors.Vector2DFloat prior = P0, current;
for (int i = 1; i < arrayMax; i++)
{
current = PointOnCurve(i / (float)ArcLengthDivisionCount);
_ArcLength += current.Distance(prior);
_ArcLengths.Add(_ArcLength);
prior = current;
}
}
public override bool Equals(object obj)
{
return this.Equals((BezierCubic2D)obj);
}
public bool Equals(BezierCubic2D p)
{
// If parameter is null, return false.
if (p == null)
{
return false;
}
// Optimization for a common success case.
if (this == p)
{
return true;
}
// If run-time types are not exactly the same, return false.
if (this.GetType() != p.GetType())
{
return false;
}
// Return true if the fields match.
// Note that the base class is not invoked because it is
// System.Object, which defines Equals as reference equality.
return (P0 == p.P0) && (P1 == p.P1) && (P2 == p.P2) && (P3 == p.P3);
}
public override int GetHashCode()
{
return P0.GetHashCode() + P1.GetHashCode() + P2.GetHashCode() + P3.GetHashCode() % int.MaxValue;
}
public static bool operator ==(BezierCubic2D first, BezierCubic2D second)
{
// Check for null on left side.
if (first == null)
{
if (second == null)
{
// null == null = true.
return true;
}
// Only the left side is null.
return false;
}
// Equals handles case of null on right side.
return first.Equals(second);
}
public static bool operator !=(BezierCubic2D first, BezierCubic2D second)
{
return !(first == second);
}
}
public struct BezierSplineCubic2D
{
public BezierCubic2D[] Beziers;
public BezierCubic2D this[int index] { get { return Beziers[index]; } }
public int Length { get { return Beziers.Length; } }
public List<float> ArcLengths { get { if (_ArcLengths.Count == 0) CalculateArcLength(); return _ArcLengths; } }
public float ArcLength { get { if (_ArcLength == 0.0F) CalculateArcLength(); return _ArcLength; } }
private List<float> _ArcLengths;
private float _ArcLength;
public BezierSplineCubic2D(Vectors.Vector2DFloat[] source)
{
if (source == null || source.Length < 4 || (source.Length - 4) % 3 != 0) { Beziers = null; _ArcLength = 0.0F; _ArcLengths = new List<float>(); return; }
int length = ((source.Length - 4) / 3) + 1;
Beziers = new BezierCubic2D[length];
Beziers[0] = new BezierCubic2D(source[0], source[1], source[2], source[3]);
for (int i = 1; i < length; i++)
Beziers[i] = new BezierCubic2D(source[(i * 3)], source[(i * 3) + 1], source[(i * 3) + 2], source[(i * 3) + 3]);
_ArcLength = 0.0F;
_ArcLengths = new List<float>();
}
public BezierSplineCubic2D(System.Drawing.PointF[] source)
{
if (source == null || source.Length < 4 || (source.Length - 4) % 3 != 0) { Beziers = null; _ArcLength = 0.0F; _ArcLengths = new List<float>(); return; }
int length = ((source.Length - 4) / 3) + 1;
Beziers = new BezierCubic2D[length];
Beziers[0] = new BezierCubic2D(source[0], source[1], source[2], source[3]);
for (int i = 1; i < length; i++)
Beziers[i] = new BezierCubic2D(source[(i * 3)], source[(i * 3) + 1], source[(i * 3) + 2], source[(i * 3) + 3]);
_ArcLength = 0.0F;
_ArcLengths = new List<float>();
}
public BezierSplineCubic2D(System.Drawing.Point[] source)
{
if (source == null || source.Length < 4 || (source.Length - 4) % 3 != 0) { Beziers = null; _ArcLength = 0.0F; _ArcLengths = new List<float>(); return; }
int length = ((source.Length - 4) / 3) + 1;
Beziers = new BezierCubic2D[length];
Beziers[0] = new BezierCubic2D(source[0], source[1], source[2], source[3]);
for (int i = 1; i < length; i++)
Beziers[i] = new BezierCubic2D(source[(i * 3)], source[(i * 3) + 1], source[(i * 3) + 2], source[(i * 3) + 3]);
_ArcLength = 0.0F;
_ArcLengths = new List<float>();
}
public bool FindTAtPointOnSpline(float distanceAlongSpline, out BezierCubic2D bezier, out float t)
{
// to do: cache last distance and bezier. if new distance > old then start from old bezier.
if (distanceAlongSpline > ArcLength) { bezier = Beziers[Beziers.Length - 1]; t = distanceAlongSpline / ArcLength; return false; }
if (distanceAlongSpline <= 0.0F)
{
bezier = Beziers[0];
t = 0.0F;
return true;
}
for (int i = 0; i < Beziers.Length; i++)
{
float distanceRemainingBeyondCurrentBezier = distanceAlongSpline - Beziers[i].ArcLength;
if (distanceRemainingBeyondCurrentBezier < 0.0F)
{
// t is in current bezier.
bezier = Beziers[i];
t = bezier.FindTAtPointOnBezier(distanceAlongSpline);
return true;
}
else if (distanceRemainingBeyondCurrentBezier == 0.0F)
{
// t is 1.0F. Bezier is current one.
bezier = Beziers[i];
t = 1.0F;
return true;
}
// reduce the distance by the length of the bezier.
distanceAlongSpline -= Beziers[i].ArcLength;
}
// point is outside the spline.
bezier = new BezierCubic2D();
t = 0.0F;
return false;
}
public void BendShapeToSpline(System.Drawing.RectangleF bounds, int dataLength, ref System.Drawing.PointF[] data, ref byte[] dataTypes)
{
System.Drawing.PointF pt;
// move the origin for the data to 0,0
float left = bounds.Left, height = bounds.Y + bounds.Height;
for (int i = 0; i < dataLength; i++)
{
pt = data[i];
float textX = pt.X - left;
float textY = pt.Y - height;
if (FindTAtPointOnSpline(textX, out BezierCubic2D bezier, out float t))
{
data[i] = bezier.PointOnCurvePerpendicular(t, true, textY).ToPointF();
}
else
{
// roll back all points until we reach curvedTypes[i] == 0
for (int j = i - 1; j > -1; j--)
{
if ((dataTypes[j] & 0x80) == 0x80)
{
System.Drawing.PointF[] temp1 = new System.Drawing.PointF[j + 1];
Array.Copy(data, 0, temp1, 0, j + 1);
byte[] temp2 = new byte[j + 1];
Array.Copy(dataTypes, 0, temp2, 0, j + 1);
data = temp1;
dataTypes = temp2;
break;
}
}
break;
}
}
}
private void CalculateArcLength()
{
_ArcLength = 0.0F;
_ArcLengths = new List<float>(Beziers.Length);
for (int i = 0; i < Beziers.Length; i++)
{
_ArcLength += Beziers[i].ArcLength;
_ArcLengths.Add(_ArcLength);
}
}
internal static System.Drawing.PointF[] GetCurveTangents(System.Drawing.Point[] points, int count, float tension, int curveType)
{
if (points == null)
throw new ArgumentNullException("points");
System.Drawing.PointF[] pointfs = new System.Drawing.PointF[count];
for (int p = 0; p < count; p++)
{
pointfs[p] = new System.Drawing.PointF(points[p].X, points[p].Y);
}
return GetCurveTangents(pointfs, count, tension, curveType);
}
internal static System.Drawing.PointF[] GetCurveTangents(System.Drawing.PointF[] points, int count, float tension, int curveType)
{
float coefficient = tension / 3f;
System.Drawing.PointF[] tangents = new System.Drawing.PointF[count];
if (count < 2)
return tangents;
for (int i = 0; i < count; i++)
{
int r = i + 1;
int s = i - 1;
if (r >= count)
r = count - 1;
if (curveType == 0) // 0 == CurveType.Open
{
if (s < 0)
s = 0;
}
else // 1 == CurveType.Closed, end point jumps to start point
{
if (s < 0)
s += count;
}
tangents[i].X += (coefficient * (points[r].X - points[s].X));
tangents[i].Y += (coefficient * (points[r].Y - points[s].Y));
}
return tangents;
}
internal static System.Drawing.PointF[] CreateCurve(System.Drawing.Point[] points, int offset, int length, int curveType)
{
if (points == null)
throw new ArgumentNullException("points");
System.Drawing.PointF[] pointfs = new System.Drawing.PointF[length];
for (int p = 0; p < length; p++)
{
pointfs[p] = new System.Drawing.PointF(points[p].X, points[p].Y);
}
System.Drawing.PointF[] tangents = GetCurveTangents(pointfs, length, 0.5F, 0);
return CreateCurve(pointfs, tangents, offset, length, curveType);
}
internal static System.Drawing.PointF[] CreateCurve(System.Drawing.Point[] points, System.Drawing.PointF[] tangents, int offset, int length, int curveType)
{
if (points == null)
throw new ArgumentNullException("points");
System.Drawing.PointF[] pointfs = new System.Drawing.PointF[length];
for (int p = 0; p < length; p++)
{
pointfs[p] = new System.Drawing.PointF(points[p].X, points[p].Y);
}
return CreateCurve(pointfs, tangents, offset, length, curveType);
}
internal static System.Drawing.PointF[] CreateCurve(System.Drawing.PointF[] points, System.Drawing.PointF[] tangents, int offset, int length, int curveType)
{
List<System.Drawing.PointF> curve = new List<System.Drawing.PointF>();
int i;
Append(curve, points[offset].X, points[offset].Y, true);
for (i = offset; i < offset + length - 1; i++)
{
int j = i + 1;
float x1 = points[i].X + tangents[i].X;
float y1 = points[i].Y + tangents[i].Y;
float x2 = points[j].X - tangents[j].X;
float y2 = points[j].Y - tangents[j].Y;
float x3 = points[j].X;
float y3 = points[j].Y;
AppendBezier(curve, x1, y1, x2, y2, x3, y3, false);
}
return curve.ToArray<System.Drawing.PointF>();
}
internal static void Append(List<System.Drawing.PointF> points, float x, float y, bool compress)
{
System.Drawing.PointF pt = System.Drawing.PointF.Empty;
/* in some case we're allowed to compress identical points */
if (compress && (points.Count > 0))
{
/* points (X, Y) must be identical */
System.Drawing.PointF lastPoint = points[points.Count - 1];
if ((lastPoint.X == x) && (lastPoint.Y == y))
{
return;
}
}
pt.X = x;
pt.Y = y;
points.Add(pt);
}
internal static void AppendBezier(List<System.Drawing.PointF> points, float x1, float y1, float x2, float y2, float x3, float y3, bool isReverseWindingOnFill)
{
if (isReverseWindingOnFill)
{
Append(points, y1, x1, false);
Append(points, y2, x2, false);
Append(points, y3, x3, false);
}
else
{
Append(points, x1, y1, false);
Append(points, x2, y2, false);
Append(points, x3, y3, false);
}
}
}
}
}
I think the only way is to render each character individually and use the
Graphics.RotateTransform
to rotate the text. You'll need to work out the rotation angle and rendering offset yourself. You can use the
Graphics.MeasureCharacterRanges
to get the size of each character.
Unfortunatelly in GDI+ there is no way to attach Strings to a path (this is what you would be looking for).
So the only way to do this is doing it "by hand". That means splitting up the string into characters and placing them based on your own path calculations.
Unless you want to put a lot of work into this you should try to find a library (potentially complete GDI+ replacement) to do this or give up on your rainbow.
With WPF you can render text on a path (see link for a howto)

Categories