How to calculate the orientation of a shape in wpf - c#

The code snippet below generates a curve line and arrow at the end of the path, as seen in the image below in wpf using C#. All you have to do is supply the function a starting and end point. You'll see in the image below that the Red Arrow on the left is incorrectly aligned to the path. The Red Arrow on the right is aligned correctly, however I'm not sure how to calculate the correct orientation? Does anyone have advice or a solution on how i could do this? The green points in the image demonstrate the points being generated in the function.
Thank you.
// Return the shape's path and arrow geometry.
protected override Geometry DefiningGeometry
{
get
{
GeometryGroup group = new GeometryGroup();
// Used for curvey lines
GenerateCurvedLine(group);
//GeneratedCurvedArrow(group);
// Used for straight lines
//GeneratedStraightLine(group);
GenerateArrowHeadGeometry(group);
// Return cached geometry.
return group;
}
}
// Generate the geometry for a curved line connecting the start and end points.
private void GenerateCurvedLine(GeometryGroup geometryGroup)
{
//Calculate points between start and end plugs
Point secondPoint = new Point(this.Start.X, this.Start.Y + 50);
Point thirdPoint = new Point(this.End.X, this.End.Y - 50);
// Build geometry for the curvey line.
PathFigure curvedPath = new PathFigure();
curvedPath.IsClosed = false;
curvedPath.IsFilled = false;
curvedPath.StartPoint = this.Start;
curvedPath.Segments.Add(new BezierSegment(secondPoint, thirdPoint, this.End, true));
PathGeometry pathGeometry = new PathGeometry();
pathGeometry.Figures.Add(curvedPath);
geometryGroup.Children.Add(pathGeometry);
}
Generates the Arrow at the end of the path, while orienting it towards the starting point.
// Generate the geometry for the three optional arrow symbol at the end of the path.
private void GenerateArrowHeadGeometry(GeometryGroup geometryGroup)
{
EllipseGeometry ellipse = new EllipseGeometry(this.Start, DotSize, DotSize);
geometryGroup.Children.Add(ellipse);
Vector startDir = this.End - this.Start;
startDir.Normalize();
Point basePoint = this.End - (startDir * ArrowHeadLength);
Vector crossDir = new Vector(-startDir.Y, startDir.X);
Point[] arrowHeadPoints = new Point[3];
arrowHeadPoints[0] = this.End;
arrowHeadPoints[1] = basePoint - (crossDir * (ArrowHeadWidth / 2));
arrowHeadPoints[2] = basePoint + (crossDir * (ArrowHeadWidth / 2));
// Build geometry for the arrow head.
PathFigure arrowHeadFig = new PathFigure();
arrowHeadFig.IsClosed = true;
arrowHeadFig.IsFilled = true;
arrowHeadFig.StartPoint = arrowHeadPoints[1];
arrowHeadFig.Segments.Add(new LineSegment(arrowHeadPoints[0], true));
arrowHeadFig.Segments.Add(new LineSegment(arrowHeadPoints[2], true));
PathGeometry pathGeometry = new PathGeometry();
pathGeometry.Figures.Add(arrowHeadFig);
geometryGroup.Children.Add(pathGeometry);
}

DISCLAIMER: I'm NOT a math expert... so this could be all wrong.
You need to figure out the slope of the curve towards the end of the line. If you use the parametric equation for a Bezier curve at time t=0.95 (you may need to adjust this value) and then again at time t=1.0 (which is just this.End) and then subtract them, you will have what you need (the slope of the curve at the end).
To calculate it, see Quadratic Bezier Curve: Calculate Point - you'll want the cubic answer (2nd answer).
In GenerateCurvedLine, calculate the arrow direction like this:
var t = 0.95;
var x1 = (1 - t) * (1 - t) * (1 - t) * this.Start.X
+ 3 * (1 - t) * (1 - t) * t * secondPoint.X
+ 3 * (1 - t) * t * t * thirdPoint.X
+ t * t * t * this.End.X;
var y1 = (1 - t) * (1 - t) * (1 - t) * this.Start.Y
+ 3 * (1 - t) * (1 - t) * t * secondPoint.Y
+ 3 * (1 - t) * t * t * thirdPoint.Y
+ t * t * t * this.End.Y;
arrowDir = new Vector(this.End.X - x1, this.End.Y - y1);
In GenerateArrowHeadGeometry, use the arrowDir above instead of your startDir.
FYI - t (time) ranges from 0 to 1 for points on your curve. 0 will be this.Start and 1 will be this.End. So, t=0.95 would be towards the end of the curve.

