I am looking for an algorithm in C# that will take a polyline (i.e. a line with nodes/corners) and, given a width/buffer, generate a polygon. In other words, if you picture a polyline with a certain width, I'd like to get the bounding polygon that fits around that polyline.
The polyline is simply a list of (x,y) coordinates which I'd like to input into the algorith, together with a certain width, and have it spit out a list of (x,y) coordinates describing the nodes/corners of the polygon.
So something like:
point[] PolylineToPolygon(Point[] points, double width)
{
// algorithm
}
void Convert()
{
Point[] linePoints = new Point[] { new Point { X = -25.125675, Y = 28.434342 }, new Point { X = -26.232687, Y = 29.958363 }, new Point { X = -24.554377, Y = 26.445767 } };
point[] polygonPoints = PolylineToPolygon(linePoints, 0.003);
}
From what I've read, I need to use the Minkowski algorithm but I cannot find an implementation in C#, nor am I sure exactly which Minkowski algorithm to use...
Any help or pointers would be greatly appreciated!
You can use lib NetTopologySuite. See github.com/NetTopologySuite/NetTopologySuite Here you can buffer your polyline and the resulting geometry will be a polygon.
Your code would be something like this:
LineString ls = new LineString(new Coordinate[] { new Coordinate { X = -25.125675, Y = 28.434342 }, new Coordinate { X = -26.232687, Y = 29.958363 }, new Coordinate { X = -24.554377, Y = 26.445767 } });
Polygon result = ls.Buffer(0.003) as Polygon;
Btw: This is an implementation of Minkowski sum.
Related
I need to pixelize / get points from closed 2d polygon. Not outline but fill it with "pixels" voxels an retrieve their positions as points.
For now I have C# code for line rasteriation, is there any method similar for polygons?
Just draw a polygon using your DrawLine(). There's no specific algorithm for drawing a polygon.
void DrawPoly(IEnumerable<Point> points)
{
endpoints = points.Skip(1).Concat(new []{points.First()});
pairs = points.Zip(endpoints, Tuple.Create);
for(var pair in pairs)
{
DrawLine(pair.Item1, pair.Item2);
}
}
void DrawLine(Point p1, Point p2)
{
// Your Bresenham code here
}
According to the edit, you want a filled polygon. If you wish to manually implement this, try one of these.
It sounds like you want a big list of all the coordinates where the polygon is filled. There is likely a better way to solve the underlying problem. However, you have two options:
Store each point as you draw it. This requires you to rewrite the algorithms mentioned above.
Draw a polygon using an existing library. Then, take the resulting image and attach coordinates to the image matrix, flatten it into a 1D list, and then filter out the non-black values:
/* (0,0), (0,1), ..., (w,h) */
grid = Enumerable.Range(0, width)
.SelectMany(x => Enumerable.Range(0, height)
.Select(y => new Point(x, y)));
flattened = image.SelectMany(p => p)
.Zip(grid, (a,b) => new {PixelValue = a, Coordinate = b});
filledPoints = flattened.Where(p => p.PixelValue == 0)
.Select(p => p.Coordinate);
How can I connect this thing with matrix to stored filled pixels?
public static List<Tuple<int, int>> PixelizePolygon(PaintEventArgs e)
{
List<Tuple<int,int>> pixels = new List<Tuple<int, int>>();
// Create solid brush.
SolidBrush blueBrush = new SolidBrush(Color.Blue);
// Create points that define polygon.
PointF point1 = new PointF(50.0F, 50.0F);
PointF point2 = new PointF(100.0F, 25.0F);
PointF point3 = new PointF(200.0F, 5.0F);
PointF point4 = new PointF(250.0F, 50.0F);
PointF point5 = new PointF(300.0F, 100.0F);
PointF point6 = new PointF(350.0F, 200.0F);
PointF point7 = new PointF(250.0F, 250.0F);
PointF[] curvePoints = { point1, point2, point3, point4, point5, point6, point7 };
// Define fill mode.
FillMode newFillMode = FillMode.Winding;
// Fill polygon to screen.
e.Graphics.FillPolygon(blueBrush, curvePoints, newFillMode);
return pixels;
}
I am trying to union a multipolygon but it is not working well as shown in the picture below:
The code I am using is:
using ClipperLib;
using Polygon = System.Collections.Generic.List<ClipperLib.IntPoint>;
using Polygons = System.Collections.Generic.List<System.Collections.Generic.List<ClipperLib.IntPoint>>;
namespace Ylp.ComputationalGeometry
{
public static class Merge
{
public static IList<IList<double>> Multipolygon(IList<IList<IList<IList<double>>>> multiPolygon)
{
const double precisionFactor = 1000000000000000.0;
//precondition: all your polygons have the same orientation
//(ie either clockwise or counter clockwise)
Polygons polys = new Polygons();
multiPolygon.ForEach(x =>
{
Polygon polygon = x.First().Select( y => new IntPoint()
{
X = (long)(y[0] * precisionFactor),
Y = (long)(y[1] * precisionFactor)
}).ToList();
polys.Add(polygon);
});
Polygons solution = new Polygons();
Clipper c = new Clipper();
c.AddPaths(polys, PolyType.ptSubject,true);
c.Execute(ClipType.ctUnion, solution,
PolyFillType.pftNonZero, PolyFillType.pftNonZero);
var coordinates = solution.SelectMany(x => x.Select(y=> (IList<double>)new List<double>()
{
y.X / precisionFactor,
y.Y / precisionFactor
}).ToList()) .ToList();
return coordinates;
}
}
}
What I really want the oucome of the union to look like is something like the following, so any intersections in the middle should be ignored and an area covering both shapes should be created:
and the original shape is:
I have a certain point such as fixedPoint = (142, 12). And I want to create n (x, y) points randomly. Then I want to calculate closest distance from fixedPoint to other (x,y) point in C#.
Is there a tutorial that explains how to get this done? Or any sample code?
This is just looping through the points and using the Pythagorean theorem to find distance, and keeping track of the best match.
Basically (pseudocode - it's been awhile since I've worked in C#):
struct Point
{
public int x;
public int y;
};
var fixed = new Point(142,42);
const int numberOfPoints = 20; // arbitrary number
List<Point> points = new List<Point>(numberOfPoints);
var random = new Random();
for(int i = 0; i < numberOfPoints; ++i)
{
int x = random.Next(-200,200);
int y = random.Next(-200,200);
points.Add(new Point(x,y));
}
Point closestPoint = null;
float closestDistanceSquared = float.Max;
/// find closest point to fixed
foreach(var point in points)
{
var distanceSquared = Math.Pow(point.x - fixed.x,2) + Math.Pow(point.y - fixed.y,2);
if (distanceSquared < closestDistanceSquared)
{
closestDistanceSquared = distanceSquared;
closestPoint = point;
}
}
/// closestPoint is now a reference to the closest to fixedPoint
/// distance between the two is Math.Sqrt(distanceSquared)
I use the squared distance rather than taking the square root within the loop since it's slightly more efficient, and sufficient for distance comparisons.
It's worth noting that you could do this while generating the points rather than having a separate loop.
I'm stucked on plotting a surface in ILSurface.
The scenario is the following:
Plot an irregular grid:
float[] x = new float[sizeX]; // filled with timestamps
float[] y = new float[sizeY]; // filled with values
float[,] z = new float[sizeX, sizeY]; // filled with values mapped by [x,y]
ILInArray<float> inX = ILMath.array(x);
ILInArray<float> inY = ILMath.array(y);
ILInArray<float> inZ = ILMath.meshgrid(inX * inY, inX, inY);
// how do i fill the inZ with z[,]?
ILRetArray<float> outMesh = ILMath.meshgrid(inX, inY, inZ, null, null);
plotCube.Add(new ILSurface(outMesh, null, null, null, null));
// plotCube already attached to the scene, and the scene with the ILPanel
ilPanel1.Refresh();
Want to map this in a array so it can be plot on a ILSurface.
I've tried out some ILMath.meshgrid to fill the ILInArray<double> ZXYArray with no success.
Hope i've been clear. Any help welcome.
Thanks.
Here comes a simple example how to plot a 3D surface with ILNumerics and provide custom X and Y ranges. It expects the regular setup of a Windows.Forms application with ILNumerics:
private void ilPanel1_Load(object sender, EventArgs e) {
// define X and Y range
ILArray<float> X = ILMath.vec<float>(-10.0, 0.1, 10.0);
ILArray<float> Y = ILMath.vec<float>(-6.0, 0.1, 6.0);
// compute X and Y coordinates for every grid point
ILArray<float> YMat = 1; // provide YMat as output to meshgrid
ILArray<float> XMat = ILMath.meshgrid(X, Y, YMat); // only need mesh for 2D function here
// preallocate data array for ILSurface: X by Y by 3
// Note the order: 3 matrix slices of X by Y each, for Z,X,Y coordinates of every grid point
ILArray<float> A = ILMath.zeros<float>(Y.Length, X.Length, 3);
// fill in Z values (replace this with your own function / data!!)
A[":;:;0"] = ILMath.sin(XMat) * ILMath.sin(YMat) * ILMath.exp(-ILMath.abs(XMat * YMat) / 5);
A[":;:;1"] = XMat; // X coordinates for every grid point
A[":;:;2"] = YMat; // Y coordinates for every grid point
// setup the scene + plot cube + surface
ilPanel1.Scene = new ILScene() {
new ILPlotCube(twoDMode: false) {
new ILSurface(A) {
UseLighting = true,
Children = { new ILColorbar() }
}
}
};
}
It produces the following result:
Here is the same example as interactive Web Component.
Note the order the grid point coordinates are defined. See the documentation here: http://ilnumerics.net/surface-plots.html
I'm currently playing around with the ILNumerics API and started to plot a few points in a cube.
Then I calculated a regression plane right through those points.
Now I'd like to plot the plane in the same scene plot but only with the same size than the point cloud.
I got the paramters of the plane (a,b,c): f(x,y) = a*x + b*y + c;
I know that only z is interesting for plotting a plane but I've got no clue how pass the right coodinates to the scene so that the plane size is about the same size than the maximum and minimum area of the points.
Could you guys give me a simple example of plotting a plane and a little suggetion how to set the bounds of that plane right?
Here is what I got so far:
private void ilPanel1_Load(object sender, EventArgs e)
{
// get the X and Y bounds and calculate Z with parameters
// plot it!
var scene = new ILScene {
new ILPlotCube(twoDMode: false) {
new ILSurface( ??? ) {
}
}
};
// view angle etc
scene.First<ILPlotCube>().Rotation = Matrix4.Rotation(
new Vector3(1f, 0.23f, 1), 0.7f);
ilPanel1.Scene = scene;
}
I hope that someone can help me ...
Thanks in advance !!!
You could take the Limits of the plotcube.Plots group and derive the coords from the bounding box from it. This gives you the min and max x and y coord for the plane. Use them to get the corresponding z values by evaluating you plane equation.
Once you have x,y and z of the plane, use them with ILSurface to plot the plane.
If you need more help, I can try to add an example.
#Edit: the following Example plots a plane through 3 arbitrary points. The planes orientation and position is computed by help of a plane function zEval. Its coefficients a,b,c are computed here from the 3 (concrete) points. You will have to compute your own equation coefficients here.
The plane is realized with a surface. One might as well take the 4 coords computed in 'P' and use an ILTriangleFan and an ILLineStrip to create the plane and the border. But the surface already comes with a Fill and a Wireframe, so we take this as a quick solution.
private void ilPanel1_Load(object sender, EventArgs e) {
// 3 arbitrary points
float[,] A = new float[3, 3] {
{ 1.0f, 2.0f, 3.0f },
{ 2.0f, 2.0f, 4.0f },
{ 2.0f, -2.0f, 2.0f }
};
// construct a new plotcube and plot the points
var scene = new ILScene {
new ILPlotCube(twoDMode: false) {
new ILPoints {
Positions = A,
Size = 4,
}
}
};
// Plane equation: this is derived from the concrete example points. In your
// real world app you will have to adopt the weights a,b and c to your points.
Func<float, float, float> zEval = (x, y) => {
float a = 1, b = 0.5f, c = 1;
return a * x + b * y + c;
};
// find bounding box of the plot contents
scene.Configure();
var limits = scene.First<ILPlotCube>().Plots.Limits;
// Construct the surface / plane to draw
// The 'plane' will be a surface constructed from a 2x2 mesh only.
// The x/y coordinates of the corners / grid points of the surface are taken from
// the limits of the plots /points. The corresponding Z coordinates are computed
// by the zEval function. So we give the ILSurface constructor not only Z coordinates
// as 2x2 matrix - but an Z,X,Y Array of size 2x2x3
ILArray<float> P = ILMath.zeros<float>(2, 2, 3);
Vector3 min = limits.Min, max = limits.Max;
P[":;:;1"] = new float[,] { { min.X, min.X }, { max.X, max.X } };
P[":;:;2"] = new float[,] { { max.Y, min.Y }, { max.Y, min.Y } };
P[":;:;0"] = new float[,] {
{ zEval(min.X, max.Y) , zEval(min.X, min.Y) },
{ zEval(max.X, max.Y) , zEval(max.X, min.Y) },
};
// create the surface, make it semitransparent and modify the colormap
scene.First<ILPlotCube>().Add(new ILSurface(P) {
Alpha = 0.6f,
Colormap = Colormaps.Prism
});
// give the scene to the panel
ilPanel1.Scene = scene;
}
This would create an image similar to this one:
#Edit2: you asked, how to disable the automatic scaling of the plot cube while adding the surface:
// before adding the surface:
var plotCube = scene.First<ILPlotCube>();
plotCube.AutoScaleOnAdd = false;
Alternatively, you can set the limits of the cube manually:
plotCube.Limits.Set(min,max);
You probably will want to disable some mouse interaction as well, since they would allow the user to rescale the cube in a similar (unwanted?) way:
plotCube.AllowZoom = false; // disables the mouse wheel zoom
plotCube.MouseDoubleClick += (_,arg) => {
arg.Cancel = true; // disable the double click - resetting for the plot cube
};