/// <summary> /// Generate the 3D model and assign it to the Geometry property /// </summary> /// <param name="points">a list of points representing a polygon</param> /// <param name="triangles">a list of triangles representing the polygon shape</param> /// <param name="scale">the scale of our object</param> /// <param name="height">the height of the model</param> private void AssembleModel(List <PointF> points, List <Triangle> triangles, float scale, float height) { var mesh = new MeshGeometry3D(); Point3DCollection pointsCollection = new Point3DCollection(); Int32Collection triangleIndices = new Int32Collection(); for (int i = 0; i < points.Count; i++) { // Add the points pointsCollection.Add(new Point3D((points[i].X / Scale /* + WorldPosition.X*/) * scale, (points[i].Y / Scale /* + WorldPosition.Y*/) * scale, 0)); pointsCollection.Add(new Point3D((points[i].X / Scale /* + WorldPosition.X*/) * scale, (points[i].Y / Scale /* + WorldPosition.Y*/) * scale, height)); var previousBottomIndex = CustomTriangulator.CirculateIndex(i - 1, points.Count) * 2; var previousTopIndex = previousBottomIndex + 1; var currentBottomIndex = i * 2; var currentTopIndex = i * 2 + 1; //Add the triangles triangleIndices.Add(currentTopIndex); triangleIndices.Add(currentBottomIndex); triangleIndices.Add(previousBottomIndex); triangleIndices.Add(previousTopIndex); triangleIndices.Add(currentTopIndex); triangleIndices.Add(previousBottomIndex); } for (int i = 0; i < triangles.Count; i++) { var p1Index = points.IndexOf(triangles[i].Point1) * 2; var p2Index = points.IndexOf(triangles[i].MiddlePoint) * 2; var p3Index = points.IndexOf(triangles[i].Point2) * 2; // Bottom Face triangle triangleIndices.Add(p1Index); triangleIndices.Add(p2Index); triangleIndices.Add(p3Index); // Top Face triangle triangleIndices.Add(p3Index + 1); triangleIndices.Add(p2Index + 1); triangleIndices.Add(p1Index + 1); } mesh.Positions = pointsCollection; mesh.TriangleIndices = triangleIndices; // Add children on the main thread, otherwise wpf throws an error dispatcher.Invoke(() => { Area.Children.Add(new GeometryModel3D { Geometry = mesh }); }); }
/// <summary> /// Interpret the MultiPolygon object and start processing it to create a 3D model /// </summary> /// <param name="multiPolygon">the multipolygon to process</param> /// <param name="scale">the scale of the 3d object</param> /// <param name="height">the height of the model</param> private void GenerateModelFromMultiPolygon(MultiPolygon multiPolygon, float scale, float height) { int side = 400; float maxX = 0; float maxY = 0; float minX = float.MaxValue; float minY = float.MaxValue; // Get the extreme points, these will be used for normalization foreach (Polygon p in multiPolygon.Coordinates) { foreach (LineString lstring in p.Coordinates) { var maxX2 = lstring.Coordinates.Select(position => GetPointFromCoordinates(side, side, position).X).Max(); var maxY2 = lstring.Coordinates.Select(position => GetPointFromCoordinates(side, side, position).Y).Max(); var minX2 = lstring.Coordinates.Select(position => GetPointFromCoordinates(side, side, position).X).Min(); var minY2 = lstring.Coordinates.Select(position => GetPointFromCoordinates(side, side, position).Y).Min(); if (maxX2 > maxX) { maxX = maxX2; } if (maxY2 > maxY) { maxY = maxY2; } if (minX2 < minX) { minX = minX2; } if (minY2 < minY) { minY = minY2; } } } WorldPosition = new PointF(minX, minY); var o = multiPolygon.Coordinates.OrderBy(coords => coords.Coordinates.Select(coordinate => coordinate.Coordinates.Count).Max()); for (int i = 0; i < o.Count(); i++) { System.Diagnostics.Debug.WriteLine("Holecount: " + (o.ElementAt(i).Coordinates.Count - 1)); List <List <PointF> > normalizedPolygon = new List <List <PointF> >(); foreach (LineString lstring in o.ElementAt(i).Coordinates) { List <PointF> points = new List <PointF>(); // Loop over the points, rescale them and add them to the list foreach (IPosition point in lstring.Coordinates) { var xy = GetPointFromCoordinates(side, side, point); var normalizedPoint = NormalizePoint(xy, minX, minY, maxX, maxY, side); points.Add(normalizedPoint); } // Use Peucker to eliminate points List <PointF> scarcePoints = EliminatePoints(points); // The algorithm requires the polygon to be in the first index and all holes to follow, we assume that the longest sequence of points is the polygon and all others are holes if (lstring.Coordinates.Count == o.ElementAt(i).Coordinates.Select(coordinate => coordinate.Coordinates.Count).Max()) { normalizedPolygon.Insert(0, scarcePoints); } else { normalizedPolygon.Add(scarcePoints); } } // Create a single polygon of the polygon and all it's holes List <PointF> compositePolygon = CustomHoleTriangulator.ConstructPolygon(normalizedPolygon); // Triangulate the polygon List <Triangle> triangles = CustomTriangulator.Triangulate(compositePolygon); // Generate the model AssembleModel(compositePolygon, triangles, scale, height); } }