Related

Rotate Point around pivot Point repeatedly

For a while now I've been using the following function to rotate a series of Points around a pivot point in various programs of mine.
private Point RotatePoint(Point point, Point pivot, double radians)
{
var cosTheta = Math.Cos(radians);
var sinTheta = Math.Sin(radians);
var x = (cosTheta * (point.X - pivot.X) - sinTheta * (point.Y - pivot.Y) + pivot.X);
var y = (sinTheta * (point.X - pivot.X) + cosTheta * (point.Y - pivot.Y) + pivot.Y);
return new Point((int)x, (int)y);
}
This has always worked great, until I tried to rotate a shape repeatedly by small amounts. For example, this is what I get from calling it for 45° on a rectangular polygon made up of 4 points:
foreach (var point in points)
Rotate(point, center, Math.PI / 180f * 45);
But this is what I get by calling rotate 45 times for 1°:
for (var i = 0; i < 45; ++i)
foreach (var point in points)
Rotate(point, center, Math.PI / 180f * 1)
As long as I call it only once it's fine, and it also seems like it gets gradually worse the lower the rotation degree is. Is there some flaw in the function, or am I misunderstanding something fundamental about what this function does?
How could I rotate repeatedly by small amounts? I could save the base points and use them to update the current points whenever the rotation changes, but is that the only way?
Your Point position measure is off because of the integer rounding generated by the RotatePoint() method.
A simple correction in the method returned value, using float coordinates, will produce the correct measure:
To test it, create a Timer and register its Tick event as RotateTimerTick():
Added a rotation spin increment (see the rotationSpin Field) to emphasize the motion effect.
PointF pivotPoint = new PointF(100F, 100F);
PointF rotatingPoint = new PointF(50F, 100F);
double rotationSpin = 0D;
private PointF RotatePoint(PointF point, PointF pivot, double radians)
{
var cosTheta = Math.Cos(radians);
var sinTheta = Math.Sin(radians);
var x = (cosTheta * (point.X - pivot.X) - sinTheta * (point.Y - pivot.Y) + pivot.X);
var y = (sinTheta * (point.X - pivot.X) + cosTheta * (point.Y - pivot.Y) + pivot.Y);
return new PointF((float)x, (float)y);
}
private void RotateTimerTick(object sender, EventArgs e)
{
rotationSpin += .5;
if (rotationSpin > 90) rotationSpin = 0;
rotatingPoint = RotatePoint(rotatingPoint, pivotPoint, (Math.PI / 180f) * rotationSpin);
Panel1.Invalidate(new Rectangle(new Point(50,50), new Size(110, 110)));
}
private void Panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.FillEllipse(Brushes.White, new RectangleF(100, 100, 8, 8));
e.Graphics.FillEllipse(Brushes.Yellow, new RectangleF(rotatingPoint, new SizeF(8, 8)));
}
This is the result using float values:
And this is what happens using integer values:
If you want you can use the Media3D to only deal with matrix and simplify the coding. Something as simple as this will work.
public Point3D Rotate(Point3D point, Point3D rotationCenter, Vector3D rotation, double degree)
{
// create empty matrix
var matrix = new Matrix3D();
// translate matrix to rotation point
matrix.Translate(rotationCenter - new Point3D());
// rotate it the way we need
matrix.Rotate(new Quaternion(rotation, degree));
// apply the matrix to our point
point = matrix.Transform(point);
return point;
}
Then you simply call the method and specify the rotation. Lets say you work with 2D (like in your example) and lets assume we work with XY plane so the rotation is in Z. You can do something like :
var rotationPoint = new Point3D(0, 0, 0);
var currentPoint = new Point3D(10, 0, 0);
// rotate the current point around the rotation point in Z by 45 degree
var newPoint = Rotate(currentPoint, rotation, new Vector3D(0, 0, 1), 45d);

