rj-action-library/Runtime/Math/Geometry/Shape2.cs

170 lines
4.2 KiB
C#

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<Path2> paths = new List<Path2>();
public Shape2( params Path2[] paths )
{
this.paths.AddRange( paths );
}
public Shape2( List<Path2> 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<List<ClipperLib.IntPoint>> ToClipperPaths( Shape2 s )
{
var paths = new List<List<ClipperLib.IntPoint>>();
s.paths.ForEach( p => paths.Add( Path2.ToClipperPath( p ) ) );
return paths;
}
public static Shape2 FromClipperPaths( List<List<ClipperLib.IntPoint>> 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<List<ClipperLib.IntPoint>>();
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<Vector3>( vertexCount );
meshGeometry.uvs = new List<Vector2>( vertexCount );
meshGeometry.indices = new List<int>( triangleCount * 3 );
meshGeometry.normals = new List<Vector3>( 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<string>();
for ( int i = 0; i < vertices.Length; i++ )
{
meshGeometry.indices.Add( t.GetVertexID( i ) );
}
}
return meshGeometry;
}
}
}