using System.Collections; using System.Collections.Generic; using Godot; using TriangleNet.Geometry; using TriangleNet.Meshing; using TriangleNet.Meshing.Algorithm; namespace Rokojori { public class Shape2 { public List paths = new List(); public Shape2( params Path2[] paths ) { this.paths.AddRange( paths ); } public Shape2( List paths ) { this.paths.AddRange( paths ); } Polygon _CreateTNetPolygon() { // RJLog.Log( "Creating polygon", paths.Count ); var polygon = new Polygon(); var index = 0; paths.ForEach( ( path )=> { var vertices = Lists.Map( path.points, p => new Vertex( p.X, p.Y ) ); var isHole = path.isClockwise; // RJLog.Log( "Adding contour", vertices.Count, isHole ); polygon.Add( new Contour( vertices, index ), isHole ); index ++; } ); return polygon; } public static List> ToClipperPaths( Shape2 s ) { var paths = new List>(); s.paths.ForEach( p => paths.Add( Path2.ToClipperPath( p ) ) ); return paths; } public static Shape2 FromClipperPaths( List> paths ) { var shape = new Shape2(); paths.ForEach( p => shape.paths.Add( Path2.FromClipperPath( p ) ) ); return shape; } public static Shape2 Boolean( Shape2 a, Shape2 b, Geometry2D.PolyBooleanOperation booleanOperation, bool simplify = true ) { // RJLog.Log( "Using Clipper Library" ); var clipperPathsA = ToClipperPaths( a ); var clipperPathsB = ToClipperPaths( b ); var resultPaths = new List>(); var type = ClipperLib.ClipType.ctUnion; if ( Geometry2D.PolyBooleanOperation.Difference == booleanOperation ) { type = ClipperLib.ClipType.ctDifference; } else if ( Geometry2D.PolyBooleanOperation.Intersection == booleanOperation ) { type = ClipperLib.ClipType.ctIntersection; } else if ( Geometry2D.PolyBooleanOperation.Xor == booleanOperation ) { type = ClipperLib.ClipType.ctXor; } RJLog.Log( "ShapeBool", "type: " + type, "boolOp: " + booleanOperation, "A|B >>",clipperPathsA.Count, clipperPathsB.Count ); var clipper = new ClipperLib.Clipper(); clipper.AddPaths( clipperPathsA, ClipperLib.PolyType.ptSubject, true); clipper.AddPaths( clipperPathsB, ClipperLib.PolyType.ptClip, true); clipper.Execute( type, resultPaths ); if ( simplify ) { resultPaths = ClipperLib.Clipper.SimplifyPolygons( resultPaths ); } var s = new Shape2(); resultPaths.ForEach( ( r ) => { s.paths.Add( Path2.FromClipperPath( r ) ); } ); return s; } public MeshGeometry CreateMeshGeometry() { if ( paths.Count == 1 ) { // RJLog.Log( "Only 1 path" ); return paths[ 0 ].CreateMeshGeometry(); } var polygon = _CreateTNetPolygon(); var options = new ConstraintOptions(); var quality = new QualityOptions(); var triangulator = new Dwyer(); IMesh mesh = polygon.Triangulate( options, quality, triangulator ); int vertexCount = mesh.Vertices.Count; int triangleCount = mesh.Triangles.Count; var meshGeometry = new MeshGeometry(); meshGeometry.vertices = new List( vertexCount ); meshGeometry.uvs = new List( vertexCount ); meshGeometry.indices = new List( triangleCount * 3 ); meshGeometry.normals = new List( vertexCount ); foreach ( var v in mesh.Vertices ) { var x = (float)v.x; var y = (float)v.y; meshGeometry.vertices.Add( new Vector3( x, 0, y ) ); meshGeometry.uvs.Add( new Vector2( x, y ) ); meshGeometry.normals.Add( Vector3.Up ); } foreach ( var t in mesh.Triangles ) { var vertices = t.vertices; var indicesList = new List(); for ( int i = 0; i < vertices.Length; i++ ) { meshGeometry.indices.Add( t.GetVertexID( i ) ); } } return meshGeometry; } } }