Detect Pushpins Inside a VE Shape on a Bing Map

I have a map which populates pushpins from a database. And a circle. I want to be able to figure out what other pushpins are within the VE Shape circles radius and create a list of their addresses.
function addPushPin(lat, long) {
//var point = new Microsoft.Maps.Point(e.getX(), e.getY());
//var pushpinLocation = new(lat, long);
var s = new VEShape(VEShapeType.Pushpin, new VELatLong(lat,long));
var customIcon = new VECustomIconSpecification();
//customIcon.TextContent = "yo bana4na4";
customIcon.Image = "http://universidadescancun.com/wp-content/themes/GeoUnis3/images/pinpoint-fiesta.png";
//customIcon.ImageOffset = new VEPixel();
s.SetTitle("First Pin <span style=color:red>Demo Title<>/span>");
s.SetDescription("This comes to the <b>description</b> of pushpin.");
s.SetMoreInfoURL("http://dotnetricks.blogspot.com");
s.SetPhotoURL("http://upload.wikimedia.org/wikipedia/commons/8/8a/Banana-Single.jpg");
s.SetCustomIcon(customIcon);
s.SetIconAnchor(new VELatLong(lat, long));
map.AddShape(s);
}
I also have my map populate a circle around one of these pushpins. It is populated like so.
function getCircleNow(lat, long) {
var latin = lat;
var lonin = long;
var latlong = new VELatLong(latin, lonin);
centerPoint = (latlong);
//var center = map.GetCenter().toString();
//alert(center);
//Get initial circle points
var circlePoints = buildCircle(centerPoint.Latitude, centerPoint.Longitude, 50.74);
//Build circle
circle = new VEShape(VEShapeType.Polygon, circlePoints);
circle.HideIcon();
circle.SetLineWidth(2);
map.AddShape(circle);
//Build mask
circleMask = new VEShape(VEShapeType.Polygon, circlePoints);
circleMask.HideIcon();
circleMask.Hide();
circleMask.SetLineColor(new VEColor(0, 0, 0, 0.5));
circleMask.SetFillColor(new VEColor(0, 0, 0, 0.0));
circleMask.Primitives[0].symbol.stroke_dashstyle = "Dash";
map.AddShape(circleMask);
}
And here is the buildCircle() function it calls.
function buildCircle(latin, lonin, radius) {
var locs = new Array();
var lat1 = latin * Math.PI / 180.0;
var lon1 = lonin * Math.PI / 180.0;
var d = radius / 3956;
var x;
for (x = 0; x <= 360; x += 10) {
var tc = (x / 90) * Math.PI / 2;
var lat = Math.asin(Math.sin(lat1) * Math.cos(d) + Math.cos(lat1) * Math.sin(d) * Math.cos(tc));
lat = 180.0 * lat / Math.PI;
var lon;
if (Math.cos(lat1) == 0) {
lon = lonin; // endpoint a pole
}
else {
lon = ((lon1 - Math.asin(Math.sin(tc) * Math.sin(d) / Math.cos(lat1)) + Math.PI) % (2 * Math.PI)) - Math.PI;
}
lon = 180.0 * lon / Math.PI;
var loc = new VELatLong(lat, lon);
locs.push(loc);
}
return locs;
}
This is what my map looks like after I have added the pushpins and I have populated the circle on the map, with it centered around one of the locations and with a 50 mile radius.
WHAT I WANT TO BE ABLE TO DO
I want to be able to use either JavaScript or C# to detect what other pushpins, besides the one the circle is centered around, are within the radius of the center location. Or more simply, what other locations/pushpins are in the circle, and populate a list with values of each address. I can't think of any special methods that can do this so any help would be appreciated. This is Bing maps That I'm using.
This is fairly easy to do. Take the center point and radius of the circle do the following:
Loop through all pushpins and measure their distance from the center point using the Haversine formula: http://rbrundritt.wordpress.com/2008/10/14/calculate-distance-between-two-coordinates/
If the distance is less than or equal to the circles radius then the pushpin is inside the circle.
That's it. I also wrote an MSDN article on this for v6 of Bing maps a long time ago but it looks like it has been taken offline, along with all the documentation for v6.3 of Bing maps.
With all this said, its worth pointing out that Bing Maps V6.3 is really old and was last updated about 5 or 6 years ago. It's not recommended to do any new development on it but to instead make use of version 7 which is a lot more powerful, has more features, and is significantly smaller in terms of download size. I have a migration guide here: http://social.technet.microsoft.com/wiki/contents/articles/20958.migrating-bing-maps-v6-3-to-v7.aspx

