I have a polygon with vertices P1, P2,P3,.....P11. My vertex coordinate data types are double.
I also have a line between P1, P7.
I want to create a partial convex hull between P1 and P7 and keep my original polygon vertices after P7.
So final polygon will be as follow;
So far I convert the whole polygon to convex hull, delete vertices in convex hull and add hull vertices. It works fine with small polygons but it won't be easy to manage that way when vertex number increases.
I tried to look for c# algorithms available for partial convex hull but i couldn't find anything except some researches.
Any ideas?
Remove vertices P8 through P11 from your polygon and store them off, then run the convex hull conversion on what's left (P1 through P7). After, re-insert vertices P8 through P11 (between P7 and P1).
I clipped the polygon by using DotSpatial
internal static IGeometry Polygonize(IGeometry geometry)
{
var lines = LineStringExtracter.GetLines(geometry);
var polygonizer = new Polygonizer();
polygonizer.Add(lines);
var polys = polygonizer.GetPolygons();
var polyArray = GeometryFactory.ToGeometryArray(polys);
return geometry.Factory.CreateGeometryCollection(polyArray);
}
internal static IGeometry PolygonizeForClip(IGeometry geometry, IPreparedGeometry clip)
{
var lines = LineStringExtracter.GetLines(geometry);
var clippedLines = new List<IGeometry>();
foreach (ILineString line in lines)
{
if (clip.Contains(line))
clippedLines.Add(line);
}
var polygonizer = new Polygonizer();
polygonizer.Add(clippedLines);
var polys = polygonizer.GetPolygons();
var polyArray = GeometryFactory.ToGeometryArray(polys);
return geometry.Factory.CreateGeometryCollection(polyArray);
}
internal static IGeometry SplitPolygon(IGeometry polygon, IGeometry line)
{
var nodedLinework = polygon.Boundary.Union(line);
var polygons = Polygonize(nodedLinework);
// only keep polygons which are inside the input
var output = new List<IGeometry>();
for (var i = 0; i < polygons.NumGeometries; i++)
{
var candpoly = (IPolygon)polygons.GetGeometryN(i);
if (polygon.Contains(candpoly.InteriorPoint))
output.Add(candpoly);
}
return polygon.Factory.BuildGeometry(output);
}
internal static IGeometry ClipPolygon(IGeometry polygon, IPolygonal clipPolygonal)
{
var clipPolygon = (IGeometry)clipPolygonal;
var nodedLinework = polygon.Boundary.Union(clipPolygon.Boundary);
var polygons = Polygonize(nodedLinework);
// only keep polygons which are inside the input
var output = new List<IGeometry>();
for (var i = 0; i < polygons.NumGeometries; i++)
{
var candpoly = (IPolygon)polygons.GetGeometryN(i);
var interiorPoint = candpoly.InteriorPoint;
if (polygon.Contains(interiorPoint) &&
clipPolygon.Contains(interiorPoint))
output.Add(candpoly);
}
return polygon.Factory.BuildGeometry(output);
}
then
var Splitted = SplitPolygon(Polygon, Line);
Then added parts.
Related
I am trying to split a LinearRing into multiple LinearRings using multiple lines. The expected result is a collection of LinearRings (as in the attached image). Expected result is here attached in the image. (https://i.stack.imgur.com/Kuzr8.jpg) I am not understanding how to call the recursive function. Below is the code I tried.
If it was one single line which was cutting the polygon, I could have done that. But the problem here is that we have multiple linesstrings cutting the polygon, into multiple 'pieces'. Also, I know that each time if the current line is cutting and making two pieces of the current polygon, the current polygon should be removed from the final result. This seems a bit complicated for me.
The Main Function:
static void Main(string[] args)
{
Coordinate c22 = new Coordinate(2889.0, 1277.0);
Coordinate c23 = new Coordinate(2894.0, 1288.0);
Coordinate c24 = new Coordinate(2901.0, 1284.0);
Coordinate c25 = new Coordinate(2909.0, 1289.0);
Coordinate c26 = new Coordinate(2916.0, 1281.0);
Coordinate c27 = new Coordinate(2912.0, 1270.0);
Coordinate c28 = new Coordinate(2906.0, 1275.0);
Coordinate c29 = new Coordinate(2898.0, 1273.0);
LinearRing outerBoundary = new LinearRing(new Coordinate[] { c22, c23, c24, c25, c26, c27, c28, c29, c22 });
List<LineString> lines = new List<LineString>();
Coordinate p30 = new Coordinate(2892.0, 1270.0);
Coordinate p31 = new Coordinate(2900.0, 1294.0);
LineString ln1 = new LineString(new Coordinate[] { p30, p31 });
lines.Add(ln1);
Coordinate p32 = new Coordinate(2909.0, 1268.0);
Coordinate p33 = new Coordinate(2907.0, 1294.0);
LineString ln2 = new LineString(new Coordinate[] {p32, p33});
lines.Add(ln2);
Coordinate p34 = new Coordinate(2886.0, 1286.0);
Coordinate p35 = new Coordinate(2922.0, 1278.0);
LineString ln3 = new LineString(new Coordinate[] {p34, p35});
lines.Add(ln3);
Coordinate p36 = new Coordinate(2883.0, 1281.0);
Coordinate p37 = new Coordinate(2923.0, 1273.0);
LineString ln4 = new LineString(new Coordinate[] {p36, p37});
lines.Add(ln4);
Geometry polygons = CookieCutter(outerBoundary, new List<LineString>() { ln1, ln2, ln3, ln4 });
}
The Recursive function. This is where I am stuck;
public static Geometry CookieCutter(LinearRing polygon, List<LineString> cuttingEdges, Geometry pieces = null)
{
if (pieces == null) { pieces = Polygonize(polygon.Union(cuttingEdges[0])); }
foreach (var cuttingEdge in cuttingEdges)
{
for (int i = 0; i < pieces.NumGeometries; i++)
{
pieces = pieces.GetGeometryN(i);
Geometry newPieces = Polygonize(polygon.Union(cuttingEdge));
// I know somewhere here it should call CookieCutter on new pieces
// But how to...?
}
}
}
Here is the function to polygonize;
public static Geometry Polygonize(Geometry geometry)
{
var lines = LineStringExtracter.GetLines(geometry);
var polygonizer = new Polygonizer(false);
polygonizer.Add(lines);
var polys = new List<Geometry>(polygonizer.GetPolygons());
var polyArray = GeometryFactory.ToGeometryArray(polys);
return geometry.Factory.BuildGeometry(polyArray);
}
Ohh... the solution was simple. I was trying to complicate it unnecessarily. There was no need of any recursion. I resolved it like this;
public static Geometry CookieCutter(
Geometry polygon,
List<LineString> cuttingEdges
)
{
MultiLineString mls = new MultiLineString(cuttingEdges.ToArray());
var union = polygon.Union(mls);
var polygons = Polygonize(union);
return polygons;
}
I have to draw a circle with N meters diameter around some geolocation.
I could able to generate coordinates for vertically oriented ellipse only.
C#
using NetTopologySuite;
//
const int SRID = 4326;
var fact = new GeometryFactory(new PrecisionModel(), SRID);
var point = fact.CreatePoint(new Coordinate(lat, long));
var bufferParameters = new BufferParameters();
var poly = point.Buffer(0.0005, bufferParameters) as NetTopologySuite.Geometries.Polygon;
var coords = new List<BasicGeoposition>();
foreach (var cItem in poly.Coordinates)
{
coords.Add(new BasicGeoposition() { Latitude = cItem.X, Longitude = cItem.Y });
}
Please help me to understand how to set the radius and make circle instead of an ellipse.
I generate all of them the same, but some of them can't be colored (image on the bottom)
Steps:
I'm creating list of faces from solid
internal static List<List<XYZ>> GetFacesFromSolidTriangulate(Solid geomSolid)
{
List<List<XYZ>> faces = new List<List<XYZ>>();
foreach (Face face in geomSolid.Faces)
{
Mesh mesh_space = face.Triangulate();
for (int i = 0; i < mesh_space.NumTriangles; i++)
{
MeshTriangle triangle = mesh_space.get_Triangle(i);
XYZ p1 = triangle.get_Vertex(0);
XYZ p2 = triangle.get_Vertex(1);
XYZ p3 = triangle.get_Vertex(2);
List<XYZ> xyz = new List<XYZ>();
xyz.Add(triangle.get_Vertex(0));
xyz.Add(triangle.get_Vertex(1));
xyz.Add(triangle.get_Vertex(2));
faces.Add(xyz);
}
}
return faces;
}
I'm creating Direct Shape using
static public DirectShape NewDrawDirectShape(Document doc, List<List<XYZ>> faces, ElementId matId, string name)
{
TessellatedShapeBuilder builder = new TessellatedShapeBuilder();
builder.OpenConnectedFaceSet(true);
foreach(List<XYZ> face in faces)
{
builder.AddFace(new TessellatedFace(face, matId));
}
builder.CloseConnectedFaceSet();
builder.Build();
TessellatedShapeBuilderResult result = builder.GetBuildResult();
DirectShape ds = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel));
ds.SetShape(result.GetGeometricalObjects());
ds.Name = name;
return ds;
}
And here is the problem, I can't paint few of them even using Revit "paint" tool...
Red arrow define what I'm trying achieve on this direct shape, blue direct shapes works correctly
Have you checked the triangle orientations? Maybe some of the triangles returned in the MeshTriangle objects are incorrectly oriented. To try it out, you could create a little model line in the centre of each triangle indicating its normal vector and this the edge orientation.
I'm trying to get the bounds of multiple ui images inside a canvas. I'm using this code:
Bounds bounds = new Bounds(imageList[0].transform.position, Vector3.zero);
for (int i = 0; i < imageList.Count; i++)
{
bounds.Encapsulate(imageList[i].transform.position);
}
But if I have two images, the bound will start and ends in the middle of each image. This code is working when using a gameobject cube, sphere etc. but different result when using UI.
You can use RectTransform.GetWorldCorners in order to get the 4 corners of each Image in word coordinates.
Then you can iterate over them and use Vector3.Min and Vector3.Max to calculate the minimum and maximum of all corners of all images.
And finally use Bounds.SetMinMax in order to create a bounding box using this minimum and maximum.
public class Example : MonoBehaviour
{
public List<Image> imageList = new List<Image>();
private void OnDrawGizmos()
{
var min = Vector3.positiveInfinity;
var max = Vector3.negativeInfinity;
foreach (var image in imageList)
{
if(!image) continue;
// Get the 4 corners in world coordinates
var v = new Vector3[4];
image.rectTransform.GetWorldCorners(v);
// update min and max
foreach (var vector3 in v)
{
min = Vector3.Min(min, vector3);
max = Vector3.Max(max, vector3);
}
}
// create the bounds
var bounds = new Bounds();
bounds.SetMinMax(min, max);
Gizmos.color = Color.red;
Gizmos.DrawWireCube(bounds.center, bounds.size);
}
}
Note: This bounding box will be world aligned with the global XYZ axis (as was your original attempt).
Use RectTransform.GetWorldCorners to get all corners of the images.
Find the minimal and maximal points.
Use Bounds.SetMinMax to get the bounds.
Given a list of coordinates, what would be the most efficient way to determine the direction changes?
What I have currently tried:
List<Point> path = GetRoute();
List<Point> pointsToKeep = new List<Point>();
pointsToKeep.Add(path.First());
int previousX = path.First().X;
int previousY = path.First().Y;
foreach (var item in path)
{
int diffY = (item.Y - previousY);
bool up = diffY > 0;
bool down = diffY < 0;
if (up || down)
{
pointsToKeep.Add(item);
}
previousX = item.X;
previousY = item.Y;
}
Ideally would want the all the points highlighted in Orange to be added to the pointsToKeep list
As the path is going from Left to Right, the X coordinate will never decrease
You need to keep track of not just the previous point, but the direction that you took to arrive at that previous point. If you squint a little, you could say that this is analogous to why you need derivatives to find the relative minimum and relative maximum in the plot of a function f(x).
Now back to your case. Assuming your possible directions are at 0, 45 or 90 degrees, then just keeping track of two signs should be more than enough to represent all possible slopes. (If my assumption doesn't hold for your case, you need to keep track of something more detailed than just the sign.)
All that being said, you probably need something like this:
var previous = path.First();
var direction = new Point(0, 0);
foreach (var item in path)
{
var signX = Math.Sign(item.X - previous.X);
var signY = Math.Sign(item.Y - previous.Y);
if (signX != direction.X || signY != direction.Y)
{
pointsToKeep.Add(previous);
}
previous = item;
direction = new Point(signX, signY);
}
Note that if you add item to the list, you're keeping track of the first point of the new direction, while if you add previous to the list, you're keeping track of the last point of the previous direction. (I believe you'll probably want to keep previous, but see it for yourself.)
You can try this
List<Point> path = Point.GetData();
List<Point> pointsToKeep = new List<Point>();
var current = path[0];
var currentDiffX = 0;
var currentDiffY = 0;
foreach (var point in path)
{
var diffX = point.X - current.X;
var diffY = point.Y - current.Y;
if (diffX != currentDiffX || diffY != currentDiffY)
pointsToKeep.Add(current);
current = point;
currentDiffX = diffX;
currentDiffY = diffY;
}
// add the last point
pointsToKeep.Add(path[path.Count-1]);
foreach(var point in pointsToKeep)
Console.WriteLine("Ponint({0}.{1})",point.X,point.Y);
for more accurate result
-you can track the slope ( y2- y1)/(x2-x1) and once the slope changes, then add the point
here a working demo