Concentric offset on polygon - c#

I am using the Angus Johnson's Clipper Library in .NET/C# and I would like to do several concentric offset on a Polygon with the ClipperOffset class. (http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/_Body.htm).
I want to fill my polygon like the kangaroo (from Angus Johnson's Homepage).
I have written a function to do that, but it needs a big calculation time if I call it 10 times for example :
static public Polygon doOffset(Polygon p, double offset_nm)
{
// Offset to grow up the forbidden polygon
Polygons solution = new Polygons();
ClipperOffset co = new ClipperOffset();
co.AddPath(p, JoinType.jtRound, EndType.etClosedPolygon);
co.Execute(ref solution, offset_nm);
return solution[0];
}
Do you know a more efficient way to do that ? Thanks.
Kangaroo

Related

Map projection with System.Drawing.Graphics

I have a set of geo coordinates as latitude/longitude pairs that I wish to project onto a 2D surface. Some of the coordinates are connected by lines forming a shape/outline/polygon.
I understand how to project individual points using one of the many available map projections like Mercator and then drawing them with Graphics.DrawArc but how do I go about projecting the connecting lines between them? I can't just project the two defining coordinates of a line and draw it Graphics.DrawLine because every single point on that line has to be projected as well, right? I don't know much about these things, so I hope you understand what I mean.
Is it even possible to do what I'm trying to do using just the methods provided by the System.Drawing.Graphics class? Can I do this with a projection matrix? If anyone could explain a bit how I would go about doing this, I would greatly appreciate it. Thanks.
If your are only drawing lines it is probably easiest just to subdivide the lines into short segments, project each segment point and draw straight lines between them. This might not be the most performant or exact way to do it. But it should be fairly easy to implement.
For example:
public static void DrawSubdivided(this Graphics g, Pen pen, Vector2 p1, Vector2 p2, float subdivisionLength)
{
var d = p2 - p1;
var length = d.Length();
// Add check for zero-length lines
var dn = d / length;
var points = new List<Vector2>();
for (float l = 0; l < length; l += subdivisionLength)
{
points.Add( p1 + dn * l);
}
points.Add(p2);
// apply transformation to the points
g.DrawLines(pen, points.Select(p => new PointF(p.X, p.Y)).ToArray());
}
This uses System.Numerics.Vector2 since it has reasonable arithmetic operations defined, in contrast to PointF.

Find corners/edges on a shape (minimum vertices that can define that shape)

I'm trying to get the corners of the following shape:
By corners I mean this (red dots):
The minimum quantity of points that can define this shape.
And I have implemented the following:
public Shape Optimize()
{
// If the vertices are null or empty this can't be executed
if (vertices.IsNullOrEmpty())
return this; // In this case, return the same instance.
if (!edges.IsNullOrEmpty())
edges = null; //Reset edges, because a recalculation was requested
// The corners available on each iteration
var corners = new Point[] { Point.upperLeft, Point.upperRight, Point.downLeft, Point.downRight };
//The idea is to know if any of the following or previous vertice is inside of the the array from upside, if it is true then we can add it.
Point[] vs = vertices.ToArray();
for (int i = 0; i < vertices.Count - 1; ++i)
{
Point backPos = i > 0 ? vs[i - 1] : vs[vertices.Count - 1],
curPos = vs[i], //Punto actual
nextPos = i < vertices.Count - 1 ? vs[i + 1] : vs[0];
// We get the difference point between the actual point and the back & next point
Point backDiff = backPos - curPos,
nextDiff = nextPos - curPos,
totalDiff = nextPos - backPos;
if (corners.Contains(backDiff) || corners.Contains(nextDiff) || corners.Contains(totalDiff))
AddEdge(curPos, center); // If any of the two points are defined in the corners of the point of before or after it means that the actual vertice is a edge/corner
}
return this;
}
This works rectangled shapes, but rotated shapes are very sharp, so, this code doesn't work well:
Blue pixels (in this photo and the following) are the vertices variable processed on Optimize method.
Green pixels are the detected corners/edges (on both photos).
But sharpness in a shape only defines the side inclination, so what can I do to improve this?
Also, I have tested Accord.NET BaseCornersDetector inherited classes, but the best result is obtained with HarrisCornersDetector, but:
Many edges/corners are innecesary, and they aren't in the needed place (see first photo).
Well, after hours of research I found a library called Simplify.NET that internally runs the Ramer–Douglas–Peucker algorithm.
Also, you maybe interested on the Bresenham algorithm, with this algorithm you can draw a line using two Points.
With this algorithm, you can check if your tolerance is too high, comparing the actual points and the points that this algorithm outputs and making some kind of percentage calculator of similarity.
Finally, is interesting to mention Concave Hull and Convex Hull algorithms.
All this is related to Unity3D.
My outputs:
And my implementation.
It's very important to say, that points needs to be sorted forcing them to be connected. If the shape is concave as you can see on the second photo maybe you need to iterate walls of the shape.
You can see an example of implementation here. Thanks to #Bunny83.

Accord.Net Get equation of the SVM model

I have been testing the sample Kernel Support Vector Machines for regression problems and I would like to know how do you get the equation of the model.
For example, if the machine is created using a polynomial kernel (degree = 1), how do you get the line equation (mx + b) of this model. Is there any method in the SupportVectorMachine Class to get the model equation? or is there any way to calculate the parameters of the equation from the variables obtained after the machine is created.
Thanks in advance.
Looks like you can use this method below:
ToWeights(), which
Converts a Linear-kernel machine into an array of linear coefficients.
The first position in the array is the Threshold value.
So in your language, the first position in the array is the bias b and the rest are your linear coefficients m.
I got weird coefficients from ToWeights() when using SequentialMinimalOptimization() from which I couldn't derive the hyperplane equation. Using LinearCoordinateDescent() yielded usable coefficients for the model, however, in the form of [a,b,c...] which could be plugged in as 0 = a + bx + cy + ...
Hope that helps!
As #zrolfs noted, if you are using Accord.NET with Sequential Minimal Optimization, the ToWeights() function does not currently return relevant coefficients for the decision function. Nevertheless, you can calculate these coefficients directly. In order to do so, multiply the SVM weights vector by the matrix of support vectors, like so:
double[] DecisionFunctionCoefficients = new double[dwTotalFeatures];
for (int iFeature = 0; iFeature < dwTotalFeatures; iFeature++) {
for (int iVector = 0; iVector < SVM.SupportVectors.Length; iVector++) {
DecisionFunctionCoefficients[iFeature] += (SVM.SupportVectors[iVector][iFeature] * SVM.Weights[iVector]);
}
}

How to get the center point of a Face or a PlanarFace element in Revit

I'm doing a Revit Macro to get the center point of a part (floor part) to check if it is inside a room or a space.
I couldn't get much of the BoundingBox object which is giving me a point outside the part, so I tried to use the Geometry element internal faces getting the mesh vertices but I'm stuck calculating the mid point.
I'm using a rather naive algorithm shown in the snippet below, but it's giving me false results as it seems to be affected by the initial default of min/max variables.
Any suggestions?
PS: DebugTools is a custom helper class of my own.
public void ZoneDetect()
{
Document doc = this.ActiveUIDocument.Document;
using (Transaction t = new Transaction(doc,"Set Rooms By Region"))
{
t.Start();
FilteredElementCollector fec =
new FilteredElementCollector(doc)
.OfClass(typeof(Part))
.OfCategory(BuiltInCategory.OST_Parts)
.Cast<Part>();
foreach (Part p in fec)
{
Options op = new Options();
op.View=doc.ActiveView;
op.ComputeReferences=true;
GeometryElement gm=p.get_Geometry(op);
Solid so = gm.First() as Solid;
PlanarFace fc=so.Faces.get_Item(0) as PlanarFace;
foreach (PlanarFace f in so.Faces)
{
if (f.Normal == new XYZ(0,0,-1)) fc=f;
}
XYZ max = new XYZ();
XYZ min = new XYZ();
int no = 0;
foreach (XYZ vx in fc.Triangulate().Vertices)
{
// Just for debugging
DebugTools.DrawModelTick(vx,doc,"Max");
doc.Regenerate();
TaskDialog.Show("Point:"+no.ToString(),vx.ToString());
no++;
//Comparing points
if (vx.X>max.X) max=new XYZ (vx.X,max.Y,0);
if (vx.Y>max.Y) max=new XYZ (max.X,vx.Y,0);
if (vx.X<min.X) min=new XYZ (vx.X,min.Y,0);
if (vx.Y<min.Y) min=new XYZ (min.X,vx.Y,0);
}
XYZ mid = new XYZ(max.X-min.X,max.Y-min.Y,0);
DebugTools.DrawModelTick(mid,doc,"Mid");
DebugTools.DrawModelTick(max,doc,"Max");
DebugTools.DrawModelTick(min,doc,"Min");
}
t.Commit();
}
}
It seems like you're looking for the center of gravity of a polygon. An algorithm for that can be found here: Center of gravity of a polygon
Once you have a Face object, you can enumerate its edges to receive a list of vertex points. Use the longest of the EdgeLoops in the face. Collect all the points and make sure that they are in the right order (the start and end points of the edges might need to be swapped).
Daren & Matt thanks a lot for your answers,
Since I'm dealing with rather simple shapes ( mainly rectangles ) I just needed to get a point roughly near the center to test whether it is inside a room, my problem was with the naive algorithm I was using which turned out to be wrong.
I corrected it as follows:
XYZ midSum = Max + Min;
XYZ mid = new XYZ(midSum.X/2 , midSum.Y/2,0);
I will look into refining it using the link you've provided, but as for now I will get into finishing my task in hand.
Many thanks

Converting Lat / long to PointClass

IPoint pPoint = new ESRI.ArcGIS.Geometry.PointClass();
pPoint.PutCoords(-92.96000, 44.9227); //This should be near Minneapolis
mapControl.CenterAt(pPoint); //mapControl is a AxMapControl
When I run this code the point always ends up near Kansas. Can anyone help me convert lat / longs to an PointClass that will work properly?
I'm using VS2010 ArcEngine 10 C#
There is a lot more to this than you have currently given. Both a lat/long point and your map have a specific spatial reference. If they do not match, it is likely your point will plot in an unexpected way.
The point you are showing is a standard Latitude/Longitude point. Which is likely Nad83 (North American), or WGS84 (World). These are Spatial References with a Geographical Coordinate System. You are likely trying to plot the point on a Projected Coordinate System.
You need to make your MapControl's Spatial Reference match the types of points you are trying to plot.
Since I do not know the Spatial Reference of your Map, I can only give you an example of translating a Lat/Lon into what the MapControl's current spatial reference is.
ISpatialReferenceFactory srFactory = new SpatialReferenceEnvironmentClass();
IGeographicCoordinateSystem gcs = srFactory.CreateGeographicCoordinateSystem((int)esriSRGeoCSType.esriSRGeoCS_WGS1984);
ISpatialReference sr1 = gcs;
IPoint point = new PointClass() as IPoint;
point.PutCoords(-92.96000, 44.9227);
IGeometry geometryShape;
geometryShape = point;
geometryShape.SpatialReference = sr1;
geometryShape.Project(mapControl.SpatialReference);
mapControl.DrawShape(geometryShape);
This takes your point and projects it to the MapControls current spatial reference, then plots the point.
Good Luck.
Here is the code to zoom and center on a lat / long, the above poster was helpful but his solution did not work for me.
mapControl.MapScale = mapControl.MapScale / 2; //for zooming
ISpatialReferenceFactory srFactory = new SpatialReferenceEnvironmentClass(); //move up top later
IGeographicCoordinateSystem gcs = srFactory.CreateGeographicCoordinateSystem((int)esriSRGeoCSType.esriSRGeoCS_WGS1984); //World lat / long format
ISpatialReference sr1 = gcs;
IPoint point = new PointClass();
point.SpatialReference = gcs;
point.PutCoords(-92.96000, 44.9227);
point.Project(mapControl.SpatialReference);
mapControl.CenterAt(point);

Categories

Resources