Draw 2D trail of ship. XNA

I've been looking for a solution to this for some time now and already have many elements to work with but not really how to piece them together.
Objective: Draw a trail for the player's ship.
So far: Since the ship's direction is unpredictable I have only the previous positions of the player's ship to work with. To draw the trail I could simply draw a pixel (or a texture) at the previous position of the player but this is memory expensive and it doesn't draw curves, it won't achieve a pleasing to the eye curved effect.
I've been looking into Beziers Paths and Cathmull Rom for solutions.
Now I can get the control points for a given point, then from 2 points and 2 control points calculate a curve, from here I make an array of VertexPositionColor with a distance between points to make a triangleStrip from the curve.
These are the main functions I have so far:
public Vector2[] GetControlPoints(Vector2 p0, Vector2 p1, Vector2 p2, float tension = 0.5f)
{
// get length of lines [p0-p1] and [p1-p2]
float d01 = Vector2.Distance(p0, p1);
float d12 = Vector2.Distance(p1, p2);
// calculate scaling factors as fractions of total
float sa = tension * d01 / (d01 + d12);
float sb = tension * d12 / (d01 + d12);
// left control point
float c1x = p1.X - sa * (p2.X - p0.X);
float c1y = p1.Y - sa * (p2.Y - p0.Y);
// right control point
float c2x = p1.X + sb * (p2.X - p0.X);
float c2y = p1.Y + sb * (p2.Y - p0.Y);
return new Vector2[] {new Vector2(c1x, c1y), new Vector2(c2x, c2y) };
}
// Given 2 points and 2 control points
public static VertexPositionColor[] bezierCurve(Vector2 start, Vector2 end, Vector2 c1, Vector2 c2)
{
VertexPositionColor[] points = new VertexPositionColor[SUBDIVISIONS + 2];
float fraction;
for (int i = 0; i < SUBDIVISIONS + 2; i++)
{
fraction = i * (1f / (float)SUBDIVISIONS);
points[i] = new VertexPositionColor(new Vector3((float)((start.X * Math.Pow((1 - fraction), 3))
+(c1.X * 3 * fraction * Math.Pow(1-fraction, 2))
+(c2.X * 3 * Math.Pow(fraction,2) * (1-fraction))
+(end.X * Math.Pow(fraction,3))),
(float)((start.Y * Math.Pow((1 - fraction), 3))
+ (c1.Y * 3 * fraction * Math.Pow(1 - fraction, 2))
+ (c2.Y * 3 * Math.Pow(fraction, 2) * (1 - fraction))
+ (end.Y * Math.Pow(fraction, 3))), 0), UNLIT);
}
return points;
}
/*
* This function treats the curve as a series of straight lines and calculates points on a line perpendicular to each point, resulting in two points THICKNESS appart.
* Requires THICKNESS to be set
*/
public static VertexPositionColor[] curveToStrip(VertexPositionColor[] curve)
{
VertexPositionColor[] strip = new VertexPositionColor[curve.Length * 2];
VertexPositionColor[] new1 = new VertexPositionColor[curve.Length];
VertexPositionColor[] new2 = new VertexPositionColor[curve.Length];
for (int i = 0; i < curve.Length; i++)
{
if (i < curve.Length-1)
{
Vector2 p1 = new Vector2(curve[i].Position.X, curve[i].Position.Y);
Vector2 p2 = new Vector2(curve[i + 1].Position.X, curve[i + 1].Position.Y);
Vector2 perpPoint = perpendicularPoint(p1, p2);
new1[i] = new VertexPositionColor(new Vector3(distanceToPoint(p1, perpPoint, THICKNESS / 2), 0), UNLIT);
new2[i] = new VertexPositionColor(new Vector3(distanceToPoint(p1, perpPoint, -1 * THICKNESS / 2), 0), UNLIT);
}
else
{
Vector2 p1 = new Vector2(curve[i].Position.X, curve[i].Position.Y);
Vector2 p2 = new Vector2(curve[i - 1].Position.X, curve[i - 1].Position.Y);
Vector2 perpPoint = perpendicularPoint(p1, p2);
new1[i] = new VertexPositionColor(new Vector3(distanceToPoint(p1, perpPoint, -1 * THICKNESS / 2), 0), UNLIT);
new2[i] = new VertexPositionColor(new Vector3(distanceToPoint(p1, perpPoint, THICKNESS / 2), 0), UNLIT);
}
}
I thought about calling the functions on the draw phase but this seems very expensive just to make a tiny curve and to draw a bigger Beziers path I imagine it worse. Since I would get a point at each frame, each function would be called to calculate the curve between points just to draw 1 curve of 3 pixels (or less).
How can I proceed? Any suggestions?
I am still a beginner on this kind of stuff!
All this I got from several sources:
CathmullRom
Beziers and Triangle strip
http://www.imagehosting.cz/images/trails.gif
I will just briefly explain how this works:
It is function that receives position, it is called each time you want add next segment of trail.
When function is called it adds two vertices on position, look at tangent vector from previous step, creates normal vector to current position and place vertices along that normal according to trail width.
Next it looks to at previous two vertexes and align them according to average of current and previous tangent, creating trapezoid.
I suggest to leave calculation of fading on GPU (effectively using approach of GPU particles).
If you know velocity vector of object when you are calling update of trail you can use it to optimize that algorithm. Use of dynamic vertex buffer is probably without saying (just use your own vertex format that will include current time at moment when you create those vertices so you can fade it on GPU).
One way could be that you create a list of trails/particles, and you init that on every frame or how much you like. i will try to explain in pseudo code below. i also rotate a bit every trail, and use different size and color of smoke texture, and added a bit of ofsset +- 5 pixels on init.
class Trail
position as vector2d
duration as single
velocity as vector2d
fade as integer = 1
active = true
end class
class Trails
inherits list of Trail
sub Init(position as vector2d, velocity as vector2d)
// add trail to list
end sub
sub Update()
for each trail in me.findAll(function(c) c.active))
position += velocity
fade -= .05 // or some value
end for
me.removeAll(function(c) not(c.active)) // remove from list when unused
end sub
sub Draw()
for each trail in me.findAll(function(c) c.active))
draw(smokeTexture, position, size, rotate, color * trail.fade)
end for
end sub
end class
by this i have achieved this effect, it's barely visible but it gives effect.

C# WPF Draw equilateral polygons programatically

I'm working on a method to programatically draw equilateral polygon shapes in C#, WPF. But I have been stuck and I can't solve calculating the angles. Where is the problem? How should I correct this? I have given the public int, R(radius) a value of 100.
private Path EquilateralPolygon(int sides)
{
//Centering
Point center = new Point(canvasSize.Width / 2, canvasSize.Height / 2);
PathFigure myPathFigure = new PathFigure();
int alfa = 360 / sides;
int[] angles = new int[6];
for (int i = 0; i < sides; i++)
angles[i] = 360 - alfa * i;
MessageBox.Show(angles.Sum().ToString());
Point A = new Point(center.X, center.Y - R);
myPathFigure.StartPoint = A;
PolyLineSegment myLineSegment = new PolyLineSegment();
for (int i = 1; i < sides; i++)
{
myLineSegment.Points.Add(new Point(center.X + Math.Cos(angles[i]) * R, center.Y + Math.Sin(angles[i]) * R));
}
myLineSegment.Points.Add(A);
PathSegmentCollection myPathSegmentCollection = new PathSegmentCollection();
myPathSegmentCollection.Add(myLineSegment);
myPathFigure.Segments = myPathSegmentCollection;
PathFigureCollection myPathFigureCollection = new PathFigureCollection();
myPathFigureCollection.Add(myPathFigure);
PathGeometry myPathGeometry = new PathGeometry();
myPathGeometry.Figures = myPathFigureCollection;
Path myPath = new Path();
myPath.Stroke = Brushes.Red;
myPath.StrokeThickness = 1;
myPath.Data = myPathGeometry;
return myPath;
}
You've posted a lot of code and were not specific about how it's not working, so there may be more than one issue with your code. However, one big issue is that the Math.Cos (and related trig methods) take the angle in the form of radians, not degrees as you have them.
Parameters
d
Type: System.Double An angle, measured in radians.
You will need to convert them to radians. To convert, multiply by π (available via Math.PI) then divide by 180 degrees.
myLineSegment.Points.Add(
new Point(center.X + Math.Cos(angles[i] * Math.PI / 180.0) * R,
center.Y + Math.Sin(angles[i] * Math.PI / 180) * R));
EDIT: In addition to the radians/degrees issue, I can see you may be experiencing integer truncation, both in the use of your angles array and your calculation of alfa. I would suggest you try changing your use of integers to double so that your code works fine with fractions of a degree.

Detecting light projections and intersections in 2D space using C#

A light source is an entity in 2D space that sits in a single coordinate.
There are multiple light sources around in various locations and each gives off 8 rays of light in directions N, S, E, W, NW, NE, SW, SE. The coordinates of all lights are known.
I need to calculate all intersections of these rays within the grid.
long width = int.MaxValue; // 2D grid width.
long height = int.MaxValue * 3; // 2D grid height.
List<Point> lights = a bunch of randomly placed light sources.
List<Point> intersections = calculate all possible intersections.
for (int i=0; i < lights.Count - 1; i++)
{
for (int j=i + 1; j < lights.Count; j++)
{
// How to compare using integers only?
// If that is not possible, what is the fastest alternative?
}
}
My answer is based off of your comment on a linked question: is there also an easy way to determine at which coordinates diagonal rays of light intersect each other for two given points? It looks like you want to determine the points of the intersection for the rays given by the light sources.
From what you have already described, the horizontal/vertical cases are easy. The points between the two sources describe the intersection. The diagonal cases are more tricky, and I think the easiest way to approach it is just calculating line intersections.
You can describe each diagonal/anti-diagonal as a line described by a vector equation ray = s + u * d where s is the position of the light source and d is the direction of the ray (either [1, 1] ,[1, -1], [1, 0], or [0, 1]). You have four of such equations for each source, one for each direction. Now, to find the intersection of the diagonal, just find the intersection of the non-parallel lines for the two sources (one pair will be parallel, and so cannot intersection).
Sorry if this isn't clear, I'll try to update this.
Update
As a simple optimization, rays intersect diagonally if and only if the rectilinear distance (|x1 - x2| + |y1 - y2|) between the sources is even. I think there's other conditions that help to simplify your case.
Here's a derivation to find the equations you need. We start with two rays:
ray1 = s1 + u1 * d1
ray2 = s2 + u2 * d2
In cartesian coordinates:
ray1x = s1x + u1 * d1x
ray1y = s1y + u1 * d1y
ray2x = s2x + u2 * d2x
ray2y = s2y + u2 * d2y
At the intersection, ray1x = ray2x and ray1y = ray2y:
s1x + u1 * d1x = s2x + u2 * d2x
s1y + u1 * d1y = s2y + u2 * d2y
To make things easier, we can isolate and eliminate u2:
u2 = (s1x - s2x + u1 * d1x) / d2x
u2 = (s1y - s2y + u1 * d1y) / d2y
(s1x - s2x + u1 * d1x) / d2x = (s1y - s2y + u1 * d1y) / d2y
(s1x - s2x + u1 * d1x) * d2y = (s1y - s2y + u1 * d1y) * d2x
Then solve for u1:
(s1x - s2x) * d2y + u1 * d1x * d2y = (s1y - s2y) * d2x + u1 * d1y * d2x
u1 * (d1x * d2y - d1y * d2x) = (s1y - s2y) * d2x - (s1x - s2x) * d2y
u1 = ((s1y - s2y) * d2x - (s1x - s2x) * d2y) / (d1x * d2y - d1y * d2x)
To find u2 you can just evaluate one of equations above or use:
u2 = ((s2y - s1y) * d1x - (s2x - s1x) * d1y) / (d2x * d1y - d2y * d1x)
So there you have it. Two equations to solve for u1 and u2 given the source locations s1, s2 and ray directions d1, d2. You just plug in u1 and u2 values into the original ray equations and you have the intersections for one pair. In your case, an intersection exists if and only if u1 and u2 are integers. There's one case where a division by zero occurs, when the directions are [1, 0] and [0, 1], but that case is trivial to solve (the non-zero coordinates of the sources form the coordinates of the intersection).
Assuming you have a fixed coordinate plane size, and you will be doing these calculation many times for light sources in different positions, you can do better than iterating over every point.
You can create four bool (or bit) Arrays.
Horiz
Verti
DiagR
DiagL
and for each of our light sources, we 'project' them onto those 1-dimensional arrays.
(in the picture I only show two of the projections).
Projecting onto the Horiz and Verti are simple.
Projecting a point (x,y) on the DiagR array shown in the picture is as easy as x plus y.
Now you could simply walk over all of the grid points, and see if at least 2 of its projections are set to true.
But we can do better,
For instance, in the example we can start by walking over the Verti array.
We notice that Verti[0] is set to true, now we want to see if it intersects with Horiz, DiagR, DiagL.
We compute that to check for an intersection with DiagR (the other array in our picture) we only need to see if DiagR[0], DiagR[1], DiagR[2], and DiagR[3] are true, and we can ignore the rest of that array.
The light of Verti[0] can intersect with horiz at any of its elements.
The light of Verti[0] can intersect with DiagL only at DiagL positions 0,1,2, and 3.
Continue for the rest of Verti[i].
We can now do something similar looking for intersections from the true Horiz[i]'s with DiagR and DiagL.
Lastly, we walk over DiagR and look for intersections with DiagL.
This will return you a list of all intersection points of rays, but which also includes the points of the light sources.
You could either just ignore all intersection points that occur where there are point sources, or use some ingenuity to account for those points.
I've lifted the maths from here,
Okay so each point has 4 "cardinal rays", a ray being a infinite line that passes between two points.
// A line in the form Ax+By=C from 2 points
public struct Ray
{
public readonly float A;
public readonly float B;
public readonly float C;
public Ray(PointF one, PointF two)
{
this.A = two.y - one.y;
this.B = one.x - two.x;
this.C = (this.A * one.x) + (this.B * two.x);
}
}
To get the cardinals we can extend PointF
private readonly SizeF NS = new SizeF(0.0F, 1.0F);
private readonly SizeF EW = new SizeF(1.0F, 0.0F);
private readonly SizeF NESW = new SizeF(1.0F, 1.0F);
private readonly SizeF NWSE = new SizeF(-1.0F, 1.0F);
public static IEnumerable<Ray> GetCardinals(this PointF point)
{
yield return new Ray(point + NS, point - NS);
yield return new Ray(point + EW, point - EW);
yield return new Ray(point + NESW, point - NESW);
yield return new Ray(point + NWSE, point - NWSE);
}
To find the inersection of two rays we can do
static PointF Intersection(Ray one, Ray two)
{
var delta = (one.A * two.B) - (two.A * one.B);
if (delta == 0.0F)
{
//Lines are parallel
return PointF.Empty;
}
else
{
var x = ((two.B * one.C) - (one.B * two.C)) / delta;
var y = ((one.A * two.C) - (two.A * one.C)) / delta;
return new PointF(x, y);
}
}
So, to get the intersections for the cardinals of two points,
public static IEnumerable<PointF> GetCardinalIntersections(
this PointF point,
PointF other);
{
return point.GetCardianls().SelectMany(other.GetCardinals(), Intersection)
.Where(i => !i.IsEmpty());
}
Which then enables,
public static IEnumerable<PointF> GetCardinalIntersections(
this PointF point,
IEnumerable<PointF> others);
{
return others.SelectMany((o) => point.GetCardinalIntersections(o));
}
We can then use this functionality like this.
var point = new PointF(1.0F, 1.0F);
var others = new [] { new PointF(2.0F, 5.0F), new PointF(-13.0F, 32.0F) };
var intersections = point.GetCardinalIntersections(others);
Obviously there is lots of iteration here, I haven't compiled or tested this but since, at its nub, the maths seems fairly efficient I'm optimistic about performance.

Categories